{"version":3,"file":"http-host.mjs","names":[],"sources":["../../src/hosts/http-host.ts"],"sourcesContent":["/* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD */\n/* SPDX-License-Identifier: Apache-2.0 */\n\n/**\n * In-memory BrookesiaHost for HTTP — Node/vitest only, not used on device.\n * Import: `@brookesia/service/hosts/http`\n */\n\nimport type { BrookesiaHost } from '../common/brookesia-host.ts';\nimport { serializeRpcError, serializeRpcResult } from '../common/rpc-json.ts';\nimport { HTTP_FUNCTIONS, HTTP_SERVICE_NAME } from '../http/http-client.ts';\nimport type {\n  HttpRequest,\n  HttpRequestState,\n  HttpRequestStateInfo,\n  HttpResponse,\n  HttpStatistics,\n} from '../http/http-types.ts';\n\ntype HttpRequestRecord = {\n  request: HttpRequest;\n  state: HttpRequestState;\n  response: HttpResponse;\n};\n\ntype HttpMockState = {\n  nextRequestId: number;\n  requests: Map<number, HttpRequestRecord>;\n  statistics: HttpStatistics;\n};\n\nlet nextHandle = 1;\nconst handles = new Map<number, HttpMockState>();\n\nfunction defaultState(): HttpMockState {\n  return {\n    nextRequestId: 1,\n    requests: new Map(),\n    statistics: {\n      submitted_count: 0,\n      completed_count: 0,\n      failed_count: 0,\n      canceled_count: 0,\n    },\n  };\n}\n\nfunction getState(handle: number): HttpMockState {\n  const state = handles.get(handle);\n  if (!state) {\n    throw new Error(`HTTP memory host: invalid handle ${handle}`);\n  }\n  return state;\n}\n\nfunction buildResponse(requestId: number, req: HttpRequest): HttpResponse {\n  return {\n    request_id: requestId,\n    status_code: 200,\n    body: JSON.stringify({ url: req.url, method: req.method ?? 'Get', ok: true }),\n    file_path: '',\n    error: 'Ok',\n    error_message: '',\n  };\n}\n\nfunction toStateInfo(record: HttpRequestRecord): HttpRequestStateInfo {\n  return {\n    request_id: record.response.request_id ?? 0,\n    state: record.state,\n    error: record.response.error ?? 'Ok',\n    status_code: record.response.status_code ?? 0,\n    downloaded: record.response.body?.length ?? 0,\n    total: record.response.body?.length ?? 0,\n  };\n}\n\nexport type HttpHostOptions = {\n  /** Fixed response status code for mock requests (default 200). */\n  statusCode?: number;\n};\n\nexport function createHttpHost(options?: HttpHostOptions): BrookesiaHost {\n  const statusCode = options?.statusCode ?? 200;\n\n  return {\n    service_available(name: string): boolean {\n      return name === HTTP_SERVICE_NAME;\n    },\n\n    start_service(name: string): number {\n      if (name !== HTTP_SERVICE_NAME) {\n        throw new Error(`HTTP memory host: unknown service ${name}`);\n      }\n      const handle = nextHandle++;\n      handles.set(handle, defaultState());\n      return handle;\n    },\n\n    stop_service(handle: number): void {\n      handles.delete(handle);\n    },\n\n    call_function(handle: number, functionName: string, paramsJson: string): string {\n      const state = getState(handle);\n      const params = JSON.parse(paramsJson) as Record<string, unknown>;\n\n      try {\n        if (functionName === HTTP_FUNCTIONS.Request) {\n          const req = params.Request as HttpRequest;\n          const requestId = state.nextRequestId++;\n          const response = { ...buildResponse(requestId, req), status_code: statusCode };\n          state.statistics.submitted_count += 1;\n          state.statistics.completed_count += 1;\n          state.requests.set(requestId, { request: req, state: 'Completed', response });\n          return serializeRpcResult(response);\n        }\n\n        if (functionName === HTTP_FUNCTIONS.RequestAsync) {\n          const req = params.Request as HttpRequest;\n          const requestId = state.nextRequestId++;\n          const response = { ...buildResponse(requestId, req), status_code: statusCode };\n          state.statistics.submitted_count += 1;\n          state.statistics.completed_count += 1;\n          state.requests.set(requestId, { request: req, state: 'Completed', response });\n          return serializeRpcResult(requestId);\n        }\n\n        if (functionName === HTTP_FUNCTIONS.CancelRequest) {\n          const requestId = Number(params.RequestId);\n          const record = state.requests.get(requestId);\n          if (record) {\n            record.state = 'Canceled';\n            record.response.error = 'Canceled';\n            state.statistics.canceled_count += 1;\n          }\n          return serializeRpcResult();\n        }\n\n        if (functionName === HTTP_FUNCTIONS.GetRequestState) {\n          const requestId = Number(params.RequestId);\n          const record = state.requests.get(requestId);\n          if (!record) {\n            return serializeRpcError(`unknown request id ${requestId}`);\n          }\n          return serializeRpcResult(toStateInfo(record));\n        }\n\n        if (functionName === HTTP_FUNCTIONS.ResetStatistics) {\n          state.statistics = {\n            submitted_count: 0,\n            completed_count: 0,\n            failed_count: 0,\n            canceled_count: 0,\n          };\n          return serializeRpcResult();\n        }\n\n        return serializeRpcError(`unknown function ${functionName}`);\n      } catch (err) {\n        const message = err instanceof Error ? err.message : String(err);\n        return serializeRpcError(message);\n      }\n    },\n  };\n}\n\nexport function resetHttpHostState(): void {\n  handles.clear();\n  nextHandle = 1;\n}\n"],"mappings":";;;AA+BA,IAAI,aAAa;AACjB,MAAM,0BAAU,IAAI,IAA2B;AAE/C,SAAS,eAA8B;CACrC,OAAO;EACL,eAAe;EACf,0BAAU,IAAI,IAAI;EAClB,YAAY;GACV,iBAAiB;GACjB,iBAAiB;GACjB,cAAc;GACd,gBAAgB;EAClB;CACF;AACF;AAEA,SAAS,SAAS,QAA+B;CAC/C,MAAM,QAAQ,QAAQ,IAAI,MAAM;CAChC,IAAI,CAAC,OACH,MAAM,IAAI,MAAM,oCAAoC,QAAQ;CAE9D,OAAO;AACT;AAEA,SAAS,cAAc,WAAmB,KAAgC;CACxE,OAAO;EACL,YAAY;EACZ,aAAa;EACb,MAAM,KAAK,UAAU;GAAE,KAAK,IAAI;GAAK,QAAQ,IAAI,UAAU;GAAO,IAAI;EAAK,CAAC;EAC5E,WAAW;EACX,OAAO;EACP,eAAe;CACjB;AACF;AAEA,SAAS,YAAY,QAAiD;CACpE,OAAO;EACL,YAAY,OAAO,SAAS,cAAc;EAC1C,OAAO,OAAO;EACd,OAAO,OAAO,SAAS,SAAS;EAChC,aAAa,OAAO,SAAS,eAAe;EAC5C,YAAY,OAAO,SAAS,MAAM,UAAU;EAC5C,OAAO,OAAO,SAAS,MAAM,UAAU;CACzC;AACF;AAOA,SAAgB,eAAe,SAA0C;CACvE,MAAM,aAAa,SAAS,cAAc;CAE1C,OAAO;EACL,kBAAkB,MAAuB;GACvC,OAAO,SAAS;EAClB;EAEA,cAAc,MAAsB;GAClC,IAAI,SAAA,QACF,MAAM,IAAI,MAAM,qCAAqC,MAAM;GAE7D,MAAM,SAAS;GACf,QAAQ,IAAI,QAAQ,aAAa,CAAC;GAClC,OAAO;EACT;EAEA,aAAa,QAAsB;GACjC,QAAQ,OAAO,MAAM;EACvB;EAEA,cAAc,QAAgB,cAAsB,YAA4B;GAC9E,MAAM,QAAQ,SAAS,MAAM;GAC7B,MAAM,SAAS,KAAK,MAAM,UAAU;GAEpC,IAAI;IACF,IAAI,iBAAiB,eAAe,SAAS;KAC3C,MAAM,MAAM,OAAO;KACnB,MAAM,YAAY,MAAM;KACxB,MAAM,WAAW;MAAE,GAAG,cAAc,WAAW,GAAG;MAAG,aAAa;KAAW;KAC7E,MAAM,WAAW,mBAAmB;KACpC,MAAM,WAAW,mBAAmB;KACpC,MAAM,SAAS,IAAI,WAAW;MAAE,SAAS;MAAK,OAAO;MAAa;KAAS,CAAC;KAC5E,OAAO,mBAAmB,QAAQ;IACpC;IAEA,IAAI,iBAAiB,eAAe,cAAc;KAChD,MAAM,MAAM,OAAO;KACnB,MAAM,YAAY,MAAM;KACxB,MAAM,WAAW;MAAE,GAAG,cAAc,WAAW,GAAG;MAAG,aAAa;KAAW;KAC7E,MAAM,WAAW,mBAAmB;KACpC,MAAM,WAAW,mBAAmB;KACpC,MAAM,SAAS,IAAI,WAAW;MAAE,SAAS;MAAK,OAAO;MAAa;KAAS,CAAC;KAC5E,OAAO,mBAAmB,SAAS;IACrC;IAEA,IAAI,iBAAiB,eAAe,eAAe;KACjD,MAAM,YAAY,OAAO,OAAO,SAAS;KACzC,MAAM,SAAS,MAAM,SAAS,IAAI,SAAS;KAC3C,IAAI,QAAQ;MACV,OAAO,QAAQ;MACf,OAAO,SAAS,QAAQ;MACxB,MAAM,WAAW,kBAAkB;KACrC;KACA,OAAO,mBAAmB;IAC5B;IAEA,IAAI,iBAAiB,eAAe,iBAAiB;KACnD,MAAM,YAAY,OAAO,OAAO,SAAS;KACzC,MAAM,SAAS,MAAM,SAAS,IAAI,SAAS;KAC3C,IAAI,CAAC,QACH,OAAO,kBAAkB,sBAAsB,WAAW;KAE5D,OAAO,mBAAmB,YAAY,MAAM,CAAC;IAC/C;IAEA,IAAI,iBAAiB,eAAe,iBAAiB;KACnD,MAAM,aAAa;MACjB,iBAAiB;MACjB,iBAAiB;MACjB,cAAc;MACd,gBAAgB;KAClB;KACA,OAAO,mBAAmB;IAC5B;IAEA,OAAO,kBAAkB,oBAAoB,cAAc;GAC7D,SAAS,KAAK;IAEZ,OAAO,kBADS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAC/B;GAClC;EACF;CACF;AACF;AAEA,SAAgB,qBAA2B;CACzC,QAAQ,MAAM;CACd,aAAa;AACf"}