# Changelog

## Unreleased

- **Single-element muxed playout is now the canonical way to consume an `"av"`
  parent.** `<Video stream="av" muted={false} />` puts BOTH tracks into ONE
  `MediaStream` on ONE element, so the browser's native A/V sync machinery owns
  the alignment — the same machinery an ordinary video player relies on.
  Consuming the same pair as two elements (`<Video stream="video">` +
  `<Audio stream="audio">`) gives the browser two independently-buffered
  playout paths with two element lifecycles and nothing aligning them; audio's
  jitter buffer accumulates standing delay on every delivery gap while video
  renders on arrival, and the drift is bounded only by RTCP sender reports an
  SFU may re-mint per track (liveavatar measured 2.2–2.7s of audio lagging
  video that way).

  The track plumbing already existed. What an UNMUTED muxed element needs —
  it IS the audio sink, so it is gesture-gated exactly like an `<Audio>` — is
  new: `VideoHandle.unlock()` / `VideoHandle.unlocked` and
  `VideoProps.onUnlockChange`, mirroring `<Audio>`'s contract. Previously an
  unmuted `<Video>` swallowed a blocked `play()` (`NotAllowedError`) into a
  `console.debug` and went silently mute with no affordance for the app to
  render. Also clears the stale `muted` ATTRIBUTE on an unmuted element (React
  sets the property but never the attribute; WebKit reads the attribute), and
  drops the unlocked claim when an element is re-rendered muted.

  **No breaking changes**: two-element consumers are unchanged, and
  `audioStream={false}` remains the opt-out.

- `UrunProvider` gains an optional `audioPlayout` prop, forwarded to
  `@urun-sh/core`'s `AppOptions.audioPlayout` — the inbound audio
  jitter-buffer cap (default 300ms) that bounds how far audio playout can
  drift behind video. Like `eventsUrl` and `releaseOnLeave` it is tuning, not
  identity, so it is deliberately NOT part of the shared-session key: changing
  it never redials a live session (first value wins per session).

## 0.2.29

- **`useApp()` render-safety hardened to full reference stability** (owner contract
  2026-07-16: "the app from useApp is supposed to come back render safe automatically";
  rolling-sink PR #7 finding). `app.<fn>` is now the **same function reference** on every
  property access and every re-render (bound proxy methods are cached per function name and
  reset exactly when the provider config changes). Session reference stability
  (same handle across calls/re-renders; new reference only after disconnect/end) was already
  guaranteed and is now pinned by `use-app-identity.test.tsx`.
- **React Compiler compatibility grounded and pinned.** The `react-hooks/purity`
  misclassification of event handlers (Date.now() rejected as render-scope with the
  unmemoized one-liner) is NOT an identity bug: the compiler freezes only values flowing
  through hook-*named* calls (`/^use[A-Z0-9]/`). Canonical zero-memoization composition:
  read the session through a hook you want anyway (`useSessionPhase(session)`), which
  freezes it for the compiler; writing handlers with impure stamps then lint clean under
  the default create-next-app config. Both sides pinned by lint fixtures in
  `react-compiler-lint.test.ts` (canonical composition = clean; unhooked
  single-component write boundary = still rejected, flips loudly on plugin upgrades).
- `useApp()` JSDoc + README now document the identity contract and the compiler note.

## 0.2.24

- Docs-only release: README accuracy pass against the 0.2.23+ surface — the quick start now
  composes `<SessionScope>` + `<Camera>`/`<Video>` (the prebaked components) instead of
  hand-rolled track plumbing, the API section documents the component/hook surface, and the
  retired "generative UI" framing is replaced by the component registry. No runtime changes.

## 0.2.23

- **Prebaked media I/O, bare-name canonical exports** (owner mandate 2026-07-13 — the UrunX
  component convention is dead): the declarative send/show surface, keyed on session stream
  names, bound via the new `<SessionScope session={...}>` (every component also takes an
  explicit `session` prop).
  - `<Video stream="output" />` — NEW root-entry live video display (the canonical
    "LiveVideo" pattern the app frontends hand-rolled: one persistent `srcObject`,
    muted+autoplay+playsinline with iOS attrs asserted, missed-event poll fallback,
    fill-container sizing, honest empty state via `placeholder` +
    `data-urun-video-live`). No video.js — the root entry stays player-framework-free.
  - `<Image stream="canvas" />` — NEW semantic wrapper over `<Video>` for the platform
    image lane (images ship as H.264 intra frames on a video stream; default stream
    `"image"`).
  - `<Camera stream="cam" front|back visible />` — canonical rename of `UrunCamera` plus
    the declarative surface: `front`/`back` facing props, auto-start on mount
    (level-triggered convergence; `autoStart={false}` for the imperative `ref.start()`
    mode), and **viewless by default** (`visible` opts into the self-view).
  - `<Mic stream="mic" />` — NEW viewless mic publish (capture-only half of `<Voice>`,
    same one-capture/whenLive/attach engine); `visible` renders a minimal level meter.
  - `<Audio stream="voice" />` — canonical rename of `UrunAudio` (sound-only by default,
    `controls` for a native player; unchanged autoplay-unlock contract).
  - `<Voice />` — canonical rename of `UrunVoice`; new `playback={false}` capture-only
    mode (what `<Mic>` rides on).
  - `@urun-sh/react/video`: `UrunVideo` renamed `VideoPlayer` — the video.js-backed
    player now positioned for VOD/recordings playback (seek/scrub); live session streams
    should use the root `<Video>`.
  - Deprecated aliases kept for ONE release so pinned fleet frontends compile:
    `UrunAudio`, `UrunVoice`, `UrunCamera` (behavior-preserving shim: preview on,
    no auto-start), `UrunVideo` (subpath), plus their prop/handle type names.
    **Removed next minor.**
- Removed the dead compositing-era files (`use-scene`, `use-layout`,
  `compositor-provider`, `scene-store` — never exported from the package root), guarded
  against reintroduction by `compositing-era-removed.test.ts`.

## 0.2.19

- `UrunProvider` gains an optional `eventsUrl` prop: a trusted (typically
  loopback) endpoint that receives session diagnostics (`media-stall`,
  `decode-wall`), phase errors (raw reason/code/http status), and the
  first-live event as JSON POSTs — the hook `urun demo` uses to mirror
  browser-side media failures into the demo terminal. Never sends auth tokens
  or function arguments. Reporting config is deliberately excluded from the
  shared-session identity key (first-value-wins at handle creation), so
  changing `eventsUrl` never redials a live session.
- Session handles expose `onDiagnostic` (delegates to
  `@urun-sh/core`'s `Session.onDiagnostic`).

## 0.2.12

- Version-lockstep release with `@urun-sh/core@0.2.12` (legacy `stream.subscribe`
  handshake deleted in core; no react surface change — `UrunStreamTail` et al.
  ride the same `stream.messages()` API, now fed exclusively by the §5
  server-push DataConsumer path).

## 0.2.7

- **BREAKING**: `UrunVideo` moved from the root entry to the `./video` subpath
  — import it as `import { UrunVideo } from '@urun-sh/react/video'`. video.js
  is an optional peer dependency, but the root entry statically imported
  `video.js/dist/video-js.css` (kept external), so every consumer WITHOUT
  video.js installed failed module resolution at build time on ANY
  `@urun-sh/react` import. The root entry graph is now video.js-free (guarded
  by `videojs-optional-peer.test.ts` and `pack:check`); only apps that render
  video install video.js and import from `@urun-sh/react/video`.

## 0.2.5

- Session workbench building blocks: generic, composable debug/steer components
  over the public `Session` primitives — `UrunStreamTail` (named data-lane tail
  over `stream.messages()`), `UrunDocPanel` + `UrunControlSender` (view/patch a
  session doc), `UrunEventSpine` (SDK-native activity log from `onPhase` /
  track / doc events), `UrunSessionStatus` + `UrunSessionGate`, plus the hooks
  `useSessionTrack` / `useSessionDoc` / `useStreamMessages`. Media panes reuse
  the existing `UrunVideo` / `UrunAudio` / `UrunVoice`. See
  `docs/session-workbench-building-blocks.md`.
- `useStreamMessages` now cancels the stream's async iterator (calls
  `.return()`) on unmount / name change, instead of only flipping a local
  flag — on a quiet stream the old subscription used to linger until the next
  message ever arrived.
- `UrunControlSender`'s doc-change log line is labeled `change` instead of
  `recv`: the core doc has no origin marker, so the line also fires for this
  component's own writes and `recv` overclaimed it came from the runtime.

## 0.2.1

- Republish of the 0.2.0 line (`@urun-sh/core` / `@urun-sh/react` 0.2.0 published but never persisted in the npm packument; bumped to 0.2.1 for a clean, resolvable release). No source changes vs 0.2.0.

## 0.1.33

- `useSession()` now surfaces the typed `reconnecting` state (via the core `stateChange` transport event) while the SDK re-dials / re-resolves the session connect handle, so apps can render the reconnect lifecycle instead of treating drops as terminal.

## 0.1.32

- Version alignment for the core Yjs control-doc sync over the signaling WebSocket.

## 0.1.27

- Version alignment for fresh-token allocation polling.

## 0.1.26

- Version alignment for queued global production allocation polling.

## 0.1.21

- Version alignment for repeated session allocation token refresh handling.

## 0.1.20

- Pass the live auth token provider through `useApp()` so session creation can recover from stale WorkOS tokens.

## 0.1.17

- Make the WorkOS Next.js AuthKit import statically discoverable so Next.js bundles the token bridge in production apps.

## 0.1.15

- Read WorkOS access tokens from the Next.js AuthKit provider before falling back to authkit-react.
- Show a visible sign-in status while WorkOS tokens are pending instead of rendering a blank app shell.

## 0.1.14

- Read known public auth environment variables directly so browser bundlers can inline them.

## 0.1.13

- Let explicit `NEXT_PUBLIC_AUTH_MODE` override production environment defaults.

## 0.1.12

- Export the SDK auth-mode helpers used by reference apps and middleware.

## 0.1.11

- Resolve browser auth mode from public environment, use `NEXT_PUBLIC_SESSION_TOKEN` only in JWT/test mode, and wait for WorkOS access tokens before rendering session-creating children.

## 0.1.10

- Bumped peer dependency to `@urun-sh/core@0.1.10`.

## 0.1.6

- Updated React development/test dependencies to React 19 and aligned React DOM.

## 0.1.5

- Stripped declaration comments, hid internal transport exports from the root API, and added packed tarball leakage checks.

## 0.1.4

- Made React WorkOS authentication automatic via `authProvider="workos"`, including refreshed token forwarding.

## 0.1.3

- Removed source maps from published packages and bundled/minified internal media transport dependencies.

## 0.1.2

- Documented and wired browser JWT authentication using `orgId`, `jwt`, and provider metadata.

## 0.1.1

- Re-published packages under the public `@urun-sh` npm scope.

## 0.1.0

- Initial TypeScript monorepo setup for `@urun-sh/core` and `@urun-sh/react`.
- Package builds emit ESM, CommonJS, TypeScript declarations, and sourcemaps.
- React package exports `@urun-sh/react/styles.css`.
