{"version":3,"sources":["../src/recents.ts"],"sourcesContent":["// The recent-projects record — app-facing surface (R3-485, OSO §4.3).\n//\n// `listRecentProjects()` reads the HOST-OWNED record through the one elevated,\n// app-scoped `recents:read` capability. Two gates stand between an app and the\n// record, and both must pass: the §8.4 capability grant (declared as\n// `requests: { 'recents:read': {} }` and consented per (app, principal)), and the\n// handler's structural binding — the read is granted only to the app bound at\n// `page.home`. For every other app the call resolves to a refusal, whatever it\n// declared.\n//\n// A project entry is COORDINATES (provider/namespace/repository/ref + when) — a\n// location the user already navigated to under their own authority, never an\n// in-repo path. Opening one runs the ordinary load path with the ordinary\n// consent: the record confers nothing (R-OSO-22).\nimport { protocolRequest } from './sandboxUtils';\nimport { throwOnRefusal } from './protocolRefusal';\nimport { PROTOCOL_RECENTS } from './generated/protocol';\nimport { SCHEMES } from './protocolSchemes';\n\n/** One recently opened project — repository coordinates, never an in-repo path. */\nexport interface RecentProject {\n  provider: string;\n  namespace: string;\n  repository: string;\n  /** The ref the user opened (`main`, a sha). */\n  ref: string;\n  /** When the user last opened it (epoch ms). */\n  ts: number;\n}\n\ninterface RecentsReply {\n  /** Newest-first; `null` when the record is empty or the user cleared it. */\n  projects: RecentProject[] | null;\n}\n\n/**\n * The host wraps every action result as `{ ok: true, data }` and every refusal\n * as `{ ok: false, code, message }` INSIDE the reply's `result` — refusals\n * resolve rather than reject, because the sandbox's protocolRequest rejects\n * with a bare `Error` and would drop the `code` (requestDispatcher's reply\n * contract). Unwrap here, like every other gated module, so callers see the\n * coded refusal and never a silent `null`.\n */\ntype RecentsResult = { ok: true; data: RecentsReply } | { ok: false; code: string; message?: string };\n\n/**\n * The one call shape the SDK speaks under `protocol-recents` — read, or clear\n * (same gated surface). A single typed call site keeps the wire contract exact:\n * the snapshot gate reads THIS shape, and the host handler accepts the same.\n */\nconst recentsRequest = async (params: { clear?: boolean }): Promise<RecentsReply> => {\n  const res = (await protocolRequest(SCHEMES[PROTOCOL_RECENTS], 'list', [params])) as RecentsResult;\n  // The one refusal unwrap (R6, R3-816) — same observable behaviour: the coded\n  // refusal throws with its code; an absent reply throws as 'unknown'.\n  throwOnRefusal(res, 'recents request failed');\n  return res.data;\n};\n\n/**\n * The user's recently opened projects, newest-first, or `null` when the record is\n * absent (R-OSO-22: cleared is absent, never an empty list). Refuses for any app\n * that is not the `page.home` binding.\n *\n * @throws a coded error — `forbidden` without the grant or the binding, or the\n *         host's own code; never a silent `null` for a refusal.\n */\nexport async function listRecentProjects(): Promise<RecentProject[] | null> {\n  const res = await recentsRequest({});\n  return res.projects ?? null;\n}\n\n/**\n * Clear the user's recent-projects record (the surface afterwards is absent, not\n * empty). Rides the same page.home binding gate as the read.\n *\n * @throws a coded error when the clear is refused — the caller must not treat\n *         the record as cleared.\n */\nexport async function clearRecentProjects(): Promise<void> {\n  await recentsRequest({ clear: true });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAcA,0BAAgC;AAChC,6BAA+B;AAC/B,sBAAiC;AACjC,6BAAwB;AAiCxB,MAAM,iBAAiB,OAAO,WAAuD;AACnF,QAAM,MAAO,UAAM,qCAAgB,+BAAQ,gCAAgB,GAAG,QAAQ,CAAC,MAAM,CAAC;AAG9E,6CAAe,KAAK,wBAAwB;AAC5C,SAAO,IAAI;AACb;AAUA,eAAsB,qBAAsD;AAC1E,QAAM,MAAM,MAAM,eAAe,CAAC,CAAC;AACnC,SAAO,IAAI,YAAY;AACzB;AASA,eAAsB,sBAAqC;AACzD,QAAM,eAAe,EAAE,OAAO,KAAK,CAAC;AACtC;","names":[]}