{"version":3,"file":"packageApi.mjs","names":[],"sources":["../../../src/services/testing/packageApi.ts"],"sourcesContent":["import {\n  type DoomApi,\n  type DoomApiContext,\n  type DoomApiHandler,\n  DOOM_API_ROUTE_PREFIX,\n  type DoomApiScope,\n} from '../../schemas/packageApi.ts';\n\n/**\n * A package's HTTP surface, mounted the way a host mounts it.\n *\n * A package tests its routes by calling its Hono app directly, which proves the\n * routes but not the mount: the host serves them under\n * `/api/plugin/<basePath>` and strips that prefix before the app sees the\n * request. A package whose routes are written absolute passes its own suite and\n * answers nothing in a running cockpit.\n *\n * This mounts the declared `DoomApi` the same way, so a test asks for the URL a\n * browser would ask for. Plain `Request` and `Response`; no HTTP framework and\n * no server.\n */\n\nexport interface MountPackageApiOptions {\n  scope?: DoomApiScope;\n  sessionId?: string;\n  cwd?: string;\n}\n\nexport interface MountedPackageApi {\n  /** Where the host mounted it, for building a URL the way a client does. */\n  readonly mountPath: string;\n  /** What the API reported to its host; a package that notices nothing leaves this empty. */\n  readonly notices: readonly string[];\n  /**\n   * Fetches one path.\n   *\n   * The path is host-absolute, so it carries the mount the client would send:\n   * `/api/plugin/workflow/runs/repo/blog-4`. A path outside the mount is a 404\n   * from this, exactly as it is from the host.\n   */\n  fetch(path: string, init?: RequestInit): Promise<Response>;\n  close(): void;\n}\n\nconst DEFAULT_SCOPE: DoomApiScope = 'hub';\nconst DEFAULT_CWD = '/repo';\n/** Any origin will do: the host routes on path, and a Request needs an absolute URL. */\nconst ORIGIN = 'http://host';\n\nexport function mountPackageApi(api: DoomApi, options: MountPackageApiOptions = {}): MountedPackageApi {\n  const scope = options.scope ?? DEFAULT_SCOPE;\n  const notices: string[] = [];\n  const mountPath = `${DOOM_API_ROUTE_PREFIX}/${api.basePath}`;\n  const context: DoomApiContext = {\n    scope,\n    // A session-scoped host knows both; a hub-scoped one knows neither, and an\n    // API that reads them anyway should see the same undefined it would there.\n    ...(scope === 'session' ? { sessionId: options.sessionId ?? 'test-session', cwd: options.cwd ?? DEFAULT_CWD } : {}),\n    onNotice: (message) => notices.push(message),\n  };\n  const handler: DoomApiHandler = api.start(context);\n\n  return {\n    mountPath,\n    notices,\n    async fetch(path, init) {\n      const url = new URL(path, ORIGIN);\n      if (url.pathname !== mountPath && !url.pathname.startsWith(`${mountPath}/`)) {\n        return new Response(`No package API is mounted at ${url.pathname}.`, { status: 404 });\n      }\n      // The host strips its own mount, so a package declares '/runs/:id' and\n      // never repeats where it was mounted.\n      url.pathname = url.pathname.slice(mountPath.length) || '/';\n      return handler.fetch(new Request(url, init));\n    },\n    close: () => handler.close(),\n  };\n}\n"],"mappings":"qEAiDA,SAAgB,EAAgB,EAAc,EAAkC,CAAC,EAAsB,CACrG,IAAM,EAAQ,EAAQ,OAAS,MACzB,EAAoB,CAAC,EACrB,EAAY,GAAG,EAAsB,GAAG,EAAI,WAC5C,EAA0B,CAC9B,QAGA,GAAI,IAAU,UAAY,CAAE,UAAW,EAAQ,WAAa,eAAgB,IAAK,EAAQ,KAAO,OAAY,EAAI,CAAC,EACjH,SAAW,GAAY,EAAQ,KAAK,CAAO,CAC7C,EACM,EAA0B,EAAI,MAAM,CAAO,EAEjD,MAAO,CACL,YACA,UACA,MAAM,MAAM,EAAM,EAAM,CACtB,IAAM,EAAM,IAAI,IAAI,EAAM,aAAM,EAOhC,OANI,EAAI,WAAa,GAAa,CAAC,EAAI,SAAS,WAAW,GAAG,EAAU,EAAE,EACjE,IAAI,SAAS,gCAAgC,EAAI,SAAS,GAAI,CAAE,OAAQ,GAAI,CAAC,GAItF,EAAI,SAAW,EAAI,SAAS,MAAM,EAAU,MAAM,GAAK,IAChD,EAAQ,MAAM,IAAI,QAAQ,EAAK,CAAI,CAAC,EAC7C,EACA,UAAa,EAAQ,MAAM,CAC7B,CACF"}