{"version":3,"sources":["../src/ready.ts"],"sourcesContent":["// The `ir.interactive` boot signal — the app-facing `reportReady()` / `onReady()` /\n// `getReadyState()` surface (LOAD_PROFILING_SPEC §3.1, R3-46). This closes the\n// \"existing SDK boot signal\" that `UI_AS_APPS_SPEC §6.2` referenced but never\n// defined.\n//\n// The runtime marks `ir.interactive` when the app's root render commits. An app\n// whose USEFULLY-interactive moment is later than first commit (e.g. after an\n// initial data load) calls `reportReady()` to DELAY the signal — which, per LP2-3,\n// can only ever push interactive later, never earlier than the commit (the host\n// resolves `max(commit, reportReady)`; see `resolveInteractive`). `onReady` /\n// `getReadyState` expose the report state in the same poll+subscribe shape as\n// `auth` / `mounts`.\n\nimport { sendMessage as defaultSend } from './sandboxUtils';\n\n// The `{ send, now }` seam and the `realNow` fallback below are mirrored in\n// `markers.ts`, which emits the other half of the boot marks over the same\n// transport. They stay two copies because the only way to share them is a new\n// module-level export, and this package's surface is additive-only: every export\n// is a public subpath API a pinned app can import forever.\n\n/** The app's `reportReady()` state, mirrored by {@link onReady}/{@link getReadyState}. */\nexport interface ReadyState {\n  /** Whether the app has called `reportReady()`. */\n  reported: boolean;\n  /** The app-reported timestamp (`performance.now()`), if it has reported. */\n  reportedAt?: number;\n}\n\ninterface ReadyDeps {\n  send: (type: string, data?: Record<string, unknown>) => void;\n  now: () => number;\n}\n\nconst realNow = (): number =>\n  typeof performance !== 'undefined' && typeof performance.now === 'function' ? performance.now() : Date.now();\n\nconst defaultDeps: ReadyDeps = { send: defaultSend, now: realNow };\n\nlet deps: ReadyDeps = defaultDeps;\nlet state: ReadyState = { reported: false };\nconst listeners = new Set<(s: ReadyState) => void>();\n\n/**\n * Signal that the app is usefully interactive (e.g. after an initial data load).\n * IDEMPOTENT — only the FIRST call counts; later calls are ignored. Forwards the\n * report to the runtime (`ir-report-ready`) so the host can resolve\n * `ir.interactive = max(rootRenderCommit, reportedAt)` (LP2-3) — calling it before\n * the root render commits can only delay the signal, never advance it.\n *\n * UX contract (LOADING_UX_SPEC §9.1): calling this tells the host *\"keep your\n * loading skeleton up; I am not done yet\"* — the host holds the §3 reveal until\n * this call (or the load budget). Call it ONCE, when the first USEFULLY-interactive\n * frame is on screen — not at mount, and not after every async settle. An app that\n * never calls it reveals automatically at the root-render commit (the default path).\n */\nexport function reportReady(): void {\n  if (state.reported) return;\n  state = { reported: true, reportedAt: deps.now() };\n  try {\n    deps.send('ir-report-ready', { at: state.reportedAt });\n  } catch {\n    /* transport not ready — the runtime still marks interactive at root commit */\n  }\n  for (const l of listeners) l(state);\n}\n\n/** Pollable snapshot of the report state. */\nexport function getReadyState(): ReadyState {\n  return state;\n}\n\n/**\n * Subscribe to the ready signal. Invoked immediately with the current state (so a\n * late subscriber after `reportReady()` still fires) and again whenever it reports.\n * Returns an unsubscribe.\n */\nexport function onReady(listener: (s: ReadyState) => void): () => void {\n  listeners.add(listener);\n  listener(state);\n  return () => {\n    listeners.delete(listener);\n  };\n}\n\n/** Test seam: override the transport/clock. */\nexport function __setReadyDeps(d: Partial<ReadyDeps>): void {\n  deps = { ...defaultDeps, ...d };\n}\n\n/** Test seam: reset module state between cases. */\nexport function __resetReady(): void {\n  deps = defaultDeps;\n  state = { reported: false };\n  listeners.clear();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAaA,0BAA2C;AAqB3C,MAAM,UAAU,MACd,OAAO,gBAAgB,eAAe,OAAO,YAAY,QAAQ,aAAa,YAAY,IAAI,IAAI,KAAK,IAAI;AAE7G,MAAM,cAAyB,EAAE,MAAM,oBAAAA,aAAa,KAAK,QAAQ;AAEjE,IAAI,OAAkB;AACtB,IAAI,QAAoB,EAAE,UAAU,MAAM;AAC1C,MAAM,YAAY,oBAAI,IAA6B;AAe5C,SAAS,cAAoB;AAClC,MAAI,MAAM,SAAU;AACpB,UAAQ,EAAE,UAAU,MAAM,YAAY,KAAK,IAAI,EAAE;AACjD,MAAI;AACF,SAAK,KAAK,mBAAmB,EAAE,IAAI,MAAM,WAAW,CAAC;AAAA,EACvD,QAAQ;AAAA,EAER;AACA,aAAW,KAAK,UAAW,GAAE,KAAK;AACpC;AAGO,SAAS,gBAA4B;AAC1C,SAAO;AACT;AAOO,SAAS,QAAQ,UAA+C;AACrE,YAAU,IAAI,QAAQ;AACtB,WAAS,KAAK;AACd,SAAO,MAAM;AACX,cAAU,OAAO,QAAQ;AAAA,EAC3B;AACF;AAGO,SAAS,eAAe,GAA6B;AAC1D,SAAO,EAAE,GAAG,aAAa,GAAG,EAAE;AAChC;AAGO,SAAS,eAAqB;AACnC,SAAO;AACP,UAAQ,EAAE,UAAU,MAAM;AAC1B,YAAU,MAAM;AAClB;","names":["defaultSend"]}