{"version":3,"sources":["../src/diagnostics.ts"],"sourcesContent":["// The `diagnostics:read` channel — app-facing surface (LLM_AND_AGENTS_SPEC §3.3/§4,\n// D4; roadmap R3-74 / P3-72).\n//\n// A *sibling* agent app (one holding `diagnostics:read`) observes the result of its\n// edits to the PREVIEWED app: the build/transpile errors from the sandbox bundler\n// and the previewed app's captured `console.*`. This is the in-browser analogue of\n// a local coding agent reading compiler/test output (P3-73's `get_diagnostics()`\n// tool). Read-only and scoped host-side to the paired previewed app's OWN\n// diagnostics — never another app's (the host channel projection enforces it; the\n// previewed app itself holds no `diagnostics:read`, so it is origin-excluded).\n//\n// Recipe-A push channel, identical get/onChange/use trio as `secrets`/`catalog`:\n// the host pushes `diagnostics` on change and answers a `request-diagnostics` poll,\n// gated per-frame by the read ACL. Inert until the host wires the channel\n// (site-main `channelBridge`); the contract ships here so the agent app (P3-73) can\n// be written against it.\nimport { createPushChannel } from './pushChannel';\nimport { DIAGNOSTICS, REQUEST_DIAGNOSTICS } from './generated/protocol';\n\n/** One build/transpile error from the sandbox bundler's compile of the previewed\n *  app. `path` is repo-relative (leading slash) when the error is file-located. */\nexport interface BuildError {\n  message: string;\n  path?: string;\n  line?: number;\n  column?: number;\n}\n\n/** The `console.*` method a captured entry came from. */\nexport type ConsoleLevel = 'log' | 'info' | 'warn' | 'error' | 'debug';\n\n/** One captured `console.*` entry from the previewed app. The host renders the\n *  console arguments to text host-side — the agent never receives live object\n *  handles across the boundary. */\nexport interface ConsoleEntry {\n  level: ConsoleLevel;\n  text: string;\n  /** Host-side timestamp (ms) at capture. */\n  at: number;\n}\n\n/** Provenance (D4 / EDITOR_AS_APP_SPEC §12.3): WHICH previewed app + compile this\n *  snapshot describes, so a consumer can tell stale output from fresh and never\n *  conflates two apps' diagnostics. `null` until a first compile is observed. */\nexport interface DiagnosticsProvenance {\n  /** The previewed app's stable key (`provider/ns/repo`). */\n  appKey?: string;\n  /** Monotonic id of the compile that produced these build errors. */\n  compileId?: string;\n}\n\n/** A snapshot of the previewed app's diagnostics: its build errors, captured\n *  console output, and the {@link DiagnosticsProvenance} of the compile. */\nexport interface Diagnostics {\n  buildErrors: BuildError[];\n  consoleEntries: ConsoleEntry[];\n  provenance: DiagnosticsProvenance | null;\n}\n\n/** Value before the host answers — also the value when the app may not read the\n *  channel (no `diagnostics:read`): an empty, provenance-less snapshot. */\nconst EMPTY: Diagnostics = { buildErrors: [], consoleEntries: [], provenance: null };\n\nconst channel = createPushChannel<Diagnostics>({\n  pushType: DIAGNOSTICS,\n  requestType: REQUEST_DIAGNOSTICS,\n  initial: EMPTY,\n  parse: (msg) => {\n    // Require both arrays; tolerate an absent/partial provenance. A malformed push\n    // is ignored (returns undefined) so the last good snapshot stands.\n    if (!Array.isArray(msg.buildErrors) || !Array.isArray(msg.consoleEntries)) return undefined;\n    const provenance =\n      msg.provenance && typeof msg.provenance === 'object' ? (msg.provenance as DiagnosticsProvenance) : null;\n    return {\n      buildErrors: msg.buildErrors as BuildError[],\n      consoleEntries: msg.consoleEntries as ConsoleEntry[],\n      provenance,\n    };\n  },\n});\n\n/** One-off read of the previewed app's current diagnostics. Returns the empty\n *  snapshot until the host answers (or if the app lacks `diagnostics:read`). Use\n *  {@link onDiagnosticsChange}/{@link useDiagnostics} to react to live updates. */\nexport const getDiagnostics = (): Diagnostics => channel.get();\n\n/** Subscribe to diagnostics. Invoked immediately with the current value, then on\n *  every host push. Returns an unsubscribe. */\nexport const onDiagnosticsChange = (listener: (d: Diagnostics) => void): (() => void) => channel.onChange(listener);\n\n/** React hook: the current diagnostics, re-rendering on every change. */\nexport const useDiagnostics = (): Diagnostics => channel.use();\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBA,yBAAkC;AAClC,sBAAiD;AA4CjD,MAAM,QAAqB,EAAE,aAAa,CAAC,GAAG,gBAAgB,CAAC,GAAG,YAAY,KAAK;AAEnF,MAAM,cAAU,sCAA+B;AAAA,EAC7C,UAAU;AAAA,EACV,aAAa;AAAA,EACb,SAAS;AAAA,EACT,OAAO,CAAC,QAAQ;AAGd,QAAI,CAAC,MAAM,QAAQ,IAAI,WAAW,KAAK,CAAC,MAAM,QAAQ,IAAI,cAAc,EAAG,QAAO;AAClF,UAAM,aACJ,IAAI,cAAc,OAAO,IAAI,eAAe,WAAY,IAAI,aAAuC;AACrG,WAAO;AAAA,MACL,aAAa,IAAI;AAAA,MACjB,gBAAgB,IAAI;AAAA,MACpB;AAAA,IACF;AAAA,EACF;AACF,CAAC;AAKM,MAAM,iBAAiB,MAAmB,QAAQ,IAAI;AAItD,MAAM,sBAAsB,CAAC,aAAqD,QAAQ,SAAS,QAAQ;AAG3G,MAAM,iBAAiB,MAAmB,QAAQ,IAAI;","names":[]}