{"version":3,"file":"nes-host.mjs","names":[],"sources":["../../src/hosts/nes-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 NES — Node/vitest only, not used on device.\n * Import: `@brookesia/service/hosts/nes`\n */\n\nimport type { BrookesiaHost } from '../common/brookesia-host.ts';\nimport { serializeRpcError, serializeRpcResult } from '../common/rpc-json.ts';\nimport { NES_FUNCTIONS, NES_SERVICE_NAME } from '../nes/nes-client.ts';\nimport type { NesConfig, NesGamepadState, NesState } from '../nes/nes-types.ts';\n\ntype NesMockState = {\n  state: NesState;\n  config: NesConfig | null;\n  gamepad: NesGamepadState;\n  saveCount: number;\n};\n\nlet nextHandle = 1;\nconst handles = new Map<number, NesMockState>();\n\nfunction defaultState(): NesMockState {\n  return {\n    state: 'Idle',\n    config: null,\n    gamepad: {},\n    saveCount: 0,\n  };\n}\n\nfunction getState(handle: number): NesMockState {\n  const state = handles.get(handle);\n  if (!state) {\n    throw new Error(`NES memory host: invalid handle ${handle}`);\n  }\n  return state;\n}\n\nexport function createNesHost(initial?: Partial<NesMockState>): BrookesiaHost {\n  const seed = { ...defaultState(), ...initial };\n\n  return {\n    service_available(name: string): boolean {\n      return name === NES_SERVICE_NAME;\n    },\n\n    start_service(name: string): number {\n      if (name !== NES_SERVICE_NAME) {\n        throw new Error(`NES memory host: unknown service ${name}`);\n      }\n      const handle = nextHandle++;\n      handles.set(handle, {\n        ...defaultState(),\n        state: seed.state,\n        config: seed.config ? { ...seed.config } : null,\n        gamepad: { ...seed.gamepad },\n      });\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 === NES_FUNCTIONS.Load) {\n          state.config = params.Config as NesConfig;\n          state.state = 'Loaded';\n          return serializeRpcResult();\n        }\n\n        if (functionName === NES_FUNCTIONS.Start) {\n          if (state.state === 'Loaded' || state.state === 'Paused' || state.state === 'Stopped') {\n            state.state = 'Running';\n          }\n          return serializeRpcResult();\n        }\n\n        if (functionName === NES_FUNCTIONS.Pause) {\n          if (state.state === 'Running') {\n            state.state = 'Paused';\n          }\n          return serializeRpcResult();\n        }\n\n        if (functionName === NES_FUNCTIONS.Resume) {\n          if (state.state === 'Paused') {\n            state.state = 'Running';\n          }\n          return serializeRpcResult();\n        }\n\n        if (functionName === NES_FUNCTIONS.Stop) {\n          if (state.state === 'Running' || state.state === 'Paused') {\n            state.state = 'Stopped';\n          }\n          return serializeRpcResult();\n        }\n\n        if (functionName === NES_FUNCTIONS.Reset) {\n          if (state.config && state.state !== 'Idle') {\n            state.state = 'Loaded';\n          }\n          return serializeRpcResult();\n        }\n\n        if (functionName === NES_FUNCTIONS.Save) {\n          if (state.config?.save_path) {\n            state.saveCount += 1;\n          }\n          return serializeRpcResult();\n        }\n\n        if (functionName === NES_FUNCTIONS.SetGamepadState) {\n          state.gamepad = { ...(params.State as NesGamepadState) };\n          return serializeRpcResult();\n        }\n\n        if (functionName === NES_FUNCTIONS.GetState) {\n          return serializeRpcResult(state.state);\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 resetNesHostState(): void {\n  handles.clear();\n  nextHandle = 1;\n}\n"],"mappings":";;;AAoBA,IAAI,aAAa;AACjB,MAAM,0BAAU,IAAI,IAA0B;AAE9C,SAAS,eAA6B;CACpC,OAAO;EACL,OAAO;EACP,QAAQ;EACR,SAAS,CAAC;EACV,WAAW;CACb;AACF;AAEA,SAAS,SAAS,QAA8B;CAC9C,MAAM,QAAQ,QAAQ,IAAI,MAAM;CAChC,IAAI,CAAC,OACH,MAAM,IAAI,MAAM,mCAAmC,QAAQ;CAE7D,OAAO;AACT;AAEA,SAAgB,cAAc,SAAgD;CAC5E,MAAM,OAAO;EAAE,GAAG,aAAa;EAAG,GAAG;CAAQ;CAE7C,OAAO;EACL,kBAAkB,MAAuB;GACvC,OAAO,SAAA;EACT;EAEA,cAAc,MAAsB;GAClC,IAAI,SAAA,OACF,MAAM,IAAI,MAAM,oCAAoC,MAAM;GAE5D,MAAM,SAAS;GACf,QAAQ,IAAI,QAAQ;IAClB,GAAG,aAAa;IAChB,OAAO,KAAK;IACZ,QAAQ,KAAK,SAAS,EAAE,GAAG,KAAK,OAAO,IAAI;IAC3C,SAAS,EAAE,GAAG,KAAK,QAAQ;GAC7B,CAAC;GACD,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,cAAc,MAAM;KACvC,MAAM,SAAS,OAAO;KACtB,MAAM,QAAQ;KACd,OAAO,mBAAmB;IAC5B;IAEA,IAAI,iBAAiB,cAAc,OAAO;KACxC,IAAI,MAAM,UAAU,YAAY,MAAM,UAAU,YAAY,MAAM,UAAU,WAC1E,MAAM,QAAQ;KAEhB,OAAO,mBAAmB;IAC5B;IAEA,IAAI,iBAAiB,cAAc,OAAO;KACxC,IAAI,MAAM,UAAU,WAClB,MAAM,QAAQ;KAEhB,OAAO,mBAAmB;IAC5B;IAEA,IAAI,iBAAiB,cAAc,QAAQ;KACzC,IAAI,MAAM,UAAU,UAClB,MAAM,QAAQ;KAEhB,OAAO,mBAAmB;IAC5B;IAEA,IAAI,iBAAiB,cAAc,MAAM;KACvC,IAAI,MAAM,UAAU,aAAa,MAAM,UAAU,UAC/C,MAAM,QAAQ;KAEhB,OAAO,mBAAmB;IAC5B;IAEA,IAAI,iBAAiB,cAAc,OAAO;KACxC,IAAI,MAAM,UAAU,MAAM,UAAU,QAClC,MAAM,QAAQ;KAEhB,OAAO,mBAAmB;IAC5B;IAEA,IAAI,iBAAiB,cAAc,MAAM;KACvC,IAAI,MAAM,QAAQ,WAChB,MAAM,aAAa;KAErB,OAAO,mBAAmB;IAC5B;IAEA,IAAI,iBAAiB,cAAc,iBAAiB;KAClD,MAAM,UAAU,EAAE,GAAI,OAAO,MAA0B;KACvD,OAAO,mBAAmB;IAC5B;IAEA,IAAI,iBAAiB,cAAc,UACjC,OAAO,mBAAmB,MAAM,KAAK;IAGvC,OAAO,kBAAkB,oBAAoB,cAAc;GAC7D,SAAS,KAAK;IAEZ,OAAO,kBADS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAC/B;GAClC;EACF;CACF;AACF;AAEA,SAAgB,oBAA0B;CACxC,QAAQ,MAAM;CACd,aAAa;AACf"}