{"name":"@playlive/react-pipeline","dist-tags":{"latest":"0.4.20"},"versions":{"0.4.18":{"name":"@playlive/react-pipeline","version":"0.4.18","description":"React bindings for @playlive/realtime-pipeline","type":"module","sideEffects":false,"main":"./index.js","types":"./index.d.ts","exports":{".":{"import":"./index.js","types":"./index.d.ts"},"./provider":{"import":"./provider/index.js","types":"./provider/index.d.ts"},"./legacy":{"import":"./legacy/index.js","types":"./legacy/index.d.ts"},"./fusion":{"import":"./fusion/index.js","types":"./fusion/index.d.ts"}},"dependencies":{},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","zustand":"^5.0.0","@playlive/realtime-pipeline":"^0.3.0","@playlive/tiltify-core":"^0.4.17","@tanstack/react-query":"^5.0.0","@playlive/react-query":"^0.4.2","@playlive/fundraiser-data":"^0.5.2"},"peerDependenciesMeta":{"@tanstack/react-query":{"optional":true},"@playlive/react-query":{"optional":true},"@playlive/fundraiser-data":{"optional":true}},"playlive":{"target":"browser","frontendEligible":true,"coverageFloor":85},"publishConfig":{"access":"restricted","registry":"https://playlive-767397689694.d.codeartifact.us-east-1.amazonaws.com/npm/playlive/"},"integrity":"sha512-zQCnu9q2UmbN+DJtgr2WXa2uhIntyNCvicJlOe28F04xIMoKWQNFnVuy0p3U3qsknYqmHZwAqsOH51wLQIATzQ==","shasum":"8242e89647dcaddc4fb7225858f9e7278e87caeb","readme":"# @playlive/react-pipeline\n\nReact bindings for [`@playlive/realtime-pipeline`](../realtime-pipeline/).\nDrop-in replacement for `playlive-overlay-data-layer/src/websocket/*`\nwith **zero** dependency on `react-use-websocket-lite` — built directly\non Zustand v5's `useStore` + `useSyncExternalStore`.\n\n![Coverage](./coverage-badge.svg)\n\n## Three tiers — take only what you need\n\nPick the surface that matches how much of the legacy\n`UnifiedDataPipelineProvider` API you actually want. Each tier is\nstrictly larger than the last and lives at its own subpath so unused\ncode is tree-shaken out.\n\n| Tier | Subpath                            | Surface                                                                         | Use when                                                              |\n| ---- | ---------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| 1    | `@playlive/react-pipeline`         | `RealtimePipelineProvider` + `usePipelineValue` (universal selector)            | Greenfield app; you want one selector primitive and nothing else.     |\n| 2    | `@playlive/react-pipeline`         | Tier 1 **plus** 22 typed per-slice hooks (`usePipelineDonations(id)`, …)        | You want stable, typed one-liners for the common reads.               |\n| 3    | `@playlive/react-pipeline/provider`| Tier 2 **plus** `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, etc.   | Porting an existing overlay-data-layer consumer with minimal churn.   |\n| —    | `@playlive/react-pipeline/legacy`  | Tier 3 **plus** `useUDPStore` / `useUDPStoreApi` / `createUDPStore` aliases     | Soft-deprecated shim for the legacy symbol names (one-release window).|\n\n## Install\n\n```bash\nbun add @playlive/react-pipeline\nbun add -d react react-dom zustand\n```\n\nPeer dependencies (jose-style — consumer brings their own):\n`react@^19`, `react-dom@^19`, `zustand@^5`,\n`@playlive/realtime-pipeline` (workspace), `@playlive/tiltify-core`\n(workspace, type-only — stripped at compile time).\n\nNothing else ships in the bundle.\n\n## Quick start — Tier 1\n\n```tsx\nimport { RealtimePipelineProvider, usePipelineCampaigns } from \"@playlive/react-pipeline\";\n\nfunction App() {\n  return (\n    <RealtimePipelineProvider\n      url=\"wss://pipeline.example.com/socket\"\n      charityType=\"tiltify\"\n      autoConnect\n    >\n      <Overlay />\n    </RealtimePipelineProvider>\n  );\n}\n\nfunction Overlay() {\n  const campaigns = usePipelineCampaigns();\n  return <ul>{campaigns.map((c) => <li key={c.id}>{c.name}</li>)}</ul>;\n}\n```\n\n`RealtimePipelineProvider` mounts a Zustand store + opens (or accepts)\na `PipelineConnection`. Children call any Tier 1 / Tier 2 hook to\nsubscribe.\n\n## Subpath exports\n\n| Subpath                              | Description                                                                       |\n| ------------------------------------ | --------------------------------------------------------------------------------- |\n| `@playlive/react-pipeline`           | Default barrel — Tier 1 + Tier 2 (provider + 21 typed slice hooks).               |\n| `@playlive/react-pipeline/provider`  | Tier 3 — `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, add-pipeline\\*. |\n| `@playlive/react-pipeline/fusion`    | Cross-package composition hooks (REST baseline + WS delta) — `useCurrentBlockRaised`, `useLiveSchedule`, `useCurrentScheduleItem`, `useLinkedTeamCampaign`, `useDonorSpotlight`, plus the WS-store-first / HTTP-fallback hybrid slice hooks (`useHybridRewards`, `useHybridTargets`, `useHybridMilestones`, `useHybridPolls`, `useHybridCause`, `useHybridFundraisingEvent`, `useHybridFundraisingEventSupportingCampaigns`, `useHybridAuctionHouses`) + the shared `useLoadingTimeout` primitive. |\n| `@playlive/react-pipeline/legacy`    | Soft-deprecated alias bundle (`useUDPStore`, `createUDPStore`, `UDPStore`, etc.). |\n\nEach subpath ships an ESM bundle, a Bun source condition, and `.d.ts`\ndeclarations. Frontend-eligible bundles are not minified (PRD §6.1)\nso stack traces stay readable in production.\n\n## API reference\n\n### Tier 1 — universal primitive\n\n| Export                          | Signature                                                            |\n| ------------------------------- | -------------------------------------------------------------------- |\n| `RealtimePipelineProvider`      | `(props: RealtimePipelineProviderProps) => JSX.Element`              |\n| `usePipelineValue`              | `<T>(selector: (s: PipelineStore) => T) => T`                        |\n\n`usePipelineValue` is single-arg by design. Zustand v5 dropped the\nthird `equalityFn` parameter on `useStore` to keep the package\nfree of the `use-sync-external-store` shim peer. For shallow / custom\nequality, wrap your selector with `useShallow` from\n`zustand/shallow` (zero-extra-dep):\n\n```tsx\nimport { useShallow } from \"zustand/shallow\";\nimport { usePipelineValue } from \"@playlive/react-pipeline\";\n\nconst { connected, connecting } = usePipelineValue(\n  useShallow((s) => ({ connected: s.connected, connecting: s.connecting })),\n);\n```\n\n### Tier 2 — typed per-slice hooks\n\nAll 22 hooks are stable wrappers over `usePipelineValue` with a typed\nselector + memo-stable empty-array fallbacks (no infinite-render\nchurn from fresh `[]` allocations — Decision §3 in CHANGELOG).\n\n| Hook                              | Returns                                                             |\n| --------------------------------- | ------------------------------------------------------------------- |\n| `usePipelineCampaigns()`          | `TiltifyCampaign[]`                                                 |\n| `usePipelineTeamCampaigns()`      | `TiltifyTeamCampaign[]`                                             |\n| `usePipelineFundraisingEvents()`  | `TiltifyFundraisingEvent[]`                                         |\n| `usePipelineCauses()`             | `TiltifyCause[]`                                                    |\n| `usePipelineDonations()`          | `Record<string, TiltifyDonationWithTestFlag[]>`                     |\n| `usePipelineDonations(id)`        | `TiltifyDonationWithTestFlag[]` (memo-stable `[]` if id unknown)    |\n| `usePipelineDonationSum(id, opts?)` | `PipelineDonationSumResult` — live per-currency sum over `[start, end)` of the WS donation firehose. Pair with the REST `/schedules/campaigns/{id}/raised` `asOf` field to replace polling with a `baseline + WS-delta` view. |\n| `usePipelineDonationTrains(id, withStatus?)` | `DonationTrain[]`                                        |\n| `usePipelineSubathonTimer(id, withStatus?)`  | `SubathonTimer[]`                                        |\n| `usePipelineDonorSpotlight(id)`   | `ComputedSpotlight \\| null`                                         |\n| `usePipelinePolls(id)`            | `TiltifyPoll[]`                                                     |\n| `usePipelineRewards(id)`          | `TiltifyReward[]`                                                   |\n| `usePipelineTargets(id)`          | `TiltifyTarget[]`                                                   |\n| `usePipelineMilestones(id)`       | `TiltifyMilestone[]`                                                |\n| `usePipelineLeaderboardEntries(id)` | `TiltifyLeaderboardEntry[]`                                       |\n| `usePipelineAuctionHouses()`      | `AuctionHouse[]`                                                    |\n| `usePipelineTwitchChat()`         | `TwitchChatMessage[]`                                               |\n| `usePipelineConnectionState()`    | `PipelineConnectionState` (10 split selectors — no false re-renders)|\n| `usePipelineConnectionActions()`  | `PipelineConnectionActions` (stable `connect/disconnect/refresh/…`) |\n| `usePipelineSubscriptions()`      | `Record<string, TiltifyWebhookSubscription>`                        |\n| `usePipelineTiltifySettings()`    | `TiltifyWebSocketSettings`                                          |\n| `usePipelineLastRefreshResponse()`| `RefreshResponseMessage \\| null`                                    |\n| `usePipelineDonorSpotlightSettings()` | `Record<string, DonorSpotlightSettings>`                        |\n\n### Tier 3 — kitchen-sink provider (`/provider`)\n\n| Export                              | Notes                                                                                  |\n| ----------------------------------- | -------------------------------------------------------------------------------------- |\n| `UnifiedDataPipelineProvider`       | Superset of `RealtimePipelineProvider`; adds legacy `reloadOnErrorReconnect` + `debug`.|\n| `useUnifiedDataPipeline()`          | One-call kitchen-sink hook. Returns every slice + actions + legacy field aliases.      |\n| `useAddPipelineCampaignIDs(ids)`    | Effect hook — registers campaign ids with the store on mount.                          |\n| `useAddPipelineCampaigns(items)`    | Effect hook — registers full campaign records on mount.                                |\n| `useAddPipelineFundraisingEventIDs(ids)` | Effect hook — registers fundraising-event ids on mount.                           |\n| `useCommonContextHooks(…)`          | URL / mode / charity-type sync to the store. Same shape as the legacy hook.            |\n| `useIdentify()` / `useIdentify(…11 legacy args)` | Zero-arg returns the store's bound `identify` action. Positional arg form is preserved as an `@deprecated` overload — args are ignored, warns once. |\n\n### Fusion — hybrid slice hooks (`/fusion`)\n\nOverlay-parity `usePipeline*` hooks lifted from\n`playlive-overlay-vite/src/hooks/pipeline/` so every overlay that\nwants the same WS-store-first / HTTP-fallback shape can drop the\nlocal copy. Every hook accepts a `mode: \"ws\" | \"http\"` param that\npicks between:\n\n- `\"ws\"` — read the Tier-2 store selector for the slice, latch a\n  local `hasLoadedX` boolean on the first slice arrival (or on the\n  server-side \"feature disabled\" signal), and fire\n  `updateTiltifySettings({ <sliceKey> })` whenever the local\n  `xEnabled` toggle flips so the pipeline dynamically un/subscribes.\n- `\"http\"` — delegate to the matching `@playlive/react-query` REST\n  hook (`useRewards`, `useTargets`, …) with the WS store slice\n  ignored.\n\nAll hybrid hooks share `useLoadingTimeout` — a 15 s safety-net that\nforce-completes the WS branch when the store never delivers.\n\n| Hook                                                     | Returns                                                                        |\n| -------------------------------------------------------- | ------------------------------------------------------------------------------ |\n| `useHybridRewards(params)`                               | `{ currentRewards, setHasLoadedRewards, hasLoadedRewards, isLoadingRewards, isLoading, setRewardsEnabled, rewardsEnabled }` |\n| `useHybridTargets(params)`                               | `{ currentTargets, setHasLoadedTargets, hasLoadedTargets, isLoading, targetsEnabled, isLoadingTargets, setTargetsEnabled }` |\n| `useHybridMilestones(params)`                            | `{ isLoadingMilestones, currentMilestones, milestonesEnabled, setMilestonesEnabled }` |\n| `useHybridPolls(params)`                                 | `{ isLoadingPolls, currentPolls, pollsEnabled, setPollsEnabled }`              |\n| `useHybridCause({ mode, initialCampaign })`              | `{ cause, isLoading }` — single-slot semantics (uses `getFirstCause`).         |\n| `useHybridFundraisingEvent({ mode, initialCampaign })`   | `{ fundraisingEvent, isLoading }` — single-slot semantics.                     |\n| `useHybridFundraisingEventSupportingCampaigns(params)`   | `{ supportingCampaigns, isLoading, setSupportingCampaignsEnabled, supportingCampaignsEnabled, isLoadingSupportingCampaigns, hasLoadedSupportingCampaigns }` |\n| `useHybridAuctionHouses({ mode, enabled, campaignID })`  | `{ auctionHouses, auctionHouseTotal, auctionHousesEnabled, setAuctionHousesEnabled }` — WS-only (no REST fallback yet). |\n| `useLoadingTimeout(params)`                              | `void` — safety-net timer primitive shared by every hook above.                |\n\nReturn shapes are preserved verbatim from the overlay so overlay\ncall-sites can eventually collapse to a re-export.\n\n### Fusion — baseline + WS delta hooks (`/fusion`)\n\n| Hook                                        | Returns                                                                     |\n| ------------------------------------------- | --------------------------------------------------------------------------- |\n| `useCurrentBlockRaised(campaignID, start, end, opts?)` | `{ raised, data, isLoading, isError, refetch }` — REST baseline + WS delta. `opts.demoMode: true` short-circuits to `DemoProvider.getDemoScheduleBlockRaised` / `getDemoBlockRaised` fallback. |\n| `useLiveSchedule(params, opts?)`            | `{ schedule, isLoading, isError, refetch }` — WS-refetch on transition. `opts.demoMode: true` (+ optional `demoIntervalMs`) short-circuits to `DemoProvider.getDemoSchedule` / `buildDemoSchedule` fallback with a live tick timer.     |\n| `useCurrentScheduleItem(schedule, opts?)`   | `{ currentItem, nextItem }` — pure-client rollover projection.              |\n| `useLinkedTeamCampaign(params, opts?)`      | `{ linkedTeamCampaign, … }` — WS-store first, HTTP fallback.                |\n| `useDonorSpotlight(params, opts?)`          | `{ spotlight, isLoading, refetch }` — WS store preferred + REST snapshot.   |\n\n### Legacy aliases (`/legacy`)\n\nSoft-deprecated. Identical behavior to the canonical exports —\nrename the import specifier once and the rest of your code base\nkeeps working unchanged:\n\n| Legacy name            | Canonical replacement                                          |\n| ---------------------- | -------------------------------------------------------------- |\n| `useUDPStore`          | `usePipelineValue` (Tier 1) / typed slice hooks (Tier 2)       |\n| `useUDPStoreApi`       | (use a typed slice hook; or reach for the raw store API)       |\n| `createUDPStore`       | `createPipelineStore` from `@playlive/realtime-pipeline/store` |\n| `UDPStore` (type)      | `PipelineStore` from `@playlive/realtime-pipeline/store`       |\n| `UDPStoreApi` (type)   | `PipelineStoreApi` from `@playlive/realtime-pipeline/store`    |\n| `CharityTypes`         | re-exported from `@playlive/realtime-pipeline/protocol`         |\n\nPlus every Tier 1+2+3 export under its canonical name.\n\n## Migration from `playlive-overlay-data-layer`\n\nOne-shot sed-friendly rewrite of every import specifier (no body\nedits needed for the happy path):\n\n```bash\n# Tier 3 — kitchen-sink (drop-in replacement)\nrg -l '@playlive/overlay-data-layer/websocket' . \\\n  | xargs sed -i '' \\\n    -e 's|@playlive/overlay-data-layer/websocket|@playlive/react-pipeline/provider|g'\n```\n\nFor the soft-deprecated names (`useUDPStore`, `createUDPStore`, …)\nsubstitute `/legacy` instead of `/provider` to opt into the alias\nbundle, then migrate at your leisure (PRD §10 — one-release window).\n\nSee the phase-8 row in the root [MIGRATION.md](../../MIGRATION.md) for\nthe full per-symbol mapping plus call-site walkthroughs.\n\n## Upstream spec\n\nNo external API surface — internal workspace package. All transport\nruns through `@playlive/realtime-pipeline/connection`.\n\n## Twitch Extension URL disclosure\n\n`KNOWN_URLS` enumerates every absolute URL or host this package can\nfetch. **It is empty.** This package opens WebSocket connections only\nto URLs the consumer passes to `<RealtimePipelineProvider url={…}>`;\nthose URLs must be added to your Extension submission's URL\ndisclosure list, not this one.\n\n```ts\nimport { KNOWN_URLS } from \"@playlive/react-pipeline\";\nconsole.log(KNOWN_URLS); // []\n```\n\nSee [`docs/twitch-extension-checklist.md`](../../docs/twitch-extension-checklist.md).\n\n## Examples\n\nLive overlay apps consuming this provider land under `apps/*`\n(phase 10). Until then, the Tier 1 quick-start above is the minimum\nviable wiring.\n\n## Contributing\n\nSee [CONTRIBUTING.md](../../CONTRIBUTING.md). To add new Tier-2\nhooks, mirror an existing slice hook in `src/hooks/index.ts` and\nadd a `it.each` entry in `tests/unit/index.test.ts`.\n\n## License\n\nMIT — see [LICENSE](../../LICENSE). Distributed via Play Live\nCodeArtifact (PRD §6).\n","readmeFilename":"README.md","dist":{"tarball":"https://packages.playlive.experience.stjude.org/@playlive/react-pipeline/-/react-pipeline-0.4.18.tgz","shasum":"8242e89647dcaddc4fb7225858f9e7278e87caeb","integrity":"sha512-zQCnu9q2UmbN+DJtgr2WXa2uhIntyNCvicJlOe28F04xIMoKWQNFnVuy0p3U3qsknYqmHZwAqsOH51wLQIATzQ=="}},"0.1.0":{"name":"@playlive/react-pipeline","version":"0.1.0","description":"React bindings for @playlive/realtime-pipeline — three usage tiers (single selector / typed per-slice / kitchen-sink). Twitch-Extension safe; no react-use-websocket-lite.","type":"module","sideEffects":false,"main":"./index.js","types":"./index.d.ts","exports":{".":{"import":"./index.js","types":"./index.d.ts"},"./provider":{"import":"./provider/index.js","types":"./provider/index.d.ts"},"./legacy":{"import":"./legacy/index.js","types":"./legacy/index.d.ts"}},"dependencies":{"@playlive/realtime-pipeline":"^0.1.1"},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","zustand":"^5.0.0","@playlive/realtime-pipeline":"^0.1.1","@playlive/tiltify-core":"^0.1.1"},"playlive":{"target":"browser","frontendEligible":true,"coverageFloor":85},"publishConfig":{"access":"restricted","registry":"https://playlive-767397689694.d.codeartifact.us-east-1.amazonaws.com/npm/playlive/"},"integrity":"sha512-57LS92X5Fc2ABJRFnxf5cn6mvpgSL8fImbXgZ6hHYKhN1rdCdeJEG8/xalnadCTs7PN1drDEKtipBhSRA/utnw==","shasum":"1ac47157e68a96cee6a4253e58d2f853e434dd33","readme":"# @playlive/react-pipeline\n\nReact bindings for [`@playlive/realtime-pipeline`](../realtime-pipeline/).\nDrop-in replacement for `playlive-overlay-data-layer/src/websocket/*`\nwith **zero** dependency on `react-use-websocket-lite` — built directly\non Zustand v5's `useStore` + `useSyncExternalStore`.\n\n![Coverage](./coverage-badge.svg)\n\n## Three tiers — take only what you need\n\nPick the surface that matches how much of the legacy\n`UnifiedDataPipelineProvider` API you actually want. Each tier is\nstrictly larger than the last and lives at its own subpath so unused\ncode is tree-shaken out.\n\n| Tier | Subpath                            | Surface                                                                         | Use when                                                              |\n| ---- | ---------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| 1    | `@playlive/react-pipeline`         | `RealtimePipelineProvider` + `usePipelineValue` (universal selector)            | Greenfield app; you want one selector primitive and nothing else.     |\n| 2    | `@playlive/react-pipeline`         | Tier 1 **plus** 21 typed per-slice hooks (`usePipelineDonations(id)`, …)        | You want stable, typed one-liners for the common reads.               |\n| 3    | `@playlive/react-pipeline/provider`| Tier 2 **plus** `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, etc.   | Porting an existing overlay-data-layer consumer with minimal churn.   |\n| —    | `@playlive/react-pipeline/legacy`  | Tier 3 **plus** `useUDPStore` / `useUDPStoreApi` / `createUDPStore` aliases     | Soft-deprecated shim for the legacy symbol names (one-release window).|\n\n## Install\n\n```bash\nbun add @playlive/react-pipeline\nbun add -d react react-dom zustand\n```\n\nPeer dependencies (jose-style — consumer brings their own):\n`react@^19`, `react-dom@^19`, `zustand@^5`,\n`@playlive/realtime-pipeline` (workspace), `@playlive/tiltify-core`\n(workspace, type-only — stripped at compile time).\n\nNothing else ships in the bundle.\n\n## Quick start — Tier 1\n\n```tsx\nimport { RealtimePipelineProvider, usePipelineCampaigns } from \"@playlive/react-pipeline\";\n\nfunction App() {\n  return (\n    <RealtimePipelineProvider\n      url=\"wss://pipeline.example.com/socket\"\n      charityType=\"tiltify\"\n      autoConnect\n    >\n      <Overlay />\n    </RealtimePipelineProvider>\n  );\n}\n\nfunction Overlay() {\n  const campaigns = usePipelineCampaigns();\n  return <ul>{campaigns.map((c) => <li key={c.id}>{c.name}</li>)}</ul>;\n}\n```\n\n`RealtimePipelineProvider` mounts a Zustand store + opens (or accepts)\na `PipelineConnection`. Children call any Tier 1 / Tier 2 hook to\nsubscribe.\n\n## Subpath exports\n\n| Subpath                              | Description                                                                       |\n| ------------------------------------ | --------------------------------------------------------------------------------- |\n| `@playlive/react-pipeline`           | Default barrel — Tier 1 + Tier 2 (provider + 21 typed slice hooks).               |\n| `@playlive/react-pipeline/provider`  | Tier 3 — `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, add-pipeline\\*. |\n| `@playlive/react-pipeline/legacy`    | Soft-deprecated alias bundle (`useUDPStore`, `createUDPStore`, `UDPStore`, etc.). |\n\nEach subpath ships an ESM bundle, a Bun source condition, and `.d.ts`\ndeclarations. Frontend-eligible bundles are not minified (PRD §6.1)\nso stack traces stay readable in production.\n\n## API reference\n\n### Tier 1 — universal primitive\n\n| Export                          | Signature                                                            |\n| ------------------------------- | -------------------------------------------------------------------- |\n| `RealtimePipelineProvider`      | `(props: RealtimePipelineProviderProps) => JSX.Element`              |\n| `usePipelineValue`              | `<T>(selector: (s: PipelineStore) => T) => T`                        |\n\n`usePipelineValue` is single-arg by design. Zustand v5 dropped the\nthird `equalityFn` parameter on `useStore` to keep the package\nfree of the `use-sync-external-store` shim peer. For shallow / custom\nequality, wrap your selector with `useShallow` from\n`zustand/shallow` (zero-extra-dep):\n\n```tsx\nimport { useShallow } from \"zustand/shallow\";\nimport { usePipelineValue } from \"@playlive/react-pipeline\";\n\nconst { connected, connecting } = usePipelineValue(\n  useShallow((s) => ({ connected: s.connected, connecting: s.connecting })),\n);\n```\n\n### Tier 2 — typed per-slice hooks\n\nAll 21 hooks are stable wrappers over `usePipelineValue` with a typed\nselector + memo-stable empty-array fallbacks (no infinite-render\nchurn from fresh `[]` allocations — Decision §3 in CHANGELOG).\n\n| Hook                              | Returns                                                             |\n| --------------------------------- | ------------------------------------------------------------------- |\n| `usePipelineCampaigns()`          | `TiltifyCampaign[]`                                                 |\n| `usePipelineTeamCampaigns()`      | `TiltifyTeamCampaign[]`                                             |\n| `usePipelineFundraisingEvents()`  | `TiltifyFundraisingEvent[]`                                         |\n| `usePipelineCauses()`             | `TiltifyCause[]`                                                    |\n| `usePipelineDonations()`          | `Record<string, TiltifyDonationWithTestFlag[]>`                     |\n| `usePipelineDonations(id)`        | `TiltifyDonationWithTestFlag[]` (memo-stable `[]` if id unknown)    |\n| `usePipelineDonationTrains(id, withStatus?)` | `DonationTrain[]`                                        |\n| `usePipelineSubathonTimer(id, withStatus?)`  | `SubathonTimer[]`                                        |\n| `usePipelineDonorSpotlight(id)`   | `ComputedSpotlight \\| null`                                         |\n| `usePipelinePolls(id)`            | `TiltifyPoll[]`                                                     |\n| `usePipelineRewards(id)`          | `TiltifyReward[]`                                                   |\n| `usePipelineTargets(id)`          | `TiltifyTarget[]`                                                   |\n| `usePipelineMilestones(id)`       | `TiltifyMilestone[]`                                                |\n| `usePipelineLeaderboardEntries(id)` | `TiltifyLeaderboardEntry[]`                                       |\n| `usePipelineAuctionHouses()`      | `AuctionHouse[]`                                                    |\n| `usePipelineTwitchChat()`         | `TwitchChatMessage[]`                                               |\n| `usePipelineConnectionState()`    | `PipelineConnectionState` (10 split selectors — no false re-renders)|\n| `usePipelineConnectionActions()`  | `PipelineConnectionActions` (stable `connect/disconnect/refresh/…`) |\n| `usePipelineSubscriptions()`      | `Record<string, TiltifyWebhookSubscription>`                        |\n| `usePipelineTiltifySettings()`    | `TiltifyWebSocketSettings`                                          |\n| `usePipelineLastRefreshResponse()`| `RefreshResponseMessage \\| null`                                    |\n| `usePipelineDonorSpotlightSettings()` | `Record<string, DonorSpotlightSettings>`                        |\n\n### Tier 3 — kitchen-sink provider (`/provider`)\n\n| Export                              | Notes                                                                                  |\n| ----------------------------------- | -------------------------------------------------------------------------------------- |\n| `UnifiedDataPipelineProvider`       | Superset of `RealtimePipelineProvider`; adds legacy `reloadOnErrorReconnect` + `debug`.|\n| `useUnifiedDataPipeline()`          | One-call kitchen-sink hook. Returns every slice + actions + legacy field aliases.      |\n| `useAddPipelineCampaignIDs(ids)`    | Effect hook — registers campaign ids with the store on mount.                          |\n| `useAddPipelineCampaigns(items)`    | Effect hook — registers full campaign records on mount.                                |\n| `useAddPipelineFundraisingEventIDs(ids)` | Effect hook — registers fundraising-event ids on mount.                           |\n| `useCommonContextHooks(…)`          | URL / mode / charity-type sync to the store. Same shape as the legacy hook.            |\n| `useIdentify()` / `useIdentify(…11 legacy args)` | Zero-arg returns the store's bound `identify` action. Positional arg form is preserved as an `@deprecated` overload — args are ignored, warns once. |\n\n### Legacy aliases (`/legacy`)\n\nSoft-deprecated. Identical behaviour to the canonical exports —\nrename the import specifier once and the rest of your code base\nkeeps working unchanged:\n\n| Legacy name            | Canonical replacement                                          |\n| ---------------------- | -------------------------------------------------------------- |\n| `useUDPStore`          | `usePipelineValue` (Tier 1) / typed slice hooks (Tier 2)       |\n| `useUDPStoreApi`       | (use a typed slice hook; or reach for the raw store API)       |\n| `createUDPStore`       | `createPipelineStore` from `@playlive/realtime-pipeline/store` |\n| `UDPStore` (type)      | `PipelineStore` from `@playlive/realtime-pipeline/store`       |\n| `UDPStoreApi` (type)   | `PipelineStoreApi` from `@playlive/realtime-pipeline/store`    |\n| `CharityTypes`         | re-exported from `@playlive/realtime-pipeline/protocol`         |\n\nPlus every Tier 1+2+3 export under its canonical name.\n\n## Migration from `playlive-overlay-data-layer`\n\nOne-shot sed-friendly rewrite of every import specifier (no body\nedits needed for the happy path):\n\n```bash\n# Tier 3 — kitchen-sink (drop-in replacement)\nrg -l '@playlive/overlay-data-layer/websocket' . \\\n  | xargs sed -i '' \\\n    -e 's|@playlive/overlay-data-layer/websocket|@playlive/react-pipeline/provider|g'\n```\n\nFor the soft-deprecated names (`useUDPStore`, `createUDPStore`, …)\nsubstitute `/legacy` instead of `/provider` to opt into the alias\nbundle, then migrate at your leisure (PRD §10 — one-release window).\n\nSee the phase-8 row in the root [MIGRATION.md](../../MIGRATION.md) for\nthe full per-symbol mapping plus call-site walkthroughs.\n\n## Upstream spec\n\nNo external API surface — internal workspace package. All transport\nruns through `@playlive/realtime-pipeline/connection`.\n\n## Twitch Extension URL disclosure\n\n`KNOWN_URLS` enumerates every absolute URL or host this package can\nfetch. **It is empty.** This package opens WebSocket connections only\nto URLs the consumer passes to `<RealtimePipelineProvider url={…}>`;\nthose URLs must be added to your Extension submission's URL\ndisclosure list, not this one.\n\n```ts\nimport { KNOWN_URLS } from \"@playlive/react-pipeline\";\nconsole.log(KNOWN_URLS); // []\n```\n\nSee [`docs/twitch-extension-checklist.md`](../../docs/twitch-extension-checklist.md).\n\n## Examples\n\nLive overlay apps consuming this provider land under `apps/*`\n(phase 10). Until then, the Tier 1 quick-start above is the minimum\nviable wiring.\n\n## Contributing\n\nSee [CONTRIBUTING.md](../../CONTRIBUTING.md). To add new Tier-2\nhooks, mirror an existing slice hook in `src/hooks/index.ts` and\nadd a `it.each` entry in `tests/unit/index.test.ts`.\n\n## License\n\nMIT — see [LICENSE](../../LICENSE). Distributed via Play Live\nCodeArtifact (PRD §6).\n","readmeFilename":"README.md","dist":{"tarball":"https://packages.playlive.experience.stjude.org/@playlive/react-pipeline/-/react-pipeline-0.1.0.tgz","shasum":"1ac47157e68a96cee6a4253e58d2f853e434dd33","integrity":"sha512-57LS92X5Fc2ABJRFnxf5cn6mvpgSL8fImbXgZ6hHYKhN1rdCdeJEG8/xalnadCTs7PN1drDEKtipBhSRA/utnw=="}},"0.1.1":{"name":"@playlive/react-pipeline","version":"0.1.1","description":"React bindings for @playlive/realtime-pipeline — three usage tiers (single selector / typed per-slice / kitchen-sink). Twitch-Extension safe; no react-use-websocket-lite.","type":"module","sideEffects":false,"main":"./index.js","types":"./index.d.ts","exports":{".":{"import":"./index.js","types":"./index.d.ts"},"./provider":{"import":"./provider/index.js","types":"./provider/index.d.ts"},"./legacy":{"import":"./legacy/index.js","types":"./legacy/index.d.ts"}},"dependencies":{"@playlive/realtime-pipeline":"^0.1.1"},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","zustand":"^5.0.0","@playlive/realtime-pipeline":"^0.1.1","@playlive/tiltify-core":"^0.1.1"},"playlive":{"target":"browser","frontendEligible":true,"coverageFloor":85},"publishConfig":{"access":"restricted","registry":"https://playlive-767397689694.d.codeartifact.us-east-1.amazonaws.com/npm/playlive/"},"integrity":"sha512-6fLmVMUgI/nw0NeDIQ10NAFxu8mRPtv3reHSk245aPKN+WoNzWJ7MpsZk6FEqNKDsCzC7dYC234fEQJ9DEGIzg==","shasum":"6c35428a2df813fccb4bd94944a9fcec83c6321c","readme":"# @playlive/react-pipeline\n\nReact bindings for [`@playlive/realtime-pipeline`](../realtime-pipeline/).\nDrop-in replacement for `playlive-overlay-data-layer/src/websocket/*`\nwith **zero** dependency on `react-use-websocket-lite` — built directly\non Zustand v5's `useStore` + `useSyncExternalStore`.\n\n![Coverage](./coverage-badge.svg)\n\n## Three tiers — take only what you need\n\nPick the surface that matches how much of the legacy\n`UnifiedDataPipelineProvider` API you actually want. Each tier is\nstrictly larger than the last and lives at its own subpath so unused\ncode is tree-shaken out.\n\n| Tier | Subpath                            | Surface                                                                         | Use when                                                              |\n| ---- | ---------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| 1    | `@playlive/react-pipeline`         | `RealtimePipelineProvider` + `usePipelineValue` (universal selector)            | Greenfield app; you want one selector primitive and nothing else.     |\n| 2    | `@playlive/react-pipeline`         | Tier 1 **plus** 21 typed per-slice hooks (`usePipelineDonations(id)`, …)        | You want stable, typed one-liners for the common reads.               |\n| 3    | `@playlive/react-pipeline/provider`| Tier 2 **plus** `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, etc.   | Porting an existing overlay-data-layer consumer with minimal churn.   |\n| —    | `@playlive/react-pipeline/legacy`  | Tier 3 **plus** `useUDPStore` / `useUDPStoreApi` / `createUDPStore` aliases     | Soft-deprecated shim for the legacy symbol names (one-release window).|\n\n## Install\n\n```bash\nbun add @playlive/react-pipeline\nbun add -d react react-dom zustand\n```\n\nPeer dependencies (jose-style — consumer brings their own):\n`react@^19`, `react-dom@^19`, `zustand@^5`,\n`@playlive/realtime-pipeline` (workspace), `@playlive/tiltify-core`\n(workspace, type-only — stripped at compile time).\n\nNothing else ships in the bundle.\n\n## Quick start — Tier 1\n\n```tsx\nimport { RealtimePipelineProvider, usePipelineCampaigns } from \"@playlive/react-pipeline\";\n\nfunction App() {\n  return (\n    <RealtimePipelineProvider\n      url=\"wss://pipeline.example.com/socket\"\n      charityType=\"tiltify\"\n      autoConnect\n    >\n      <Overlay />\n    </RealtimePipelineProvider>\n  );\n}\n\nfunction Overlay() {\n  const campaigns = usePipelineCampaigns();\n  return <ul>{campaigns.map((c) => <li key={c.id}>{c.name}</li>)}</ul>;\n}\n```\n\n`RealtimePipelineProvider` mounts a Zustand store + opens (or accepts)\na `PipelineConnection`. Children call any Tier 1 / Tier 2 hook to\nsubscribe.\n\n## Subpath exports\n\n| Subpath                              | Description                                                                       |\n| ------------------------------------ | --------------------------------------------------------------------------------- |\n| `@playlive/react-pipeline`           | Default barrel — Tier 1 + Tier 2 (provider + 21 typed slice hooks).               |\n| `@playlive/react-pipeline/provider`  | Tier 3 — `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, add-pipeline\\*. |\n| `@playlive/react-pipeline/legacy`    | Soft-deprecated alias bundle (`useUDPStore`, `createUDPStore`, `UDPStore`, etc.). |\n\nEach subpath ships an ESM bundle, a Bun source condition, and `.d.ts`\ndeclarations. Frontend-eligible bundles are not minified (PRD §6.1)\nso stack traces stay readable in production.\n\n## API reference\n\n### Tier 1 — universal primitive\n\n| Export                          | Signature                                                            |\n| ------------------------------- | -------------------------------------------------------------------- |\n| `RealtimePipelineProvider`      | `(props: RealtimePipelineProviderProps) => JSX.Element`              |\n| `usePipelineValue`              | `<T>(selector: (s: PipelineStore) => T) => T`                        |\n\n`usePipelineValue` is single-arg by design. Zustand v5 dropped the\nthird `equalityFn` parameter on `useStore` to keep the package\nfree of the `use-sync-external-store` shim peer. For shallow / custom\nequality, wrap your selector with `useShallow` from\n`zustand/shallow` (zero-extra-dep):\n\n```tsx\nimport { useShallow } from \"zustand/shallow\";\nimport { usePipelineValue } from \"@playlive/react-pipeline\";\n\nconst { connected, connecting } = usePipelineValue(\n  useShallow((s) => ({ connected: s.connected, connecting: s.connecting })),\n);\n```\n\n### Tier 2 — typed per-slice hooks\n\nAll 21 hooks are stable wrappers over `usePipelineValue` with a typed\nselector + memo-stable empty-array fallbacks (no infinite-render\nchurn from fresh `[]` allocations — Decision §3 in CHANGELOG).\n\n| Hook                              | Returns                                                             |\n| --------------------------------- | ------------------------------------------------------------------- |\n| `usePipelineCampaigns()`          | `TiltifyCampaign[]`                                                 |\n| `usePipelineTeamCampaigns()`      | `TiltifyTeamCampaign[]`                                             |\n| `usePipelineFundraisingEvents()`  | `TiltifyFundraisingEvent[]`                                         |\n| `usePipelineCauses()`             | `TiltifyCause[]`                                                    |\n| `usePipelineDonations()`          | `Record<string, TiltifyDonationWithTestFlag[]>`                     |\n| `usePipelineDonations(id)`        | `TiltifyDonationWithTestFlag[]` (memo-stable `[]` if id unknown)    |\n| `usePipelineDonationTrains(id, withStatus?)` | `DonationTrain[]`                                        |\n| `usePipelineSubathonTimer(id, withStatus?)`  | `SubathonTimer[]`                                        |\n| `usePipelineDonorSpotlight(id)`   | `ComputedSpotlight \\| null`                                         |\n| `usePipelinePolls(id)`            | `TiltifyPoll[]`                                                     |\n| `usePipelineRewards(id)`          | `TiltifyReward[]`                                                   |\n| `usePipelineTargets(id)`          | `TiltifyTarget[]`                                                   |\n| `usePipelineMilestones(id)`       | `TiltifyMilestone[]`                                                |\n| `usePipelineLeaderboardEntries(id)` | `TiltifyLeaderboardEntry[]`                                       |\n| `usePipelineAuctionHouses()`      | `AuctionHouse[]`                                                    |\n| `usePipelineTwitchChat()`         | `TwitchChatMessage[]`                                               |\n| `usePipelineConnectionState()`    | `PipelineConnectionState` (10 split selectors — no false re-renders)|\n| `usePipelineConnectionActions()`  | `PipelineConnectionActions` (stable `connect/disconnect/refresh/…`) |\n| `usePipelineSubscriptions()`      | `Record<string, TiltifyWebhookSubscription>`                        |\n| `usePipelineTiltifySettings()`    | `TiltifyWebSocketSettings`                                          |\n| `usePipelineLastRefreshResponse()`| `RefreshResponseMessage \\| null`                                    |\n| `usePipelineDonorSpotlightSettings()` | `Record<string, DonorSpotlightSettings>`                        |\n\n### Tier 3 — kitchen-sink provider (`/provider`)\n\n| Export                              | Notes                                                                                  |\n| ----------------------------------- | -------------------------------------------------------------------------------------- |\n| `UnifiedDataPipelineProvider`       | Superset of `RealtimePipelineProvider`; adds legacy `reloadOnErrorReconnect` + `debug`.|\n| `useUnifiedDataPipeline()`          | One-call kitchen-sink hook. Returns every slice + actions + legacy field aliases.      |\n| `useAddPipelineCampaignIDs(ids)`    | Effect hook — registers campaign ids with the store on mount.                          |\n| `useAddPipelineCampaigns(items)`    | Effect hook — registers full campaign records on mount.                                |\n| `useAddPipelineFundraisingEventIDs(ids)` | Effect hook — registers fundraising-event ids on mount.                           |\n| `useCommonContextHooks(…)`          | URL / mode / charity-type sync to the store. Same shape as the legacy hook.            |\n| `useIdentify()` / `useIdentify(…11 legacy args)` | Zero-arg returns the store's bound `identify` action. Positional arg form is preserved as an `@deprecated` overload — args are ignored, warns once. |\n\n### Legacy aliases (`/legacy`)\n\nSoft-deprecated. Identical behaviour to the canonical exports —\nrename the import specifier once and the rest of your code base\nkeeps working unchanged:\n\n| Legacy name            | Canonical replacement                                          |\n| ---------------------- | -------------------------------------------------------------- |\n| `useUDPStore`          | `usePipelineValue` (Tier 1) / typed slice hooks (Tier 2)       |\n| `useUDPStoreApi`       | (use a typed slice hook; or reach for the raw store API)       |\n| `createUDPStore`       | `createPipelineStore` from `@playlive/realtime-pipeline/store` |\n| `UDPStore` (type)      | `PipelineStore` from `@playlive/realtime-pipeline/store`       |\n| `UDPStoreApi` (type)   | `PipelineStoreApi` from `@playlive/realtime-pipeline/store`    |\n| `CharityTypes`         | re-exported from `@playlive/realtime-pipeline/protocol`         |\n\nPlus every Tier 1+2+3 export under its canonical name.\n\n## Migration from `playlive-overlay-data-layer`\n\nOne-shot sed-friendly rewrite of every import specifier (no body\nedits needed for the happy path):\n\n```bash\n# Tier 3 — kitchen-sink (drop-in replacement)\nrg -l '@playlive/overlay-data-layer/websocket' . \\\n  | xargs sed -i '' \\\n    -e 's|@playlive/overlay-data-layer/websocket|@playlive/react-pipeline/provider|g'\n```\n\nFor the soft-deprecated names (`useUDPStore`, `createUDPStore`, …)\nsubstitute `/legacy` instead of `/provider` to opt into the alias\nbundle, then migrate at your leisure (PRD §10 — one-release window).\n\nSee the phase-8 row in the root [MIGRATION.md](../../MIGRATION.md) for\nthe full per-symbol mapping plus call-site walkthroughs.\n\n## Upstream spec\n\nNo external API surface — internal workspace package. All transport\nruns through `@playlive/realtime-pipeline/connection`.\n\n## Twitch Extension URL disclosure\n\n`KNOWN_URLS` enumerates every absolute URL or host this package can\nfetch. **It is empty.** This package opens WebSocket connections only\nto URLs the consumer passes to `<RealtimePipelineProvider url={…}>`;\nthose URLs must be added to your Extension submission's URL\ndisclosure list, not this one.\n\n```ts\nimport { KNOWN_URLS } from \"@playlive/react-pipeline\";\nconsole.log(KNOWN_URLS); // []\n```\n\nSee [`docs/twitch-extension-checklist.md`](../../docs/twitch-extension-checklist.md).\n\n## Examples\n\nLive overlay apps consuming this provider land under `apps/*`\n(phase 10). Until then, the Tier 1 quick-start above is the minimum\nviable wiring.\n\n## Contributing\n\nSee [CONTRIBUTING.md](../../CONTRIBUTING.md). To add new Tier-2\nhooks, mirror an existing slice hook in `src/hooks/index.ts` and\nadd a `it.each` entry in `tests/unit/index.test.ts`.\n\n## License\n\nMIT — see [LICENSE](../../LICENSE). Distributed via Play Live\nCodeArtifact (PRD §6).\n","readmeFilename":"README.md","dist":{"tarball":"https://packages.playlive.experience.stjude.org/@playlive/react-pipeline/-/react-pipeline-0.1.1.tgz","shasum":"6c35428a2df813fccb4bd94944a9fcec83c6321c","integrity":"sha512-6fLmVMUgI/nw0NeDIQ10NAFxu8mRPtv3reHSk245aPKN+WoNzWJ7MpsZk6FEqNKDsCzC7dYC234fEQJ9DEGIzg=="}},"0.2.0":{"name":"@playlive/react-pipeline","version":"0.2.0","description":"React bindings for @playlive/realtime-pipeline — three usage tiers (single selector / typed per-slice / kitchen-sink). Twitch-Extension safe; no react-use-websocket-lite.","type":"module","sideEffects":false,"main":"./index.js","types":"./index.d.ts","exports":{".":{"import":"./index.js","types":"./index.d.ts"},"./provider":{"import":"./provider/index.js","types":"./provider/index.d.ts"},"./legacy":{"import":"./legacy/index.js","types":"./legacy/index.d.ts"}},"dependencies":{"@playlive/realtime-pipeline":"^0.2.0"},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","zustand":"^5.0.0","@playlive/realtime-pipeline":"^0.2.0","@playlive/tiltify-core":"^0.1.1"},"playlive":{"target":"browser","frontendEligible":true,"coverageFloor":85},"publishConfig":{"access":"restricted","registry":"https://playlive-767397689694.d.codeartifact.us-east-1.amazonaws.com/npm/playlive/"},"integrity":"sha512-LxLB/2VpdxqC4qJHSjcC9WhJ5fYnX3OLrVW8mTBONcq7ZsrlikHVnKwjcmJDJ9FSjXkuZ9sXtsfgrBtDbOcsbQ==","shasum":"ff702e63e9e35e16a3db68ac8298c7ccdfc744f6","readme":"# @playlive/react-pipeline\n\nReact bindings for [`@playlive/realtime-pipeline`](../realtime-pipeline/).\nDrop-in replacement for `playlive-overlay-data-layer/src/websocket/*`\nwith **zero** dependency on `react-use-websocket-lite` — built directly\non Zustand v5's `useStore` + `useSyncExternalStore`.\n\n![Coverage](./coverage-badge.svg)\n\n## Three tiers — take only what you need\n\nPick the surface that matches how much of the legacy\n`UnifiedDataPipelineProvider` API you actually want. Each tier is\nstrictly larger than the last and lives at its own subpath so unused\ncode is tree-shaken out.\n\n| Tier | Subpath                            | Surface                                                                         | Use when                                                              |\n| ---- | ---------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| 1    | `@playlive/react-pipeline`         | `RealtimePipelineProvider` + `usePipelineValue` (universal selector)            | Greenfield app; you want one selector primitive and nothing else.     |\n| 2    | `@playlive/react-pipeline`         | Tier 1 **plus** 21 typed per-slice hooks (`usePipelineDonations(id)`, …)        | You want stable, typed one-liners for the common reads.               |\n| 3    | `@playlive/react-pipeline/provider`| Tier 2 **plus** `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, etc.   | Porting an existing overlay-data-layer consumer with minimal churn.   |\n| —    | `@playlive/react-pipeline/legacy`  | Tier 3 **plus** `useUDPStore` / `useUDPStoreApi` / `createUDPStore` aliases     | Soft-deprecated shim for the legacy symbol names (one-release window).|\n\n## Install\n\n```bash\nbun add @playlive/react-pipeline\nbun add -d react react-dom zustand\n```\n\nPeer dependencies (jose-style — consumer brings their own):\n`react@^19`, `react-dom@^19`, `zustand@^5`,\n`@playlive/realtime-pipeline` (workspace), `@playlive/tiltify-core`\n(workspace, type-only — stripped at compile time).\n\nNothing else ships in the bundle.\n\n## Quick start — Tier 1\n\n```tsx\nimport { RealtimePipelineProvider, usePipelineCampaigns } from \"@playlive/react-pipeline\";\n\nfunction App() {\n  return (\n    <RealtimePipelineProvider\n      url=\"wss://pipeline.example.com/socket\"\n      charityType=\"tiltify\"\n      autoConnect\n    >\n      <Overlay />\n    </RealtimePipelineProvider>\n  );\n}\n\nfunction Overlay() {\n  const campaigns = usePipelineCampaigns();\n  return <ul>{campaigns.map((c) => <li key={c.id}>{c.name}</li>)}</ul>;\n}\n```\n\n`RealtimePipelineProvider` mounts a Zustand store + opens (or accepts)\na `PipelineConnection`. Children call any Tier 1 / Tier 2 hook to\nsubscribe.\n\n## Subpath exports\n\n| Subpath                              | Description                                                                       |\n| ------------------------------------ | --------------------------------------------------------------------------------- |\n| `@playlive/react-pipeline`           | Default barrel — Tier 1 + Tier 2 (provider + 21 typed slice hooks).               |\n| `@playlive/react-pipeline/provider`  | Tier 3 — `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, add-pipeline\\*. |\n| `@playlive/react-pipeline/legacy`    | Soft-deprecated alias bundle (`useUDPStore`, `createUDPStore`, `UDPStore`, etc.). |\n\nEach subpath ships an ESM bundle, a Bun source condition, and `.d.ts`\ndeclarations. Frontend-eligible bundles are not minified (PRD §6.1)\nso stack traces stay readable in production.\n\n## API reference\n\n### Tier 1 — universal primitive\n\n| Export                          | Signature                                                            |\n| ------------------------------- | -------------------------------------------------------------------- |\n| `RealtimePipelineProvider`      | `(props: RealtimePipelineProviderProps) => JSX.Element`              |\n| `usePipelineValue`              | `<T>(selector: (s: PipelineStore) => T) => T`                        |\n\n`usePipelineValue` is single-arg by design. Zustand v5 dropped the\nthird `equalityFn` parameter on `useStore` to keep the package\nfree of the `use-sync-external-store` shim peer. For shallow / custom\nequality, wrap your selector with `useShallow` from\n`zustand/shallow` (zero-extra-dep):\n\n```tsx\nimport { useShallow } from \"zustand/shallow\";\nimport { usePipelineValue } from \"@playlive/react-pipeline\";\n\nconst { connected, connecting } = usePipelineValue(\n  useShallow((s) => ({ connected: s.connected, connecting: s.connecting })),\n);\n```\n\n### Tier 2 — typed per-slice hooks\n\nAll 21 hooks are stable wrappers over `usePipelineValue` with a typed\nselector + memo-stable empty-array fallbacks (no infinite-render\nchurn from fresh `[]` allocations — Decision §3 in CHANGELOG).\n\n| Hook                              | Returns                                                             |\n| --------------------------------- | ------------------------------------------------------------------- |\n| `usePipelineCampaigns()`          | `TiltifyCampaign[]`                                                 |\n| `usePipelineTeamCampaigns()`      | `TiltifyTeamCampaign[]`                                             |\n| `usePipelineFundraisingEvents()`  | `TiltifyFundraisingEvent[]`                                         |\n| `usePipelineCauses()`             | `TiltifyCause[]`                                                    |\n| `usePipelineDonations()`          | `Record<string, TiltifyDonationWithTestFlag[]>`                     |\n| `usePipelineDonations(id)`        | `TiltifyDonationWithTestFlag[]` (memo-stable `[]` if id unknown)    |\n| `usePipelineDonationTrains(id, withStatus?)` | `DonationTrain[]`                                        |\n| `usePipelineSubathonTimer(id, withStatus?)`  | `SubathonTimer[]`                                        |\n| `usePipelineDonorSpotlight(id)`   | `ComputedSpotlight \\| null`                                         |\n| `usePipelinePolls(id)`            | `TiltifyPoll[]`                                                     |\n| `usePipelineRewards(id)`          | `TiltifyReward[]`                                                   |\n| `usePipelineTargets(id)`          | `TiltifyTarget[]`                                                   |\n| `usePipelineMilestones(id)`       | `TiltifyMilestone[]`                                                |\n| `usePipelineLeaderboardEntries(id)` | `TiltifyLeaderboardEntry[]`                                       |\n| `usePipelineAuctionHouses()`      | `AuctionHouse[]`                                                    |\n| `usePipelineTwitchChat()`         | `TwitchChatMessage[]`                                               |\n| `usePipelineConnectionState()`    | `PipelineConnectionState` (10 split selectors — no false re-renders)|\n| `usePipelineConnectionActions()`  | `PipelineConnectionActions` (stable `connect/disconnect/refresh/…`) |\n| `usePipelineSubscriptions()`      | `Record<string, TiltifyWebhookSubscription>`                        |\n| `usePipelineTiltifySettings()`    | `TiltifyWebSocketSettings`                                          |\n| `usePipelineLastRefreshResponse()`| `RefreshResponseMessage \\| null`                                    |\n| `usePipelineDonorSpotlightSettings()` | `Record<string, DonorSpotlightSettings>`                        |\n\n### Tier 3 — kitchen-sink provider (`/provider`)\n\n| Export                              | Notes                                                                                  |\n| ----------------------------------- | -------------------------------------------------------------------------------------- |\n| `UnifiedDataPipelineProvider`       | Superset of `RealtimePipelineProvider`; adds legacy `reloadOnErrorReconnect` + `debug`.|\n| `useUnifiedDataPipeline()`          | One-call kitchen-sink hook. Returns every slice + actions + legacy field aliases.      |\n| `useAddPipelineCampaignIDs(ids)`    | Effect hook — registers campaign ids with the store on mount.                          |\n| `useAddPipelineCampaigns(items)`    | Effect hook — registers full campaign records on mount.                                |\n| `useAddPipelineFundraisingEventIDs(ids)` | Effect hook — registers fundraising-event ids on mount.                           |\n| `useCommonContextHooks(…)`          | URL / mode / charity-type sync to the store. Same shape as the legacy hook.            |\n| `useIdentify()` / `useIdentify(…11 legacy args)` | Zero-arg returns the store's bound `identify` action. Positional arg form is preserved as an `@deprecated` overload — args are ignored, warns once. |\n\n### Legacy aliases (`/legacy`)\n\nSoft-deprecated. Identical behaviour to the canonical exports —\nrename the import specifier once and the rest of your code base\nkeeps working unchanged:\n\n| Legacy name            | Canonical replacement                                          |\n| ---------------------- | -------------------------------------------------------------- |\n| `useUDPStore`          | `usePipelineValue` (Tier 1) / typed slice hooks (Tier 2)       |\n| `useUDPStoreApi`       | (use a typed slice hook; or reach for the raw store API)       |\n| `createUDPStore`       | `createPipelineStore` from `@playlive/realtime-pipeline/store` |\n| `UDPStore` (type)      | `PipelineStore` from `@playlive/realtime-pipeline/store`       |\n| `UDPStoreApi` (type)   | `PipelineStoreApi` from `@playlive/realtime-pipeline/store`    |\n| `CharityTypes`         | re-exported from `@playlive/realtime-pipeline/protocol`         |\n\nPlus every Tier 1+2+3 export under its canonical name.\n\n## Migration from `playlive-overlay-data-layer`\n\nOne-shot sed-friendly rewrite of every import specifier (no body\nedits needed for the happy path):\n\n```bash\n# Tier 3 — kitchen-sink (drop-in replacement)\nrg -l '@playlive/overlay-data-layer/websocket' . \\\n  | xargs sed -i '' \\\n    -e 's|@playlive/overlay-data-layer/websocket|@playlive/react-pipeline/provider|g'\n```\n\nFor the soft-deprecated names (`useUDPStore`, `createUDPStore`, …)\nsubstitute `/legacy` instead of `/provider` to opt into the alias\nbundle, then migrate at your leisure (PRD §10 — one-release window).\n\nSee the phase-8 row in the root [MIGRATION.md](../../MIGRATION.md) for\nthe full per-symbol mapping plus call-site walkthroughs.\n\n## Upstream spec\n\nNo external API surface — internal workspace package. All transport\nruns through `@playlive/realtime-pipeline/connection`.\n\n## Twitch Extension URL disclosure\n\n`KNOWN_URLS` enumerates every absolute URL or host this package can\nfetch. **It is empty.** This package opens WebSocket connections only\nto URLs the consumer passes to `<RealtimePipelineProvider url={…}>`;\nthose URLs must be added to your Extension submission's URL\ndisclosure list, not this one.\n\n```ts\nimport { KNOWN_URLS } from \"@playlive/react-pipeline\";\nconsole.log(KNOWN_URLS); // []\n```\n\nSee [`docs/twitch-extension-checklist.md`](../../docs/twitch-extension-checklist.md).\n\n## Examples\n\nLive overlay apps consuming this provider land under `apps/*`\n(phase 10). Until then, the Tier 1 quick-start above is the minimum\nviable wiring.\n\n## Contributing\n\nSee [CONTRIBUTING.md](../../CONTRIBUTING.md). To add new Tier-2\nhooks, mirror an existing slice hook in `src/hooks/index.ts` and\nadd a `it.each` entry in `tests/unit/index.test.ts`.\n\n## License\n\nMIT — see [LICENSE](../../LICENSE). Distributed via Play Live\nCodeArtifact (PRD §6).\n","readmeFilename":"README.md","dist":{"tarball":"https://packages.playlive.experience.stjude.org/@playlive/react-pipeline/-/react-pipeline-0.2.0.tgz","shasum":"ff702e63e9e35e16a3db68ac8298c7ccdfc744f6","integrity":"sha512-LxLB/2VpdxqC4qJHSjcC9WhJ5fYnX3OLrVW8mTBONcq7ZsrlikHVnKwjcmJDJ9FSjXkuZ9sXtsfgrBtDbOcsbQ=="}},"0.2.1":{"name":"@playlive/react-pipeline","version":"0.2.1","description":"React bindings for @playlive/realtime-pipeline — three usage tiers (single selector / typed per-slice / kitchen-sink). Twitch-Extension safe; no react-use-websocket-lite.","type":"module","sideEffects":false,"main":"./index.js","types":"./index.d.ts","exports":{".":{"import":"./index.js","types":"./index.d.ts"},"./provider":{"import":"./provider/index.js","types":"./provider/index.d.ts"},"./legacy":{"import":"./legacy/index.js","types":"./legacy/index.d.ts"}},"dependencies":{"@playlive/realtime-pipeline":"^0.2.1"},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","zustand":"^5.0.0","@playlive/realtime-pipeline":"^0.2.1","@playlive/tiltify-core":"^0.4.4"},"playlive":{"target":"browser","frontendEligible":true,"coverageFloor":85},"publishConfig":{"access":"restricted","registry":"https://playlive-767397689694.d.codeartifact.us-east-1.amazonaws.com/npm/playlive/"},"integrity":"sha512-r3/Dj/zJ3/qhv716StdszSUVUAlhUFfixkUqwKH5snEfiVQzROr/PRqC8yrZTUF7Nvw3jcmXFe68JyboKOKgpw==","shasum":"f8195ce717c9488b2b751aed7578783027fc1efe","readme":"# @playlive/react-pipeline\n\nReact bindings for [`@playlive/realtime-pipeline`](../realtime-pipeline/).\nDrop-in replacement for `playlive-overlay-data-layer/src/websocket/*`\nwith **zero** dependency on `react-use-websocket-lite` — built directly\non Zustand v5's `useStore` + `useSyncExternalStore`.\n\n![Coverage](./coverage-badge.svg)\n\n## Three tiers — take only what you need\n\nPick the surface that matches how much of the legacy\n`UnifiedDataPipelineProvider` API you actually want. Each tier is\nstrictly larger than the last and lives at its own subpath so unused\ncode is tree-shaken out.\n\n| Tier | Subpath                            | Surface                                                                         | Use when                                                              |\n| ---- | ---------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| 1    | `@playlive/react-pipeline`         | `RealtimePipelineProvider` + `usePipelineValue` (universal selector)            | Greenfield app; you want one selector primitive and nothing else.     |\n| 2    | `@playlive/react-pipeline`         | Tier 1 **plus** 21 typed per-slice hooks (`usePipelineDonations(id)`, …)        | You want stable, typed one-liners for the common reads.               |\n| 3    | `@playlive/react-pipeline/provider`| Tier 2 **plus** `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, etc.   | Porting an existing overlay-data-layer consumer with minimal churn.   |\n| —    | `@playlive/react-pipeline/legacy`  | Tier 3 **plus** `useUDPStore` / `useUDPStoreApi` / `createUDPStore` aliases     | Soft-deprecated shim for the legacy symbol names (one-release window).|\n\n## Install\n\n```bash\nbun add @playlive/react-pipeline\nbun add -d react react-dom zustand\n```\n\nPeer dependencies (jose-style — consumer brings their own):\n`react@^19`, `react-dom@^19`, `zustand@^5`,\n`@playlive/realtime-pipeline` (workspace), `@playlive/tiltify-core`\n(workspace, type-only — stripped at compile time).\n\nNothing else ships in the bundle.\n\n## Quick start — Tier 1\n\n```tsx\nimport { RealtimePipelineProvider, usePipelineCampaigns } from \"@playlive/react-pipeline\";\n\nfunction App() {\n  return (\n    <RealtimePipelineProvider\n      url=\"wss://pipeline.example.com/socket\"\n      charityType=\"tiltify\"\n      autoConnect\n    >\n      <Overlay />\n    </RealtimePipelineProvider>\n  );\n}\n\nfunction Overlay() {\n  const campaigns = usePipelineCampaigns();\n  return <ul>{campaigns.map((c) => <li key={c.id}>{c.name}</li>)}</ul>;\n}\n```\n\n`RealtimePipelineProvider` mounts a Zustand store + opens (or accepts)\na `PipelineConnection`. Children call any Tier 1 / Tier 2 hook to\nsubscribe.\n\n## Subpath exports\n\n| Subpath                              | Description                                                                       |\n| ------------------------------------ | --------------------------------------------------------------------------------- |\n| `@playlive/react-pipeline`           | Default barrel — Tier 1 + Tier 2 (provider + 21 typed slice hooks).               |\n| `@playlive/react-pipeline/provider`  | Tier 3 — `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, add-pipeline\\*. |\n| `@playlive/react-pipeline/legacy`    | Soft-deprecated alias bundle (`useUDPStore`, `createUDPStore`, `UDPStore`, etc.). |\n\nEach subpath ships an ESM bundle, a Bun source condition, and `.d.ts`\ndeclarations. Frontend-eligible bundles are not minified (PRD §6.1)\nso stack traces stay readable in production.\n\n## API reference\n\n### Tier 1 — universal primitive\n\n| Export                          | Signature                                                            |\n| ------------------------------- | -------------------------------------------------------------------- |\n| `RealtimePipelineProvider`      | `(props: RealtimePipelineProviderProps) => JSX.Element`              |\n| `usePipelineValue`              | `<T>(selector: (s: PipelineStore) => T) => T`                        |\n\n`usePipelineValue` is single-arg by design. Zustand v5 dropped the\nthird `equalityFn` parameter on `useStore` to keep the package\nfree of the `use-sync-external-store` shim peer. For shallow / custom\nequality, wrap your selector with `useShallow` from\n`zustand/shallow` (zero-extra-dep):\n\n```tsx\nimport { useShallow } from \"zustand/shallow\";\nimport { usePipelineValue } from \"@playlive/react-pipeline\";\n\nconst { connected, connecting } = usePipelineValue(\n  useShallow((s) => ({ connected: s.connected, connecting: s.connecting })),\n);\n```\n\n### Tier 2 — typed per-slice hooks\n\nAll 21 hooks are stable wrappers over `usePipelineValue` with a typed\nselector + memo-stable empty-array fallbacks (no infinite-render\nchurn from fresh `[]` allocations — Decision §3 in CHANGELOG).\n\n| Hook                              | Returns                                                             |\n| --------------------------------- | ------------------------------------------------------------------- |\n| `usePipelineCampaigns()`          | `TiltifyCampaign[]`                                                 |\n| `usePipelineTeamCampaigns()`      | `TiltifyTeamCampaign[]`                                             |\n| `usePipelineFundraisingEvents()`  | `TiltifyFundraisingEvent[]`                                         |\n| `usePipelineCauses()`             | `TiltifyCause[]`                                                    |\n| `usePipelineDonations()`          | `Record<string, TiltifyDonationWithTestFlag[]>`                     |\n| `usePipelineDonations(id)`        | `TiltifyDonationWithTestFlag[]` (memo-stable `[]` if id unknown)    |\n| `usePipelineDonationTrains(id, withStatus?)` | `DonationTrain[]`                                        |\n| `usePipelineSubathonTimer(id, withStatus?)`  | `SubathonTimer[]`                                        |\n| `usePipelineDonorSpotlight(id)`   | `ComputedSpotlight \\| null`                                         |\n| `usePipelinePolls(id)`            | `TiltifyPoll[]`                                                     |\n| `usePipelineRewards(id)`          | `TiltifyReward[]`                                                   |\n| `usePipelineTargets(id)`          | `TiltifyTarget[]`                                                   |\n| `usePipelineMilestones(id)`       | `TiltifyMilestone[]`                                                |\n| `usePipelineLeaderboardEntries(id)` | `TiltifyLeaderboardEntry[]`                                       |\n| `usePipelineAuctionHouses()`      | `AuctionHouse[]`                                                    |\n| `usePipelineTwitchChat()`         | `TwitchChatMessage[]`                                               |\n| `usePipelineConnectionState()`    | `PipelineConnectionState` (10 split selectors — no false re-renders)|\n| `usePipelineConnectionActions()`  | `PipelineConnectionActions` (stable `connect/disconnect/refresh/…`) |\n| `usePipelineSubscriptions()`      | `Record<string, TiltifyWebhookSubscription>`                        |\n| `usePipelineTiltifySettings()`    | `TiltifyWebSocketSettings`                                          |\n| `usePipelineLastRefreshResponse()`| `RefreshResponseMessage \\| null`                                    |\n| `usePipelineDonorSpotlightSettings()` | `Record<string, DonorSpotlightSettings>`                        |\n\n### Tier 3 — kitchen-sink provider (`/provider`)\n\n| Export                              | Notes                                                                                  |\n| ----------------------------------- | -------------------------------------------------------------------------------------- |\n| `UnifiedDataPipelineProvider`       | Superset of `RealtimePipelineProvider`; adds legacy `reloadOnErrorReconnect` + `debug`.|\n| `useUnifiedDataPipeline()`          | One-call kitchen-sink hook. Returns every slice + actions + legacy field aliases.      |\n| `useAddPipelineCampaignIDs(ids)`    | Effect hook — registers campaign ids with the store on mount.                          |\n| `useAddPipelineCampaigns(items)`    | Effect hook — registers full campaign records on mount.                                |\n| `useAddPipelineFundraisingEventIDs(ids)` | Effect hook — registers fundraising-event ids on mount.                           |\n| `useCommonContextHooks(…)`          | URL / mode / charity-type sync to the store. Same shape as the legacy hook.            |\n| `useIdentify()` / `useIdentify(…11 legacy args)` | Zero-arg returns the store's bound `identify` action. Positional arg form is preserved as an `@deprecated` overload — args are ignored, warns once. |\n\n### Legacy aliases (`/legacy`)\n\nSoft-deprecated. Identical behavior to the canonical exports —\nrename the import specifier once and the rest of your code base\nkeeps working unchanged:\n\n| Legacy name            | Canonical replacement                                          |\n| ---------------------- | -------------------------------------------------------------- |\n| `useUDPStore`          | `usePipelineValue` (Tier 1) / typed slice hooks (Tier 2)       |\n| `useUDPStoreApi`       | (use a typed slice hook; or reach for the raw store API)       |\n| `createUDPStore`       | `createPipelineStore` from `@playlive/realtime-pipeline/store` |\n| `UDPStore` (type)      | `PipelineStore` from `@playlive/realtime-pipeline/store`       |\n| `UDPStoreApi` (type)   | `PipelineStoreApi` from `@playlive/realtime-pipeline/store`    |\n| `CharityTypes`         | re-exported from `@playlive/realtime-pipeline/protocol`         |\n\nPlus every Tier 1+2+3 export under its canonical name.\n\n## Migration from `playlive-overlay-data-layer`\n\nOne-shot sed-friendly rewrite of every import specifier (no body\nedits needed for the happy path):\n\n```bash\n# Tier 3 — kitchen-sink (drop-in replacement)\nrg -l '@playlive/overlay-data-layer/websocket' . \\\n  | xargs sed -i '' \\\n    -e 's|@playlive/overlay-data-layer/websocket|@playlive/react-pipeline/provider|g'\n```\n\nFor the soft-deprecated names (`useUDPStore`, `createUDPStore`, …)\nsubstitute `/legacy` instead of `/provider` to opt into the alias\nbundle, then migrate at your leisure (PRD §10 — one-release window).\n\nSee the phase-8 row in the root [MIGRATION.md](../../MIGRATION.md) for\nthe full per-symbol mapping plus call-site walkthroughs.\n\n## Upstream spec\n\nNo external API surface — internal workspace package. All transport\nruns through `@playlive/realtime-pipeline/connection`.\n\n## Twitch Extension URL disclosure\n\n`KNOWN_URLS` enumerates every absolute URL or host this package can\nfetch. **It is empty.** This package opens WebSocket connections only\nto URLs the consumer passes to `<RealtimePipelineProvider url={…}>`;\nthose URLs must be added to your Extension submission's URL\ndisclosure list, not this one.\n\n```ts\nimport { KNOWN_URLS } from \"@playlive/react-pipeline\";\nconsole.log(KNOWN_URLS); // []\n```\n\nSee [`docs/twitch-extension-checklist.md`](../../docs/twitch-extension-checklist.md).\n\n## Examples\n\nLive overlay apps consuming this provider land under `apps/*`\n(phase 10). Until then, the Tier 1 quick-start above is the minimum\nviable wiring.\n\n## Contributing\n\nSee [CONTRIBUTING.md](../../CONTRIBUTING.md). To add new Tier-2\nhooks, mirror an existing slice hook in `src/hooks/index.ts` and\nadd a `it.each` entry in `tests/unit/index.test.ts`.\n\n## License\n\nMIT — see [LICENSE](../../LICENSE). Distributed via Play Live\nCodeArtifact (PRD §6).\n","readmeFilename":"README.md","dist":{"tarball":"https://packages.playlive.experience.stjude.org/@playlive/react-pipeline/-/react-pipeline-0.2.1.tgz","shasum":"f8195ce717c9488b2b751aed7578783027fc1efe","integrity":"sha512-r3/Dj/zJ3/qhv716StdszSUVUAlhUFfixkUqwKH5snEfiVQzROr/PRqC8yrZTUF7Nvw3jcmXFe68JyboKOKgpw=="}},"0.2.2":{"name":"@playlive/react-pipeline","version":"0.2.2","description":"React bindings for @playlive/realtime-pipeline — three usage tiers (single selector / typed per-slice / kitchen-sink). Twitch-Extension safe; no react-use-websocket-lite.","type":"module","sideEffects":false,"main":"./index.js","types":"./index.d.ts","exports":{".":{"import":"./index.js","types":"./index.d.ts"},"./provider":{"import":"./provider/index.js","types":"./provider/index.d.ts"},"./legacy":{"import":"./legacy/index.js","types":"./legacy/index.d.ts"}},"dependencies":{},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","zustand":"^5.0.0","@playlive/realtime-pipeline":"^0.2.1","@playlive/tiltify-core":"^0.4.9"},"playlive":{"target":"browser","frontendEligible":true,"coverageFloor":85},"publishConfig":{"access":"restricted","registry":"https://playlive-767397689694.d.codeartifact.us-east-1.amazonaws.com/npm/playlive/"},"integrity":"sha512-klIKTXXtLaUQLSh+tX0NamHcW+FrLWYmHw8uvJsMM9cWnop3l2VfPKQvHWjNxzHYi3a2K58+zH4qd3PGi/cwRQ==","shasum":"2b5f6c09b43be727f850ba580a72163ffd58a29e","readme":"# @playlive/react-pipeline\n\nReact bindings for [`@playlive/realtime-pipeline`](../realtime-pipeline/).\nDrop-in replacement for `playlive-overlay-data-layer/src/websocket/*`\nwith **zero** dependency on `react-use-websocket-lite` — built directly\non Zustand v5's `useStore` + `useSyncExternalStore`.\n\n![Coverage](./coverage-badge.svg)\n\n## Three tiers — take only what you need\n\nPick the surface that matches how much of the legacy\n`UnifiedDataPipelineProvider` API you actually want. Each tier is\nstrictly larger than the last and lives at its own subpath so unused\ncode is tree-shaken out.\n\n| Tier | Subpath                            | Surface                                                                         | Use when                                                              |\n| ---- | ---------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| 1    | `@playlive/react-pipeline`         | `RealtimePipelineProvider` + `usePipelineValue` (universal selector)            | Greenfield app; you want one selector primitive and nothing else.     |\n| 2    | `@playlive/react-pipeline`         | Tier 1 **plus** 21 typed per-slice hooks (`usePipelineDonations(id)`, …)        | You want stable, typed one-liners for the common reads.               |\n| 3    | `@playlive/react-pipeline/provider`| Tier 2 **plus** `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, etc.   | Porting an existing overlay-data-layer consumer with minimal churn.   |\n| —    | `@playlive/react-pipeline/legacy`  | Tier 3 **plus** `useUDPStore` / `useUDPStoreApi` / `createUDPStore` aliases     | Soft-deprecated shim for the legacy symbol names (one-release window).|\n\n## Install\n\n```bash\nbun add @playlive/react-pipeline\nbun add -d react react-dom zustand\n```\n\nPeer dependencies (jose-style — consumer brings their own):\n`react@^19`, `react-dom@^19`, `zustand@^5`,\n`@playlive/realtime-pipeline` (workspace), `@playlive/tiltify-core`\n(workspace, type-only — stripped at compile time).\n\nNothing else ships in the bundle.\n\n## Quick start — Tier 1\n\n```tsx\nimport { RealtimePipelineProvider, usePipelineCampaigns } from \"@playlive/react-pipeline\";\n\nfunction App() {\n  return (\n    <RealtimePipelineProvider\n      url=\"wss://pipeline.example.com/socket\"\n      charityType=\"tiltify\"\n      autoConnect\n    >\n      <Overlay />\n    </RealtimePipelineProvider>\n  );\n}\n\nfunction Overlay() {\n  const campaigns = usePipelineCampaigns();\n  return <ul>{campaigns.map((c) => <li key={c.id}>{c.name}</li>)}</ul>;\n}\n```\n\n`RealtimePipelineProvider` mounts a Zustand store + opens (or accepts)\na `PipelineConnection`. Children call any Tier 1 / Tier 2 hook to\nsubscribe.\n\n## Subpath exports\n\n| Subpath                              | Description                                                                       |\n| ------------------------------------ | --------------------------------------------------------------------------------- |\n| `@playlive/react-pipeline`           | Default barrel — Tier 1 + Tier 2 (provider + 21 typed slice hooks).               |\n| `@playlive/react-pipeline/provider`  | Tier 3 — `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, add-pipeline\\*. |\n| `@playlive/react-pipeline/legacy`    | Soft-deprecated alias bundle (`useUDPStore`, `createUDPStore`, `UDPStore`, etc.). |\n\nEach subpath ships an ESM bundle, a Bun source condition, and `.d.ts`\ndeclarations. Frontend-eligible bundles are not minified (PRD §6.1)\nso stack traces stay readable in production.\n\n## API reference\n\n### Tier 1 — universal primitive\n\n| Export                          | Signature                                                            |\n| ------------------------------- | -------------------------------------------------------------------- |\n| `RealtimePipelineProvider`      | `(props: RealtimePipelineProviderProps) => JSX.Element`              |\n| `usePipelineValue`              | `<T>(selector: (s: PipelineStore) => T) => T`                        |\n\n`usePipelineValue` is single-arg by design. Zustand v5 dropped the\nthird `equalityFn` parameter on `useStore` to keep the package\nfree of the `use-sync-external-store` shim peer. For shallow / custom\nequality, wrap your selector with `useShallow` from\n`zustand/shallow` (zero-extra-dep):\n\n```tsx\nimport { useShallow } from \"zustand/shallow\";\nimport { usePipelineValue } from \"@playlive/react-pipeline\";\n\nconst { connected, connecting } = usePipelineValue(\n  useShallow((s) => ({ connected: s.connected, connecting: s.connecting })),\n);\n```\n\n### Tier 2 — typed per-slice hooks\n\nAll 21 hooks are stable wrappers over `usePipelineValue` with a typed\nselector + memo-stable empty-array fallbacks (no infinite-render\nchurn from fresh `[]` allocations — Decision §3 in CHANGELOG).\n\n| Hook                              | Returns                                                             |\n| --------------------------------- | ------------------------------------------------------------------- |\n| `usePipelineCampaigns()`          | `TiltifyCampaign[]`                                                 |\n| `usePipelineTeamCampaigns()`      | `TiltifyTeamCampaign[]`                                             |\n| `usePipelineFundraisingEvents()`  | `TiltifyFundraisingEvent[]`                                         |\n| `usePipelineCauses()`             | `TiltifyCause[]`                                                    |\n| `usePipelineDonations()`          | `Record<string, TiltifyDonationWithTestFlag[]>`                     |\n| `usePipelineDonations(id)`        | `TiltifyDonationWithTestFlag[]` (memo-stable `[]` if id unknown)    |\n| `usePipelineDonationTrains(id, withStatus?)` | `DonationTrain[]`                                        |\n| `usePipelineSubathonTimer(id, withStatus?)`  | `SubathonTimer[]`                                        |\n| `usePipelineDonorSpotlight(id)`   | `ComputedSpotlight \\| null`                                         |\n| `usePipelinePolls(id)`            | `TiltifyPoll[]`                                                     |\n| `usePipelineRewards(id)`          | `TiltifyReward[]`                                                   |\n| `usePipelineTargets(id)`          | `TiltifyTarget[]`                                                   |\n| `usePipelineMilestones(id)`       | `TiltifyMilestone[]`                                                |\n| `usePipelineLeaderboardEntries(id)` | `TiltifyLeaderboardEntry[]`                                       |\n| `usePipelineAuctionHouses()`      | `AuctionHouse[]`                                                    |\n| `usePipelineTwitchChat()`         | `TwitchChatMessage[]`                                               |\n| `usePipelineConnectionState()`    | `PipelineConnectionState` (10 split selectors — no false re-renders)|\n| `usePipelineConnectionActions()`  | `PipelineConnectionActions` (stable `connect/disconnect/refresh/…`) |\n| `usePipelineSubscriptions()`      | `Record<string, TiltifyWebhookSubscription>`                        |\n| `usePipelineTiltifySettings()`    | `TiltifyWebSocketSettings`                                          |\n| `usePipelineLastRefreshResponse()`| `RefreshResponseMessage \\| null`                                    |\n| `usePipelineDonorSpotlightSettings()` | `Record<string, DonorSpotlightSettings>`                        |\n\n### Tier 3 — kitchen-sink provider (`/provider`)\n\n| Export                              | Notes                                                                                  |\n| ----------------------------------- | -------------------------------------------------------------------------------------- |\n| `UnifiedDataPipelineProvider`       | Superset of `RealtimePipelineProvider`; adds legacy `reloadOnErrorReconnect` + `debug`.|\n| `useUnifiedDataPipeline()`          | One-call kitchen-sink hook. Returns every slice + actions + legacy field aliases.      |\n| `useAddPipelineCampaignIDs(ids)`    | Effect hook — registers campaign ids with the store on mount.                          |\n| `useAddPipelineCampaigns(items)`    | Effect hook — registers full campaign records on mount.                                |\n| `useAddPipelineFundraisingEventIDs(ids)` | Effect hook — registers fundraising-event ids on mount.                           |\n| `useCommonContextHooks(…)`          | URL / mode / charity-type sync to the store. Same shape as the legacy hook.            |\n| `useIdentify()` / `useIdentify(…11 legacy args)` | Zero-arg returns the store's bound `identify` action. Positional arg form is preserved as an `@deprecated` overload — args are ignored, warns once. |\n\n### Legacy aliases (`/legacy`)\n\nSoft-deprecated. Identical behavior to the canonical exports —\nrename the import specifier once and the rest of your code base\nkeeps working unchanged:\n\n| Legacy name            | Canonical replacement                                          |\n| ---------------------- | -------------------------------------------------------------- |\n| `useUDPStore`          | `usePipelineValue` (Tier 1) / typed slice hooks (Tier 2)       |\n| `useUDPStoreApi`       | (use a typed slice hook; or reach for the raw store API)       |\n| `createUDPStore`       | `createPipelineStore` from `@playlive/realtime-pipeline/store` |\n| `UDPStore` (type)      | `PipelineStore` from `@playlive/realtime-pipeline/store`       |\n| `UDPStoreApi` (type)   | `PipelineStoreApi` from `@playlive/realtime-pipeline/store`    |\n| `CharityTypes`         | re-exported from `@playlive/realtime-pipeline/protocol`         |\n\nPlus every Tier 1+2+3 export under its canonical name.\n\n## Migration from `playlive-overlay-data-layer`\n\nOne-shot sed-friendly rewrite of every import specifier (no body\nedits needed for the happy path):\n\n```bash\n# Tier 3 — kitchen-sink (drop-in replacement)\nrg -l '@playlive/overlay-data-layer/websocket' . \\\n  | xargs sed -i '' \\\n    -e 's|@playlive/overlay-data-layer/websocket|@playlive/react-pipeline/provider|g'\n```\n\nFor the soft-deprecated names (`useUDPStore`, `createUDPStore`, …)\nsubstitute `/legacy` instead of `/provider` to opt into the alias\nbundle, then migrate at your leisure (PRD §10 — one-release window).\n\nSee the phase-8 row in the root [MIGRATION.md](../../MIGRATION.md) for\nthe full per-symbol mapping plus call-site walkthroughs.\n\n## Upstream spec\n\nNo external API surface — internal workspace package. All transport\nruns through `@playlive/realtime-pipeline/connection`.\n\n## Twitch Extension URL disclosure\n\n`KNOWN_URLS` enumerates every absolute URL or host this package can\nfetch. **It is empty.** This package opens WebSocket connections only\nto URLs the consumer passes to `<RealtimePipelineProvider url={…}>`;\nthose URLs must be added to your Extension submission's URL\ndisclosure list, not this one.\n\n```ts\nimport { KNOWN_URLS } from \"@playlive/react-pipeline\";\nconsole.log(KNOWN_URLS); // []\n```\n\nSee [`docs/twitch-extension-checklist.md`](../../docs/twitch-extension-checklist.md).\n\n## Examples\n\nLive overlay apps consuming this provider land under `apps/*`\n(phase 10). Until then, the Tier 1 quick-start above is the minimum\nviable wiring.\n\n## Contributing\n\nSee [CONTRIBUTING.md](../../CONTRIBUTING.md). To add new Tier-2\nhooks, mirror an existing slice hook in `src/hooks/index.ts` and\nadd a `it.each` entry in `tests/unit/index.test.ts`.\n\n## License\n\nMIT — see [LICENSE](../../LICENSE). Distributed via Play Live\nCodeArtifact (PRD §6).\n","readmeFilename":"README.md","dist":{"tarball":"https://packages.playlive.experience.stjude.org/@playlive/react-pipeline/-/react-pipeline-0.2.2.tgz","shasum":"2b5f6c09b43be727f850ba580a72163ffd58a29e","integrity":"sha512-klIKTXXtLaUQLSh+tX0NamHcW+FrLWYmHw8uvJsMM9cWnop3l2VfPKQvHWjNxzHYi3a2K58+zH4qd3PGi/cwRQ=="}},"0.2.3":{"name":"@playlive/react-pipeline","version":"0.2.3","description":"React bindings for @playlive/realtime-pipeline — three usage tiers (single selector / typed per-slice / kitchen-sink). Twitch-Extension safe; no react-use-websocket-lite.","type":"module","sideEffects":false,"main":"./index.js","types":"./index.d.ts","exports":{".":{"import":"./index.js","types":"./index.d.ts"},"./provider":{"import":"./provider/index.js","types":"./provider/index.d.ts"},"./legacy":{"import":"./legacy/index.js","types":"./legacy/index.d.ts"}},"dependencies":{},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","zustand":"^5.0.0","@playlive/realtime-pipeline":"^0.2.2","@playlive/tiltify-core":"^0.4.11"},"playlive":{"target":"browser","frontendEligible":true,"coverageFloor":85},"publishConfig":{"access":"restricted","registry":"https://playlive-767397689694.d.codeartifact.us-east-1.amazonaws.com/npm/playlive/"},"integrity":"sha512-t09ua6rbFX2yfeKnkYC1GIFuLRlPu9nZdal0VszgT0Fy9E29tpnC11zgo/L5pkxc0EydH4r88p3FSi4N4ZdyMQ==","shasum":"b1f7c151a0204d3ea18107387291f82eaabd74cb","readme":"# @playlive/react-pipeline\n\nReact bindings for [`@playlive/realtime-pipeline`](../realtime-pipeline/).\nDrop-in replacement for `playlive-overlay-data-layer/src/websocket/*`\nwith **zero** dependency on `react-use-websocket-lite` — built directly\non Zustand v5's `useStore` + `useSyncExternalStore`.\n\n![Coverage](./coverage-badge.svg)\n\n## Three tiers — take only what you need\n\nPick the surface that matches how much of the legacy\n`UnifiedDataPipelineProvider` API you actually want. Each tier is\nstrictly larger than the last and lives at its own subpath so unused\ncode is tree-shaken out.\n\n| Tier | Subpath                            | Surface                                                                         | Use when                                                              |\n| ---- | ---------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| 1    | `@playlive/react-pipeline`         | `RealtimePipelineProvider` + `usePipelineValue` (universal selector)            | Greenfield app; you want one selector primitive and nothing else.     |\n| 2    | `@playlive/react-pipeline`         | Tier 1 **plus** 22 typed per-slice hooks (`usePipelineDonations(id)`, …)        | You want stable, typed one-liners for the common reads.               |\n| 3    | `@playlive/react-pipeline/provider`| Tier 2 **plus** `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, etc.   | Porting an existing overlay-data-layer consumer with minimal churn.   |\n| —    | `@playlive/react-pipeline/legacy`  | Tier 3 **plus** `useUDPStore` / `useUDPStoreApi` / `createUDPStore` aliases     | Soft-deprecated shim for the legacy symbol names (one-release window).|\n\n## Install\n\n```bash\nbun add @playlive/react-pipeline\nbun add -d react react-dom zustand\n```\n\nPeer dependencies (jose-style — consumer brings their own):\n`react@^19`, `react-dom@^19`, `zustand@^5`,\n`@playlive/realtime-pipeline` (workspace), `@playlive/tiltify-core`\n(workspace, type-only — stripped at compile time).\n\nNothing else ships in the bundle.\n\n## Quick start — Tier 1\n\n```tsx\nimport { RealtimePipelineProvider, usePipelineCampaigns } from \"@playlive/react-pipeline\";\n\nfunction App() {\n  return (\n    <RealtimePipelineProvider\n      url=\"wss://pipeline.example.com/socket\"\n      charityType=\"tiltify\"\n      autoConnect\n    >\n      <Overlay />\n    </RealtimePipelineProvider>\n  );\n}\n\nfunction Overlay() {\n  const campaigns = usePipelineCampaigns();\n  return <ul>{campaigns.map((c) => <li key={c.id}>{c.name}</li>)}</ul>;\n}\n```\n\n`RealtimePipelineProvider` mounts a Zustand store + opens (or accepts)\na `PipelineConnection`. Children call any Tier 1 / Tier 2 hook to\nsubscribe.\n\n## Subpath exports\n\n| Subpath                              | Description                                                                       |\n| ------------------------------------ | --------------------------------------------------------------------------------- |\n| `@playlive/react-pipeline`           | Default barrel — Tier 1 + Tier 2 (provider + 21 typed slice hooks).               |\n| `@playlive/react-pipeline/provider`  | Tier 3 — `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, add-pipeline\\*. |\n| `@playlive/react-pipeline/legacy`    | Soft-deprecated alias bundle (`useUDPStore`, `createUDPStore`, `UDPStore`, etc.). |\n\nEach subpath ships an ESM bundle, a Bun source condition, and `.d.ts`\ndeclarations. Frontend-eligible bundles are not minified (PRD §6.1)\nso stack traces stay readable in production.\n\n## API reference\n\n### Tier 1 — universal primitive\n\n| Export                          | Signature                                                            |\n| ------------------------------- | -------------------------------------------------------------------- |\n| `RealtimePipelineProvider`      | `(props: RealtimePipelineProviderProps) => JSX.Element`              |\n| `usePipelineValue`              | `<T>(selector: (s: PipelineStore) => T) => T`                        |\n\n`usePipelineValue` is single-arg by design. Zustand v5 dropped the\nthird `equalityFn` parameter on `useStore` to keep the package\nfree of the `use-sync-external-store` shim peer. For shallow / custom\nequality, wrap your selector with `useShallow` from\n`zustand/shallow` (zero-extra-dep):\n\n```tsx\nimport { useShallow } from \"zustand/shallow\";\nimport { usePipelineValue } from \"@playlive/react-pipeline\";\n\nconst { connected, connecting } = usePipelineValue(\n  useShallow((s) => ({ connected: s.connected, connecting: s.connecting })),\n);\n```\n\n### Tier 2 — typed per-slice hooks\n\nAll 22 hooks are stable wrappers over `usePipelineValue` with a typed\nselector + memo-stable empty-array fallbacks (no infinite-render\nchurn from fresh `[]` allocations — Decision §3 in CHANGELOG).\n\n| Hook                              | Returns                                                             |\n| --------------------------------- | ------------------------------------------------------------------- |\n| `usePipelineCampaigns()`          | `TiltifyCampaign[]`                                                 |\n| `usePipelineTeamCampaigns()`      | `TiltifyTeamCampaign[]`                                             |\n| `usePipelineFundraisingEvents()`  | `TiltifyFundraisingEvent[]`                                         |\n| `usePipelineCauses()`             | `TiltifyCause[]`                                                    |\n| `usePipelineDonations()`          | `Record<string, TiltifyDonationWithTestFlag[]>`                     |\n| `usePipelineDonations(id)`        | `TiltifyDonationWithTestFlag[]` (memo-stable `[]` if id unknown)    |\n| `usePipelineDonationSum(id, opts?)` | `PipelineDonationSumResult` — live per-currency sum over `[start, end)` of the WS donation firehose. Pair with the REST `/schedules/campaigns/{id}/raised` `asOf` field to replace polling with a `baseline + WS-delta` view. |\n| `usePipelineDonationTrains(id, withStatus?)` | `DonationTrain[]`                                        |\n| `usePipelineSubathonTimer(id, withStatus?)`  | `SubathonTimer[]`                                        |\n| `usePipelineDonorSpotlight(id)`   | `ComputedSpotlight \\| null`                                         |\n| `usePipelinePolls(id)`            | `TiltifyPoll[]`                                                     |\n| `usePipelineRewards(id)`          | `TiltifyReward[]`                                                   |\n| `usePipelineTargets(id)`          | `TiltifyTarget[]`                                                   |\n| `usePipelineMilestones(id)`       | `TiltifyMilestone[]`                                                |\n| `usePipelineLeaderboardEntries(id)` | `TiltifyLeaderboardEntry[]`                                       |\n| `usePipelineAuctionHouses()`      | `AuctionHouse[]`                                                    |\n| `usePipelineTwitchChat()`         | `TwitchChatMessage[]`                                               |\n| `usePipelineConnectionState()`    | `PipelineConnectionState` (10 split selectors — no false re-renders)|\n| `usePipelineConnectionActions()`  | `PipelineConnectionActions` (stable `connect/disconnect/refresh/…`) |\n| `usePipelineSubscriptions()`      | `Record<string, TiltifyWebhookSubscription>`                        |\n| `usePipelineTiltifySettings()`    | `TiltifyWebSocketSettings`                                          |\n| `usePipelineLastRefreshResponse()`| `RefreshResponseMessage \\| null`                                    |\n| `usePipelineDonorSpotlightSettings()` | `Record<string, DonorSpotlightSettings>`                        |\n\n### Tier 3 — kitchen-sink provider (`/provider`)\n\n| Export                              | Notes                                                                                  |\n| ----------------------------------- | -------------------------------------------------------------------------------------- |\n| `UnifiedDataPipelineProvider`       | Superset of `RealtimePipelineProvider`; adds legacy `reloadOnErrorReconnect` + `debug`.|\n| `useUnifiedDataPipeline()`          | One-call kitchen-sink hook. Returns every slice + actions + legacy field aliases.      |\n| `useAddPipelineCampaignIDs(ids)`    | Effect hook — registers campaign ids with the store on mount.                          |\n| `useAddPipelineCampaigns(items)`    | Effect hook — registers full campaign records on mount.                                |\n| `useAddPipelineFundraisingEventIDs(ids)` | Effect hook — registers fundraising-event ids on mount.                           |\n| `useCommonContextHooks(…)`          | URL / mode / charity-type sync to the store. Same shape as the legacy hook.            |\n| `useIdentify()` / `useIdentify(…11 legacy args)` | Zero-arg returns the store's bound `identify` action. Positional arg form is preserved as an `@deprecated` overload — args are ignored, warns once. |\n\n### Legacy aliases (`/legacy`)\n\nSoft-deprecated. Identical behavior to the canonical exports —\nrename the import specifier once and the rest of your code base\nkeeps working unchanged:\n\n| Legacy name            | Canonical replacement                                          |\n| ---------------------- | -------------------------------------------------------------- |\n| `useUDPStore`          | `usePipelineValue` (Tier 1) / typed slice hooks (Tier 2)       |\n| `useUDPStoreApi`       | (use a typed slice hook; or reach for the raw store API)       |\n| `createUDPStore`       | `createPipelineStore` from `@playlive/realtime-pipeline/store` |\n| `UDPStore` (type)      | `PipelineStore` from `@playlive/realtime-pipeline/store`       |\n| `UDPStoreApi` (type)   | `PipelineStoreApi` from `@playlive/realtime-pipeline/store`    |\n| `CharityTypes`         | re-exported from `@playlive/realtime-pipeline/protocol`         |\n\nPlus every Tier 1+2+3 export under its canonical name.\n\n## Migration from `playlive-overlay-data-layer`\n\nOne-shot sed-friendly rewrite of every import specifier (no body\nedits needed for the happy path):\n\n```bash\n# Tier 3 — kitchen-sink (drop-in replacement)\nrg -l '@playlive/overlay-data-layer/websocket' . \\\n  | xargs sed -i '' \\\n    -e 's|@playlive/overlay-data-layer/websocket|@playlive/react-pipeline/provider|g'\n```\n\nFor the soft-deprecated names (`useUDPStore`, `createUDPStore`, …)\nsubstitute `/legacy` instead of `/provider` to opt into the alias\nbundle, then migrate at your leisure (PRD §10 — one-release window).\n\nSee the phase-8 row in the root [MIGRATION.md](../../MIGRATION.md) for\nthe full per-symbol mapping plus call-site walkthroughs.\n\n## Upstream spec\n\nNo external API surface — internal workspace package. All transport\nruns through `@playlive/realtime-pipeline/connection`.\n\n## Twitch Extension URL disclosure\n\n`KNOWN_URLS` enumerates every absolute URL or host this package can\nfetch. **It is empty.** This package opens WebSocket connections only\nto URLs the consumer passes to `<RealtimePipelineProvider url={…}>`;\nthose URLs must be added to your Extension submission's URL\ndisclosure list, not this one.\n\n```ts\nimport { KNOWN_URLS } from \"@playlive/react-pipeline\";\nconsole.log(KNOWN_URLS); // []\n```\n\nSee [`docs/twitch-extension-checklist.md`](../../docs/twitch-extension-checklist.md).\n\n## Examples\n\nLive overlay apps consuming this provider land under `apps/*`\n(phase 10). Until then, the Tier 1 quick-start above is the minimum\nviable wiring.\n\n## Contributing\n\nSee [CONTRIBUTING.md](../../CONTRIBUTING.md). To add new Tier-2\nhooks, mirror an existing slice hook in `src/hooks/index.ts` and\nadd a `it.each` entry in `tests/unit/index.test.ts`.\n\n## License\n\nMIT — see [LICENSE](../../LICENSE). Distributed via Play Live\nCodeArtifact (PRD §6).\n","readmeFilename":"README.md","dist":{"tarball":"https://packages.playlive.experience.stjude.org/@playlive/react-pipeline/-/react-pipeline-0.2.3.tgz","shasum":"b1f7c151a0204d3ea18107387291f82eaabd74cb","integrity":"sha512-t09ua6rbFX2yfeKnkYC1GIFuLRlPu9nZdal0VszgT0Fy9E29tpnC11zgo/L5pkxc0EydH4r88p3FSi4N4ZdyMQ=="}},"0.3.0":{"name":"@playlive/react-pipeline","version":"0.3.0","description":"React bindings for @playlive/realtime-pipeline — three usage tiers (single selector / typed per-slice / kitchen-sink). Twitch-Extension safe; no react-use-websocket-lite.","type":"module","sideEffects":false,"main":"./index.js","types":"./index.d.ts","exports":{".":{"import":"./index.js","types":"./index.d.ts"},"./provider":{"import":"./provider/index.js","types":"./provider/index.d.ts"},"./legacy":{"import":"./legacy/index.js","types":"./legacy/index.d.ts"},"./fusion":{"import":"./fusion/index.js","types":"./fusion/index.d.ts"}},"dependencies":{},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","zustand":"^5.0.0","@playlive/realtime-pipeline":"^0.2.2","@playlive/tiltify-core":"^0.4.11","@tanstack/react-query":"^5.0.0","@playlive/react-query":"^0.2.0","@playlive/fundraiser-data":"^0.2.0"},"peerDependenciesMeta":{"@tanstack/react-query":{"optional":true},"@playlive/react-query":{"optional":true},"@playlive/fundraiser-data":{"optional":true}},"playlive":{"target":"browser","frontendEligible":true,"coverageFloor":85},"publishConfig":{"access":"restricted","registry":"https://playlive-767397689694.d.codeartifact.us-east-1.amazonaws.com/npm/playlive/"},"integrity":"sha512-cxr1mZ/0zIX6qqlCa6Gs2xrziutIc2aJBiU/nl813a+4vMVU0CgCH5+9aeFln5haLeW8scc5DeD3P0hQRMwg1g==","shasum":"b7cb609ad51eb10a1e9ae5f134914401076db3cb","readme":"# @playlive/react-pipeline\n\nReact bindings for [`@playlive/realtime-pipeline`](../realtime-pipeline/).\nDrop-in replacement for `playlive-overlay-data-layer/src/websocket/*`\nwith **zero** dependency on `react-use-websocket-lite` — built directly\non Zustand v5's `useStore` + `useSyncExternalStore`.\n\n![Coverage](./coverage-badge.svg)\n\n## Three tiers — take only what you need\n\nPick the surface that matches how much of the legacy\n`UnifiedDataPipelineProvider` API you actually want. Each tier is\nstrictly larger than the last and lives at its own subpath so unused\ncode is tree-shaken out.\n\n| Tier | Subpath                            | Surface                                                                         | Use when                                                              |\n| ---- | ---------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| 1    | `@playlive/react-pipeline`         | `RealtimePipelineProvider` + `usePipelineValue` (universal selector)            | Greenfield app; you want one selector primitive and nothing else.     |\n| 2    | `@playlive/react-pipeline`         | Tier 1 **plus** 22 typed per-slice hooks (`usePipelineDonations(id)`, …)        | You want stable, typed one-liners for the common reads.               |\n| 3    | `@playlive/react-pipeline/provider`| Tier 2 **plus** `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, etc.   | Porting an existing overlay-data-layer consumer with minimal churn.   |\n| —    | `@playlive/react-pipeline/legacy`  | Tier 3 **plus** `useUDPStore` / `useUDPStoreApi` / `createUDPStore` aliases     | Soft-deprecated shim for the legacy symbol names (one-release window).|\n\n## Install\n\n```bash\nbun add @playlive/react-pipeline\nbun add -d react react-dom zustand\n```\n\nPeer dependencies (jose-style — consumer brings their own):\n`react@^19`, `react-dom@^19`, `zustand@^5`,\n`@playlive/realtime-pipeline` (workspace), `@playlive/tiltify-core`\n(workspace, type-only — stripped at compile time).\n\nNothing else ships in the bundle.\n\n## Quick start — Tier 1\n\n```tsx\nimport { RealtimePipelineProvider, usePipelineCampaigns } from \"@playlive/react-pipeline\";\n\nfunction App() {\n  return (\n    <RealtimePipelineProvider\n      url=\"wss://pipeline.example.com/socket\"\n      charityType=\"tiltify\"\n      autoConnect\n    >\n      <Overlay />\n    </RealtimePipelineProvider>\n  );\n}\n\nfunction Overlay() {\n  const campaigns = usePipelineCampaigns();\n  return <ul>{campaigns.map((c) => <li key={c.id}>{c.name}</li>)}</ul>;\n}\n```\n\n`RealtimePipelineProvider` mounts a Zustand store + opens (or accepts)\na `PipelineConnection`. Children call any Tier 1 / Tier 2 hook to\nsubscribe.\n\n## Subpath exports\n\n| Subpath                              | Description                                                                       |\n| ------------------------------------ | --------------------------------------------------------------------------------- |\n| `@playlive/react-pipeline`           | Default barrel — Tier 1 + Tier 2 (provider + 21 typed slice hooks).               |\n| `@playlive/react-pipeline/provider`  | Tier 3 — `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, add-pipeline\\*. |\n| `@playlive/react-pipeline/legacy`    | Soft-deprecated alias bundle (`useUDPStore`, `createUDPStore`, `UDPStore`, etc.). |\n\nEach subpath ships an ESM bundle, a Bun source condition, and `.d.ts`\ndeclarations. Frontend-eligible bundles are not minified (PRD §6.1)\nso stack traces stay readable in production.\n\n## API reference\n\n### Tier 1 — universal primitive\n\n| Export                          | Signature                                                            |\n| ------------------------------- | -------------------------------------------------------------------- |\n| `RealtimePipelineProvider`      | `(props: RealtimePipelineProviderProps) => JSX.Element`              |\n| `usePipelineValue`              | `<T>(selector: (s: PipelineStore) => T) => T`                        |\n\n`usePipelineValue` is single-arg by design. Zustand v5 dropped the\nthird `equalityFn` parameter on `useStore` to keep the package\nfree of the `use-sync-external-store` shim peer. For shallow / custom\nequality, wrap your selector with `useShallow` from\n`zustand/shallow` (zero-extra-dep):\n\n```tsx\nimport { useShallow } from \"zustand/shallow\";\nimport { usePipelineValue } from \"@playlive/react-pipeline\";\n\nconst { connected, connecting } = usePipelineValue(\n  useShallow((s) => ({ connected: s.connected, connecting: s.connecting })),\n);\n```\n\n### Tier 2 — typed per-slice hooks\n\nAll 22 hooks are stable wrappers over `usePipelineValue` with a typed\nselector + memo-stable empty-array fallbacks (no infinite-render\nchurn from fresh `[]` allocations — Decision §3 in CHANGELOG).\n\n| Hook                              | Returns                                                             |\n| --------------------------------- | ------------------------------------------------------------------- |\n| `usePipelineCampaigns()`          | `TiltifyCampaign[]`                                                 |\n| `usePipelineTeamCampaigns()`      | `TiltifyTeamCampaign[]`                                             |\n| `usePipelineFundraisingEvents()`  | `TiltifyFundraisingEvent[]`                                         |\n| `usePipelineCauses()`             | `TiltifyCause[]`                                                    |\n| `usePipelineDonations()`          | `Record<string, TiltifyDonationWithTestFlag[]>`                     |\n| `usePipelineDonations(id)`        | `TiltifyDonationWithTestFlag[]` (memo-stable `[]` if id unknown)    |\n| `usePipelineDonationSum(id, opts?)` | `PipelineDonationSumResult` — live per-currency sum over `[start, end)` of the WS donation firehose. Pair with the REST `/schedules/campaigns/{id}/raised` `asOf` field to replace polling with a `baseline + WS-delta` view. |\n| `usePipelineDonationTrains(id, withStatus?)` | `DonationTrain[]`                                        |\n| `usePipelineSubathonTimer(id, withStatus?)`  | `SubathonTimer[]`                                        |\n| `usePipelineDonorSpotlight(id)`   | `ComputedSpotlight \\| null`                                         |\n| `usePipelinePolls(id)`            | `TiltifyPoll[]`                                                     |\n| `usePipelineRewards(id)`          | `TiltifyReward[]`                                                   |\n| `usePipelineTargets(id)`          | `TiltifyTarget[]`                                                   |\n| `usePipelineMilestones(id)`       | `TiltifyMilestone[]`                                                |\n| `usePipelineLeaderboardEntries(id)` | `TiltifyLeaderboardEntry[]`                                       |\n| `usePipelineAuctionHouses()`      | `AuctionHouse[]`                                                    |\n| `usePipelineTwitchChat()`         | `TwitchChatMessage[]`                                               |\n| `usePipelineConnectionState()`    | `PipelineConnectionState` (10 split selectors — no false re-renders)|\n| `usePipelineConnectionActions()`  | `PipelineConnectionActions` (stable `connect/disconnect/refresh/…`) |\n| `usePipelineSubscriptions()`      | `Record<string, TiltifyWebhookSubscription>`                        |\n| `usePipelineTiltifySettings()`    | `TiltifyWebSocketSettings`                                          |\n| `usePipelineLastRefreshResponse()`| `RefreshResponseMessage \\| null`                                    |\n| `usePipelineDonorSpotlightSettings()` | `Record<string, DonorSpotlightSettings>`                        |\n\n### Tier 3 — kitchen-sink provider (`/provider`)\n\n| Export                              | Notes                                                                                  |\n| ----------------------------------- | -------------------------------------------------------------------------------------- |\n| `UnifiedDataPipelineProvider`       | Superset of `RealtimePipelineProvider`; adds legacy `reloadOnErrorReconnect` + `debug`.|\n| `useUnifiedDataPipeline()`          | One-call kitchen-sink hook. Returns every slice + actions + legacy field aliases.      |\n| `useAddPipelineCampaignIDs(ids)`    | Effect hook — registers campaign ids with the store on mount.                          |\n| `useAddPipelineCampaigns(items)`    | Effect hook — registers full campaign records on mount.                                |\n| `useAddPipelineFundraisingEventIDs(ids)` | Effect hook — registers fundraising-event ids on mount.                           |\n| `useCommonContextHooks(…)`          | URL / mode / charity-type sync to the store. Same shape as the legacy hook.            |\n| `useIdentify()` / `useIdentify(…11 legacy args)` | Zero-arg returns the store's bound `identify` action. Positional arg form is preserved as an `@deprecated` overload — args are ignored, warns once. |\n\n### Legacy aliases (`/legacy`)\n\nSoft-deprecated. Identical behavior to the canonical exports —\nrename the import specifier once and the rest of your code base\nkeeps working unchanged:\n\n| Legacy name            | Canonical replacement                                          |\n| ---------------------- | -------------------------------------------------------------- |\n| `useUDPStore`          | `usePipelineValue` (Tier 1) / typed slice hooks (Tier 2)       |\n| `useUDPStoreApi`       | (use a typed slice hook; or reach for the raw store API)       |\n| `createUDPStore`       | `createPipelineStore` from `@playlive/realtime-pipeline/store` |\n| `UDPStore` (type)      | `PipelineStore` from `@playlive/realtime-pipeline/store`       |\n| `UDPStoreApi` (type)   | `PipelineStoreApi` from `@playlive/realtime-pipeline/store`    |\n| `CharityTypes`         | re-exported from `@playlive/realtime-pipeline/protocol`         |\n\nPlus every Tier 1+2+3 export under its canonical name.\n\n## Migration from `playlive-overlay-data-layer`\n\nOne-shot sed-friendly rewrite of every import specifier (no body\nedits needed for the happy path):\n\n```bash\n# Tier 3 — kitchen-sink (drop-in replacement)\nrg -l '@playlive/overlay-data-layer/websocket' . \\\n  | xargs sed -i '' \\\n    -e 's|@playlive/overlay-data-layer/websocket|@playlive/react-pipeline/provider|g'\n```\n\nFor the soft-deprecated names (`useUDPStore`, `createUDPStore`, …)\nsubstitute `/legacy` instead of `/provider` to opt into the alias\nbundle, then migrate at your leisure (PRD §10 — one-release window).\n\nSee the phase-8 row in the root [MIGRATION.md](../../MIGRATION.md) for\nthe full per-symbol mapping plus call-site walkthroughs.\n\n## Upstream spec\n\nNo external API surface — internal workspace package. All transport\nruns through `@playlive/realtime-pipeline/connection`.\n\n## Twitch Extension URL disclosure\n\n`KNOWN_URLS` enumerates every absolute URL or host this package can\nfetch. **It is empty.** This package opens WebSocket connections only\nto URLs the consumer passes to `<RealtimePipelineProvider url={…}>`;\nthose URLs must be added to your Extension submission's URL\ndisclosure list, not this one.\n\n```ts\nimport { KNOWN_URLS } from \"@playlive/react-pipeline\";\nconsole.log(KNOWN_URLS); // []\n```\n\nSee [`docs/twitch-extension-checklist.md`](../../docs/twitch-extension-checklist.md).\n\n## Examples\n\nLive overlay apps consuming this provider land under `apps/*`\n(phase 10). Until then, the Tier 1 quick-start above is the minimum\nviable wiring.\n\n## Contributing\n\nSee [CONTRIBUTING.md](../../CONTRIBUTING.md). To add new Tier-2\nhooks, mirror an existing slice hook in `src/hooks/index.ts` and\nadd a `it.each` entry in `tests/unit/index.test.ts`.\n\n## License\n\nMIT — see [LICENSE](../../LICENSE). Distributed via Play Live\nCodeArtifact (PRD §6).\n","readmeFilename":"README.md","dist":{"tarball":"https://packages.playlive.experience.stjude.org/@playlive/react-pipeline/-/react-pipeline-0.3.0.tgz","shasum":"b7cb609ad51eb10a1e9ae5f134914401076db3cb","integrity":"sha512-cxr1mZ/0zIX6qqlCa6Gs2xrziutIc2aJBiU/nl813a+4vMVU0CgCH5+9aeFln5haLeW8scc5DeD3P0hQRMwg1g=="}},"0.3.1":{"name":"@playlive/react-pipeline","version":"0.3.1","description":"React bindings for @playlive/realtime-pipeline — three usage tiers (single selector / typed per-slice / kitchen-sink). Twitch-Extension safe; no react-use-websocket-lite.","type":"module","sideEffects":false,"main":"./index.js","types":"./index.d.ts","exports":{".":{"import":"./index.js","types":"./index.d.ts"},"./provider":{"import":"./provider/index.js","types":"./provider/index.d.ts"},"./legacy":{"import":"./legacy/index.js","types":"./legacy/index.d.ts"},"./fusion":{"import":"./fusion/index.js","types":"./fusion/index.d.ts"}},"dependencies":{},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","zustand":"^5.0.0","@playlive/realtime-pipeline":"^0.2.2","@playlive/tiltify-core":"^0.4.11","@tanstack/react-query":"^5.0.0","@playlive/react-query":"^0.2.1","@playlive/fundraiser-data":"^0.2.0"},"peerDependenciesMeta":{"@tanstack/react-query":{"optional":true},"@playlive/react-query":{"optional":true},"@playlive/fundraiser-data":{"optional":true}},"playlive":{"target":"browser","frontendEligible":true,"coverageFloor":85},"publishConfig":{"access":"restricted","registry":"https://playlive-767397689694.d.codeartifact.us-east-1.amazonaws.com/npm/playlive/"},"integrity":"sha512-H8iVYo1DyirklS3F4Ei3K1MbWANlVUQul2k238mWma1QDsw112XNQTmOwU/nGsi7S5SMVQUXvFEej56hAHT93g==","shasum":"4cc755a3880d7da0e0b819f9c93c68b4c98cc048","readme":"# @playlive/react-pipeline\n\nReact bindings for [`@playlive/realtime-pipeline`](../realtime-pipeline/).\nDrop-in replacement for `playlive-overlay-data-layer/src/websocket/*`\nwith **zero** dependency on `react-use-websocket-lite` — built directly\non Zustand v5's `useStore` + `useSyncExternalStore`.\n\n![Coverage](./coverage-badge.svg)\n\n## Three tiers — take only what you need\n\nPick the surface that matches how much of the legacy\n`UnifiedDataPipelineProvider` API you actually want. Each tier is\nstrictly larger than the last and lives at its own subpath so unused\ncode is tree-shaken out.\n\n| Tier | Subpath                            | Surface                                                                         | Use when                                                              |\n| ---- | ---------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| 1    | `@playlive/react-pipeline`         | `RealtimePipelineProvider` + `usePipelineValue` (universal selector)            | Greenfield app; you want one selector primitive and nothing else.     |\n| 2    | `@playlive/react-pipeline`         | Tier 1 **plus** 22 typed per-slice hooks (`usePipelineDonations(id)`, …)        | You want stable, typed one-liners for the common reads.               |\n| 3    | `@playlive/react-pipeline/provider`| Tier 2 **plus** `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, etc.   | Porting an existing overlay-data-layer consumer with minimal churn.   |\n| —    | `@playlive/react-pipeline/legacy`  | Tier 3 **plus** `useUDPStore` / `useUDPStoreApi` / `createUDPStore` aliases     | Soft-deprecated shim for the legacy symbol names (one-release window).|\n\n## Install\n\n```bash\nbun add @playlive/react-pipeline\nbun add -d react react-dom zustand\n```\n\nPeer dependencies (jose-style — consumer brings their own):\n`react@^19`, `react-dom@^19`, `zustand@^5`,\n`@playlive/realtime-pipeline` (workspace), `@playlive/tiltify-core`\n(workspace, type-only — stripped at compile time).\n\nNothing else ships in the bundle.\n\n## Quick start — Tier 1\n\n```tsx\nimport { RealtimePipelineProvider, usePipelineCampaigns } from \"@playlive/react-pipeline\";\n\nfunction App() {\n  return (\n    <RealtimePipelineProvider\n      url=\"wss://pipeline.example.com/socket\"\n      charityType=\"tiltify\"\n      autoConnect\n    >\n      <Overlay />\n    </RealtimePipelineProvider>\n  );\n}\n\nfunction Overlay() {\n  const campaigns = usePipelineCampaigns();\n  return <ul>{campaigns.map((c) => <li key={c.id}>{c.name}</li>)}</ul>;\n}\n```\n\n`RealtimePipelineProvider` mounts a Zustand store + opens (or accepts)\na `PipelineConnection`. Children call any Tier 1 / Tier 2 hook to\nsubscribe.\n\n## Subpath exports\n\n| Subpath                              | Description                                                                       |\n| ------------------------------------ | --------------------------------------------------------------------------------- |\n| `@playlive/react-pipeline`           | Default barrel — Tier 1 + Tier 2 (provider + 21 typed slice hooks).               |\n| `@playlive/react-pipeline/provider`  | Tier 3 — `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, add-pipeline\\*. |\n| `@playlive/react-pipeline/legacy`    | Soft-deprecated alias bundle (`useUDPStore`, `createUDPStore`, `UDPStore`, etc.). |\n\nEach subpath ships an ESM bundle, a Bun source condition, and `.d.ts`\ndeclarations. Frontend-eligible bundles are not minified (PRD §6.1)\nso stack traces stay readable in production.\n\n## API reference\n\n### Tier 1 — universal primitive\n\n| Export                          | Signature                                                            |\n| ------------------------------- | -------------------------------------------------------------------- |\n| `RealtimePipelineProvider`      | `(props: RealtimePipelineProviderProps) => JSX.Element`              |\n| `usePipelineValue`              | `<T>(selector: (s: PipelineStore) => T) => T`                        |\n\n`usePipelineValue` is single-arg by design. Zustand v5 dropped the\nthird `equalityFn` parameter on `useStore` to keep the package\nfree of the `use-sync-external-store` shim peer. For shallow / custom\nequality, wrap your selector with `useShallow` from\n`zustand/shallow` (zero-extra-dep):\n\n```tsx\nimport { useShallow } from \"zustand/shallow\";\nimport { usePipelineValue } from \"@playlive/react-pipeline\";\n\nconst { connected, connecting } = usePipelineValue(\n  useShallow((s) => ({ connected: s.connected, connecting: s.connecting })),\n);\n```\n\n### Tier 2 — typed per-slice hooks\n\nAll 22 hooks are stable wrappers over `usePipelineValue` with a typed\nselector + memo-stable empty-array fallbacks (no infinite-render\nchurn from fresh `[]` allocations — Decision §3 in CHANGELOG).\n\n| Hook                              | Returns                                                             |\n| --------------------------------- | ------------------------------------------------------------------- |\n| `usePipelineCampaigns()`          | `TiltifyCampaign[]`                                                 |\n| `usePipelineTeamCampaigns()`      | `TiltifyTeamCampaign[]`                                             |\n| `usePipelineFundraisingEvents()`  | `TiltifyFundraisingEvent[]`                                         |\n| `usePipelineCauses()`             | `TiltifyCause[]`                                                    |\n| `usePipelineDonations()`          | `Record<string, TiltifyDonationWithTestFlag[]>`                     |\n| `usePipelineDonations(id)`        | `TiltifyDonationWithTestFlag[]` (memo-stable `[]` if id unknown)    |\n| `usePipelineDonationSum(id, opts?)` | `PipelineDonationSumResult` — live per-currency sum over `[start, end)` of the WS donation firehose. Pair with the REST `/schedules/campaigns/{id}/raised` `asOf` field to replace polling with a `baseline + WS-delta` view. |\n| `usePipelineDonationTrains(id, withStatus?)` | `DonationTrain[]`                                        |\n| `usePipelineSubathonTimer(id, withStatus?)`  | `SubathonTimer[]`                                        |\n| `usePipelineDonorSpotlight(id)`   | `ComputedSpotlight \\| null`                                         |\n| `usePipelinePolls(id)`            | `TiltifyPoll[]`                                                     |\n| `usePipelineRewards(id)`          | `TiltifyReward[]`                                                   |\n| `usePipelineTargets(id)`          | `TiltifyTarget[]`                                                   |\n| `usePipelineMilestones(id)`       | `TiltifyMilestone[]`                                                |\n| `usePipelineLeaderboardEntries(id)` | `TiltifyLeaderboardEntry[]`                                       |\n| `usePipelineAuctionHouses()`      | `AuctionHouse[]`                                                    |\n| `usePipelineTwitchChat()`         | `TwitchChatMessage[]`                                               |\n| `usePipelineConnectionState()`    | `PipelineConnectionState` (10 split selectors — no false re-renders)|\n| `usePipelineConnectionActions()`  | `PipelineConnectionActions` (stable `connect/disconnect/refresh/…`) |\n| `usePipelineSubscriptions()`      | `Record<string, TiltifyWebhookSubscription>`                        |\n| `usePipelineTiltifySettings()`    | `TiltifyWebSocketSettings`                                          |\n| `usePipelineLastRefreshResponse()`| `RefreshResponseMessage \\| null`                                    |\n| `usePipelineDonorSpotlightSettings()` | `Record<string, DonorSpotlightSettings>`                        |\n\n### Tier 3 — kitchen-sink provider (`/provider`)\n\n| Export                              | Notes                                                                                  |\n| ----------------------------------- | -------------------------------------------------------------------------------------- |\n| `UnifiedDataPipelineProvider`       | Superset of `RealtimePipelineProvider`; adds legacy `reloadOnErrorReconnect` + `debug`.|\n| `useUnifiedDataPipeline()`          | One-call kitchen-sink hook. Returns every slice + actions + legacy field aliases.      |\n| `useAddPipelineCampaignIDs(ids)`    | Effect hook — registers campaign ids with the store on mount.                          |\n| `useAddPipelineCampaigns(items)`    | Effect hook — registers full campaign records on mount.                                |\n| `useAddPipelineFundraisingEventIDs(ids)` | Effect hook — registers fundraising-event ids on mount.                           |\n| `useCommonContextHooks(…)`          | URL / mode / charity-type sync to the store. Same shape as the legacy hook.            |\n| `useIdentify()` / `useIdentify(…11 legacy args)` | Zero-arg returns the store's bound `identify` action. Positional arg form is preserved as an `@deprecated` overload — args are ignored, warns once. |\n\n### Legacy aliases (`/legacy`)\n\nSoft-deprecated. Identical behavior to the canonical exports —\nrename the import specifier once and the rest of your code base\nkeeps working unchanged:\n\n| Legacy name            | Canonical replacement                                          |\n| ---------------------- | -------------------------------------------------------------- |\n| `useUDPStore`          | `usePipelineValue` (Tier 1) / typed slice hooks (Tier 2)       |\n| `useUDPStoreApi`       | (use a typed slice hook; or reach for the raw store API)       |\n| `createUDPStore`       | `createPipelineStore` from `@playlive/realtime-pipeline/store` |\n| `UDPStore` (type)      | `PipelineStore` from `@playlive/realtime-pipeline/store`       |\n| `UDPStoreApi` (type)   | `PipelineStoreApi` from `@playlive/realtime-pipeline/store`    |\n| `CharityTypes`         | re-exported from `@playlive/realtime-pipeline/protocol`         |\n\nPlus every Tier 1+2+3 export under its canonical name.\n\n## Migration from `playlive-overlay-data-layer`\n\nOne-shot sed-friendly rewrite of every import specifier (no body\nedits needed for the happy path):\n\n```bash\n# Tier 3 — kitchen-sink (drop-in replacement)\nrg -l '@playlive/overlay-data-layer/websocket' . \\\n  | xargs sed -i '' \\\n    -e 's|@playlive/overlay-data-layer/websocket|@playlive/react-pipeline/provider|g'\n```\n\nFor the soft-deprecated names (`useUDPStore`, `createUDPStore`, …)\nsubstitute `/legacy` instead of `/provider` to opt into the alias\nbundle, then migrate at your leisure (PRD §10 — one-release window).\n\nSee the phase-8 row in the root [MIGRATION.md](../../MIGRATION.md) for\nthe full per-symbol mapping plus call-site walkthroughs.\n\n## Upstream spec\n\nNo external API surface — internal workspace package. All transport\nruns through `@playlive/realtime-pipeline/connection`.\n\n## Twitch Extension URL disclosure\n\n`KNOWN_URLS` enumerates every absolute URL or host this package can\nfetch. **It is empty.** This package opens WebSocket connections only\nto URLs the consumer passes to `<RealtimePipelineProvider url={…}>`;\nthose URLs must be added to your Extension submission's URL\ndisclosure list, not this one.\n\n```ts\nimport { KNOWN_URLS } from \"@playlive/react-pipeline\";\nconsole.log(KNOWN_URLS); // []\n```\n\nSee [`docs/twitch-extension-checklist.md`](../../docs/twitch-extension-checklist.md).\n\n## Examples\n\nLive overlay apps consuming this provider land under `apps/*`\n(phase 10). Until then, the Tier 1 quick-start above is the minimum\nviable wiring.\n\n## Contributing\n\nSee [CONTRIBUTING.md](../../CONTRIBUTING.md). To add new Tier-2\nhooks, mirror an existing slice hook in `src/hooks/index.ts` and\nadd a `it.each` entry in `tests/unit/index.test.ts`.\n\n## License\n\nMIT — see [LICENSE](../../LICENSE). Distributed via Play Live\nCodeArtifact (PRD §6).\n","readmeFilename":"README.md","dist":{"tarball":"https://packages.playlive.experience.stjude.org/@playlive/react-pipeline/-/react-pipeline-0.3.1.tgz","shasum":"4cc755a3880d7da0e0b819f9c93c68b4c98cc048","integrity":"sha512-H8iVYo1DyirklS3F4Ei3K1MbWANlVUQul2k238mWma1QDsw112XNQTmOwU/nGsi7S5SMVQUXvFEej56hAHT93g=="}},"0.3.2":{"name":"@playlive/react-pipeline","version":"0.3.2","description":"React bindings for @playlive/realtime-pipeline — three usage tiers (single selector / typed per-slice / kitchen-sink). Twitch-Extension safe; no react-use-websocket-lite.","type":"module","sideEffects":false,"main":"./index.js","types":"./index.d.ts","exports":{".":{"import":"./index.js","types":"./index.d.ts"},"./provider":{"import":"./provider/index.js","types":"./provider/index.d.ts"},"./legacy":{"import":"./legacy/index.js","types":"./legacy/index.d.ts"},"./fusion":{"import":"./fusion/index.js","types":"./fusion/index.d.ts"}},"dependencies":{},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","zustand":"^5.0.0","@playlive/realtime-pipeline":"^0.2.3","@playlive/tiltify-core":"^0.4.11","@tanstack/react-query":"^5.0.0","@playlive/react-query":"^0.2.2","@playlive/fundraiser-data":"^0.2.0"},"peerDependenciesMeta":{"@tanstack/react-query":{"optional":true},"@playlive/react-query":{"optional":true},"@playlive/fundraiser-data":{"optional":true}},"playlive":{"target":"browser","frontendEligible":true,"coverageFloor":85},"publishConfig":{"access":"restricted","registry":"https://playlive-767397689694.d.codeartifact.us-east-1.amazonaws.com/npm/playlive/"},"integrity":"sha512-i36Eu72v8MA7VFAmgFB0IAAepN9aPGMpTvHCaPLi2Zd4LzqYx1uigtuWUAw2ViXVtkP7/nWyuoA9A3us5SqbaA==","shasum":"7303a49a46fc0bcde03fa8239a6dd99c800fedc4","readme":"# @playlive/react-pipeline\n\nReact bindings for [`@playlive/realtime-pipeline`](../realtime-pipeline/).\nDrop-in replacement for `playlive-overlay-data-layer/src/websocket/*`\nwith **zero** dependency on `react-use-websocket-lite` — built directly\non Zustand v5's `useStore` + `useSyncExternalStore`.\n\n![Coverage](./coverage-badge.svg)\n\n## Three tiers — take only what you need\n\nPick the surface that matches how much of the legacy\n`UnifiedDataPipelineProvider` API you actually want. Each tier is\nstrictly larger than the last and lives at its own subpath so unused\ncode is tree-shaken out.\n\n| Tier | Subpath                            | Surface                                                                         | Use when                                                              |\n| ---- | ---------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| 1    | `@playlive/react-pipeline`         | `RealtimePipelineProvider` + `usePipelineValue` (universal selector)            | Greenfield app; you want one selector primitive and nothing else.     |\n| 2    | `@playlive/react-pipeline`         | Tier 1 **plus** 22 typed per-slice hooks (`usePipelineDonations(id)`, …)        | You want stable, typed one-liners for the common reads.               |\n| 3    | `@playlive/react-pipeline/provider`| Tier 2 **plus** `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, etc.   | Porting an existing overlay-data-layer consumer with minimal churn.   |\n| —    | `@playlive/react-pipeline/legacy`  | Tier 3 **plus** `useUDPStore` / `useUDPStoreApi` / `createUDPStore` aliases     | Soft-deprecated shim for the legacy symbol names (one-release window).|\n\n## Install\n\n```bash\nbun add @playlive/react-pipeline\nbun add -d react react-dom zustand\n```\n\nPeer dependencies (jose-style — consumer brings their own):\n`react@^19`, `react-dom@^19`, `zustand@^5`,\n`@playlive/realtime-pipeline` (workspace), `@playlive/tiltify-core`\n(workspace, type-only — stripped at compile time).\n\nNothing else ships in the bundle.\n\n## Quick start — Tier 1\n\n```tsx\nimport { RealtimePipelineProvider, usePipelineCampaigns } from \"@playlive/react-pipeline\";\n\nfunction App() {\n  return (\n    <RealtimePipelineProvider\n      url=\"wss://pipeline.example.com/socket\"\n      charityType=\"tiltify\"\n      autoConnect\n    >\n      <Overlay />\n    </RealtimePipelineProvider>\n  );\n}\n\nfunction Overlay() {\n  const campaigns = usePipelineCampaigns();\n  return <ul>{campaigns.map((c) => <li key={c.id}>{c.name}</li>)}</ul>;\n}\n```\n\n`RealtimePipelineProvider` mounts a Zustand store + opens (or accepts)\na `PipelineConnection`. Children call any Tier 1 / Tier 2 hook to\nsubscribe.\n\n## Subpath exports\n\n| Subpath                              | Description                                                                       |\n| ------------------------------------ | --------------------------------------------------------------------------------- |\n| `@playlive/react-pipeline`           | Default barrel — Tier 1 + Tier 2 (provider + 21 typed slice hooks).               |\n| `@playlive/react-pipeline/provider`  | Tier 3 — `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, add-pipeline\\*. |\n| `@playlive/react-pipeline/legacy`    | Soft-deprecated alias bundle (`useUDPStore`, `createUDPStore`, `UDPStore`, etc.). |\n\nEach subpath ships an ESM bundle, a Bun source condition, and `.d.ts`\ndeclarations. Frontend-eligible bundles are not minified (PRD §6.1)\nso stack traces stay readable in production.\n\n## API reference\n\n### Tier 1 — universal primitive\n\n| Export                          | Signature                                                            |\n| ------------------------------- | -------------------------------------------------------------------- |\n| `RealtimePipelineProvider`      | `(props: RealtimePipelineProviderProps) => JSX.Element`              |\n| `usePipelineValue`              | `<T>(selector: (s: PipelineStore) => T) => T`                        |\n\n`usePipelineValue` is single-arg by design. Zustand v5 dropped the\nthird `equalityFn` parameter on `useStore` to keep the package\nfree of the `use-sync-external-store` shim peer. For shallow / custom\nequality, wrap your selector with `useShallow` from\n`zustand/shallow` (zero-extra-dep):\n\n```tsx\nimport { useShallow } from \"zustand/shallow\";\nimport { usePipelineValue } from \"@playlive/react-pipeline\";\n\nconst { connected, connecting } = usePipelineValue(\n  useShallow((s) => ({ connected: s.connected, connecting: s.connecting })),\n);\n```\n\n### Tier 2 — typed per-slice hooks\n\nAll 22 hooks are stable wrappers over `usePipelineValue` with a typed\nselector + memo-stable empty-array fallbacks (no infinite-render\nchurn from fresh `[]` allocations — Decision §3 in CHANGELOG).\n\n| Hook                              | Returns                                                             |\n| --------------------------------- | ------------------------------------------------------------------- |\n| `usePipelineCampaigns()`          | `TiltifyCampaign[]`                                                 |\n| `usePipelineTeamCampaigns()`      | `TiltifyTeamCampaign[]`                                             |\n| `usePipelineFundraisingEvents()`  | `TiltifyFundraisingEvent[]`                                         |\n| `usePipelineCauses()`             | `TiltifyCause[]`                                                    |\n| `usePipelineDonations()`          | `Record<string, TiltifyDonationWithTestFlag[]>`                     |\n| `usePipelineDonations(id)`        | `TiltifyDonationWithTestFlag[]` (memo-stable `[]` if id unknown)    |\n| `usePipelineDonationSum(id, opts?)` | `PipelineDonationSumResult` — live per-currency sum over `[start, end)` of the WS donation firehose. Pair with the REST `/schedules/campaigns/{id}/raised` `asOf` field to replace polling with a `baseline + WS-delta` view. |\n| `usePipelineDonationTrains(id, withStatus?)` | `DonationTrain[]`                                        |\n| `usePipelineSubathonTimer(id, withStatus?)`  | `SubathonTimer[]`                                        |\n| `usePipelineDonorSpotlight(id)`   | `ComputedSpotlight \\| null`                                         |\n| `usePipelinePolls(id)`            | `TiltifyPoll[]`                                                     |\n| `usePipelineRewards(id)`          | `TiltifyReward[]`                                                   |\n| `usePipelineTargets(id)`          | `TiltifyTarget[]`                                                   |\n| `usePipelineMilestones(id)`       | `TiltifyMilestone[]`                                                |\n| `usePipelineLeaderboardEntries(id)` | `TiltifyLeaderboardEntry[]`                                       |\n| `usePipelineAuctionHouses()`      | `AuctionHouse[]`                                                    |\n| `usePipelineTwitchChat()`         | `TwitchChatMessage[]`                                               |\n| `usePipelineConnectionState()`    | `PipelineConnectionState` (10 split selectors — no false re-renders)|\n| `usePipelineConnectionActions()`  | `PipelineConnectionActions` (stable `connect/disconnect/refresh/…`) |\n| `usePipelineSubscriptions()`      | `Record<string, TiltifyWebhookSubscription>`                        |\n| `usePipelineTiltifySettings()`    | `TiltifyWebSocketSettings`                                          |\n| `usePipelineLastRefreshResponse()`| `RefreshResponseMessage \\| null`                                    |\n| `usePipelineDonorSpotlightSettings()` | `Record<string, DonorSpotlightSettings>`                        |\n\n### Tier 3 — kitchen-sink provider (`/provider`)\n\n| Export                              | Notes                                                                                  |\n| ----------------------------------- | -------------------------------------------------------------------------------------- |\n| `UnifiedDataPipelineProvider`       | Superset of `RealtimePipelineProvider`; adds legacy `reloadOnErrorReconnect` + `debug`.|\n| `useUnifiedDataPipeline()`          | One-call kitchen-sink hook. Returns every slice + actions + legacy field aliases.      |\n| `useAddPipelineCampaignIDs(ids)`    | Effect hook — registers campaign ids with the store on mount.                          |\n| `useAddPipelineCampaigns(items)`    | Effect hook — registers full campaign records on mount.                                |\n| `useAddPipelineFundraisingEventIDs(ids)` | Effect hook — registers fundraising-event ids on mount.                           |\n| `useCommonContextHooks(…)`          | URL / mode / charity-type sync to the store. Same shape as the legacy hook.            |\n| `useIdentify()` / `useIdentify(…11 legacy args)` | Zero-arg returns the store's bound `identify` action. Positional arg form is preserved as an `@deprecated` overload — args are ignored, warns once. |\n\n### Legacy aliases (`/legacy`)\n\nSoft-deprecated. Identical behavior to the canonical exports —\nrename the import specifier once and the rest of your code base\nkeeps working unchanged:\n\n| Legacy name            | Canonical replacement                                          |\n| ---------------------- | -------------------------------------------------------------- |\n| `useUDPStore`          | `usePipelineValue` (Tier 1) / typed slice hooks (Tier 2)       |\n| `useUDPStoreApi`       | (use a typed slice hook; or reach for the raw store API)       |\n| `createUDPStore`       | `createPipelineStore` from `@playlive/realtime-pipeline/store` |\n| `UDPStore` (type)      | `PipelineStore` from `@playlive/realtime-pipeline/store`       |\n| `UDPStoreApi` (type)   | `PipelineStoreApi` from `@playlive/realtime-pipeline/store`    |\n| `CharityTypes`         | re-exported from `@playlive/realtime-pipeline/protocol`         |\n\nPlus every Tier 1+2+3 export under its canonical name.\n\n## Migration from `playlive-overlay-data-layer`\n\nOne-shot sed-friendly rewrite of every import specifier (no body\nedits needed for the happy path):\n\n```bash\n# Tier 3 — kitchen-sink (drop-in replacement)\nrg -l '@playlive/overlay-data-layer/websocket' . \\\n  | xargs sed -i '' \\\n    -e 's|@playlive/overlay-data-layer/websocket|@playlive/react-pipeline/provider|g'\n```\n\nFor the soft-deprecated names (`useUDPStore`, `createUDPStore`, …)\nsubstitute `/legacy` instead of `/provider` to opt into the alias\nbundle, then migrate at your leisure (PRD §10 — one-release window).\n\nSee the phase-8 row in the root [MIGRATION.md](../../MIGRATION.md) for\nthe full per-symbol mapping plus call-site walkthroughs.\n\n## Upstream spec\n\nNo external API surface — internal workspace package. All transport\nruns through `@playlive/realtime-pipeline/connection`.\n\n## Twitch Extension URL disclosure\n\n`KNOWN_URLS` enumerates every absolute URL or host this package can\nfetch. **It is empty.** This package opens WebSocket connections only\nto URLs the consumer passes to `<RealtimePipelineProvider url={…}>`;\nthose URLs must be added to your Extension submission's URL\ndisclosure list, not this one.\n\n```ts\nimport { KNOWN_URLS } from \"@playlive/react-pipeline\";\nconsole.log(KNOWN_URLS); // []\n```\n\nSee [`docs/twitch-extension-checklist.md`](../../docs/twitch-extension-checklist.md).\n\n## Examples\n\nLive overlay apps consuming this provider land under `apps/*`\n(phase 10). Until then, the Tier 1 quick-start above is the minimum\nviable wiring.\n\n## Contributing\n\nSee [CONTRIBUTING.md](../../CONTRIBUTING.md). To add new Tier-2\nhooks, mirror an existing slice hook in `src/hooks/index.ts` and\nadd a `it.each` entry in `tests/unit/index.test.ts`.\n\n## License\n\nMIT — see [LICENSE](../../LICENSE). Distributed via Play Live\nCodeArtifact (PRD §6).\n","readmeFilename":"README.md","dist":{"tarball":"https://packages.playlive.experience.stjude.org/@playlive/react-pipeline/-/react-pipeline-0.3.2.tgz","shasum":"7303a49a46fc0bcde03fa8239a6dd99c800fedc4","integrity":"sha512-i36Eu72v8MA7VFAmgFB0IAAepN9aPGMpTvHCaPLi2Zd4LzqYx1uigtuWUAw2ViXVtkP7/nWyuoA9A3us5SqbaA=="}},"0.3.3":{"name":"@playlive/react-pipeline","version":"0.3.3","description":"React bindings for @playlive/realtime-pipeline — three usage tiers (single selector / typed per-slice / kitchen-sink). Twitch-Extension safe; no react-use-websocket-lite.","type":"module","sideEffects":false,"main":"./index.js","types":"./index.d.ts","exports":{".":{"import":"./index.js","types":"./index.d.ts"},"./provider":{"import":"./provider/index.js","types":"./provider/index.d.ts"},"./legacy":{"import":"./legacy/index.js","types":"./legacy/index.d.ts"},"./fusion":{"import":"./fusion/index.js","types":"./fusion/index.d.ts"}},"dependencies":{},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","zustand":"^5.0.0","@playlive/realtime-pipeline":"^0.2.4","@playlive/tiltify-core":"^0.4.11","@tanstack/react-query":"^5.0.0","@playlive/react-query":"^0.2.2","@playlive/fundraiser-data":"^0.2.0"},"peerDependenciesMeta":{"@tanstack/react-query":{"optional":true},"@playlive/react-query":{"optional":true},"@playlive/fundraiser-data":{"optional":true}},"playlive":{"target":"browser","frontendEligible":true,"coverageFloor":85},"publishConfig":{"access":"restricted","registry":"https://playlive-767397689694.d.codeartifact.us-east-1.amazonaws.com/npm/playlive/"},"integrity":"sha512-76E0VJqU5czGJgjAebUKcQF1qcp08rHvzFqeTbWdKqZHCUYBnUEgKRk7VD0h3wWp/rvsTGL+RVLvfK+V3lBw7w==","shasum":"a4ae3b4ee2cd57434848d672fc9710bf278016d7","readme":"# @playlive/react-pipeline\n\nReact bindings for [`@playlive/realtime-pipeline`](../realtime-pipeline/).\nDrop-in replacement for `playlive-overlay-data-layer/src/websocket/*`\nwith **zero** dependency on `react-use-websocket-lite` — built directly\non Zustand v5's `useStore` + `useSyncExternalStore`.\n\n![Coverage](./coverage-badge.svg)\n\n## Three tiers — take only what you need\n\nPick the surface that matches how much of the legacy\n`UnifiedDataPipelineProvider` API you actually want. Each tier is\nstrictly larger than the last and lives at its own subpath so unused\ncode is tree-shaken out.\n\n| Tier | Subpath                            | Surface                                                                         | Use when                                                              |\n| ---- | ---------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| 1    | `@playlive/react-pipeline`         | `RealtimePipelineProvider` + `usePipelineValue` (universal selector)            | Greenfield app; you want one selector primitive and nothing else.     |\n| 2    | `@playlive/react-pipeline`         | Tier 1 **plus** 22 typed per-slice hooks (`usePipelineDonations(id)`, …)        | You want stable, typed one-liners for the common reads.               |\n| 3    | `@playlive/react-pipeline/provider`| Tier 2 **plus** `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, etc.   | Porting an existing overlay-data-layer consumer with minimal churn.   |\n| —    | `@playlive/react-pipeline/legacy`  | Tier 3 **plus** `useUDPStore` / `useUDPStoreApi` / `createUDPStore` aliases     | Soft-deprecated shim for the legacy symbol names (one-release window).|\n\n## Install\n\n```bash\nbun add @playlive/react-pipeline\nbun add -d react react-dom zustand\n```\n\nPeer dependencies (jose-style — consumer brings their own):\n`react@^19`, `react-dom@^19`, `zustand@^5`,\n`@playlive/realtime-pipeline` (workspace), `@playlive/tiltify-core`\n(workspace, type-only — stripped at compile time).\n\nNothing else ships in the bundle.\n\n## Quick start — Tier 1\n\n```tsx\nimport { RealtimePipelineProvider, usePipelineCampaigns } from \"@playlive/react-pipeline\";\n\nfunction App() {\n  return (\n    <RealtimePipelineProvider\n      url=\"wss://pipeline.example.com/socket\"\n      charityType=\"tiltify\"\n      autoConnect\n    >\n      <Overlay />\n    </RealtimePipelineProvider>\n  );\n}\n\nfunction Overlay() {\n  const campaigns = usePipelineCampaigns();\n  return <ul>{campaigns.map((c) => <li key={c.id}>{c.name}</li>)}</ul>;\n}\n```\n\n`RealtimePipelineProvider` mounts a Zustand store + opens (or accepts)\na `PipelineConnection`. Children call any Tier 1 / Tier 2 hook to\nsubscribe.\n\n## Subpath exports\n\n| Subpath                              | Description                                                                       |\n| ------------------------------------ | --------------------------------------------------------------------------------- |\n| `@playlive/react-pipeline`           | Default barrel — Tier 1 + Tier 2 (provider + 21 typed slice hooks).               |\n| `@playlive/react-pipeline/provider`  | Tier 3 — `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, add-pipeline\\*. |\n| `@playlive/react-pipeline/legacy`    | Soft-deprecated alias bundle (`useUDPStore`, `createUDPStore`, `UDPStore`, etc.). |\n\nEach subpath ships an ESM bundle, a Bun source condition, and `.d.ts`\ndeclarations. Frontend-eligible bundles are not minified (PRD §6.1)\nso stack traces stay readable in production.\n\n## API reference\n\n### Tier 1 — universal primitive\n\n| Export                          | Signature                                                            |\n| ------------------------------- | -------------------------------------------------------------------- |\n| `RealtimePipelineProvider`      | `(props: RealtimePipelineProviderProps) => JSX.Element`              |\n| `usePipelineValue`              | `<T>(selector: (s: PipelineStore) => T) => T`                        |\n\n`usePipelineValue` is single-arg by design. Zustand v5 dropped the\nthird `equalityFn` parameter on `useStore` to keep the package\nfree of the `use-sync-external-store` shim peer. For shallow / custom\nequality, wrap your selector with `useShallow` from\n`zustand/shallow` (zero-extra-dep):\n\n```tsx\nimport { useShallow } from \"zustand/shallow\";\nimport { usePipelineValue } from \"@playlive/react-pipeline\";\n\nconst { connected, connecting } = usePipelineValue(\n  useShallow((s) => ({ connected: s.connected, connecting: s.connecting })),\n);\n```\n\n### Tier 2 — typed per-slice hooks\n\nAll 22 hooks are stable wrappers over `usePipelineValue` with a typed\nselector + memo-stable empty-array fallbacks (no infinite-render\nchurn from fresh `[]` allocations — Decision §3 in CHANGELOG).\n\n| Hook                              | Returns                                                             |\n| --------------------------------- | ------------------------------------------------------------------- |\n| `usePipelineCampaigns()`          | `TiltifyCampaign[]`                                                 |\n| `usePipelineTeamCampaigns()`      | `TiltifyTeamCampaign[]`                                             |\n| `usePipelineFundraisingEvents()`  | `TiltifyFundraisingEvent[]`                                         |\n| `usePipelineCauses()`             | `TiltifyCause[]`                                                    |\n| `usePipelineDonations()`          | `Record<string, TiltifyDonationWithTestFlag[]>`                     |\n| `usePipelineDonations(id)`        | `TiltifyDonationWithTestFlag[]` (memo-stable `[]` if id unknown)    |\n| `usePipelineDonationSum(id, opts?)` | `PipelineDonationSumResult` — live per-currency sum over `[start, end)` of the WS donation firehose. Pair with the REST `/schedules/campaigns/{id}/raised` `asOf` field to replace polling with a `baseline + WS-delta` view. |\n| `usePipelineDonationTrains(id, withStatus?)` | `DonationTrain[]`                                        |\n| `usePipelineSubathonTimer(id, withStatus?)`  | `SubathonTimer[]`                                        |\n| `usePipelineDonorSpotlight(id)`   | `ComputedSpotlight \\| null`                                         |\n| `usePipelinePolls(id)`            | `TiltifyPoll[]`                                                     |\n| `usePipelineRewards(id)`          | `TiltifyReward[]`                                                   |\n| `usePipelineTargets(id)`          | `TiltifyTarget[]`                                                   |\n| `usePipelineMilestones(id)`       | `TiltifyMilestone[]`                                                |\n| `usePipelineLeaderboardEntries(id)` | `TiltifyLeaderboardEntry[]`                                       |\n| `usePipelineAuctionHouses()`      | `AuctionHouse[]`                                                    |\n| `usePipelineTwitchChat()`         | `TwitchChatMessage[]`                                               |\n| `usePipelineConnectionState()`    | `PipelineConnectionState` (10 split selectors — no false re-renders)|\n| `usePipelineConnectionActions()`  | `PipelineConnectionActions` (stable `connect/disconnect/refresh/…`) |\n| `usePipelineSubscriptions()`      | `Record<string, TiltifyWebhookSubscription>`                        |\n| `usePipelineTiltifySettings()`    | `TiltifyWebSocketSettings`                                          |\n| `usePipelineLastRefreshResponse()`| `RefreshResponseMessage \\| null`                                    |\n| `usePipelineDonorSpotlightSettings()` | `Record<string, DonorSpotlightSettings>`                        |\n\n### Tier 3 — kitchen-sink provider (`/provider`)\n\n| Export                              | Notes                                                                                  |\n| ----------------------------------- | -------------------------------------------------------------------------------------- |\n| `UnifiedDataPipelineProvider`       | Superset of `RealtimePipelineProvider`; adds legacy `reloadOnErrorReconnect` + `debug`.|\n| `useUnifiedDataPipeline()`          | One-call kitchen-sink hook. Returns every slice + actions + legacy field aliases.      |\n| `useAddPipelineCampaignIDs(ids)`    | Effect hook — registers campaign ids with the store on mount.                          |\n| `useAddPipelineCampaigns(items)`    | Effect hook — registers full campaign records on mount.                                |\n| `useAddPipelineFundraisingEventIDs(ids)` | Effect hook — registers fundraising-event ids on mount.                           |\n| `useCommonContextHooks(…)`          | URL / mode / charity-type sync to the store. Same shape as the legacy hook.            |\n| `useIdentify()` / `useIdentify(…11 legacy args)` | Zero-arg returns the store's bound `identify` action. Positional arg form is preserved as an `@deprecated` overload — args are ignored, warns once. |\n\n### Legacy aliases (`/legacy`)\n\nSoft-deprecated. Identical behavior to the canonical exports —\nrename the import specifier once and the rest of your code base\nkeeps working unchanged:\n\n| Legacy name            | Canonical replacement                                          |\n| ---------------------- | -------------------------------------------------------------- |\n| `useUDPStore`          | `usePipelineValue` (Tier 1) / typed slice hooks (Tier 2)       |\n| `useUDPStoreApi`       | (use a typed slice hook; or reach for the raw store API)       |\n| `createUDPStore`       | `createPipelineStore` from `@playlive/realtime-pipeline/store` |\n| `UDPStore` (type)      | `PipelineStore` from `@playlive/realtime-pipeline/store`       |\n| `UDPStoreApi` (type)   | `PipelineStoreApi` from `@playlive/realtime-pipeline/store`    |\n| `CharityTypes`         | re-exported from `@playlive/realtime-pipeline/protocol`         |\n\nPlus every Tier 1+2+3 export under its canonical name.\n\n## Migration from `playlive-overlay-data-layer`\n\nOne-shot sed-friendly rewrite of every import specifier (no body\nedits needed for the happy path):\n\n```bash\n# Tier 3 — kitchen-sink (drop-in replacement)\nrg -l '@playlive/overlay-data-layer/websocket' . \\\n  | xargs sed -i '' \\\n    -e 's|@playlive/overlay-data-layer/websocket|@playlive/react-pipeline/provider|g'\n```\n\nFor the soft-deprecated names (`useUDPStore`, `createUDPStore`, …)\nsubstitute `/legacy` instead of `/provider` to opt into the alias\nbundle, then migrate at your leisure (PRD §10 — one-release window).\n\nSee the phase-8 row in the root [MIGRATION.md](../../MIGRATION.md) for\nthe full per-symbol mapping plus call-site walkthroughs.\n\n## Upstream spec\n\nNo external API surface — internal workspace package. All transport\nruns through `@playlive/realtime-pipeline/connection`.\n\n## Twitch Extension URL disclosure\n\n`KNOWN_URLS` enumerates every absolute URL or host this package can\nfetch. **It is empty.** This package opens WebSocket connections only\nto URLs the consumer passes to `<RealtimePipelineProvider url={…}>`;\nthose URLs must be added to your Extension submission's URL\ndisclosure list, not this one.\n\n```ts\nimport { KNOWN_URLS } from \"@playlive/react-pipeline\";\nconsole.log(KNOWN_URLS); // []\n```\n\nSee [`docs/twitch-extension-checklist.md`](../../docs/twitch-extension-checklist.md).\n\n## Examples\n\nLive overlay apps consuming this provider land under `apps/*`\n(phase 10). Until then, the Tier 1 quick-start above is the minimum\nviable wiring.\n\n## Contributing\n\nSee [CONTRIBUTING.md](../../CONTRIBUTING.md). To add new Tier-2\nhooks, mirror an existing slice hook in `src/hooks/index.ts` and\nadd a `it.each` entry in `tests/unit/index.test.ts`.\n\n## License\n\nMIT — see [LICENSE](../../LICENSE). Distributed via Play Live\nCodeArtifact (PRD §6).\n","readmeFilename":"README.md","dist":{"tarball":"https://packages.playlive.experience.stjude.org/@playlive/react-pipeline/-/react-pipeline-0.3.3.tgz","shasum":"a4ae3b4ee2cd57434848d672fc9710bf278016d7","integrity":"sha512-76E0VJqU5czGJgjAebUKcQF1qcp08rHvzFqeTbWdKqZHCUYBnUEgKRk7VD0h3wWp/rvsTGL+RVLvfK+V3lBw7w=="}},"0.3.4":{"name":"@playlive/react-pipeline","version":"0.3.4","description":"React bindings for @playlive/realtime-pipeline — three usage tiers (single selector / typed per-slice / kitchen-sink). Twitch-Extension safe; no react-use-websocket-lite.","type":"module","sideEffects":false,"main":"./index.js","types":"./index.d.ts","exports":{".":{"import":"./index.js","types":"./index.d.ts"},"./provider":{"import":"./provider/index.js","types":"./provider/index.d.ts"},"./legacy":{"import":"./legacy/index.js","types":"./legacy/index.d.ts"},"./fusion":{"import":"./fusion/index.js","types":"./fusion/index.d.ts"}},"dependencies":{},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","zustand":"^5.0.0","@playlive/realtime-pipeline":"^0.2.5","@playlive/tiltify-core":"^0.4.11","@tanstack/react-query":"^5.0.0","@playlive/react-query":"^0.2.6","@playlive/fundraiser-data":"^0.2.4"},"peerDependenciesMeta":{"@tanstack/react-query":{"optional":true},"@playlive/react-query":{"optional":true},"@playlive/fundraiser-data":{"optional":true}},"playlive":{"target":"browser","frontendEligible":true,"coverageFloor":85},"publishConfig":{"access":"restricted","registry":"https://playlive-767397689694.d.codeartifact.us-east-1.amazonaws.com/npm/playlive/"},"integrity":"sha512-BzU0ha0IBzxOV4f/GKhPHEIGndIqPYOS96OS8iwm4dR+hKA4+koCpCkG9CNOUq+H8qDjpD2ULKSzAT4EZ/fP7Q==","shasum":"d63939ed6bb0d4b3c1bb54200436732f3ca4c337","readme":"# @playlive/react-pipeline\n\nReact bindings for [`@playlive/realtime-pipeline`](../realtime-pipeline/).\nDrop-in replacement for `playlive-overlay-data-layer/src/websocket/*`\nwith **zero** dependency on `react-use-websocket-lite` — built directly\non Zustand v5's `useStore` + `useSyncExternalStore`.\n\n![Coverage](./coverage-badge.svg)\n\n## Three tiers — take only what you need\n\nPick the surface that matches how much of the legacy\n`UnifiedDataPipelineProvider` API you actually want. Each tier is\nstrictly larger than the last and lives at its own subpath so unused\ncode is tree-shaken out.\n\n| Tier | Subpath                            | Surface                                                                         | Use when                                                              |\n| ---- | ---------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| 1    | `@playlive/react-pipeline`         | `RealtimePipelineProvider` + `usePipelineValue` (universal selector)            | Greenfield app; you want one selector primitive and nothing else.     |\n| 2    | `@playlive/react-pipeline`         | Tier 1 **plus** 22 typed per-slice hooks (`usePipelineDonations(id)`, …)        | You want stable, typed one-liners for the common reads.               |\n| 3    | `@playlive/react-pipeline/provider`| Tier 2 **plus** `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, etc.   | Porting an existing overlay-data-layer consumer with minimal churn.   |\n| —    | `@playlive/react-pipeline/legacy`  | Tier 3 **plus** `useUDPStore` / `useUDPStoreApi` / `createUDPStore` aliases     | Soft-deprecated shim for the legacy symbol names (one-release window).|\n\n## Install\n\n```bash\nbun add @playlive/react-pipeline\nbun add -d react react-dom zustand\n```\n\nPeer dependencies (jose-style — consumer brings their own):\n`react@^19`, `react-dom@^19`, `zustand@^5`,\n`@playlive/realtime-pipeline` (workspace), `@playlive/tiltify-core`\n(workspace, type-only — stripped at compile time).\n\nNothing else ships in the bundle.\n\n## Quick start — Tier 1\n\n```tsx\nimport { RealtimePipelineProvider, usePipelineCampaigns } from \"@playlive/react-pipeline\";\n\nfunction App() {\n  return (\n    <RealtimePipelineProvider\n      url=\"wss://pipeline.example.com/socket\"\n      charityType=\"tiltify\"\n      autoConnect\n    >\n      <Overlay />\n    </RealtimePipelineProvider>\n  );\n}\n\nfunction Overlay() {\n  const campaigns = usePipelineCampaigns();\n  return <ul>{campaigns.map((c) => <li key={c.id}>{c.name}</li>)}</ul>;\n}\n```\n\n`RealtimePipelineProvider` mounts a Zustand store + opens (or accepts)\na `PipelineConnection`. Children call any Tier 1 / Tier 2 hook to\nsubscribe.\n\n## Subpath exports\n\n| Subpath                              | Description                                                                       |\n| ------------------------------------ | --------------------------------------------------------------------------------- |\n| `@playlive/react-pipeline`           | Default barrel — Tier 1 + Tier 2 (provider + 21 typed slice hooks).               |\n| `@playlive/react-pipeline/provider`  | Tier 3 — `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, add-pipeline\\*. |\n| `@playlive/react-pipeline/legacy`    | Soft-deprecated alias bundle (`useUDPStore`, `createUDPStore`, `UDPStore`, etc.). |\n\nEach subpath ships an ESM bundle, a Bun source condition, and `.d.ts`\ndeclarations. Frontend-eligible bundles are not minified (PRD §6.1)\nso stack traces stay readable in production.\n\n## API reference\n\n### Tier 1 — universal primitive\n\n| Export                          | Signature                                                            |\n| ------------------------------- | -------------------------------------------------------------------- |\n| `RealtimePipelineProvider`      | `(props: RealtimePipelineProviderProps) => JSX.Element`              |\n| `usePipelineValue`              | `<T>(selector: (s: PipelineStore) => T) => T`                        |\n\n`usePipelineValue` is single-arg by design. Zustand v5 dropped the\nthird `equalityFn` parameter on `useStore` to keep the package\nfree of the `use-sync-external-store` shim peer. For shallow / custom\nequality, wrap your selector with `useShallow` from\n`zustand/shallow` (zero-extra-dep):\n\n```tsx\nimport { useShallow } from \"zustand/shallow\";\nimport { usePipelineValue } from \"@playlive/react-pipeline\";\n\nconst { connected, connecting } = usePipelineValue(\n  useShallow((s) => ({ connected: s.connected, connecting: s.connecting })),\n);\n```\n\n### Tier 2 — typed per-slice hooks\n\nAll 22 hooks are stable wrappers over `usePipelineValue` with a typed\nselector + memo-stable empty-array fallbacks (no infinite-render\nchurn from fresh `[]` allocations — Decision §3 in CHANGELOG).\n\n| Hook                              | Returns                                                             |\n| --------------------------------- | ------------------------------------------------------------------- |\n| `usePipelineCampaigns()`          | `TiltifyCampaign[]`                                                 |\n| `usePipelineTeamCampaigns()`      | `TiltifyTeamCampaign[]`                                             |\n| `usePipelineFundraisingEvents()`  | `TiltifyFundraisingEvent[]`                                         |\n| `usePipelineCauses()`             | `TiltifyCause[]`                                                    |\n| `usePipelineDonations()`          | `Record<string, TiltifyDonationWithTestFlag[]>`                     |\n| `usePipelineDonations(id)`        | `TiltifyDonationWithTestFlag[]` (memo-stable `[]` if id unknown)    |\n| `usePipelineDonationSum(id, opts?)` | `PipelineDonationSumResult` — live per-currency sum over `[start, end)` of the WS donation firehose. Pair with the REST `/schedules/campaigns/{id}/raised` `asOf` field to replace polling with a `baseline + WS-delta` view. |\n| `usePipelineDonationTrains(id, withStatus?)` | `DonationTrain[]`                                        |\n| `usePipelineSubathonTimer(id, withStatus?)`  | `SubathonTimer[]`                                        |\n| `usePipelineDonorSpotlight(id)`   | `ComputedSpotlight \\| null`                                         |\n| `usePipelinePolls(id)`            | `TiltifyPoll[]`                                                     |\n| `usePipelineRewards(id)`          | `TiltifyReward[]`                                                   |\n| `usePipelineTargets(id)`          | `TiltifyTarget[]`                                                   |\n| `usePipelineMilestones(id)`       | `TiltifyMilestone[]`                                                |\n| `usePipelineLeaderboardEntries(id)` | `TiltifyLeaderboardEntry[]`                                       |\n| `usePipelineAuctionHouses()`      | `AuctionHouse[]`                                                    |\n| `usePipelineTwitchChat()`         | `TwitchChatMessage[]`                                               |\n| `usePipelineConnectionState()`    | `PipelineConnectionState` (10 split selectors — no false re-renders)|\n| `usePipelineConnectionActions()`  | `PipelineConnectionActions` (stable `connect/disconnect/refresh/…`) |\n| `usePipelineSubscriptions()`      | `Record<string, TiltifyWebhookSubscription>`                        |\n| `usePipelineTiltifySettings()`    | `TiltifyWebSocketSettings`                                          |\n| `usePipelineLastRefreshResponse()`| `RefreshResponseMessage \\| null`                                    |\n| `usePipelineDonorSpotlightSettings()` | `Record<string, DonorSpotlightSettings>`                        |\n\n### Tier 3 — kitchen-sink provider (`/provider`)\n\n| Export                              | Notes                                                                                  |\n| ----------------------------------- | -------------------------------------------------------------------------------------- |\n| `UnifiedDataPipelineProvider`       | Superset of `RealtimePipelineProvider`; adds legacy `reloadOnErrorReconnect` + `debug`.|\n| `useUnifiedDataPipeline()`          | One-call kitchen-sink hook. Returns every slice + actions + legacy field aliases.      |\n| `useAddPipelineCampaignIDs(ids)`    | Effect hook — registers campaign ids with the store on mount.                          |\n| `useAddPipelineCampaigns(items)`    | Effect hook — registers full campaign records on mount.                                |\n| `useAddPipelineFundraisingEventIDs(ids)` | Effect hook — registers fundraising-event ids on mount.                           |\n| `useCommonContextHooks(…)`          | URL / mode / charity-type sync to the store. Same shape as the legacy hook.            |\n| `useIdentify()` / `useIdentify(…11 legacy args)` | Zero-arg returns the store's bound `identify` action. Positional arg form is preserved as an `@deprecated` overload — args are ignored, warns once. |\n\n### Legacy aliases (`/legacy`)\n\nSoft-deprecated. Identical behavior to the canonical exports —\nrename the import specifier once and the rest of your code base\nkeeps working unchanged:\n\n| Legacy name            | Canonical replacement                                          |\n| ---------------------- | -------------------------------------------------------------- |\n| `useUDPStore`          | `usePipelineValue` (Tier 1) / typed slice hooks (Tier 2)       |\n| `useUDPStoreApi`       | (use a typed slice hook; or reach for the raw store API)       |\n| `createUDPStore`       | `createPipelineStore` from `@playlive/realtime-pipeline/store` |\n| `UDPStore` (type)      | `PipelineStore` from `@playlive/realtime-pipeline/store`       |\n| `UDPStoreApi` (type)   | `PipelineStoreApi` from `@playlive/realtime-pipeline/store`    |\n| `CharityTypes`         | re-exported from `@playlive/realtime-pipeline/protocol`         |\n\nPlus every Tier 1+2+3 export under its canonical name.\n\n## Migration from `playlive-overlay-data-layer`\n\nOne-shot sed-friendly rewrite of every import specifier (no body\nedits needed for the happy path):\n\n```bash\n# Tier 3 — kitchen-sink (drop-in replacement)\nrg -l '@playlive/overlay-data-layer/websocket' . \\\n  | xargs sed -i '' \\\n    -e 's|@playlive/overlay-data-layer/websocket|@playlive/react-pipeline/provider|g'\n```\n\nFor the soft-deprecated names (`useUDPStore`, `createUDPStore`, …)\nsubstitute `/legacy` instead of `/provider` to opt into the alias\nbundle, then migrate at your leisure (PRD §10 — one-release window).\n\nSee the phase-8 row in the root [MIGRATION.md](../../MIGRATION.md) for\nthe full per-symbol mapping plus call-site walkthroughs.\n\n## Upstream spec\n\nNo external API surface — internal workspace package. All transport\nruns through `@playlive/realtime-pipeline/connection`.\n\n## Twitch Extension URL disclosure\n\n`KNOWN_URLS` enumerates every absolute URL or host this package can\nfetch. **It is empty.** This package opens WebSocket connections only\nto URLs the consumer passes to `<RealtimePipelineProvider url={…}>`;\nthose URLs must be added to your Extension submission's URL\ndisclosure list, not this one.\n\n```ts\nimport { KNOWN_URLS } from \"@playlive/react-pipeline\";\nconsole.log(KNOWN_URLS); // []\n```\n\nSee [`docs/twitch-extension-checklist.md`](../../docs/twitch-extension-checklist.md).\n\n## Examples\n\nLive overlay apps consuming this provider land under `apps/*`\n(phase 10). Until then, the Tier 1 quick-start above is the minimum\nviable wiring.\n\n## Contributing\n\nSee [CONTRIBUTING.md](../../CONTRIBUTING.md). To add new Tier-2\nhooks, mirror an existing slice hook in `src/hooks/index.ts` and\nadd a `it.each` entry in `tests/unit/index.test.ts`.\n\n## License\n\nMIT — see [LICENSE](../../LICENSE). Distributed via Play Live\nCodeArtifact (PRD §6).\n","readmeFilename":"README.md","dist":{"tarball":"https://packages.playlive.experience.stjude.org/@playlive/react-pipeline/-/react-pipeline-0.3.4.tgz","shasum":"d63939ed6bb0d4b3c1bb54200436732f3ca4c337","integrity":"sha512-BzU0ha0IBzxOV4f/GKhPHEIGndIqPYOS96OS8iwm4dR+hKA4+koCpCkG9CNOUq+H8qDjpD2ULKSzAT4EZ/fP7Q=="}},"0.4.0":{"name":"@playlive/react-pipeline","version":"0.4.0","description":"React bindings for @playlive/realtime-pipeline — three usage tiers (single selector / typed per-slice / kitchen-sink). Twitch-Extension safe; no react-use-websocket-lite.","type":"module","sideEffects":false,"main":"./index.js","types":"./index.d.ts","exports":{".":{"import":"./index.js","types":"./index.d.ts"},"./provider":{"import":"./provider/index.js","types":"./provider/index.d.ts"},"./legacy":{"import":"./legacy/index.js","types":"./legacy/index.d.ts"},"./fusion":{"import":"./fusion/index.js","types":"./fusion/index.d.ts"}},"dependencies":{},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","zustand":"^5.0.0","@playlive/realtime-pipeline":"^0.2.5","@playlive/tiltify-core":"^0.4.11","@tanstack/react-query":"^5.0.0","@playlive/react-query":"^0.3.0","@playlive/fundraiser-data":"^0.3.0"},"peerDependenciesMeta":{"@tanstack/react-query":{"optional":true},"@playlive/react-query":{"optional":true},"@playlive/fundraiser-data":{"optional":true}},"playlive":{"target":"browser","frontendEligible":true,"coverageFloor":85},"publishConfig":{"access":"restricted","registry":"https://playlive-767397689694.d.codeartifact.us-east-1.amazonaws.com/npm/playlive/"},"integrity":"sha512-EpQ080w18vrouR9S+sqsVnlRtr8hNBSmz77UEpvM2bdy/T4Lmsv1n25p9ZlIH2rAAOysHMRAei0Won0oUoVGsQ==","shasum":"3108e7dedff72cb68b8b32fa30a2a3c9db14f07e","readme":"# @playlive/react-pipeline\n\nReact bindings for [`@playlive/realtime-pipeline`](../realtime-pipeline/).\nDrop-in replacement for `playlive-overlay-data-layer/src/websocket/*`\nwith **zero** dependency on `react-use-websocket-lite` — built directly\non Zustand v5's `useStore` + `useSyncExternalStore`.\n\n![Coverage](./coverage-badge.svg)\n\n## Three tiers — take only what you need\n\nPick the surface that matches how much of the legacy\n`UnifiedDataPipelineProvider` API you actually want. Each tier is\nstrictly larger than the last and lives at its own subpath so unused\ncode is tree-shaken out.\n\n| Tier | Subpath                            | Surface                                                                         | Use when                                                              |\n| ---- | ---------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| 1    | `@playlive/react-pipeline`         | `RealtimePipelineProvider` + `usePipelineValue` (universal selector)            | Greenfield app; you want one selector primitive and nothing else.     |\n| 2    | `@playlive/react-pipeline`         | Tier 1 **plus** 22 typed per-slice hooks (`usePipelineDonations(id)`, …)        | You want stable, typed one-liners for the common reads.               |\n| 3    | `@playlive/react-pipeline/provider`| Tier 2 **plus** `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, etc.   | Porting an existing overlay-data-layer consumer with minimal churn.   |\n| —    | `@playlive/react-pipeline/legacy`  | Tier 3 **plus** `useUDPStore` / `useUDPStoreApi` / `createUDPStore` aliases     | Soft-deprecated shim for the legacy symbol names (one-release window).|\n\n## Install\n\n```bash\nbun add @playlive/react-pipeline\nbun add -d react react-dom zustand\n```\n\nPeer dependencies (jose-style — consumer brings their own):\n`react@^19`, `react-dom@^19`, `zustand@^5`,\n`@playlive/realtime-pipeline` (workspace), `@playlive/tiltify-core`\n(workspace, type-only — stripped at compile time).\n\nNothing else ships in the bundle.\n\n## Quick start — Tier 1\n\n```tsx\nimport { RealtimePipelineProvider, usePipelineCampaigns } from \"@playlive/react-pipeline\";\n\nfunction App() {\n  return (\n    <RealtimePipelineProvider\n      url=\"wss://pipeline.example.com/socket\"\n      charityType=\"tiltify\"\n      autoConnect\n    >\n      <Overlay />\n    </RealtimePipelineProvider>\n  );\n}\n\nfunction Overlay() {\n  const campaigns = usePipelineCampaigns();\n  return <ul>{campaigns.map((c) => <li key={c.id}>{c.name}</li>)}</ul>;\n}\n```\n\n`RealtimePipelineProvider` mounts a Zustand store + opens (or accepts)\na `PipelineConnection`. Children call any Tier 1 / Tier 2 hook to\nsubscribe.\n\n## Subpath exports\n\n| Subpath                              | Description                                                                       |\n| ------------------------------------ | --------------------------------------------------------------------------------- |\n| `@playlive/react-pipeline`           | Default barrel — Tier 1 + Tier 2 (provider + 21 typed slice hooks).               |\n| `@playlive/react-pipeline/provider`  | Tier 3 — `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, add-pipeline\\*. |\n| `@playlive/react-pipeline/legacy`    | Soft-deprecated alias bundle (`useUDPStore`, `createUDPStore`, `UDPStore`, etc.). |\n\nEach subpath ships an ESM bundle, a Bun source condition, and `.d.ts`\ndeclarations. Frontend-eligible bundles are not minified (PRD §6.1)\nso stack traces stay readable in production.\n\n## API reference\n\n### Tier 1 — universal primitive\n\n| Export                          | Signature                                                            |\n| ------------------------------- | -------------------------------------------------------------------- |\n| `RealtimePipelineProvider`      | `(props: RealtimePipelineProviderProps) => JSX.Element`              |\n| `usePipelineValue`              | `<T>(selector: (s: PipelineStore) => T) => T`                        |\n\n`usePipelineValue` is single-arg by design. Zustand v5 dropped the\nthird `equalityFn` parameter on `useStore` to keep the package\nfree of the `use-sync-external-store` shim peer. For shallow / custom\nequality, wrap your selector with `useShallow` from\n`zustand/shallow` (zero-extra-dep):\n\n```tsx\nimport { useShallow } from \"zustand/shallow\";\nimport { usePipelineValue } from \"@playlive/react-pipeline\";\n\nconst { connected, connecting } = usePipelineValue(\n  useShallow((s) => ({ connected: s.connected, connecting: s.connecting })),\n);\n```\n\n### Tier 2 — typed per-slice hooks\n\nAll 22 hooks are stable wrappers over `usePipelineValue` with a typed\nselector + memo-stable empty-array fallbacks (no infinite-render\nchurn from fresh `[]` allocations — Decision §3 in CHANGELOG).\n\n| Hook                              | Returns                                                             |\n| --------------------------------- | ------------------------------------------------------------------- |\n| `usePipelineCampaigns()`          | `TiltifyCampaign[]`                                                 |\n| `usePipelineTeamCampaigns()`      | `TiltifyTeamCampaign[]`                                             |\n| `usePipelineFundraisingEvents()`  | `TiltifyFundraisingEvent[]`                                         |\n| `usePipelineCauses()`             | `TiltifyCause[]`                                                    |\n| `usePipelineDonations()`          | `Record<string, TiltifyDonationWithTestFlag[]>`                     |\n| `usePipelineDonations(id)`        | `TiltifyDonationWithTestFlag[]` (memo-stable `[]` if id unknown)    |\n| `usePipelineDonationSum(id, opts?)` | `PipelineDonationSumResult` — live per-currency sum over `[start, end)` of the WS donation firehose. Pair with the REST `/schedules/campaigns/{id}/raised` `asOf` field to replace polling with a `baseline + WS-delta` view. |\n| `usePipelineDonationTrains(id, withStatus?)` | `DonationTrain[]`                                        |\n| `usePipelineSubathonTimer(id, withStatus?)`  | `SubathonTimer[]`                                        |\n| `usePipelineDonorSpotlight(id)`   | `ComputedSpotlight \\| null`                                         |\n| `usePipelinePolls(id)`            | `TiltifyPoll[]`                                                     |\n| `usePipelineRewards(id)`          | `TiltifyReward[]`                                                   |\n| `usePipelineTargets(id)`          | `TiltifyTarget[]`                                                   |\n| `usePipelineMilestones(id)`       | `TiltifyMilestone[]`                                                |\n| `usePipelineLeaderboardEntries(id)` | `TiltifyLeaderboardEntry[]`                                       |\n| `usePipelineAuctionHouses()`      | `AuctionHouse[]`                                                    |\n| `usePipelineTwitchChat()`         | `TwitchChatMessage[]`                                               |\n| `usePipelineConnectionState()`    | `PipelineConnectionState` (10 split selectors — no false re-renders)|\n| `usePipelineConnectionActions()`  | `PipelineConnectionActions` (stable `connect/disconnect/refresh/…`) |\n| `usePipelineSubscriptions()`      | `Record<string, TiltifyWebhookSubscription>`                        |\n| `usePipelineTiltifySettings()`    | `TiltifyWebSocketSettings`                                          |\n| `usePipelineLastRefreshResponse()`| `RefreshResponseMessage \\| null`                                    |\n| `usePipelineDonorSpotlightSettings()` | `Record<string, DonorSpotlightSettings>`                        |\n\n### Tier 3 — kitchen-sink provider (`/provider`)\n\n| Export                              | Notes                                                                                  |\n| ----------------------------------- | -------------------------------------------------------------------------------------- |\n| `UnifiedDataPipelineProvider`       | Superset of `RealtimePipelineProvider`; adds legacy `reloadOnErrorReconnect` + `debug`.|\n| `useUnifiedDataPipeline()`          | One-call kitchen-sink hook. Returns every slice + actions + legacy field aliases.      |\n| `useAddPipelineCampaignIDs(ids)`    | Effect hook — registers campaign ids with the store on mount.                          |\n| `useAddPipelineCampaigns(items)`    | Effect hook — registers full campaign records on mount.                                |\n| `useAddPipelineFundraisingEventIDs(ids)` | Effect hook — registers fundraising-event ids on mount.                           |\n| `useCommonContextHooks(…)`          | URL / mode / charity-type sync to the store. Same shape as the legacy hook.            |\n| `useIdentify()` / `useIdentify(…11 legacy args)` | Zero-arg returns the store's bound `identify` action. Positional arg form is preserved as an `@deprecated` overload — args are ignored, warns once. |\n\n### Legacy aliases (`/legacy`)\n\nSoft-deprecated. Identical behavior to the canonical exports —\nrename the import specifier once and the rest of your code base\nkeeps working unchanged:\n\n| Legacy name            | Canonical replacement                                          |\n| ---------------------- | -------------------------------------------------------------- |\n| `useUDPStore`          | `usePipelineValue` (Tier 1) / typed slice hooks (Tier 2)       |\n| `useUDPStoreApi`       | (use a typed slice hook; or reach for the raw store API)       |\n| `createUDPStore`       | `createPipelineStore` from `@playlive/realtime-pipeline/store` |\n| `UDPStore` (type)      | `PipelineStore` from `@playlive/realtime-pipeline/store`       |\n| `UDPStoreApi` (type)   | `PipelineStoreApi` from `@playlive/realtime-pipeline/store`    |\n| `CharityTypes`         | re-exported from `@playlive/realtime-pipeline/protocol`         |\n\nPlus every Tier 1+2+3 export under its canonical name.\n\n## Migration from `playlive-overlay-data-layer`\n\nOne-shot sed-friendly rewrite of every import specifier (no body\nedits needed for the happy path):\n\n```bash\n# Tier 3 — kitchen-sink (drop-in replacement)\nrg -l '@playlive/overlay-data-layer/websocket' . \\\n  | xargs sed -i '' \\\n    -e 's|@playlive/overlay-data-layer/websocket|@playlive/react-pipeline/provider|g'\n```\n\nFor the soft-deprecated names (`useUDPStore`, `createUDPStore`, …)\nsubstitute `/legacy` instead of `/provider` to opt into the alias\nbundle, then migrate at your leisure (PRD §10 — one-release window).\n\nSee the phase-8 row in the root [MIGRATION.md](../../MIGRATION.md) for\nthe full per-symbol mapping plus call-site walkthroughs.\n\n## Upstream spec\n\nNo external API surface — internal workspace package. All transport\nruns through `@playlive/realtime-pipeline/connection`.\n\n## Twitch Extension URL disclosure\n\n`KNOWN_URLS` enumerates every absolute URL or host this package can\nfetch. **It is empty.** This package opens WebSocket connections only\nto URLs the consumer passes to `<RealtimePipelineProvider url={…}>`;\nthose URLs must be added to your Extension submission's URL\ndisclosure list, not this one.\n\n```ts\nimport { KNOWN_URLS } from \"@playlive/react-pipeline\";\nconsole.log(KNOWN_URLS); // []\n```\n\nSee [`docs/twitch-extension-checklist.md`](../../docs/twitch-extension-checklist.md).\n\n## Examples\n\nLive overlay apps consuming this provider land under `apps/*`\n(phase 10). Until then, the Tier 1 quick-start above is the minimum\nviable wiring.\n\n## Contributing\n\nSee [CONTRIBUTING.md](../../CONTRIBUTING.md). To add new Tier-2\nhooks, mirror an existing slice hook in `src/hooks/index.ts` and\nadd a `it.each` entry in `tests/unit/index.test.ts`.\n\n## License\n\nMIT — see [LICENSE](../../LICENSE). Distributed via Play Live\nCodeArtifact (PRD §6).\n","readmeFilename":"README.md","dist":{"tarball":"https://packages.playlive.experience.stjude.org/@playlive/react-pipeline/-/react-pipeline-0.4.0.tgz","shasum":"3108e7dedff72cb68b8b32fa30a2a3c9db14f07e","integrity":"sha512-EpQ080w18vrouR9S+sqsVnlRtr8hNBSmz77UEpvM2bdy/T4Lmsv1n25p9ZlIH2rAAOysHMRAei0Won0oUoVGsQ=="}},"0.4.1":{"name":"@playlive/react-pipeline","version":"0.4.1","description":"React bindings for @playlive/realtime-pipeline — three usage tiers (single selector / typed per-slice / kitchen-sink). Twitch-Extension safe; no react-use-websocket-lite.","type":"module","sideEffects":false,"main":"./index.js","types":"./index.d.ts","exports":{".":{"import":"./index.js","types":"./index.d.ts"},"./provider":{"import":"./provider/index.js","types":"./provider/index.d.ts"},"./legacy":{"import":"./legacy/index.js","types":"./legacy/index.d.ts"},"./fusion":{"import":"./fusion/index.js","types":"./fusion/index.d.ts"}},"dependencies":{},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","zustand":"^5.0.0","@playlive/realtime-pipeline":"^0.2.5","@playlive/tiltify-core":"^0.4.11","@tanstack/react-query":"^5.0.0","@playlive/react-query":"^0.3.1","@playlive/fundraiser-data":"^0.3.1"},"peerDependenciesMeta":{"@tanstack/react-query":{"optional":true},"@playlive/react-query":{"optional":true},"@playlive/fundraiser-data":{"optional":true}},"playlive":{"target":"browser","frontendEligible":true,"coverageFloor":85},"publishConfig":{"access":"restricted","registry":"https://playlive-767397689694.d.codeartifact.us-east-1.amazonaws.com/npm/playlive/"},"integrity":"sha512-/AeADar/XB0WdrwYdS2ipe+BhXaoAH/KMBJPkbfWHG7rqVZ1hozGXpOjsx4NTy7ieW7zEpDvI6tKUSt7fknMRQ==","shasum":"cb5db5b5c0283c815a94abef9ae27e14deba0d01","readme":"# @playlive/react-pipeline\n\nReact bindings for [`@playlive/realtime-pipeline`](../realtime-pipeline/).\nDrop-in replacement for `playlive-overlay-data-layer/src/websocket/*`\nwith **zero** dependency on `react-use-websocket-lite` — built directly\non Zustand v5's `useStore` + `useSyncExternalStore`.\n\n![Coverage](./coverage-badge.svg)\n\n## Three tiers — take only what you need\n\nPick the surface that matches how much of the legacy\n`UnifiedDataPipelineProvider` API you actually want. Each tier is\nstrictly larger than the last and lives at its own subpath so unused\ncode is tree-shaken out.\n\n| Tier | Subpath                            | Surface                                                                         | Use when                                                              |\n| ---- | ---------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| 1    | `@playlive/react-pipeline`         | `RealtimePipelineProvider` + `usePipelineValue` (universal selector)            | Greenfield app; you want one selector primitive and nothing else.     |\n| 2    | `@playlive/react-pipeline`         | Tier 1 **plus** 22 typed per-slice hooks (`usePipelineDonations(id)`, …)        | You want stable, typed one-liners for the common reads.               |\n| 3    | `@playlive/react-pipeline/provider`| Tier 2 **plus** `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, etc.   | Porting an existing overlay-data-layer consumer with minimal churn.   |\n| —    | `@playlive/react-pipeline/legacy`  | Tier 3 **plus** `useUDPStore` / `useUDPStoreApi` / `createUDPStore` aliases     | Soft-deprecated shim for the legacy symbol names (one-release window).|\n\n## Install\n\n```bash\nbun add @playlive/react-pipeline\nbun add -d react react-dom zustand\n```\n\nPeer dependencies (jose-style — consumer brings their own):\n`react@^19`, `react-dom@^19`, `zustand@^5`,\n`@playlive/realtime-pipeline` (workspace), `@playlive/tiltify-core`\n(workspace, type-only — stripped at compile time).\n\nNothing else ships in the bundle.\n\n## Quick start — Tier 1\n\n```tsx\nimport { RealtimePipelineProvider, usePipelineCampaigns } from \"@playlive/react-pipeline\";\n\nfunction App() {\n  return (\n    <RealtimePipelineProvider\n      url=\"wss://pipeline.example.com/socket\"\n      charityType=\"tiltify\"\n      autoConnect\n    >\n      <Overlay />\n    </RealtimePipelineProvider>\n  );\n}\n\nfunction Overlay() {\n  const campaigns = usePipelineCampaigns();\n  return <ul>{campaigns.map((c) => <li key={c.id}>{c.name}</li>)}</ul>;\n}\n```\n\n`RealtimePipelineProvider` mounts a Zustand store + opens (or accepts)\na `PipelineConnection`. Children call any Tier 1 / Tier 2 hook to\nsubscribe.\n\n## Subpath exports\n\n| Subpath                              | Description                                                                       |\n| ------------------------------------ | --------------------------------------------------------------------------------- |\n| `@playlive/react-pipeline`           | Default barrel — Tier 1 + Tier 2 (provider + 21 typed slice hooks).               |\n| `@playlive/react-pipeline/provider`  | Tier 3 — `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, add-pipeline\\*. |\n| `@playlive/react-pipeline/fusion`    | Cross-package composition hooks (REST baseline + WS delta) — `useCurrentBlockRaised`, `useLiveSchedule`, `useCurrentScheduleItem`, `useLinkedTeamCampaign`, `useDonorSpotlight`. |\n| `@playlive/react-pipeline/legacy`    | Soft-deprecated alias bundle (`useUDPStore`, `createUDPStore`, `UDPStore`, etc.). |\n\nEach subpath ships an ESM bundle, a Bun source condition, and `.d.ts`\ndeclarations. Frontend-eligible bundles are not minified (PRD §6.1)\nso stack traces stay readable in production.\n\n## API reference\n\n### Tier 1 — universal primitive\n\n| Export                          | Signature                                                            |\n| ------------------------------- | -------------------------------------------------------------------- |\n| `RealtimePipelineProvider`      | `(props: RealtimePipelineProviderProps) => JSX.Element`              |\n| `usePipelineValue`              | `<T>(selector: (s: PipelineStore) => T) => T`                        |\n\n`usePipelineValue` is single-arg by design. Zustand v5 dropped the\nthird `equalityFn` parameter on `useStore` to keep the package\nfree of the `use-sync-external-store` shim peer. For shallow / custom\nequality, wrap your selector with `useShallow` from\n`zustand/shallow` (zero-extra-dep):\n\n```tsx\nimport { useShallow } from \"zustand/shallow\";\nimport { usePipelineValue } from \"@playlive/react-pipeline\";\n\nconst { connected, connecting } = usePipelineValue(\n  useShallow((s) => ({ connected: s.connected, connecting: s.connecting })),\n);\n```\n\n### Tier 2 — typed per-slice hooks\n\nAll 22 hooks are stable wrappers over `usePipelineValue` with a typed\nselector + memo-stable empty-array fallbacks (no infinite-render\nchurn from fresh `[]` allocations — Decision §3 in CHANGELOG).\n\n| Hook                              | Returns                                                             |\n| --------------------------------- | ------------------------------------------------------------------- |\n| `usePipelineCampaigns()`          | `TiltifyCampaign[]`                                                 |\n| `usePipelineTeamCampaigns()`      | `TiltifyTeamCampaign[]`                                             |\n| `usePipelineFundraisingEvents()`  | `TiltifyFundraisingEvent[]`                                         |\n| `usePipelineCauses()`             | `TiltifyCause[]`                                                    |\n| `usePipelineDonations()`          | `Record<string, TiltifyDonationWithTestFlag[]>`                     |\n| `usePipelineDonations(id)`        | `TiltifyDonationWithTestFlag[]` (memo-stable `[]` if id unknown)    |\n| `usePipelineDonationSum(id, opts?)` | `PipelineDonationSumResult` — live per-currency sum over `[start, end)` of the WS donation firehose. Pair with the REST `/schedules/campaigns/{id}/raised` `asOf` field to replace polling with a `baseline + WS-delta` view. |\n| `usePipelineDonationTrains(id, withStatus?)` | `DonationTrain[]`                                        |\n| `usePipelineSubathonTimer(id, withStatus?)`  | `SubathonTimer[]`                                        |\n| `usePipelineDonorSpotlight(id)`   | `ComputedSpotlight \\| null`                                         |\n| `usePipelinePolls(id)`            | `TiltifyPoll[]`                                                     |\n| `usePipelineRewards(id)`          | `TiltifyReward[]`                                                   |\n| `usePipelineTargets(id)`          | `TiltifyTarget[]`                                                   |\n| `usePipelineMilestones(id)`       | `TiltifyMilestone[]`                                                |\n| `usePipelineLeaderboardEntries(id)` | `TiltifyLeaderboardEntry[]`                                       |\n| `usePipelineAuctionHouses()`      | `AuctionHouse[]`                                                    |\n| `usePipelineTwitchChat()`         | `TwitchChatMessage[]`                                               |\n| `usePipelineConnectionState()`    | `PipelineConnectionState` (10 split selectors — no false re-renders)|\n| `usePipelineConnectionActions()`  | `PipelineConnectionActions` (stable `connect/disconnect/refresh/…`) |\n| `usePipelineSubscriptions()`      | `Record<string, TiltifyWebhookSubscription>`                        |\n| `usePipelineTiltifySettings()`    | `TiltifyWebSocketSettings`                                          |\n| `usePipelineLastRefreshResponse()`| `RefreshResponseMessage \\| null`                                    |\n| `usePipelineDonorSpotlightSettings()` | `Record<string, DonorSpotlightSettings>`                        |\n\n### Tier 3 — kitchen-sink provider (`/provider`)\n\n| Export                              | Notes                                                                                  |\n| ----------------------------------- | -------------------------------------------------------------------------------------- |\n| `UnifiedDataPipelineProvider`       | Superset of `RealtimePipelineProvider`; adds legacy `reloadOnErrorReconnect` + `debug`.|\n| `useUnifiedDataPipeline()`          | One-call kitchen-sink hook. Returns every slice + actions + legacy field aliases.      |\n| `useAddPipelineCampaignIDs(ids)`    | Effect hook — registers campaign ids with the store on mount.                          |\n| `useAddPipelineCampaigns(items)`    | Effect hook — registers full campaign records on mount.                                |\n| `useAddPipelineFundraisingEventIDs(ids)` | Effect hook — registers fundraising-event ids on mount.                           |\n| `useCommonContextHooks(…)`          | URL / mode / charity-type sync to the store. Same shape as the legacy hook.            |\n| `useIdentify()` / `useIdentify(…11 legacy args)` | Zero-arg returns the store's bound `identify` action. Positional arg form is preserved as an `@deprecated` overload — args are ignored, warns once. |\n\n### Legacy aliases (`/legacy`)\n\nSoft-deprecated. Identical behavior to the canonical exports —\nrename the import specifier once and the rest of your code base\nkeeps working unchanged:\n\n| Legacy name            | Canonical replacement                                          |\n| ---------------------- | -------------------------------------------------------------- |\n| `useUDPStore`          | `usePipelineValue` (Tier 1) / typed slice hooks (Tier 2)       |\n| `useUDPStoreApi`       | (use a typed slice hook; or reach for the raw store API)       |\n| `createUDPStore`       | `createPipelineStore` from `@playlive/realtime-pipeline/store` |\n| `UDPStore` (type)      | `PipelineStore` from `@playlive/realtime-pipeline/store`       |\n| `UDPStoreApi` (type)   | `PipelineStoreApi` from `@playlive/realtime-pipeline/store`    |\n| `CharityTypes`         | re-exported from `@playlive/realtime-pipeline/protocol`         |\n\nPlus every Tier 1+2+3 export under its canonical name.\n\n## Migration from `playlive-overlay-data-layer`\n\nOne-shot sed-friendly rewrite of every import specifier (no body\nedits needed for the happy path):\n\n```bash\n# Tier 3 — kitchen-sink (drop-in replacement)\nrg -l '@playlive/overlay-data-layer/websocket' . \\\n  | xargs sed -i '' \\\n    -e 's|@playlive/overlay-data-layer/websocket|@playlive/react-pipeline/provider|g'\n```\n\nFor the soft-deprecated names (`useUDPStore`, `createUDPStore`, …)\nsubstitute `/legacy` instead of `/provider` to opt into the alias\nbundle, then migrate at your leisure (PRD §10 — one-release window).\n\nSee the phase-8 row in the root [MIGRATION.md](../../MIGRATION.md) for\nthe full per-symbol mapping plus call-site walkthroughs.\n\n## Upstream spec\n\nNo external API surface — internal workspace package. All transport\nruns through `@playlive/realtime-pipeline/connection`.\n\n## Twitch Extension URL disclosure\n\n`KNOWN_URLS` enumerates every absolute URL or host this package can\nfetch. **It is empty.** This package opens WebSocket connections only\nto URLs the consumer passes to `<RealtimePipelineProvider url={…}>`;\nthose URLs must be added to your Extension submission's URL\ndisclosure list, not this one.\n\n```ts\nimport { KNOWN_URLS } from \"@playlive/react-pipeline\";\nconsole.log(KNOWN_URLS); // []\n```\n\nSee [`docs/twitch-extension-checklist.md`](../../docs/twitch-extension-checklist.md).\n\n## Examples\n\nLive overlay apps consuming this provider land under `apps/*`\n(phase 10). Until then, the Tier 1 quick-start above is the minimum\nviable wiring.\n\n## Contributing\n\nSee [CONTRIBUTING.md](../../CONTRIBUTING.md). To add new Tier-2\nhooks, mirror an existing slice hook in `src/hooks/index.ts` and\nadd a `it.each` entry in `tests/unit/index.test.ts`.\n\n## License\n\nMIT — see [LICENSE](../../LICENSE). Distributed via Play Live\nCodeArtifact (PRD §6).\n","readmeFilename":"README.md","dist":{"tarball":"https://packages.playlive.experience.stjude.org/@playlive/react-pipeline/-/react-pipeline-0.4.1.tgz","shasum":"cb5db5b5c0283c815a94abef9ae27e14deba0d01","integrity":"sha512-/AeADar/XB0WdrwYdS2ipe+BhXaoAH/KMBJPkbfWHG7rqVZ1hozGXpOjsx4NTy7ieW7zEpDvI6tKUSt7fknMRQ=="}},"0.4.2":{"name":"@playlive/react-pipeline","version":"0.4.2","description":"React bindings for @playlive/realtime-pipeline — three usage tiers (single selector / typed per-slice / kitchen-sink). Twitch-Extension safe; no react-use-websocket-lite.","type":"module","sideEffects":false,"main":"./index.js","types":"./index.d.ts","exports":{".":{"import":"./index.js","types":"./index.d.ts"},"./provider":{"import":"./provider/index.js","types":"./provider/index.d.ts"},"./legacy":{"import":"./legacy/index.js","types":"./legacy/index.d.ts"},"./fusion":{"import":"./fusion/index.js","types":"./fusion/index.d.ts"}},"dependencies":{},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","zustand":"^5.0.0","@playlive/realtime-pipeline":"^0.2.5","@playlive/tiltify-core":"^0.4.11","@tanstack/react-query":"^5.0.0","@playlive/react-query":"^0.3.1","@playlive/fundraiser-data":"^0.3.1"},"peerDependenciesMeta":{"@tanstack/react-query":{"optional":true},"@playlive/react-query":{"optional":true},"@playlive/fundraiser-data":{"optional":true}},"playlive":{"target":"browser","frontendEligible":true,"coverageFloor":85},"publishConfig":{"access":"restricted","registry":"https://playlive-767397689694.d.codeartifact.us-east-1.amazonaws.com/npm/playlive/"},"integrity":"sha512-qENL8FjXMgElTD2foC1HWOBoXpQGjrxUKgCP/A9HJzcVHlzJopCiluhA/Dorbzja8cEuY7o7yhJcYxUl4xEV1w==","shasum":"b65d073c1d6eb66d6b025d1de11da5a23e95d41f","readme":"# @playlive/react-pipeline\n\nReact bindings for [`@playlive/realtime-pipeline`](../realtime-pipeline/).\nDrop-in replacement for `playlive-overlay-data-layer/src/websocket/*`\nwith **zero** dependency on `react-use-websocket-lite` — built directly\non Zustand v5's `useStore` + `useSyncExternalStore`.\n\n![Coverage](./coverage-badge.svg)\n\n## Three tiers — take only what you need\n\nPick the surface that matches how much of the legacy\n`UnifiedDataPipelineProvider` API you actually want. Each tier is\nstrictly larger than the last and lives at its own subpath so unused\ncode is tree-shaken out.\n\n| Tier | Subpath                            | Surface                                                                         | Use when                                                              |\n| ---- | ---------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| 1    | `@playlive/react-pipeline`         | `RealtimePipelineProvider` + `usePipelineValue` (universal selector)            | Greenfield app; you want one selector primitive and nothing else.     |\n| 2    | `@playlive/react-pipeline`         | Tier 1 **plus** 22 typed per-slice hooks (`usePipelineDonations(id)`, …)        | You want stable, typed one-liners for the common reads.               |\n| 3    | `@playlive/react-pipeline/provider`| Tier 2 **plus** `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, etc.   | Porting an existing overlay-data-layer consumer with minimal churn.   |\n| —    | `@playlive/react-pipeline/legacy`  | Tier 3 **plus** `useUDPStore` / `useUDPStoreApi` / `createUDPStore` aliases     | Soft-deprecated shim for the legacy symbol names (one-release window).|\n\n## Install\n\n```bash\nbun add @playlive/react-pipeline\nbun add -d react react-dom zustand\n```\n\nPeer dependencies (jose-style — consumer brings their own):\n`react@^19`, `react-dom@^19`, `zustand@^5`,\n`@playlive/realtime-pipeline` (workspace), `@playlive/tiltify-core`\n(workspace, type-only — stripped at compile time).\n\nNothing else ships in the bundle.\n\n## Quick start — Tier 1\n\n```tsx\nimport { RealtimePipelineProvider, usePipelineCampaigns } from \"@playlive/react-pipeline\";\n\nfunction App() {\n  return (\n    <RealtimePipelineProvider\n      url=\"wss://pipeline.example.com/socket\"\n      charityType=\"tiltify\"\n      autoConnect\n    >\n      <Overlay />\n    </RealtimePipelineProvider>\n  );\n}\n\nfunction Overlay() {\n  const campaigns = usePipelineCampaigns();\n  return <ul>{campaigns.map((c) => <li key={c.id}>{c.name}</li>)}</ul>;\n}\n```\n\n`RealtimePipelineProvider` mounts a Zustand store + opens (or accepts)\na `PipelineConnection`. Children call any Tier 1 / Tier 2 hook to\nsubscribe.\n\n## Subpath exports\n\n| Subpath                              | Description                                                                       |\n| ------------------------------------ | --------------------------------------------------------------------------------- |\n| `@playlive/react-pipeline`           | Default barrel — Tier 1 + Tier 2 (provider + 21 typed slice hooks).               |\n| `@playlive/react-pipeline/provider`  | Tier 3 — `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, add-pipeline\\*. |\n| `@playlive/react-pipeline/fusion`    | Cross-package composition hooks (REST baseline + WS delta) — `useCurrentBlockRaised`, `useLiveSchedule`, `useCurrentScheduleItem`, `useLinkedTeamCampaign`, `useDonorSpotlight`. |\n| `@playlive/react-pipeline/legacy`    | Soft-deprecated alias bundle (`useUDPStore`, `createUDPStore`, `UDPStore`, etc.). |\n\nEach subpath ships an ESM bundle, a Bun source condition, and `.d.ts`\ndeclarations. Frontend-eligible bundles are not minified (PRD §6.1)\nso stack traces stay readable in production.\n\n## API reference\n\n### Tier 1 — universal primitive\n\n| Export                          | Signature                                                            |\n| ------------------------------- | -------------------------------------------------------------------- |\n| `RealtimePipelineProvider`      | `(props: RealtimePipelineProviderProps) => JSX.Element`              |\n| `usePipelineValue`              | `<T>(selector: (s: PipelineStore) => T) => T`                        |\n\n`usePipelineValue` is single-arg by design. Zustand v5 dropped the\nthird `equalityFn` parameter on `useStore` to keep the package\nfree of the `use-sync-external-store` shim peer. For shallow / custom\nequality, wrap your selector with `useShallow` from\n`zustand/shallow` (zero-extra-dep):\n\n```tsx\nimport { useShallow } from \"zustand/shallow\";\nimport { usePipelineValue } from \"@playlive/react-pipeline\";\n\nconst { connected, connecting } = usePipelineValue(\n  useShallow((s) => ({ connected: s.connected, connecting: s.connecting })),\n);\n```\n\n### Tier 2 — typed per-slice hooks\n\nAll 22 hooks are stable wrappers over `usePipelineValue` with a typed\nselector + memo-stable empty-array fallbacks (no infinite-render\nchurn from fresh `[]` allocations — Decision §3 in CHANGELOG).\n\n| Hook                              | Returns                                                             |\n| --------------------------------- | ------------------------------------------------------------------- |\n| `usePipelineCampaigns()`          | `TiltifyCampaign[]`                                                 |\n| `usePipelineTeamCampaigns()`      | `TiltifyTeamCampaign[]`                                             |\n| `usePipelineFundraisingEvents()`  | `TiltifyFundraisingEvent[]`                                         |\n| `usePipelineCauses()`             | `TiltifyCause[]`                                                    |\n| `usePipelineDonations()`          | `Record<string, TiltifyDonationWithTestFlag[]>`                     |\n| `usePipelineDonations(id)`        | `TiltifyDonationWithTestFlag[]` (memo-stable `[]` if id unknown)    |\n| `usePipelineDonationSum(id, opts?)` | `PipelineDonationSumResult` — live per-currency sum over `[start, end)` of the WS donation firehose. Pair with the REST `/schedules/campaigns/{id}/raised` `asOf` field to replace polling with a `baseline + WS-delta` view. |\n| `usePipelineDonationTrains(id, withStatus?)` | `DonationTrain[]`                                        |\n| `usePipelineSubathonTimer(id, withStatus?)`  | `SubathonTimer[]`                                        |\n| `usePipelineDonorSpotlight(id)`   | `ComputedSpotlight \\| null`                                         |\n| `usePipelinePolls(id)`            | `TiltifyPoll[]`                                                     |\n| `usePipelineRewards(id)`          | `TiltifyReward[]`                                                   |\n| `usePipelineTargets(id)`          | `TiltifyTarget[]`                                                   |\n| `usePipelineMilestones(id)`       | `TiltifyMilestone[]`                                                |\n| `usePipelineLeaderboardEntries(id)` | `TiltifyLeaderboardEntry[]`                                       |\n| `usePipelineAuctionHouses()`      | `AuctionHouse[]`                                                    |\n| `usePipelineTwitchChat()`         | `TwitchChatMessage[]`                                               |\n| `usePipelineConnectionState()`    | `PipelineConnectionState` (10 split selectors — no false re-renders)|\n| `usePipelineConnectionActions()`  | `PipelineConnectionActions` (stable `connect/disconnect/refresh/…`) |\n| `usePipelineSubscriptions()`      | `Record<string, TiltifyWebhookSubscription>`                        |\n| `usePipelineTiltifySettings()`    | `TiltifyWebSocketSettings`                                          |\n| `usePipelineLastRefreshResponse()`| `RefreshResponseMessage \\| null`                                    |\n| `usePipelineDonorSpotlightSettings()` | `Record<string, DonorSpotlightSettings>`                        |\n\n### Tier 3 — kitchen-sink provider (`/provider`)\n\n| Export                              | Notes                                                                                  |\n| ----------------------------------- | -------------------------------------------------------------------------------------- |\n| `UnifiedDataPipelineProvider`       | Superset of `RealtimePipelineProvider`; adds legacy `reloadOnErrorReconnect` + `debug`.|\n| `useUnifiedDataPipeline()`          | One-call kitchen-sink hook. Returns every slice + actions + legacy field aliases.      |\n| `useAddPipelineCampaignIDs(ids)`    | Effect hook — registers campaign ids with the store on mount.                          |\n| `useAddPipelineCampaigns(items)`    | Effect hook — registers full campaign records on mount.                                |\n| `useAddPipelineFundraisingEventIDs(ids)` | Effect hook — registers fundraising-event ids on mount.                           |\n| `useCommonContextHooks(…)`          | URL / mode / charity-type sync to the store. Same shape as the legacy hook.            |\n| `useIdentify()` / `useIdentify(…11 legacy args)` | Zero-arg returns the store's bound `identify` action. Positional arg form is preserved as an `@deprecated` overload — args are ignored, warns once. |\n\n### Legacy aliases (`/legacy`)\n\nSoft-deprecated. Identical behavior to the canonical exports —\nrename the import specifier once and the rest of your code base\nkeeps working unchanged:\n\n| Legacy name            | Canonical replacement                                          |\n| ---------------------- | -------------------------------------------------------------- |\n| `useUDPStore`          | `usePipelineValue` (Tier 1) / typed slice hooks (Tier 2)       |\n| `useUDPStoreApi`       | (use a typed slice hook; or reach for the raw store API)       |\n| `createUDPStore`       | `createPipelineStore` from `@playlive/realtime-pipeline/store` |\n| `UDPStore` (type)      | `PipelineStore` from `@playlive/realtime-pipeline/store`       |\n| `UDPStoreApi` (type)   | `PipelineStoreApi` from `@playlive/realtime-pipeline/store`    |\n| `CharityTypes`         | re-exported from `@playlive/realtime-pipeline/protocol`         |\n\nPlus every Tier 1+2+3 export under its canonical name.\n\n## Migration from `playlive-overlay-data-layer`\n\nOne-shot sed-friendly rewrite of every import specifier (no body\nedits needed for the happy path):\n\n```bash\n# Tier 3 — kitchen-sink (drop-in replacement)\nrg -l '@playlive/overlay-data-layer/websocket' . \\\n  | xargs sed -i '' \\\n    -e 's|@playlive/overlay-data-layer/websocket|@playlive/react-pipeline/provider|g'\n```\n\nFor the soft-deprecated names (`useUDPStore`, `createUDPStore`, …)\nsubstitute `/legacy` instead of `/provider` to opt into the alias\nbundle, then migrate at your leisure (PRD §10 — one-release window).\n\nSee the phase-8 row in the root [MIGRATION.md](../../MIGRATION.md) for\nthe full per-symbol mapping plus call-site walkthroughs.\n\n## Upstream spec\n\nNo external API surface — internal workspace package. All transport\nruns through `@playlive/realtime-pipeline/connection`.\n\n## Twitch Extension URL disclosure\n\n`KNOWN_URLS` enumerates every absolute URL or host this package can\nfetch. **It is empty.** This package opens WebSocket connections only\nto URLs the consumer passes to `<RealtimePipelineProvider url={…}>`;\nthose URLs must be added to your Extension submission's URL\ndisclosure list, not this one.\n\n```ts\nimport { KNOWN_URLS } from \"@playlive/react-pipeline\";\nconsole.log(KNOWN_URLS); // []\n```\n\nSee [`docs/twitch-extension-checklist.md`](../../docs/twitch-extension-checklist.md).\n\n## Examples\n\nLive overlay apps consuming this provider land under `apps/*`\n(phase 10). Until then, the Tier 1 quick-start above is the minimum\nviable wiring.\n\n## Contributing\n\nSee [CONTRIBUTING.md](../../CONTRIBUTING.md). To add new Tier-2\nhooks, mirror an existing slice hook in `src/hooks/index.ts` and\nadd a `it.each` entry in `tests/unit/index.test.ts`.\n\n## License\n\nMIT — see [LICENSE](../../LICENSE). Distributed via Play Live\nCodeArtifact (PRD §6).\n","readmeFilename":"README.md","dist":{"tarball":"https://packages.playlive.experience.stjude.org/@playlive/react-pipeline/-/react-pipeline-0.4.2.tgz","shasum":"b65d073c1d6eb66d6b025d1de11da5a23e95d41f","integrity":"sha512-qENL8FjXMgElTD2foC1HWOBoXpQGjrxUKgCP/A9HJzcVHlzJopCiluhA/Dorbzja8cEuY7o7yhJcYxUl4xEV1w=="}},"0.4.3":{"name":"@playlive/react-pipeline","version":"0.4.3","description":"React bindings for @playlive/realtime-pipeline — three usage tiers (single selector / typed per-slice / kitchen-sink). Twitch-Extension safe; no react-use-websocket-lite.","type":"module","sideEffects":false,"main":"./index.js","types":"./index.d.ts","exports":{".":{"import":"./index.js","types":"./index.d.ts"},"./provider":{"import":"./provider/index.js","types":"./provider/index.d.ts"},"./legacy":{"import":"./legacy/index.js","types":"./legacy/index.d.ts"},"./fusion":{"import":"./fusion/index.js","types":"./fusion/index.d.ts"}},"dependencies":{},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","zustand":"^5.0.0","@playlive/realtime-pipeline":"^0.2.5","@playlive/tiltify-core":"^0.4.11","@tanstack/react-query":"^5.0.0","@playlive/react-query":"^0.3.2","@playlive/fundraiser-data":"^0.3.2"},"peerDependenciesMeta":{"@tanstack/react-query":{"optional":true},"@playlive/react-query":{"optional":true},"@playlive/fundraiser-data":{"optional":true}},"playlive":{"target":"browser","frontendEligible":true,"coverageFloor":85},"publishConfig":{"access":"restricted","registry":"https://playlive-767397689694.d.codeartifact.us-east-1.amazonaws.com/npm/playlive/"},"integrity":"sha512-AzXUsyJQ8VZmhZ2VC02czsmNYWnyzQFxerGZx2LezCL3VtbbMoAT6rUKwi78MZpuFRKbkW/C1/i9/Qe2g3abaQ==","shasum":"89973ccd004c605fe2684adb5fb6a60651dbcea8","readme":"# @playlive/react-pipeline\n\nReact bindings for [`@playlive/realtime-pipeline`](../realtime-pipeline/).\nDrop-in replacement for `playlive-overlay-data-layer/src/websocket/*`\nwith **zero** dependency on `react-use-websocket-lite` — built directly\non Zustand v5's `useStore` + `useSyncExternalStore`.\n\n![Coverage](./coverage-badge.svg)\n\n## Three tiers — take only what you need\n\nPick the surface that matches how much of the legacy\n`UnifiedDataPipelineProvider` API you actually want. Each tier is\nstrictly larger than the last and lives at its own subpath so unused\ncode is tree-shaken out.\n\n| Tier | Subpath                            | Surface                                                                         | Use when                                                              |\n| ---- | ---------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| 1    | `@playlive/react-pipeline`         | `RealtimePipelineProvider` + `usePipelineValue` (universal selector)            | Greenfield app; you want one selector primitive and nothing else.     |\n| 2    | `@playlive/react-pipeline`         | Tier 1 **plus** 22 typed per-slice hooks (`usePipelineDonations(id)`, …)        | You want stable, typed one-liners for the common reads.               |\n| 3    | `@playlive/react-pipeline/provider`| Tier 2 **plus** `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, etc.   | Porting an existing overlay-data-layer consumer with minimal churn.   |\n| —    | `@playlive/react-pipeline/legacy`  | Tier 3 **plus** `useUDPStore` / `useUDPStoreApi` / `createUDPStore` aliases     | Soft-deprecated shim for the legacy symbol names (one-release window).|\n\n## Install\n\n```bash\nbun add @playlive/react-pipeline\nbun add -d react react-dom zustand\n```\n\nPeer dependencies (jose-style — consumer brings their own):\n`react@^19`, `react-dom@^19`, `zustand@^5`,\n`@playlive/realtime-pipeline` (workspace), `@playlive/tiltify-core`\n(workspace, type-only — stripped at compile time).\n\nNothing else ships in the bundle.\n\n## Quick start — Tier 1\n\n```tsx\nimport { RealtimePipelineProvider, usePipelineCampaigns } from \"@playlive/react-pipeline\";\n\nfunction App() {\n  return (\n    <RealtimePipelineProvider\n      url=\"wss://pipeline.example.com/socket\"\n      charityType=\"tiltify\"\n      autoConnect\n    >\n      <Overlay />\n    </RealtimePipelineProvider>\n  );\n}\n\nfunction Overlay() {\n  const campaigns = usePipelineCampaigns();\n  return <ul>{campaigns.map((c) => <li key={c.id}>{c.name}</li>)}</ul>;\n}\n```\n\n`RealtimePipelineProvider` mounts a Zustand store + opens (or accepts)\na `PipelineConnection`. Children call any Tier 1 / Tier 2 hook to\nsubscribe.\n\n## Subpath exports\n\n| Subpath                              | Description                                                                       |\n| ------------------------------------ | --------------------------------------------------------------------------------- |\n| `@playlive/react-pipeline`           | Default barrel — Tier 1 + Tier 2 (provider + 21 typed slice hooks).               |\n| `@playlive/react-pipeline/provider`  | Tier 3 — `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, add-pipeline\\*. |\n| `@playlive/react-pipeline/fusion`    | Cross-package composition hooks (REST baseline + WS delta) — `useCurrentBlockRaised`, `useLiveSchedule`, `useCurrentScheduleItem`, `useLinkedTeamCampaign`, `useDonorSpotlight`, plus the WS-store-first / HTTP-fallback hybrid slice hooks (`useHybridRewards`, `useHybridTargets`, `useHybridMilestones`, `useHybridPolls`, `useHybridCause`, `useHybridFundraisingEvent`, `useHybridFundraisingEventSupportingCampaigns`, `useHybridAuctionHouses`) + the shared `useLoadingTimeout` primitive. |\n| `@playlive/react-pipeline/legacy`    | Soft-deprecated alias bundle (`useUDPStore`, `createUDPStore`, `UDPStore`, etc.). |\n\nEach subpath ships an ESM bundle, a Bun source condition, and `.d.ts`\ndeclarations. Frontend-eligible bundles are not minified (PRD §6.1)\nso stack traces stay readable in production.\n\n## API reference\n\n### Tier 1 — universal primitive\n\n| Export                          | Signature                                                            |\n| ------------------------------- | -------------------------------------------------------------------- |\n| `RealtimePipelineProvider`      | `(props: RealtimePipelineProviderProps) => JSX.Element`              |\n| `usePipelineValue`              | `<T>(selector: (s: PipelineStore) => T) => T`                        |\n\n`usePipelineValue` is single-arg by design. Zustand v5 dropped the\nthird `equalityFn` parameter on `useStore` to keep the package\nfree of the `use-sync-external-store` shim peer. For shallow / custom\nequality, wrap your selector with `useShallow` from\n`zustand/shallow` (zero-extra-dep):\n\n```tsx\nimport { useShallow } from \"zustand/shallow\";\nimport { usePipelineValue } from \"@playlive/react-pipeline\";\n\nconst { connected, connecting } = usePipelineValue(\n  useShallow((s) => ({ connected: s.connected, connecting: s.connecting })),\n);\n```\n\n### Tier 2 — typed per-slice hooks\n\nAll 22 hooks are stable wrappers over `usePipelineValue` with a typed\nselector + memo-stable empty-array fallbacks (no infinite-render\nchurn from fresh `[]` allocations — Decision §3 in CHANGELOG).\n\n| Hook                              | Returns                                                             |\n| --------------------------------- | ------------------------------------------------------------------- |\n| `usePipelineCampaigns()`          | `TiltifyCampaign[]`                                                 |\n| `usePipelineTeamCampaigns()`      | `TiltifyTeamCampaign[]`                                             |\n| `usePipelineFundraisingEvents()`  | `TiltifyFundraisingEvent[]`                                         |\n| `usePipelineCauses()`             | `TiltifyCause[]`                                                    |\n| `usePipelineDonations()`          | `Record<string, TiltifyDonationWithTestFlag[]>`                     |\n| `usePipelineDonations(id)`        | `TiltifyDonationWithTestFlag[]` (memo-stable `[]` if id unknown)    |\n| `usePipelineDonationSum(id, opts?)` | `PipelineDonationSumResult` — live per-currency sum over `[start, end)` of the WS donation firehose. Pair with the REST `/schedules/campaigns/{id}/raised` `asOf` field to replace polling with a `baseline + WS-delta` view. |\n| `usePipelineDonationTrains(id, withStatus?)` | `DonationTrain[]`                                        |\n| `usePipelineSubathonTimer(id, withStatus?)`  | `SubathonTimer[]`                                        |\n| `usePipelineDonorSpotlight(id)`   | `ComputedSpotlight \\| null`                                         |\n| `usePipelinePolls(id)`            | `TiltifyPoll[]`                                                     |\n| `usePipelineRewards(id)`          | `TiltifyReward[]`                                                   |\n| `usePipelineTargets(id)`          | `TiltifyTarget[]`                                                   |\n| `usePipelineMilestones(id)`       | `TiltifyMilestone[]`                                                |\n| `usePipelineLeaderboardEntries(id)` | `TiltifyLeaderboardEntry[]`                                       |\n| `usePipelineAuctionHouses()`      | `AuctionHouse[]`                                                    |\n| `usePipelineTwitchChat()`         | `TwitchChatMessage[]`                                               |\n| `usePipelineConnectionState()`    | `PipelineConnectionState` (10 split selectors — no false re-renders)|\n| `usePipelineConnectionActions()`  | `PipelineConnectionActions` (stable `connect/disconnect/refresh/…`) |\n| `usePipelineSubscriptions()`      | `Record<string, TiltifyWebhookSubscription>`                        |\n| `usePipelineTiltifySettings()`    | `TiltifyWebSocketSettings`                                          |\n| `usePipelineLastRefreshResponse()`| `RefreshResponseMessage \\| null`                                    |\n| `usePipelineDonorSpotlightSettings()` | `Record<string, DonorSpotlightSettings>`                        |\n\n### Tier 3 — kitchen-sink provider (`/provider`)\n\n| Export                              | Notes                                                                                  |\n| ----------------------------------- | -------------------------------------------------------------------------------------- |\n| `UnifiedDataPipelineProvider`       | Superset of `RealtimePipelineProvider`; adds legacy `reloadOnErrorReconnect` + `debug`.|\n| `useUnifiedDataPipeline()`          | One-call kitchen-sink hook. Returns every slice + actions + legacy field aliases.      |\n| `useAddPipelineCampaignIDs(ids)`    | Effect hook — registers campaign ids with the store on mount.                          |\n| `useAddPipelineCampaigns(items)`    | Effect hook — registers full campaign records on mount.                                |\n| `useAddPipelineFundraisingEventIDs(ids)` | Effect hook — registers fundraising-event ids on mount.                           |\n| `useCommonContextHooks(…)`          | URL / mode / charity-type sync to the store. Same shape as the legacy hook.            |\n| `useIdentify()` / `useIdentify(…11 legacy args)` | Zero-arg returns the store's bound `identify` action. Positional arg form is preserved as an `@deprecated` overload — args are ignored, warns once. |\n\n### Fusion — hybrid slice hooks (`/fusion`)\n\nOverlay-parity `usePipeline*` hooks lifted from\n`playlive-overlay-vite/src/hooks/pipeline/` so every overlay that\nwants the same WS-store-first / HTTP-fallback shape can drop the\nlocal copy. Every hook accepts a `mode: \"ws\" | \"http\"` param that\npicks between:\n\n- `\"ws\"` — read the Tier-2 store selector for the slice, latch a\n  local `hasLoadedX` boolean on the first slice arrival (or on the\n  server-side \"feature disabled\" signal), and fire\n  `updateTiltifySettings({ <sliceKey> })` whenever the local\n  `xEnabled` toggle flips so the pipeline dynamically un/subscribes.\n- `\"http\"` — delegate to the matching `@playlive/react-query` REST\n  hook (`useRewards`, `useTargets`, …) with the WS store slice\n  ignored.\n\nAll hybrid hooks share `useLoadingTimeout` — a 15 s safety-net that\nforce-completes the WS branch when the store never delivers.\n\n| Hook                                                     | Returns                                                                        |\n| -------------------------------------------------------- | ------------------------------------------------------------------------------ |\n| `useHybridRewards(params)`                               | `{ currentRewards, setHasLoadedRewards, hasLoadedRewards, isLoadingRewards, isLoading, setRewardsEnabled, rewardsEnabled }` |\n| `useHybridTargets(params)`                               | `{ currentTargets, setHasLoadedTargets, hasLoadedTargets, isLoading, targetsEnabled, isLoadingTargets, setTargetsEnabled }` |\n| `useHybridMilestones(params)`                            | `{ isLoadingMilestones, currentMilestones, milestonesEnabled, setMilestonesEnabled }` |\n| `useHybridPolls(params)`                                 | `{ isLoadingPolls, currentPolls, pollsEnabled, setPollsEnabled }`              |\n| `useHybridCause({ mode, initialCampaign })`              | `{ cause, isLoading }` — single-slot semantics (uses `getFirstCause`).         |\n| `useHybridFundraisingEvent({ mode, initialCampaign })`   | `{ fundraisingEvent, isLoading }` — single-slot semantics.                     |\n| `useHybridFundraisingEventSupportingCampaigns(params)`   | `{ supportingCampaigns, isLoading, setSupportingCampaignsEnabled, supportingCampaignsEnabled, isLoadingSupportingCampaigns, hasLoadedSupportingCampaigns }` |\n| `useHybridAuctionHouses({ mode, enabled, campaignID })`  | `{ auctionHouses, auctionHouseTotal, auctionHousesEnabled, setAuctionHousesEnabled }` — WS-only (no REST fallback yet). |\n| `useLoadingTimeout(params)`                              | `void` — safety-net timer primitive shared by every hook above.                |\n\nReturn shapes are preserved verbatim from the overlay so overlay\ncall-sites can eventually collapse to a re-export.\n\n### Fusion — baseline + WS delta hooks (`/fusion`)\n\n| Hook                                        | Returns                                                                     |\n| ------------------------------------------- | --------------------------------------------------------------------------- |\n| `useCurrentBlockRaised(campaignID, start, end, opts?)` | `{ raised, data, isLoading, isError, refetch }` — REST baseline + WS delta. |\n| `useLiveSchedule(params, opts?)`            | `{ schedule, isLoading, isError, refetch }` — WS-refetch on transition.     |\n| `useCurrentScheduleItem(schedule, opts?)`   | `{ currentItem, nextItem }` — pure-client rollover projection.              |\n| `useLinkedTeamCampaign(params, opts?)`      | `{ linkedTeamCampaign, … }` — WS-store first, HTTP fallback.                |\n| `useDonorSpotlight(params, opts?)`          | `{ spotlight, isLoading, refetch }` — WS store preferred + REST snapshot.   |\n\n### Legacy aliases (`/legacy`)\n\nSoft-deprecated. Identical behavior to the canonical exports —\nrename the import specifier once and the rest of your code base\nkeeps working unchanged:\n\n| Legacy name            | Canonical replacement                                          |\n| ---------------------- | -------------------------------------------------------------- |\n| `useUDPStore`          | `usePipelineValue` (Tier 1) / typed slice hooks (Tier 2)       |\n| `useUDPStoreApi`       | (use a typed slice hook; or reach for the raw store API)       |\n| `createUDPStore`       | `createPipelineStore` from `@playlive/realtime-pipeline/store` |\n| `UDPStore` (type)      | `PipelineStore` from `@playlive/realtime-pipeline/store`       |\n| `UDPStoreApi` (type)   | `PipelineStoreApi` from `@playlive/realtime-pipeline/store`    |\n| `CharityTypes`         | re-exported from `@playlive/realtime-pipeline/protocol`         |\n\nPlus every Tier 1+2+3 export under its canonical name.\n\n## Migration from `playlive-overlay-data-layer`\n\nOne-shot sed-friendly rewrite of every import specifier (no body\nedits needed for the happy path):\n\n```bash\n# Tier 3 — kitchen-sink (drop-in replacement)\nrg -l '@playlive/overlay-data-layer/websocket' . \\\n  | xargs sed -i '' \\\n    -e 's|@playlive/overlay-data-layer/websocket|@playlive/react-pipeline/provider|g'\n```\n\nFor the soft-deprecated names (`useUDPStore`, `createUDPStore`, …)\nsubstitute `/legacy` instead of `/provider` to opt into the alias\nbundle, then migrate at your leisure (PRD §10 — one-release window).\n\nSee the phase-8 row in the root [MIGRATION.md](../../MIGRATION.md) for\nthe full per-symbol mapping plus call-site walkthroughs.\n\n## Upstream spec\n\nNo external API surface — internal workspace package. All transport\nruns through `@playlive/realtime-pipeline/connection`.\n\n## Twitch Extension URL disclosure\n\n`KNOWN_URLS` enumerates every absolute URL or host this package can\nfetch. **It is empty.** This package opens WebSocket connections only\nto URLs the consumer passes to `<RealtimePipelineProvider url={…}>`;\nthose URLs must be added to your Extension submission's URL\ndisclosure list, not this one.\n\n```ts\nimport { KNOWN_URLS } from \"@playlive/react-pipeline\";\nconsole.log(KNOWN_URLS); // []\n```\n\nSee [`docs/twitch-extension-checklist.md`](../../docs/twitch-extension-checklist.md).\n\n## Examples\n\nLive overlay apps consuming this provider land under `apps/*`\n(phase 10). Until then, the Tier 1 quick-start above is the minimum\nviable wiring.\n\n## Contributing\n\nSee [CONTRIBUTING.md](../../CONTRIBUTING.md). To add new Tier-2\nhooks, mirror an existing slice hook in `src/hooks/index.ts` and\nadd a `it.each` entry in `tests/unit/index.test.ts`.\n\n## License\n\nMIT — see [LICENSE](../../LICENSE). Distributed via Play Live\nCodeArtifact (PRD §6).\n","readmeFilename":"README.md","dist":{"tarball":"https://packages.playlive.experience.stjude.org/@playlive/react-pipeline/-/react-pipeline-0.4.3.tgz","shasum":"89973ccd004c605fe2684adb5fb6a60651dbcea8","integrity":"sha512-AzXUsyJQ8VZmhZ2VC02czsmNYWnyzQFxerGZx2LezCL3VtbbMoAT6rUKwi78MZpuFRKbkW/C1/i9/Qe2g3abaQ=="}},"0.4.4":{"name":"@playlive/react-pipeline","version":"0.4.4","description":"React bindings for @playlive/realtime-pipeline — three usage tiers (single selector / typed per-slice / kitchen-sink). Twitch-Extension safe; no react-use-websocket-lite.","type":"module","sideEffects":false,"main":"./index.js","types":"./index.d.ts","exports":{".":{"import":"./index.js","types":"./index.d.ts"},"./provider":{"import":"./provider/index.js","types":"./provider/index.d.ts"},"./legacy":{"import":"./legacy/index.js","types":"./legacy/index.d.ts"},"./fusion":{"import":"./fusion/index.js","types":"./fusion/index.d.ts"}},"dependencies":{},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","zustand":"^5.0.0","@playlive/realtime-pipeline":"^0.2.5","@playlive/tiltify-core":"^0.4.11","@tanstack/react-query":"^5.0.0","@playlive/react-query":"^0.3.3","@playlive/fundraiser-data":"^0.3.4"},"peerDependenciesMeta":{"@tanstack/react-query":{"optional":true},"@playlive/react-query":{"optional":true},"@playlive/fundraiser-data":{"optional":true}},"playlive":{"target":"browser","frontendEligible":true,"coverageFloor":85},"publishConfig":{"access":"restricted","registry":"https://playlive-767397689694.d.codeartifact.us-east-1.amazonaws.com/npm/playlive/"},"integrity":"sha512-korF+daxhIaUHFHFt5Z7pjmhrcJU5HWKXNUK3uqvt3Raq5LsE6TBd3pA0KszqvZ9/cwzYF4BIQCjO4RuO8JyUA==","shasum":"98ac1527d214f8a6d7fffff53e0d42e9ebb141cf","readme":"# @playlive/react-pipeline\n\nReact bindings for [`@playlive/realtime-pipeline`](../realtime-pipeline/).\nDrop-in replacement for `playlive-overlay-data-layer/src/websocket/*`\nwith **zero** dependency on `react-use-websocket-lite` — built directly\non Zustand v5's `useStore` + `useSyncExternalStore`.\n\n![Coverage](./coverage-badge.svg)\n\n## Three tiers — take only what you need\n\nPick the surface that matches how much of the legacy\n`UnifiedDataPipelineProvider` API you actually want. Each tier is\nstrictly larger than the last and lives at its own subpath so unused\ncode is tree-shaken out.\n\n| Tier | Subpath                            | Surface                                                                         | Use when                                                              |\n| ---- | ---------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| 1    | `@playlive/react-pipeline`         | `RealtimePipelineProvider` + `usePipelineValue` (universal selector)            | Greenfield app; you want one selector primitive and nothing else.     |\n| 2    | `@playlive/react-pipeline`         | Tier 1 **plus** 22 typed per-slice hooks (`usePipelineDonations(id)`, …)        | You want stable, typed one-liners for the common reads.               |\n| 3    | `@playlive/react-pipeline/provider`| Tier 2 **plus** `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, etc.   | Porting an existing overlay-data-layer consumer with minimal churn.   |\n| —    | `@playlive/react-pipeline/legacy`  | Tier 3 **plus** `useUDPStore` / `useUDPStoreApi` / `createUDPStore` aliases     | Soft-deprecated shim for the legacy symbol names (one-release window).|\n\n## Install\n\n```bash\nbun add @playlive/react-pipeline\nbun add -d react react-dom zustand\n```\n\nPeer dependencies (jose-style — consumer brings their own):\n`react@^19`, `react-dom@^19`, `zustand@^5`,\n`@playlive/realtime-pipeline` (workspace), `@playlive/tiltify-core`\n(workspace, type-only — stripped at compile time).\n\nNothing else ships in the bundle.\n\n## Quick start — Tier 1\n\n```tsx\nimport { RealtimePipelineProvider, usePipelineCampaigns } from \"@playlive/react-pipeline\";\n\nfunction App() {\n  return (\n    <RealtimePipelineProvider\n      url=\"wss://pipeline.example.com/socket\"\n      charityType=\"tiltify\"\n      autoConnect\n    >\n      <Overlay />\n    </RealtimePipelineProvider>\n  );\n}\n\nfunction Overlay() {\n  const campaigns = usePipelineCampaigns();\n  return <ul>{campaigns.map((c) => <li key={c.id}>{c.name}</li>)}</ul>;\n}\n```\n\n`RealtimePipelineProvider` mounts a Zustand store + opens (or accepts)\na `PipelineConnection`. Children call any Tier 1 / Tier 2 hook to\nsubscribe.\n\n## Subpath exports\n\n| Subpath                              | Description                                                                       |\n| ------------------------------------ | --------------------------------------------------------------------------------- |\n| `@playlive/react-pipeline`           | Default barrel — Tier 1 + Tier 2 (provider + 21 typed slice hooks).               |\n| `@playlive/react-pipeline/provider`  | Tier 3 — `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, add-pipeline\\*. |\n| `@playlive/react-pipeline/fusion`    | Cross-package composition hooks (REST baseline + WS delta) — `useCurrentBlockRaised`, `useLiveSchedule`, `useCurrentScheduleItem`, `useLinkedTeamCampaign`, `useDonorSpotlight`, plus the WS-store-first / HTTP-fallback hybrid slice hooks (`useHybridRewards`, `useHybridTargets`, `useHybridMilestones`, `useHybridPolls`, `useHybridCause`, `useHybridFundraisingEvent`, `useHybridFundraisingEventSupportingCampaigns`, `useHybridAuctionHouses`) + the shared `useLoadingTimeout` primitive. |\n| `@playlive/react-pipeline/legacy`    | Soft-deprecated alias bundle (`useUDPStore`, `createUDPStore`, `UDPStore`, etc.). |\n\nEach subpath ships an ESM bundle, a Bun source condition, and `.d.ts`\ndeclarations. Frontend-eligible bundles are not minified (PRD §6.1)\nso stack traces stay readable in production.\n\n## API reference\n\n### Tier 1 — universal primitive\n\n| Export                          | Signature                                                            |\n| ------------------------------- | -------------------------------------------------------------------- |\n| `RealtimePipelineProvider`      | `(props: RealtimePipelineProviderProps) => JSX.Element`              |\n| `usePipelineValue`              | `<T>(selector: (s: PipelineStore) => T) => T`                        |\n\n`usePipelineValue` is single-arg by design. Zustand v5 dropped the\nthird `equalityFn` parameter on `useStore` to keep the package\nfree of the `use-sync-external-store` shim peer. For shallow / custom\nequality, wrap your selector with `useShallow` from\n`zustand/shallow` (zero-extra-dep):\n\n```tsx\nimport { useShallow } from \"zustand/shallow\";\nimport { usePipelineValue } from \"@playlive/react-pipeline\";\n\nconst { connected, connecting } = usePipelineValue(\n  useShallow((s) => ({ connected: s.connected, connecting: s.connecting })),\n);\n```\n\n### Tier 2 — typed per-slice hooks\n\nAll 22 hooks are stable wrappers over `usePipelineValue` with a typed\nselector + memo-stable empty-array fallbacks (no infinite-render\nchurn from fresh `[]` allocations — Decision §3 in CHANGELOG).\n\n| Hook                              | Returns                                                             |\n| --------------------------------- | ------------------------------------------------------------------- |\n| `usePipelineCampaigns()`          | `TiltifyCampaign[]`                                                 |\n| `usePipelineTeamCampaigns()`      | `TiltifyTeamCampaign[]`                                             |\n| `usePipelineFundraisingEvents()`  | `TiltifyFundraisingEvent[]`                                         |\n| `usePipelineCauses()`             | `TiltifyCause[]`                                                    |\n| `usePipelineDonations()`          | `Record<string, TiltifyDonationWithTestFlag[]>`                     |\n| `usePipelineDonations(id)`        | `TiltifyDonationWithTestFlag[]` (memo-stable `[]` if id unknown)    |\n| `usePipelineDonationSum(id, opts?)` | `PipelineDonationSumResult` — live per-currency sum over `[start, end)` of the WS donation firehose. Pair with the REST `/schedules/campaigns/{id}/raised` `asOf` field to replace polling with a `baseline + WS-delta` view. |\n| `usePipelineDonationTrains(id, withStatus?)` | `DonationTrain[]`                                        |\n| `usePipelineSubathonTimer(id, withStatus?)`  | `SubathonTimer[]`                                        |\n| `usePipelineDonorSpotlight(id)`   | `ComputedSpotlight \\| null`                                         |\n| `usePipelinePolls(id)`            | `TiltifyPoll[]`                                                     |\n| `usePipelineRewards(id)`          | `TiltifyReward[]`                                                   |\n| `usePipelineTargets(id)`          | `TiltifyTarget[]`                                                   |\n| `usePipelineMilestones(id)`       | `TiltifyMilestone[]`                                                |\n| `usePipelineLeaderboardEntries(id)` | `TiltifyLeaderboardEntry[]`                                       |\n| `usePipelineAuctionHouses()`      | `AuctionHouse[]`                                                    |\n| `usePipelineTwitchChat()`         | `TwitchChatMessage[]`                                               |\n| `usePipelineConnectionState()`    | `PipelineConnectionState` (10 split selectors — no false re-renders)|\n| `usePipelineConnectionActions()`  | `PipelineConnectionActions` (stable `connect/disconnect/refresh/…`) |\n| `usePipelineSubscriptions()`      | `Record<string, TiltifyWebhookSubscription>`                        |\n| `usePipelineTiltifySettings()`    | `TiltifyWebSocketSettings`                                          |\n| `usePipelineLastRefreshResponse()`| `RefreshResponseMessage \\| null`                                    |\n| `usePipelineDonorSpotlightSettings()` | `Record<string, DonorSpotlightSettings>`                        |\n\n### Tier 3 — kitchen-sink provider (`/provider`)\n\n| Export                              | Notes                                                                                  |\n| ----------------------------------- | -------------------------------------------------------------------------------------- |\n| `UnifiedDataPipelineProvider`       | Superset of `RealtimePipelineProvider`; adds legacy `reloadOnErrorReconnect` + `debug`.|\n| `useUnifiedDataPipeline()`          | One-call kitchen-sink hook. Returns every slice + actions + legacy field aliases.      |\n| `useAddPipelineCampaignIDs(ids)`    | Effect hook — registers campaign ids with the store on mount.                          |\n| `useAddPipelineCampaigns(items)`    | Effect hook — registers full campaign records on mount.                                |\n| `useAddPipelineFundraisingEventIDs(ids)` | Effect hook — registers fundraising-event ids on mount.                           |\n| `useCommonContextHooks(…)`          | URL / mode / charity-type sync to the store. Same shape as the legacy hook.            |\n| `useIdentify()` / `useIdentify(…11 legacy args)` | Zero-arg returns the store's bound `identify` action. Positional arg form is preserved as an `@deprecated` overload — args are ignored, warns once. |\n\n### Fusion — hybrid slice hooks (`/fusion`)\n\nOverlay-parity `usePipeline*` hooks lifted from\n`playlive-overlay-vite/src/hooks/pipeline/` so every overlay that\nwants the same WS-store-first / HTTP-fallback shape can drop the\nlocal copy. Every hook accepts a `mode: \"ws\" | \"http\"` param that\npicks between:\n\n- `\"ws\"` — read the Tier-2 store selector for the slice, latch a\n  local `hasLoadedX` boolean on the first slice arrival (or on the\n  server-side \"feature disabled\" signal), and fire\n  `updateTiltifySettings({ <sliceKey> })` whenever the local\n  `xEnabled` toggle flips so the pipeline dynamically un/subscribes.\n- `\"http\"` — delegate to the matching `@playlive/react-query` REST\n  hook (`useRewards`, `useTargets`, …) with the WS store slice\n  ignored.\n\nAll hybrid hooks share `useLoadingTimeout` — a 15 s safety-net that\nforce-completes the WS branch when the store never delivers.\n\n| Hook                                                     | Returns                                                                        |\n| -------------------------------------------------------- | ------------------------------------------------------------------------------ |\n| `useHybridRewards(params)`                               | `{ currentRewards, setHasLoadedRewards, hasLoadedRewards, isLoadingRewards, isLoading, setRewardsEnabled, rewardsEnabled }` |\n| `useHybridTargets(params)`                               | `{ currentTargets, setHasLoadedTargets, hasLoadedTargets, isLoading, targetsEnabled, isLoadingTargets, setTargetsEnabled }` |\n| `useHybridMilestones(params)`                            | `{ isLoadingMilestones, currentMilestones, milestonesEnabled, setMilestonesEnabled }` |\n| `useHybridPolls(params)`                                 | `{ isLoadingPolls, currentPolls, pollsEnabled, setPollsEnabled }`              |\n| `useHybridCause({ mode, initialCampaign })`              | `{ cause, isLoading }` — single-slot semantics (uses `getFirstCause`).         |\n| `useHybridFundraisingEvent({ mode, initialCampaign })`   | `{ fundraisingEvent, isLoading }` — single-slot semantics.                     |\n| `useHybridFundraisingEventSupportingCampaigns(params)`   | `{ supportingCampaigns, isLoading, setSupportingCampaignsEnabled, supportingCampaignsEnabled, isLoadingSupportingCampaigns, hasLoadedSupportingCampaigns }` |\n| `useHybridAuctionHouses({ mode, enabled, campaignID })`  | `{ auctionHouses, auctionHouseTotal, auctionHousesEnabled, setAuctionHousesEnabled }` — WS-only (no REST fallback yet). |\n| `useLoadingTimeout(params)`                              | `void` — safety-net timer primitive shared by every hook above.                |\n\nReturn shapes are preserved verbatim from the overlay so overlay\ncall-sites can eventually collapse to a re-export.\n\n### Fusion — baseline + WS delta hooks (`/fusion`)\n\n| Hook                                        | Returns                                                                     |\n| ------------------------------------------- | --------------------------------------------------------------------------- |\n| `useCurrentBlockRaised(campaignID, start, end, opts?)` | `{ raised, data, isLoading, isError, refetch }` — REST baseline + WS delta. |\n| `useLiveSchedule(params, opts?)`            | `{ schedule, isLoading, isError, refetch }` — WS-refetch on transition.     |\n| `useCurrentScheduleItem(schedule, opts?)`   | `{ currentItem, nextItem }` — pure-client rollover projection.              |\n| `useLinkedTeamCampaign(params, opts?)`      | `{ linkedTeamCampaign, … }` — WS-store first, HTTP fallback.                |\n| `useDonorSpotlight(params, opts?)`          | `{ spotlight, isLoading, refetch }` — WS store preferred + REST snapshot.   |\n\n### Legacy aliases (`/legacy`)\n\nSoft-deprecated. Identical behavior to the canonical exports —\nrename the import specifier once and the rest of your code base\nkeeps working unchanged:\n\n| Legacy name            | Canonical replacement                                          |\n| ---------------------- | -------------------------------------------------------------- |\n| `useUDPStore`          | `usePipelineValue` (Tier 1) / typed slice hooks (Tier 2)       |\n| `useUDPStoreApi`       | (use a typed slice hook; or reach for the raw store API)       |\n| `createUDPStore`       | `createPipelineStore` from `@playlive/realtime-pipeline/store` |\n| `UDPStore` (type)      | `PipelineStore` from `@playlive/realtime-pipeline/store`       |\n| `UDPStoreApi` (type)   | `PipelineStoreApi` from `@playlive/realtime-pipeline/store`    |\n| `CharityTypes`         | re-exported from `@playlive/realtime-pipeline/protocol`         |\n\nPlus every Tier 1+2+3 export under its canonical name.\n\n## Migration from `playlive-overlay-data-layer`\n\nOne-shot sed-friendly rewrite of every import specifier (no body\nedits needed for the happy path):\n\n```bash\n# Tier 3 — kitchen-sink (drop-in replacement)\nrg -l '@playlive/overlay-data-layer/websocket' . \\\n  | xargs sed -i '' \\\n    -e 's|@playlive/overlay-data-layer/websocket|@playlive/react-pipeline/provider|g'\n```\n\nFor the soft-deprecated names (`useUDPStore`, `createUDPStore`, …)\nsubstitute `/legacy` instead of `/provider` to opt into the alias\nbundle, then migrate at your leisure (PRD §10 — one-release window).\n\nSee the phase-8 row in the root [MIGRATION.md](../../MIGRATION.md) for\nthe full per-symbol mapping plus call-site walkthroughs.\n\n## Upstream spec\n\nNo external API surface — internal workspace package. All transport\nruns through `@playlive/realtime-pipeline/connection`.\n\n## Twitch Extension URL disclosure\n\n`KNOWN_URLS` enumerates every absolute URL or host this package can\nfetch. **It is empty.** This package opens WebSocket connections only\nto URLs the consumer passes to `<RealtimePipelineProvider url={…}>`;\nthose URLs must be added to your Extension submission's URL\ndisclosure list, not this one.\n\n```ts\nimport { KNOWN_URLS } from \"@playlive/react-pipeline\";\nconsole.log(KNOWN_URLS); // []\n```\n\nSee [`docs/twitch-extension-checklist.md`](../../docs/twitch-extension-checklist.md).\n\n## Examples\n\nLive overlay apps consuming this provider land under `apps/*`\n(phase 10). Until then, the Tier 1 quick-start above is the minimum\nviable wiring.\n\n## Contributing\n\nSee [CONTRIBUTING.md](../../CONTRIBUTING.md). To add new Tier-2\nhooks, mirror an existing slice hook in `src/hooks/index.ts` and\nadd a `it.each` entry in `tests/unit/index.test.ts`.\n\n## License\n\nMIT — see [LICENSE](../../LICENSE). Distributed via Play Live\nCodeArtifact (PRD §6).\n","readmeFilename":"README.md","dist":{"tarball":"https://packages.playlive.experience.stjude.org/@playlive/react-pipeline/-/react-pipeline-0.4.4.tgz","shasum":"98ac1527d214f8a6d7fffff53e0d42e9ebb141cf","integrity":"sha512-korF+daxhIaUHFHFt5Z7pjmhrcJU5HWKXNUK3uqvt3Raq5LsE6TBd3pA0KszqvZ9/cwzYF4BIQCjO4RuO8JyUA=="}},"0.4.5":{"name":"@playlive/react-pipeline","version":"0.4.5","description":"React bindings for @playlive/realtime-pipeline — three usage tiers (single selector / typed per-slice / kitchen-sink). Twitch-Extension safe; no react-use-websocket-lite.","type":"module","sideEffects":false,"main":"./index.js","types":"./index.d.ts","exports":{".":{"import":"./index.js","types":"./index.d.ts"},"./provider":{"import":"./provider/index.js","types":"./provider/index.d.ts"},"./legacy":{"import":"./legacy/index.js","types":"./legacy/index.d.ts"},"./fusion":{"import":"./fusion/index.js","types":"./fusion/index.d.ts"}},"dependencies":{},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","zustand":"^5.0.0","@playlive/realtime-pipeline":"^0.2.6","@playlive/tiltify-core":"^0.4.11","@tanstack/react-query":"^5.0.0","@playlive/react-query":"^0.3.4","@playlive/fundraiser-data":"^0.4.1"},"peerDependenciesMeta":{"@tanstack/react-query":{"optional":true},"@playlive/react-query":{"optional":true},"@playlive/fundraiser-data":{"optional":true}},"playlive":{"target":"browser","frontendEligible":true,"coverageFloor":85},"publishConfig":{"access":"restricted","registry":"https://playlive-767397689694.d.codeartifact.us-east-1.amazonaws.com/npm/playlive/"},"integrity":"sha512-65YM1MEmX9UZcYSd6WwmOzujJodDYLTDumrSNdyZvOzCFfFR+9UEMI7LEeFvnIv2osMI5HSxwhTbCl5UujgoKg==","shasum":"1115b6adce8c95b38a0b5e080300846f9f38a4e1","readme":"# @playlive/react-pipeline\n\nReact bindings for [`@playlive/realtime-pipeline`](../realtime-pipeline/).\nDrop-in replacement for `playlive-overlay-data-layer/src/websocket/*`\nwith **zero** dependency on `react-use-websocket-lite` — built directly\non Zustand v5's `useStore` + `useSyncExternalStore`.\n\n![Coverage](./coverage-badge.svg)\n\n## Three tiers — take only what you need\n\nPick the surface that matches how much of the legacy\n`UnifiedDataPipelineProvider` API you actually want. Each tier is\nstrictly larger than the last and lives at its own subpath so unused\ncode is tree-shaken out.\n\n| Tier | Subpath                            | Surface                                                                         | Use when                                                              |\n| ---- | ---------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| 1    | `@playlive/react-pipeline`         | `RealtimePipelineProvider` + `usePipelineValue` (universal selector)            | Greenfield app; you want one selector primitive and nothing else.     |\n| 2    | `@playlive/react-pipeline`         | Tier 1 **plus** 22 typed per-slice hooks (`usePipelineDonations(id)`, …)        | You want stable, typed one-liners for the common reads.               |\n| 3    | `@playlive/react-pipeline/provider`| Tier 2 **plus** `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, etc.   | Porting an existing overlay-data-layer consumer with minimal churn.   |\n| —    | `@playlive/react-pipeline/legacy`  | Tier 3 **plus** `useUDPStore` / `useUDPStoreApi` / `createUDPStore` aliases     | Soft-deprecated shim for the legacy symbol names (one-release window).|\n\n## Install\n\n```bash\nbun add @playlive/react-pipeline\nbun add -d react react-dom zustand\n```\n\nPeer dependencies (jose-style — consumer brings their own):\n`react@^19`, `react-dom@^19`, `zustand@^5`,\n`@playlive/realtime-pipeline` (workspace), `@playlive/tiltify-core`\n(workspace, type-only — stripped at compile time).\n\nNothing else ships in the bundle.\n\n## Quick start — Tier 1\n\n```tsx\nimport { RealtimePipelineProvider, usePipelineCampaigns } from \"@playlive/react-pipeline\";\n\nfunction App() {\n  return (\n    <RealtimePipelineProvider\n      url=\"wss://pipeline.example.com/socket\"\n      charityType=\"tiltify\"\n      autoConnect\n    >\n      <Overlay />\n    </RealtimePipelineProvider>\n  );\n}\n\nfunction Overlay() {\n  const campaigns = usePipelineCampaigns();\n  return <ul>{campaigns.map((c) => <li key={c.id}>{c.name}</li>)}</ul>;\n}\n```\n\n`RealtimePipelineProvider` mounts a Zustand store + opens (or accepts)\na `PipelineConnection`. Children call any Tier 1 / Tier 2 hook to\nsubscribe.\n\n## Subpath exports\n\n| Subpath                              | Description                                                                       |\n| ------------------------------------ | --------------------------------------------------------------------------------- |\n| `@playlive/react-pipeline`           | Default barrel — Tier 1 + Tier 2 (provider + 21 typed slice hooks).               |\n| `@playlive/react-pipeline/provider`  | Tier 3 — `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, add-pipeline\\*. |\n| `@playlive/react-pipeline/fusion`    | Cross-package composition hooks (REST baseline + WS delta) — `useCurrentBlockRaised`, `useLiveSchedule`, `useCurrentScheduleItem`, `useLinkedTeamCampaign`, `useDonorSpotlight`, plus the WS-store-first / HTTP-fallback hybrid slice hooks (`useHybridRewards`, `useHybridTargets`, `useHybridMilestones`, `useHybridPolls`, `useHybridCause`, `useHybridFundraisingEvent`, `useHybridFundraisingEventSupportingCampaigns`, `useHybridAuctionHouses`) + the shared `useLoadingTimeout` primitive. |\n| `@playlive/react-pipeline/legacy`    | Soft-deprecated alias bundle (`useUDPStore`, `createUDPStore`, `UDPStore`, etc.). |\n\nEach subpath ships an ESM bundle, a Bun source condition, and `.d.ts`\ndeclarations. Frontend-eligible bundles are not minified (PRD §6.1)\nso stack traces stay readable in production.\n\n## API reference\n\n### Tier 1 — universal primitive\n\n| Export                          | Signature                                                            |\n| ------------------------------- | -------------------------------------------------------------------- |\n| `RealtimePipelineProvider`      | `(props: RealtimePipelineProviderProps) => JSX.Element`              |\n| `usePipelineValue`              | `<T>(selector: (s: PipelineStore) => T) => T`                        |\n\n`usePipelineValue` is single-arg by design. Zustand v5 dropped the\nthird `equalityFn` parameter on `useStore` to keep the package\nfree of the `use-sync-external-store` shim peer. For shallow / custom\nequality, wrap your selector with `useShallow` from\n`zustand/shallow` (zero-extra-dep):\n\n```tsx\nimport { useShallow } from \"zustand/shallow\";\nimport { usePipelineValue } from \"@playlive/react-pipeline\";\n\nconst { connected, connecting } = usePipelineValue(\n  useShallow((s) => ({ connected: s.connected, connecting: s.connecting })),\n);\n```\n\n### Tier 2 — typed per-slice hooks\n\nAll 22 hooks are stable wrappers over `usePipelineValue` with a typed\nselector + memo-stable empty-array fallbacks (no infinite-render\nchurn from fresh `[]` allocations — Decision §3 in CHANGELOG).\n\n| Hook                              | Returns                                                             |\n| --------------------------------- | ------------------------------------------------------------------- |\n| `usePipelineCampaigns()`          | `TiltifyCampaign[]`                                                 |\n| `usePipelineTeamCampaigns()`      | `TiltifyTeamCampaign[]`                                             |\n| `usePipelineFundraisingEvents()`  | `TiltifyFundraisingEvent[]`                                         |\n| `usePipelineCauses()`             | `TiltifyCause[]`                                                    |\n| `usePipelineDonations()`          | `Record<string, TiltifyDonationWithTestFlag[]>`                     |\n| `usePipelineDonations(id)`        | `TiltifyDonationWithTestFlag[]` (memo-stable `[]` if id unknown)    |\n| `usePipelineDonationSum(id, opts?)` | `PipelineDonationSumResult` — live per-currency sum over `[start, end)` of the WS donation firehose. Pair with the REST `/schedules/campaigns/{id}/raised` `asOf` field to replace polling with a `baseline + WS-delta` view. |\n| `usePipelineDonationTrains(id, withStatus?)` | `DonationTrain[]`                                        |\n| `usePipelineSubathonTimer(id, withStatus?)`  | `SubathonTimer[]`                                        |\n| `usePipelineDonorSpotlight(id)`   | `ComputedSpotlight \\| null`                                         |\n| `usePipelinePolls(id)`            | `TiltifyPoll[]`                                                     |\n| `usePipelineRewards(id)`          | `TiltifyReward[]`                                                   |\n| `usePipelineTargets(id)`          | `TiltifyTarget[]`                                                   |\n| `usePipelineMilestones(id)`       | `TiltifyMilestone[]`                                                |\n| `usePipelineLeaderboardEntries(id)` | `TiltifyLeaderboardEntry[]`                                       |\n| `usePipelineAuctionHouses()`      | `AuctionHouse[]`                                                    |\n| `usePipelineTwitchChat()`         | `TwitchChatMessage[]`                                               |\n| `usePipelineConnectionState()`    | `PipelineConnectionState` (10 split selectors — no false re-renders)|\n| `usePipelineConnectionActions()`  | `PipelineConnectionActions` (stable `connect/disconnect/refresh/…`) |\n| `usePipelineSubscriptions()`      | `Record<string, TiltifyWebhookSubscription>`                        |\n| `usePipelineTiltifySettings()`    | `TiltifyWebSocketSettings`                                          |\n| `usePipelineLastRefreshResponse()`| `RefreshResponseMessage \\| null`                                    |\n| `usePipelineDonorSpotlightSettings()` | `Record<string, DonorSpotlightSettings>`                        |\n\n### Tier 3 — kitchen-sink provider (`/provider`)\n\n| Export                              | Notes                                                                                  |\n| ----------------------------------- | -------------------------------------------------------------------------------------- |\n| `UnifiedDataPipelineProvider`       | Superset of `RealtimePipelineProvider`; adds legacy `reloadOnErrorReconnect` + `debug`.|\n| `useUnifiedDataPipeline()`          | One-call kitchen-sink hook. Returns every slice + actions + legacy field aliases.      |\n| `useAddPipelineCampaignIDs(ids)`    | Effect hook — registers campaign ids with the store on mount.                          |\n| `useAddPipelineCampaigns(items)`    | Effect hook — registers full campaign records on mount.                                |\n| `useAddPipelineFundraisingEventIDs(ids)` | Effect hook — registers fundraising-event ids on mount.                           |\n| `useCommonContextHooks(…)`          | URL / mode / charity-type sync to the store. Same shape as the legacy hook.            |\n| `useIdentify()` / `useIdentify(…11 legacy args)` | Zero-arg returns the store's bound `identify` action. Positional arg form is preserved as an `@deprecated` overload — args are ignored, warns once. |\n\n### Fusion — hybrid slice hooks (`/fusion`)\n\nOverlay-parity `usePipeline*` hooks lifted from\n`playlive-overlay-vite/src/hooks/pipeline/` so every overlay that\nwants the same WS-store-first / HTTP-fallback shape can drop the\nlocal copy. Every hook accepts a `mode: \"ws\" | \"http\"` param that\npicks between:\n\n- `\"ws\"` — read the Tier-2 store selector for the slice, latch a\n  local `hasLoadedX` boolean on the first slice arrival (or on the\n  server-side \"feature disabled\" signal), and fire\n  `updateTiltifySettings({ <sliceKey> })` whenever the local\n  `xEnabled` toggle flips so the pipeline dynamically un/subscribes.\n- `\"http\"` — delegate to the matching `@playlive/react-query` REST\n  hook (`useRewards`, `useTargets`, …) with the WS store slice\n  ignored.\n\nAll hybrid hooks share `useLoadingTimeout` — a 15 s safety-net that\nforce-completes the WS branch when the store never delivers.\n\n| Hook                                                     | Returns                                                                        |\n| -------------------------------------------------------- | ------------------------------------------------------------------------------ |\n| `useHybridRewards(params)`                               | `{ currentRewards, setHasLoadedRewards, hasLoadedRewards, isLoadingRewards, isLoading, setRewardsEnabled, rewardsEnabled }` |\n| `useHybridTargets(params)`                               | `{ currentTargets, setHasLoadedTargets, hasLoadedTargets, isLoading, targetsEnabled, isLoadingTargets, setTargetsEnabled }` |\n| `useHybridMilestones(params)`                            | `{ isLoadingMilestones, currentMilestones, milestonesEnabled, setMilestonesEnabled }` |\n| `useHybridPolls(params)`                                 | `{ isLoadingPolls, currentPolls, pollsEnabled, setPollsEnabled }`              |\n| `useHybridCause({ mode, initialCampaign })`              | `{ cause, isLoading }` — single-slot semantics (uses `getFirstCause`).         |\n| `useHybridFundraisingEvent({ mode, initialCampaign })`   | `{ fundraisingEvent, isLoading }` — single-slot semantics.                     |\n| `useHybridFundraisingEventSupportingCampaigns(params)`   | `{ supportingCampaigns, isLoading, setSupportingCampaignsEnabled, supportingCampaignsEnabled, isLoadingSupportingCampaigns, hasLoadedSupportingCampaigns }` |\n| `useHybridAuctionHouses({ mode, enabled, campaignID })`  | `{ auctionHouses, auctionHouseTotal, auctionHousesEnabled, setAuctionHousesEnabled }` — WS-only (no REST fallback yet). |\n| `useLoadingTimeout(params)`                              | `void` — safety-net timer primitive shared by every hook above.                |\n\nReturn shapes are preserved verbatim from the overlay so overlay\ncall-sites can eventually collapse to a re-export.\n\n### Fusion — baseline + WS delta hooks (`/fusion`)\n\n| Hook                                        | Returns                                                                     |\n| ------------------------------------------- | --------------------------------------------------------------------------- |\n| `useCurrentBlockRaised(campaignID, start, end, opts?)` | `{ raised, data, isLoading, isError, refetch }` — REST baseline + WS delta. `opts.demoMode: true` short-circuits to `DemoProvider.getDemoScheduleBlockRaised` / `getDemoBlockRaised` fallback. |\n| `useLiveSchedule(params, opts?)`            | `{ schedule, isLoading, isError, refetch }` — WS-refetch on transition. `opts.demoMode: true` (+ optional `demoIntervalMs`) short-circuits to `DemoProvider.getDemoSchedule` / `buildDemoSchedule` fallback with a live tick timer.     |\n| `useCurrentScheduleItem(schedule, opts?)`   | `{ currentItem, nextItem }` — pure-client rollover projection.              |\n| `useLinkedTeamCampaign(params, opts?)`      | `{ linkedTeamCampaign, … }` — WS-store first, HTTP fallback.                |\n| `useDonorSpotlight(params, opts?)`          | `{ spotlight, isLoading, refetch }` — WS store preferred + REST snapshot.   |\n\n### Legacy aliases (`/legacy`)\n\nSoft-deprecated. Identical behavior to the canonical exports —\nrename the import specifier once and the rest of your code base\nkeeps working unchanged:\n\n| Legacy name            | Canonical replacement                                          |\n| ---------------------- | -------------------------------------------------------------- |\n| `useUDPStore`          | `usePipelineValue` (Tier 1) / typed slice hooks (Tier 2)       |\n| `useUDPStoreApi`       | (use a typed slice hook; or reach for the raw store API)       |\n| `createUDPStore`       | `createPipelineStore` from `@playlive/realtime-pipeline/store` |\n| `UDPStore` (type)      | `PipelineStore` from `@playlive/realtime-pipeline/store`       |\n| `UDPStoreApi` (type)   | `PipelineStoreApi` from `@playlive/realtime-pipeline/store`    |\n| `CharityTypes`         | re-exported from `@playlive/realtime-pipeline/protocol`         |\n\nPlus every Tier 1+2+3 export under its canonical name.\n\n## Migration from `playlive-overlay-data-layer`\n\nOne-shot sed-friendly rewrite of every import specifier (no body\nedits needed for the happy path):\n\n```bash\n# Tier 3 — kitchen-sink (drop-in replacement)\nrg -l '@playlive/overlay-data-layer/websocket' . \\\n  | xargs sed -i '' \\\n    -e 's|@playlive/overlay-data-layer/websocket|@playlive/react-pipeline/provider|g'\n```\n\nFor the soft-deprecated names (`useUDPStore`, `createUDPStore`, …)\nsubstitute `/legacy` instead of `/provider` to opt into the alias\nbundle, then migrate at your leisure (PRD §10 — one-release window).\n\nSee the phase-8 row in the root [MIGRATION.md](../../MIGRATION.md) for\nthe full per-symbol mapping plus call-site walkthroughs.\n\n## Upstream spec\n\nNo external API surface — internal workspace package. All transport\nruns through `@playlive/realtime-pipeline/connection`.\n\n## Twitch Extension URL disclosure\n\n`KNOWN_URLS` enumerates every absolute URL or host this package can\nfetch. **It is empty.** This package opens WebSocket connections only\nto URLs the consumer passes to `<RealtimePipelineProvider url={…}>`;\nthose URLs must be added to your Extension submission's URL\ndisclosure list, not this one.\n\n```ts\nimport { KNOWN_URLS } from \"@playlive/react-pipeline\";\nconsole.log(KNOWN_URLS); // []\n```\n\nSee [`docs/twitch-extension-checklist.md`](../../docs/twitch-extension-checklist.md).\n\n## Examples\n\nLive overlay apps consuming this provider land under `apps/*`\n(phase 10). Until then, the Tier 1 quick-start above is the minimum\nviable wiring.\n\n## Contributing\n\nSee [CONTRIBUTING.md](../../CONTRIBUTING.md). To add new Tier-2\nhooks, mirror an existing slice hook in `src/hooks/index.ts` and\nadd a `it.each` entry in `tests/unit/index.test.ts`.\n\n## License\n\nMIT — see [LICENSE](../../LICENSE). Distributed via Play Live\nCodeArtifact (PRD §6).\n","readmeFilename":"README.md","dist":{"tarball":"https://packages.playlive.experience.stjude.org/@playlive/react-pipeline/-/react-pipeline-0.4.5.tgz","shasum":"1115b6adce8c95b38a0b5e080300846f9f38a4e1","integrity":"sha512-65YM1MEmX9UZcYSd6WwmOzujJodDYLTDumrSNdyZvOzCFfFR+9UEMI7LEeFvnIv2osMI5HSxwhTbCl5UujgoKg=="}},"0.4.6":{"name":"@playlive/react-pipeline","version":"0.4.6","description":"React bindings for @playlive/realtime-pipeline — three usage tiers (single selector / typed per-slice / kitchen-sink). Twitch-Extension safe; no react-use-websocket-lite.","type":"module","sideEffects":false,"main":"./index.js","types":"./index.d.ts","exports":{".":{"import":"./index.js","types":"./index.d.ts"},"./provider":{"import":"./provider/index.js","types":"./provider/index.d.ts"},"./legacy":{"import":"./legacy/index.js","types":"./legacy/index.d.ts"},"./fusion":{"import":"./fusion/index.js","types":"./fusion/index.d.ts"}},"dependencies":{},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","zustand":"^5.0.0","@playlive/realtime-pipeline":"^0.2.6","@playlive/tiltify-core":"^0.4.11","@tanstack/react-query":"^5.0.0","@playlive/react-query":"^0.3.4","@playlive/fundraiser-data":"^0.4.1"},"peerDependenciesMeta":{"@tanstack/react-query":{"optional":true},"@playlive/react-query":{"optional":true},"@playlive/fundraiser-data":{"optional":true}},"playlive":{"target":"browser","frontendEligible":true,"coverageFloor":85},"publishConfig":{"access":"restricted","registry":"https://playlive-767397689694.d.codeartifact.us-east-1.amazonaws.com/npm/playlive/"},"integrity":"sha512-jVflRSRqGGTCZ7FKIiEXEw3OBpP1dXYycPjV6uorzlV4UvJAbx9Sy1MyanCypwIe42io1KQoswcduJrN1WrCIA==","shasum":"51e08cc2582b4e2480ce5cb2c95752b102f14682","readme":"# @playlive/react-pipeline\n\nReact bindings for [`@playlive/realtime-pipeline`](../realtime-pipeline/).\nDrop-in replacement for `playlive-overlay-data-layer/src/websocket/*`\nwith **zero** dependency on `react-use-websocket-lite` — built directly\non Zustand v5's `useStore` + `useSyncExternalStore`.\n\n![Coverage](./coverage-badge.svg)\n\n## Three tiers — take only what you need\n\nPick the surface that matches how much of the legacy\n`UnifiedDataPipelineProvider` API you actually want. Each tier is\nstrictly larger than the last and lives at its own subpath so unused\ncode is tree-shaken out.\n\n| Tier | Subpath                            | Surface                                                                         | Use when                                                              |\n| ---- | ---------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| 1    | `@playlive/react-pipeline`         | `RealtimePipelineProvider` + `usePipelineValue` (universal selector)            | Greenfield app; you want one selector primitive and nothing else.     |\n| 2    | `@playlive/react-pipeline`         | Tier 1 **plus** 22 typed per-slice hooks (`usePipelineDonations(id)`, …)        | You want stable, typed one-liners for the common reads.               |\n| 3    | `@playlive/react-pipeline/provider`| Tier 2 **plus** `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, etc.   | Porting an existing overlay-data-layer consumer with minimal churn.   |\n| —    | `@playlive/react-pipeline/legacy`  | Tier 3 **plus** `useUDPStore` / `useUDPStoreApi` / `createUDPStore` aliases     | Soft-deprecated shim for the legacy symbol names (one-release window).|\n\n## Install\n\n```bash\nbun add @playlive/react-pipeline\nbun add -d react react-dom zustand\n```\n\nPeer dependencies (jose-style — consumer brings their own):\n`react@^19`, `react-dom@^19`, `zustand@^5`,\n`@playlive/realtime-pipeline` (workspace), `@playlive/tiltify-core`\n(workspace, type-only — stripped at compile time).\n\nNothing else ships in the bundle.\n\n## Quick start — Tier 1\n\n```tsx\nimport { RealtimePipelineProvider, usePipelineCampaigns } from \"@playlive/react-pipeline\";\n\nfunction App() {\n  return (\n    <RealtimePipelineProvider\n      url=\"wss://pipeline.example.com/socket\"\n      charityType=\"tiltify\"\n      autoConnect\n    >\n      <Overlay />\n    </RealtimePipelineProvider>\n  );\n}\n\nfunction Overlay() {\n  const campaigns = usePipelineCampaigns();\n  return <ul>{campaigns.map((c) => <li key={c.id}>{c.name}</li>)}</ul>;\n}\n```\n\n`RealtimePipelineProvider` mounts a Zustand store + opens (or accepts)\na `PipelineConnection`. Children call any Tier 1 / Tier 2 hook to\nsubscribe.\n\n## Subpath exports\n\n| Subpath                              | Description                                                                       |\n| ------------------------------------ | --------------------------------------------------------------------------------- |\n| `@playlive/react-pipeline`           | Default barrel — Tier 1 + Tier 2 (provider + 21 typed slice hooks).               |\n| `@playlive/react-pipeline/provider`  | Tier 3 — `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, add-pipeline\\*. |\n| `@playlive/react-pipeline/fusion`    | Cross-package composition hooks (REST baseline + WS delta) — `useCurrentBlockRaised`, `useLiveSchedule`, `useCurrentScheduleItem`, `useLinkedTeamCampaign`, `useDonorSpotlight`, plus the WS-store-first / HTTP-fallback hybrid slice hooks (`useHybridRewards`, `useHybridTargets`, `useHybridMilestones`, `useHybridPolls`, `useHybridCause`, `useHybridFundraisingEvent`, `useHybridFundraisingEventSupportingCampaigns`, `useHybridAuctionHouses`) + the shared `useLoadingTimeout` primitive. |\n| `@playlive/react-pipeline/legacy`    | Soft-deprecated alias bundle (`useUDPStore`, `createUDPStore`, `UDPStore`, etc.). |\n\nEach subpath ships an ESM bundle, a Bun source condition, and `.d.ts`\ndeclarations. Frontend-eligible bundles are not minified (PRD §6.1)\nso stack traces stay readable in production.\n\n## API reference\n\n### Tier 1 — universal primitive\n\n| Export                          | Signature                                                            |\n| ------------------------------- | -------------------------------------------------------------------- |\n| `RealtimePipelineProvider`      | `(props: RealtimePipelineProviderProps) => JSX.Element`              |\n| `usePipelineValue`              | `<T>(selector: (s: PipelineStore) => T) => T`                        |\n\n`usePipelineValue` is single-arg by design. Zustand v5 dropped the\nthird `equalityFn` parameter on `useStore` to keep the package\nfree of the `use-sync-external-store` shim peer. For shallow / custom\nequality, wrap your selector with `useShallow` from\n`zustand/shallow` (zero-extra-dep):\n\n```tsx\nimport { useShallow } from \"zustand/shallow\";\nimport { usePipelineValue } from \"@playlive/react-pipeline\";\n\nconst { connected, connecting } = usePipelineValue(\n  useShallow((s) => ({ connected: s.connected, connecting: s.connecting })),\n);\n```\n\n### Tier 2 — typed per-slice hooks\n\nAll 22 hooks are stable wrappers over `usePipelineValue` with a typed\nselector + memo-stable empty-array fallbacks (no infinite-render\nchurn from fresh `[]` allocations — Decision §3 in CHANGELOG).\n\n| Hook                              | Returns                                                             |\n| --------------------------------- | ------------------------------------------------------------------- |\n| `usePipelineCampaigns()`          | `TiltifyCampaign[]`                                                 |\n| `usePipelineTeamCampaigns()`      | `TiltifyTeamCampaign[]`                                             |\n| `usePipelineFundraisingEvents()`  | `TiltifyFundraisingEvent[]`                                         |\n| `usePipelineCauses()`             | `TiltifyCause[]`                                                    |\n| `usePipelineDonations()`          | `Record<string, TiltifyDonationWithTestFlag[]>`                     |\n| `usePipelineDonations(id)`        | `TiltifyDonationWithTestFlag[]` (memo-stable `[]` if id unknown)    |\n| `usePipelineDonationSum(id, opts?)` | `PipelineDonationSumResult` — live per-currency sum over `[start, end)` of the WS donation firehose. Pair with the REST `/schedules/campaigns/{id}/raised` `asOf` field to replace polling with a `baseline + WS-delta` view. |\n| `usePipelineDonationTrains(id, withStatus?)` | `DonationTrain[]`                                        |\n| `usePipelineSubathonTimer(id, withStatus?)`  | `SubathonTimer[]`                                        |\n| `usePipelineDonorSpotlight(id)`   | `ComputedSpotlight \\| null`                                         |\n| `usePipelinePolls(id)`            | `TiltifyPoll[]`                                                     |\n| `usePipelineRewards(id)`          | `TiltifyReward[]`                                                   |\n| `usePipelineTargets(id)`          | `TiltifyTarget[]`                                                   |\n| `usePipelineMilestones(id)`       | `TiltifyMilestone[]`                                                |\n| `usePipelineLeaderboardEntries(id)` | `TiltifyLeaderboardEntry[]`                                       |\n| `usePipelineAuctionHouses()`      | `AuctionHouse[]`                                                    |\n| `usePipelineTwitchChat()`         | `TwitchChatMessage[]`                                               |\n| `usePipelineConnectionState()`    | `PipelineConnectionState` (10 split selectors — no false re-renders)|\n| `usePipelineConnectionActions()`  | `PipelineConnectionActions` (stable `connect/disconnect/refresh/…`) |\n| `usePipelineSubscriptions()`      | `Record<string, TiltifyWebhookSubscription>`                        |\n| `usePipelineTiltifySettings()`    | `TiltifyWebSocketSettings`                                          |\n| `usePipelineLastRefreshResponse()`| `RefreshResponseMessage \\| null`                                    |\n| `usePipelineDonorSpotlightSettings()` | `Record<string, DonorSpotlightSettings>`                        |\n\n### Tier 3 — kitchen-sink provider (`/provider`)\n\n| Export                              | Notes                                                                                  |\n| ----------------------------------- | -------------------------------------------------------------------------------------- |\n| `UnifiedDataPipelineProvider`       | Superset of `RealtimePipelineProvider`; adds legacy `reloadOnErrorReconnect` + `debug`.|\n| `useUnifiedDataPipeline()`          | One-call kitchen-sink hook. Returns every slice + actions + legacy field aliases.      |\n| `useAddPipelineCampaignIDs(ids)`    | Effect hook — registers campaign ids with the store on mount.                          |\n| `useAddPipelineCampaigns(items)`    | Effect hook — registers full campaign records on mount.                                |\n| `useAddPipelineFundraisingEventIDs(ids)` | Effect hook — registers fundraising-event ids on mount.                           |\n| `useCommonContextHooks(…)`          | URL / mode / charity-type sync to the store. Same shape as the legacy hook.            |\n| `useIdentify()` / `useIdentify(…11 legacy args)` | Zero-arg returns the store's bound `identify` action. Positional arg form is preserved as an `@deprecated` overload — args are ignored, warns once. |\n\n### Fusion — hybrid slice hooks (`/fusion`)\n\nOverlay-parity `usePipeline*` hooks lifted from\n`playlive-overlay-vite/src/hooks/pipeline/` so every overlay that\nwants the same WS-store-first / HTTP-fallback shape can drop the\nlocal copy. Every hook accepts a `mode: \"ws\" | \"http\"` param that\npicks between:\n\n- `\"ws\"` — read the Tier-2 store selector for the slice, latch a\n  local `hasLoadedX` boolean on the first slice arrival (or on the\n  server-side \"feature disabled\" signal), and fire\n  `updateTiltifySettings({ <sliceKey> })` whenever the local\n  `xEnabled` toggle flips so the pipeline dynamically un/subscribes.\n- `\"http\"` — delegate to the matching `@playlive/react-query` REST\n  hook (`useRewards`, `useTargets`, …) with the WS store slice\n  ignored.\n\nAll hybrid hooks share `useLoadingTimeout` — a 15 s safety-net that\nforce-completes the WS branch when the store never delivers.\n\n| Hook                                                     | Returns                                                                        |\n| -------------------------------------------------------- | ------------------------------------------------------------------------------ |\n| `useHybridRewards(params)`                               | `{ currentRewards, setHasLoadedRewards, hasLoadedRewards, isLoadingRewards, isLoading, setRewardsEnabled, rewardsEnabled }` |\n| `useHybridTargets(params)`                               | `{ currentTargets, setHasLoadedTargets, hasLoadedTargets, isLoading, targetsEnabled, isLoadingTargets, setTargetsEnabled }` |\n| `useHybridMilestones(params)`                            | `{ isLoadingMilestones, currentMilestones, milestonesEnabled, setMilestonesEnabled }` |\n| `useHybridPolls(params)`                                 | `{ isLoadingPolls, currentPolls, pollsEnabled, setPollsEnabled }`              |\n| `useHybridCause({ mode, initialCampaign })`              | `{ cause, isLoading }` — single-slot semantics (uses `getFirstCause`).         |\n| `useHybridFundraisingEvent({ mode, initialCampaign })`   | `{ fundraisingEvent, isLoading }` — single-slot semantics.                     |\n| `useHybridFundraisingEventSupportingCampaigns(params)`   | `{ supportingCampaigns, isLoading, setSupportingCampaignsEnabled, supportingCampaignsEnabled, isLoadingSupportingCampaigns, hasLoadedSupportingCampaigns }` |\n| `useHybridAuctionHouses({ mode, enabled, campaignID })`  | `{ auctionHouses, auctionHouseTotal, auctionHousesEnabled, setAuctionHousesEnabled }` — WS-only (no REST fallback yet). |\n| `useLoadingTimeout(params)`                              | `void` — safety-net timer primitive shared by every hook above.                |\n\nReturn shapes are preserved verbatim from the overlay so overlay\ncall-sites can eventually collapse to a re-export.\n\n### Fusion — baseline + WS delta hooks (`/fusion`)\n\n| Hook                                        | Returns                                                                     |\n| ------------------------------------------- | --------------------------------------------------------------------------- |\n| `useCurrentBlockRaised(campaignID, start, end, opts?)` | `{ raised, data, isLoading, isError, refetch }` — REST baseline + WS delta. `opts.demoMode: true` short-circuits to `DemoProvider.getDemoScheduleBlockRaised` / `getDemoBlockRaised` fallback. |\n| `useLiveSchedule(params, opts?)`            | `{ schedule, isLoading, isError, refetch }` — WS-refetch on transition. `opts.demoMode: true` (+ optional `demoIntervalMs`) short-circuits to `DemoProvider.getDemoSchedule` / `buildDemoSchedule` fallback with a live tick timer.     |\n| `useCurrentScheduleItem(schedule, opts?)`   | `{ currentItem, nextItem }` — pure-client rollover projection.              |\n| `useLinkedTeamCampaign(params, opts?)`      | `{ linkedTeamCampaign, … }` — WS-store first, HTTP fallback.                |\n| `useDonorSpotlight(params, opts?)`          | `{ spotlight, isLoading, refetch }` — WS store preferred + REST snapshot.   |\n\n### Legacy aliases (`/legacy`)\n\nSoft-deprecated. Identical behavior to the canonical exports —\nrename the import specifier once and the rest of your code base\nkeeps working unchanged:\n\n| Legacy name            | Canonical replacement                                          |\n| ---------------------- | -------------------------------------------------------------- |\n| `useUDPStore`          | `usePipelineValue` (Tier 1) / typed slice hooks (Tier 2)       |\n| `useUDPStoreApi`       | (use a typed slice hook; or reach for the raw store API)       |\n| `createUDPStore`       | `createPipelineStore` from `@playlive/realtime-pipeline/store` |\n| `UDPStore` (type)      | `PipelineStore` from `@playlive/realtime-pipeline/store`       |\n| `UDPStoreApi` (type)   | `PipelineStoreApi` from `@playlive/realtime-pipeline/store`    |\n| `CharityTypes`         | re-exported from `@playlive/realtime-pipeline/protocol`         |\n\nPlus every Tier 1+2+3 export under its canonical name.\n\n## Migration from `playlive-overlay-data-layer`\n\nOne-shot sed-friendly rewrite of every import specifier (no body\nedits needed for the happy path):\n\n```bash\n# Tier 3 — kitchen-sink (drop-in replacement)\nrg -l '@playlive/overlay-data-layer/websocket' . \\\n  | xargs sed -i '' \\\n    -e 's|@playlive/overlay-data-layer/websocket|@playlive/react-pipeline/provider|g'\n```\n\nFor the soft-deprecated names (`useUDPStore`, `createUDPStore`, …)\nsubstitute `/legacy` instead of `/provider` to opt into the alias\nbundle, then migrate at your leisure (PRD §10 — one-release window).\n\nSee the phase-8 row in the root [MIGRATION.md](../../MIGRATION.md) for\nthe full per-symbol mapping plus call-site walkthroughs.\n\n## Upstream spec\n\nNo external API surface — internal workspace package. All transport\nruns through `@playlive/realtime-pipeline/connection`.\n\n## Twitch Extension URL disclosure\n\n`KNOWN_URLS` enumerates every absolute URL or host this package can\nfetch. **It is empty.** This package opens WebSocket connections only\nto URLs the consumer passes to `<RealtimePipelineProvider url={…}>`;\nthose URLs must be added to your Extension submission's URL\ndisclosure list, not this one.\n\n```ts\nimport { KNOWN_URLS } from \"@playlive/react-pipeline\";\nconsole.log(KNOWN_URLS); // []\n```\n\nSee [`docs/twitch-extension-checklist.md`](../../docs/twitch-extension-checklist.md).\n\n## Examples\n\nLive overlay apps consuming this provider land under `apps/*`\n(phase 10). Until then, the Tier 1 quick-start above is the minimum\nviable wiring.\n\n## Contributing\n\nSee [CONTRIBUTING.md](../../CONTRIBUTING.md). To add new Tier-2\nhooks, mirror an existing slice hook in `src/hooks/index.ts` and\nadd a `it.each` entry in `tests/unit/index.test.ts`.\n\n## License\n\nMIT — see [LICENSE](../../LICENSE). Distributed via Play Live\nCodeArtifact (PRD §6).\n","readmeFilename":"README.md","dist":{"tarball":"https://packages.playlive.experience.stjude.org/@playlive/react-pipeline/-/react-pipeline-0.4.6.tgz","shasum":"51e08cc2582b4e2480ce5cb2c95752b102f14682","integrity":"sha512-jVflRSRqGGTCZ7FKIiEXEw3OBpP1dXYycPjV6uorzlV4UvJAbx9Sy1MyanCypwIe42io1KQoswcduJrN1WrCIA=="}},"0.4.7":{"name":"@playlive/react-pipeline","version":"0.4.7","description":"React bindings for @playlive/realtime-pipeline — three usage tiers (single selector / typed per-slice / kitchen-sink). Twitch-Extension safe; no react-use-websocket-lite.","type":"module","sideEffects":false,"main":"./index.js","types":"./index.d.ts","exports":{".":{"import":"./index.js","types":"./index.d.ts"},"./provider":{"import":"./provider/index.js","types":"./provider/index.d.ts"},"./legacy":{"import":"./legacy/index.js","types":"./legacy/index.d.ts"},"./fusion":{"import":"./fusion/index.js","types":"./fusion/index.d.ts"}},"dependencies":{},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","zustand":"^5.0.0","@playlive/realtime-pipeline":"^0.2.7","@playlive/tiltify-core":"^0.4.11","@tanstack/react-query":"^5.0.0","@playlive/react-query":"^0.3.4","@playlive/fundraiser-data":"^0.4.1"},"peerDependenciesMeta":{"@tanstack/react-query":{"optional":true},"@playlive/react-query":{"optional":true},"@playlive/fundraiser-data":{"optional":true}},"playlive":{"target":"browser","frontendEligible":true,"coverageFloor":85},"publishConfig":{"access":"restricted","registry":"https://playlive-767397689694.d.codeartifact.us-east-1.amazonaws.com/npm/playlive/"},"integrity":"sha512-pbJrTgoNbHn+O+uqsddJ1xbI55YoAT/w5bG2ibX1Ni2JKF0T6yj2rbMUiLmrWnLlQDs9/slRnBhgX+fMrxy5Rg==","shasum":"1fd9813ffba8e499894ac9fd2c3ee2a636f85dc3","readme":"# @playlive/react-pipeline\n\nReact bindings for [`@playlive/realtime-pipeline`](../realtime-pipeline/).\nDrop-in replacement for `playlive-overlay-data-layer/src/websocket/*`\nwith **zero** dependency on `react-use-websocket-lite` — built directly\non Zustand v5's `useStore` + `useSyncExternalStore`.\n\n![Coverage](./coverage-badge.svg)\n\n## Three tiers — take only what you need\n\nPick the surface that matches how much of the legacy\n`UnifiedDataPipelineProvider` API you actually want. Each tier is\nstrictly larger than the last and lives at its own subpath so unused\ncode is tree-shaken out.\n\n| Tier | Subpath                            | Surface                                                                         | Use when                                                              |\n| ---- | ---------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| 1    | `@playlive/react-pipeline`         | `RealtimePipelineProvider` + `usePipelineValue` (universal selector)            | Greenfield app; you want one selector primitive and nothing else.     |\n| 2    | `@playlive/react-pipeline`         | Tier 1 **plus** 22 typed per-slice hooks (`usePipelineDonations(id)`, …)        | You want stable, typed one-liners for the common reads.               |\n| 3    | `@playlive/react-pipeline/provider`| Tier 2 **plus** `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, etc.   | Porting an existing overlay-data-layer consumer with minimal churn.   |\n| —    | `@playlive/react-pipeline/legacy`  | Tier 3 **plus** `useUDPStore` / `useUDPStoreApi` / `createUDPStore` aliases     | Soft-deprecated shim for the legacy symbol names (one-release window).|\n\n## Install\n\n```bash\nbun add @playlive/react-pipeline\nbun add -d react react-dom zustand\n```\n\nPeer dependencies (jose-style — consumer brings their own):\n`react@^19`, `react-dom@^19`, `zustand@^5`,\n`@playlive/realtime-pipeline` (workspace), `@playlive/tiltify-core`\n(workspace, type-only — stripped at compile time).\n\nNothing else ships in the bundle.\n\n## Quick start — Tier 1\n\n```tsx\nimport { RealtimePipelineProvider, usePipelineCampaigns } from \"@playlive/react-pipeline\";\n\nfunction App() {\n  return (\n    <RealtimePipelineProvider\n      url=\"wss://pipeline.example.com/socket\"\n      charityType=\"tiltify\"\n      autoConnect\n    >\n      <Overlay />\n    </RealtimePipelineProvider>\n  );\n}\n\nfunction Overlay() {\n  const campaigns = usePipelineCampaigns();\n  return <ul>{campaigns.map((c) => <li key={c.id}>{c.name}</li>)}</ul>;\n}\n```\n\n`RealtimePipelineProvider` mounts a Zustand store + opens (or accepts)\na `PipelineConnection`. Children call any Tier 1 / Tier 2 hook to\nsubscribe.\n\n## Subpath exports\n\n| Subpath                              | Description                                                                       |\n| ------------------------------------ | --------------------------------------------------------------------------------- |\n| `@playlive/react-pipeline`           | Default barrel — Tier 1 + Tier 2 (provider + 21 typed slice hooks).               |\n| `@playlive/react-pipeline/provider`  | Tier 3 — `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, add-pipeline\\*. |\n| `@playlive/react-pipeline/fusion`    | Cross-package composition hooks (REST baseline + WS delta) — `useCurrentBlockRaised`, `useLiveSchedule`, `useCurrentScheduleItem`, `useLinkedTeamCampaign`, `useDonorSpotlight`, plus the WS-store-first / HTTP-fallback hybrid slice hooks (`useHybridRewards`, `useHybridTargets`, `useHybridMilestones`, `useHybridPolls`, `useHybridCause`, `useHybridFundraisingEvent`, `useHybridFundraisingEventSupportingCampaigns`, `useHybridAuctionHouses`) + the shared `useLoadingTimeout` primitive. |\n| `@playlive/react-pipeline/legacy`    | Soft-deprecated alias bundle (`useUDPStore`, `createUDPStore`, `UDPStore`, etc.). |\n\nEach subpath ships an ESM bundle, a Bun source condition, and `.d.ts`\ndeclarations. Frontend-eligible bundles are not minified (PRD §6.1)\nso stack traces stay readable in production.\n\n## API reference\n\n### Tier 1 — universal primitive\n\n| Export                          | Signature                                                            |\n| ------------------------------- | -------------------------------------------------------------------- |\n| `RealtimePipelineProvider`      | `(props: RealtimePipelineProviderProps) => JSX.Element`              |\n| `usePipelineValue`              | `<T>(selector: (s: PipelineStore) => T) => T`                        |\n\n`usePipelineValue` is single-arg by design. Zustand v5 dropped the\nthird `equalityFn` parameter on `useStore` to keep the package\nfree of the `use-sync-external-store` shim peer. For shallow / custom\nequality, wrap your selector with `useShallow` from\n`zustand/shallow` (zero-extra-dep):\n\n```tsx\nimport { useShallow } from \"zustand/shallow\";\nimport { usePipelineValue } from \"@playlive/react-pipeline\";\n\nconst { connected, connecting } = usePipelineValue(\n  useShallow((s) => ({ connected: s.connected, connecting: s.connecting })),\n);\n```\n\n### Tier 2 — typed per-slice hooks\n\nAll 22 hooks are stable wrappers over `usePipelineValue` with a typed\nselector + memo-stable empty-array fallbacks (no infinite-render\nchurn from fresh `[]` allocations — Decision §3 in CHANGELOG).\n\n| Hook                              | Returns                                                             |\n| --------------------------------- | ------------------------------------------------------------------- |\n| `usePipelineCampaigns()`          | `TiltifyCampaign[]`                                                 |\n| `usePipelineTeamCampaigns()`      | `TiltifyTeamCampaign[]`                                             |\n| `usePipelineFundraisingEvents()`  | `TiltifyFundraisingEvent[]`                                         |\n| `usePipelineCauses()`             | `TiltifyCause[]`                                                    |\n| `usePipelineDonations()`          | `Record<string, TiltifyDonationWithTestFlag[]>`                     |\n| `usePipelineDonations(id)`        | `TiltifyDonationWithTestFlag[]` (memo-stable `[]` if id unknown)    |\n| `usePipelineDonationSum(id, opts?)` | `PipelineDonationSumResult` — live per-currency sum over `[start, end)` of the WS donation firehose. Pair with the REST `/schedules/campaigns/{id}/raised` `asOf` field to replace polling with a `baseline + WS-delta` view. |\n| `usePipelineDonationTrains(id, withStatus?)` | `DonationTrain[]`                                        |\n| `usePipelineSubathonTimer(id, withStatus?)`  | `SubathonTimer[]`                                        |\n| `usePipelineDonorSpotlight(id)`   | `ComputedSpotlight \\| null`                                         |\n| `usePipelinePolls(id)`            | `TiltifyPoll[]`                                                     |\n| `usePipelineRewards(id)`          | `TiltifyReward[]`                                                   |\n| `usePipelineTargets(id)`          | `TiltifyTarget[]`                                                   |\n| `usePipelineMilestones(id)`       | `TiltifyMilestone[]`                                                |\n| `usePipelineLeaderboardEntries(id)` | `TiltifyLeaderboardEntry[]`                                       |\n| `usePipelineAuctionHouses()`      | `AuctionHouse[]`                                                    |\n| `usePipelineTwitchChat()`         | `TwitchChatMessage[]`                                               |\n| `usePipelineConnectionState()`    | `PipelineConnectionState` (10 split selectors — no false re-renders)|\n| `usePipelineConnectionActions()`  | `PipelineConnectionActions` (stable `connect/disconnect/refresh/…`) |\n| `usePipelineSubscriptions()`      | `Record<string, TiltifyWebhookSubscription>`                        |\n| `usePipelineTiltifySettings()`    | `TiltifyWebSocketSettings`                                          |\n| `usePipelineLastRefreshResponse()`| `RefreshResponseMessage \\| null`                                    |\n| `usePipelineDonorSpotlightSettings()` | `Record<string, DonorSpotlightSettings>`                        |\n\n### Tier 3 — kitchen-sink provider (`/provider`)\n\n| Export                              | Notes                                                                                  |\n| ----------------------------------- | -------------------------------------------------------------------------------------- |\n| `UnifiedDataPipelineProvider`       | Superset of `RealtimePipelineProvider`; adds legacy `reloadOnErrorReconnect` + `debug`.|\n| `useUnifiedDataPipeline()`          | One-call kitchen-sink hook. Returns every slice + actions + legacy field aliases.      |\n| `useAddPipelineCampaignIDs(ids)`    | Effect hook — registers campaign ids with the store on mount.                          |\n| `useAddPipelineCampaigns(items)`    | Effect hook — registers full campaign records on mount.                                |\n| `useAddPipelineFundraisingEventIDs(ids)` | Effect hook — registers fundraising-event ids on mount.                           |\n| `useCommonContextHooks(…)`          | URL / mode / charity-type sync to the store. Same shape as the legacy hook.            |\n| `useIdentify()` / `useIdentify(…11 legacy args)` | Zero-arg returns the store's bound `identify` action. Positional arg form is preserved as an `@deprecated` overload — args are ignored, warns once. |\n\n### Fusion — hybrid slice hooks (`/fusion`)\n\nOverlay-parity `usePipeline*` hooks lifted from\n`playlive-overlay-vite/src/hooks/pipeline/` so every overlay that\nwants the same WS-store-first / HTTP-fallback shape can drop the\nlocal copy. Every hook accepts a `mode: \"ws\" | \"http\"` param that\npicks between:\n\n- `\"ws\"` — read the Tier-2 store selector for the slice, latch a\n  local `hasLoadedX` boolean on the first slice arrival (or on the\n  server-side \"feature disabled\" signal), and fire\n  `updateTiltifySettings({ <sliceKey> })` whenever the local\n  `xEnabled` toggle flips so the pipeline dynamically un/subscribes.\n- `\"http\"` — delegate to the matching `@playlive/react-query` REST\n  hook (`useRewards`, `useTargets`, …) with the WS store slice\n  ignored.\n\nAll hybrid hooks share `useLoadingTimeout` — a 15 s safety-net that\nforce-completes the WS branch when the store never delivers.\n\n| Hook                                                     | Returns                                                                        |\n| -------------------------------------------------------- | ------------------------------------------------------------------------------ |\n| `useHybridRewards(params)`                               | `{ currentRewards, setHasLoadedRewards, hasLoadedRewards, isLoadingRewards, isLoading, setRewardsEnabled, rewardsEnabled }` |\n| `useHybridTargets(params)`                               | `{ currentTargets, setHasLoadedTargets, hasLoadedTargets, isLoading, targetsEnabled, isLoadingTargets, setTargetsEnabled }` |\n| `useHybridMilestones(params)`                            | `{ isLoadingMilestones, currentMilestones, milestonesEnabled, setMilestonesEnabled }` |\n| `useHybridPolls(params)`                                 | `{ isLoadingPolls, currentPolls, pollsEnabled, setPollsEnabled }`              |\n| `useHybridCause({ mode, initialCampaign })`              | `{ cause, isLoading }` — single-slot semantics (uses `getFirstCause`).         |\n| `useHybridFundraisingEvent({ mode, initialCampaign })`   | `{ fundraisingEvent, isLoading }` — single-slot semantics.                     |\n| `useHybridFundraisingEventSupportingCampaigns(params)`   | `{ supportingCampaigns, isLoading, setSupportingCampaignsEnabled, supportingCampaignsEnabled, isLoadingSupportingCampaigns, hasLoadedSupportingCampaigns }` |\n| `useHybridAuctionHouses({ mode, enabled, campaignID })`  | `{ auctionHouses, auctionHouseTotal, auctionHousesEnabled, setAuctionHousesEnabled }` — WS-only (no REST fallback yet). |\n| `useLoadingTimeout(params)`                              | `void` — safety-net timer primitive shared by every hook above.                |\n\nReturn shapes are preserved verbatim from the overlay so overlay\ncall-sites can eventually collapse to a re-export.\n\n### Fusion — baseline + WS delta hooks (`/fusion`)\n\n| Hook                                        | Returns                                                                     |\n| ------------------------------------------- | --------------------------------------------------------------------------- |\n| `useCurrentBlockRaised(campaignID, start, end, opts?)` | `{ raised, data, isLoading, isError, refetch }` — REST baseline + WS delta. `opts.demoMode: true` short-circuits to `DemoProvider.getDemoScheduleBlockRaised` / `getDemoBlockRaised` fallback. |\n| `useLiveSchedule(params, opts?)`            | `{ schedule, isLoading, isError, refetch }` — WS-refetch on transition. `opts.demoMode: true` (+ optional `demoIntervalMs`) short-circuits to `DemoProvider.getDemoSchedule` / `buildDemoSchedule` fallback with a live tick timer.     |\n| `useCurrentScheduleItem(schedule, opts?)`   | `{ currentItem, nextItem }` — pure-client rollover projection.              |\n| `useLinkedTeamCampaign(params, opts?)`      | `{ linkedTeamCampaign, … }` — WS-store first, HTTP fallback.                |\n| `useDonorSpotlight(params, opts?)`          | `{ spotlight, isLoading, refetch }` — WS store preferred + REST snapshot.   |\n\n### Legacy aliases (`/legacy`)\n\nSoft-deprecated. Identical behavior to the canonical exports —\nrename the import specifier once and the rest of your code base\nkeeps working unchanged:\n\n| Legacy name            | Canonical replacement                                          |\n| ---------------------- | -------------------------------------------------------------- |\n| `useUDPStore`          | `usePipelineValue` (Tier 1) / typed slice hooks (Tier 2)       |\n| `useUDPStoreApi`       | (use a typed slice hook; or reach for the raw store API)       |\n| `createUDPStore`       | `createPipelineStore` from `@playlive/realtime-pipeline/store` |\n| `UDPStore` (type)      | `PipelineStore` from `@playlive/realtime-pipeline/store`       |\n| `UDPStoreApi` (type)   | `PipelineStoreApi` from `@playlive/realtime-pipeline/store`    |\n| `CharityTypes`         | re-exported from `@playlive/realtime-pipeline/protocol`         |\n\nPlus every Tier 1+2+3 export under its canonical name.\n\n## Migration from `playlive-overlay-data-layer`\n\nOne-shot sed-friendly rewrite of every import specifier (no body\nedits needed for the happy path):\n\n```bash\n# Tier 3 — kitchen-sink (drop-in replacement)\nrg -l '@playlive/overlay-data-layer/websocket' . \\\n  | xargs sed -i '' \\\n    -e 's|@playlive/overlay-data-layer/websocket|@playlive/react-pipeline/provider|g'\n```\n\nFor the soft-deprecated names (`useUDPStore`, `createUDPStore`, …)\nsubstitute `/legacy` instead of `/provider` to opt into the alias\nbundle, then migrate at your leisure (PRD §10 — one-release window).\n\nSee the phase-8 row in the root [MIGRATION.md](../../MIGRATION.md) for\nthe full per-symbol mapping plus call-site walkthroughs.\n\n## Upstream spec\n\nNo external API surface — internal workspace package. All transport\nruns through `@playlive/realtime-pipeline/connection`.\n\n## Twitch Extension URL disclosure\n\n`KNOWN_URLS` enumerates every absolute URL or host this package can\nfetch. **It is empty.** This package opens WebSocket connections only\nto URLs the consumer passes to `<RealtimePipelineProvider url={…}>`;\nthose URLs must be added to your Extension submission's URL\ndisclosure list, not this one.\n\n```ts\nimport { KNOWN_URLS } from \"@playlive/react-pipeline\";\nconsole.log(KNOWN_URLS); // []\n```\n\nSee [`docs/twitch-extension-checklist.md`](../../docs/twitch-extension-checklist.md).\n\n## Examples\n\nLive overlay apps consuming this provider land under `apps/*`\n(phase 10). Until then, the Tier 1 quick-start above is the minimum\nviable wiring.\n\n## Contributing\n\nSee [CONTRIBUTING.md](../../CONTRIBUTING.md). To add new Tier-2\nhooks, mirror an existing slice hook in `src/hooks/index.ts` and\nadd a `it.each` entry in `tests/unit/index.test.ts`.\n\n## License\n\nMIT — see [LICENSE](../../LICENSE). Distributed via Play Live\nCodeArtifact (PRD §6).\n","readmeFilename":"README.md","dist":{"tarball":"https://packages.playlive.experience.stjude.org/@playlive/react-pipeline/-/react-pipeline-0.4.7.tgz","shasum":"1fd9813ffba8e499894ac9fd2c3ee2a636f85dc3","integrity":"sha512-pbJrTgoNbHn+O+uqsddJ1xbI55YoAT/w5bG2ibX1Ni2JKF0T6yj2rbMUiLmrWnLlQDs9/slRnBhgX+fMrxy5Rg=="}},"0.4.8":{"name":"@playlive/react-pipeline","version":"0.4.8","description":"React bindings for @playlive/realtime-pipeline — three usage tiers (single selector / typed per-slice / kitchen-sink). Twitch-Extension safe; no react-use-websocket-lite.","type":"module","sideEffects":false,"main":"./index.js","types":"./index.d.ts","exports":{".":{"import":"./index.js","types":"./index.d.ts"},"./provider":{"import":"./provider/index.js","types":"./provider/index.d.ts"},"./legacy":{"import":"./legacy/index.js","types":"./legacy/index.d.ts"},"./fusion":{"import":"./fusion/index.js","types":"./fusion/index.d.ts"}},"dependencies":{},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","zustand":"^5.0.0","@playlive/realtime-pipeline":"^0.2.7","@playlive/tiltify-core":"^0.4.11","@tanstack/react-query":"^5.0.0","@playlive/react-query":"^0.3.4","@playlive/fundraiser-data":"^0.4.2"},"peerDependenciesMeta":{"@tanstack/react-query":{"optional":true},"@playlive/react-query":{"optional":true},"@playlive/fundraiser-data":{"optional":true}},"playlive":{"target":"browser","frontendEligible":true,"coverageFloor":85},"publishConfig":{"access":"restricted","registry":"https://playlive-767397689694.d.codeartifact.us-east-1.amazonaws.com/npm/playlive/"},"integrity":"sha512-AR/AV8TRf1K6UA0iQ6TvMdvMnHmW2AWc0ply0oraDhEi4ydWgE2N8GvpBkpUG7R24bwaVQ4MyG3/Mcq2QkVBVA==","shasum":"d243d56054ab95e37e5e442a787dd113b3c59314","readme":"# @playlive/react-pipeline\n\nReact bindings for [`@playlive/realtime-pipeline`](../realtime-pipeline/).\nDrop-in replacement for `playlive-overlay-data-layer/src/websocket/*`\nwith **zero** dependency on `react-use-websocket-lite` — built directly\non Zustand v5's `useStore` + `useSyncExternalStore`.\n\n![Coverage](./coverage-badge.svg)\n\n## Three tiers — take only what you need\n\nPick the surface that matches how much of the legacy\n`UnifiedDataPipelineProvider` API you actually want. Each tier is\nstrictly larger than the last and lives at its own subpath so unused\ncode is tree-shaken out.\n\n| Tier | Subpath                            | Surface                                                                         | Use when                                                              |\n| ---- | ---------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| 1    | `@playlive/react-pipeline`         | `RealtimePipelineProvider` + `usePipelineValue` (universal selector)            | Greenfield app; you want one selector primitive and nothing else.     |\n| 2    | `@playlive/react-pipeline`         | Tier 1 **plus** 22 typed per-slice hooks (`usePipelineDonations(id)`, …)        | You want stable, typed one-liners for the common reads.               |\n| 3    | `@playlive/react-pipeline/provider`| Tier 2 **plus** `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, etc.   | Porting an existing overlay-data-layer consumer with minimal churn.   |\n| —    | `@playlive/react-pipeline/legacy`  | Tier 3 **plus** `useUDPStore` / `useUDPStoreApi` / `createUDPStore` aliases     | Soft-deprecated shim for the legacy symbol names (one-release window).|\n\n## Install\n\n```bash\nbun add @playlive/react-pipeline\nbun add -d react react-dom zustand\n```\n\nPeer dependencies (jose-style — consumer brings their own):\n`react@^19`, `react-dom@^19`, `zustand@^5`,\n`@playlive/realtime-pipeline` (workspace), `@playlive/tiltify-core`\n(workspace, type-only — stripped at compile time).\n\nNothing else ships in the bundle.\n\n## Quick start — Tier 1\n\n```tsx\nimport { RealtimePipelineProvider, usePipelineCampaigns } from \"@playlive/react-pipeline\";\n\nfunction App() {\n  return (\n    <RealtimePipelineProvider\n      url=\"wss://pipeline.example.com/socket\"\n      charityType=\"tiltify\"\n      autoConnect\n    >\n      <Overlay />\n    </RealtimePipelineProvider>\n  );\n}\n\nfunction Overlay() {\n  const campaigns = usePipelineCampaigns();\n  return <ul>{campaigns.map((c) => <li key={c.id}>{c.name}</li>)}</ul>;\n}\n```\n\n`RealtimePipelineProvider` mounts a Zustand store + opens (or accepts)\na `PipelineConnection`. Children call any Tier 1 / Tier 2 hook to\nsubscribe.\n\n## Subpath exports\n\n| Subpath                              | Description                                                                       |\n| ------------------------------------ | --------------------------------------------------------------------------------- |\n| `@playlive/react-pipeline`           | Default barrel — Tier 1 + Tier 2 (provider + 21 typed slice hooks).               |\n| `@playlive/react-pipeline/provider`  | Tier 3 — `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, add-pipeline\\*. |\n| `@playlive/react-pipeline/fusion`    | Cross-package composition hooks (REST baseline + WS delta) — `useCurrentBlockRaised`, `useLiveSchedule`, `useCurrentScheduleItem`, `useLinkedTeamCampaign`, `useDonorSpotlight`, plus the WS-store-first / HTTP-fallback hybrid slice hooks (`useHybridRewards`, `useHybridTargets`, `useHybridMilestones`, `useHybridPolls`, `useHybridCause`, `useHybridFundraisingEvent`, `useHybridFundraisingEventSupportingCampaigns`, `useHybridAuctionHouses`) + the shared `useLoadingTimeout` primitive. |\n| `@playlive/react-pipeline/legacy`    | Soft-deprecated alias bundle (`useUDPStore`, `createUDPStore`, `UDPStore`, etc.). |\n\nEach subpath ships an ESM bundle, a Bun source condition, and `.d.ts`\ndeclarations. Frontend-eligible bundles are not minified (PRD §6.1)\nso stack traces stay readable in production.\n\n## API reference\n\n### Tier 1 — universal primitive\n\n| Export                          | Signature                                                            |\n| ------------------------------- | -------------------------------------------------------------------- |\n| `RealtimePipelineProvider`      | `(props: RealtimePipelineProviderProps) => JSX.Element`              |\n| `usePipelineValue`              | `<T>(selector: (s: PipelineStore) => T) => T`                        |\n\n`usePipelineValue` is single-arg by design. Zustand v5 dropped the\nthird `equalityFn` parameter on `useStore` to keep the package\nfree of the `use-sync-external-store` shim peer. For shallow / custom\nequality, wrap your selector with `useShallow` from\n`zustand/shallow` (zero-extra-dep):\n\n```tsx\nimport { useShallow } from \"zustand/shallow\";\nimport { usePipelineValue } from \"@playlive/react-pipeline\";\n\nconst { connected, connecting } = usePipelineValue(\n  useShallow((s) => ({ connected: s.connected, connecting: s.connecting })),\n);\n```\n\n### Tier 2 — typed per-slice hooks\n\nAll 22 hooks are stable wrappers over `usePipelineValue` with a typed\nselector + memo-stable empty-array fallbacks (no infinite-render\nchurn from fresh `[]` allocations — Decision §3 in CHANGELOG).\n\n| Hook                              | Returns                                                             |\n| --------------------------------- | ------------------------------------------------------------------- |\n| `usePipelineCampaigns()`          | `TiltifyCampaign[]`                                                 |\n| `usePipelineTeamCampaigns()`      | `TiltifyTeamCampaign[]`                                             |\n| `usePipelineFundraisingEvents()`  | `TiltifyFundraisingEvent[]`                                         |\n| `usePipelineCauses()`             | `TiltifyCause[]`                                                    |\n| `usePipelineDonations()`          | `Record<string, TiltifyDonationWithTestFlag[]>`                     |\n| `usePipelineDonations(id)`        | `TiltifyDonationWithTestFlag[]` (memo-stable `[]` if id unknown)    |\n| `usePipelineDonationSum(id, opts?)` | `PipelineDonationSumResult` — live per-currency sum over `[start, end)` of the WS donation firehose. Pair with the REST `/schedules/campaigns/{id}/raised` `asOf` field to replace polling with a `baseline + WS-delta` view. |\n| `usePipelineDonationTrains(id, withStatus?)` | `DonationTrain[]`                                        |\n| `usePipelineSubathonTimer(id, withStatus?)`  | `SubathonTimer[]`                                        |\n| `usePipelineDonorSpotlight(id)`   | `ComputedSpotlight \\| null`                                         |\n| `usePipelinePolls(id)`            | `TiltifyPoll[]`                                                     |\n| `usePipelineRewards(id)`          | `TiltifyReward[]`                                                   |\n| `usePipelineTargets(id)`          | `TiltifyTarget[]`                                                   |\n| `usePipelineMilestones(id)`       | `TiltifyMilestone[]`                                                |\n| `usePipelineLeaderboardEntries(id)` | `TiltifyLeaderboardEntry[]`                                       |\n| `usePipelineAuctionHouses()`      | `AuctionHouse[]`                                                    |\n| `usePipelineTwitchChat()`         | `TwitchChatMessage[]`                                               |\n| `usePipelineConnectionState()`    | `PipelineConnectionState` (10 split selectors — no false re-renders)|\n| `usePipelineConnectionActions()`  | `PipelineConnectionActions` (stable `connect/disconnect/refresh/…`) |\n| `usePipelineSubscriptions()`      | `Record<string, TiltifyWebhookSubscription>`                        |\n| `usePipelineTiltifySettings()`    | `TiltifyWebSocketSettings`                                          |\n| `usePipelineLastRefreshResponse()`| `RefreshResponseMessage \\| null`                                    |\n| `usePipelineDonorSpotlightSettings()` | `Record<string, DonorSpotlightSettings>`                        |\n\n### Tier 3 — kitchen-sink provider (`/provider`)\n\n| Export                              | Notes                                                                                  |\n| ----------------------------------- | -------------------------------------------------------------------------------------- |\n| `UnifiedDataPipelineProvider`       | Superset of `RealtimePipelineProvider`; adds legacy `reloadOnErrorReconnect` + `debug`.|\n| `useUnifiedDataPipeline()`          | One-call kitchen-sink hook. Returns every slice + actions + legacy field aliases.      |\n| `useAddPipelineCampaignIDs(ids)`    | Effect hook — registers campaign ids with the store on mount.                          |\n| `useAddPipelineCampaigns(items)`    | Effect hook — registers full campaign records on mount.                                |\n| `useAddPipelineFundraisingEventIDs(ids)` | Effect hook — registers fundraising-event ids on mount.                           |\n| `useCommonContextHooks(…)`          | URL / mode / charity-type sync to the store. Same shape as the legacy hook.            |\n| `useIdentify()` / `useIdentify(…11 legacy args)` | Zero-arg returns the store's bound `identify` action. Positional arg form is preserved as an `@deprecated` overload — args are ignored, warns once. |\n\n### Fusion — hybrid slice hooks (`/fusion`)\n\nOverlay-parity `usePipeline*` hooks lifted from\n`playlive-overlay-vite/src/hooks/pipeline/` so every overlay that\nwants the same WS-store-first / HTTP-fallback shape can drop the\nlocal copy. Every hook accepts a `mode: \"ws\" | \"http\"` param that\npicks between:\n\n- `\"ws\"` — read the Tier-2 store selector for the slice, latch a\n  local `hasLoadedX` boolean on the first slice arrival (or on the\n  server-side \"feature disabled\" signal), and fire\n  `updateTiltifySettings({ <sliceKey> })` whenever the local\n  `xEnabled` toggle flips so the pipeline dynamically un/subscribes.\n- `\"http\"` — delegate to the matching `@playlive/react-query` REST\n  hook (`useRewards`, `useTargets`, …) with the WS store slice\n  ignored.\n\nAll hybrid hooks share `useLoadingTimeout` — a 15 s safety-net that\nforce-completes the WS branch when the store never delivers.\n\n| Hook                                                     | Returns                                                                        |\n| -------------------------------------------------------- | ------------------------------------------------------------------------------ |\n| `useHybridRewards(params)`                               | `{ currentRewards, setHasLoadedRewards, hasLoadedRewards, isLoadingRewards, isLoading, setRewardsEnabled, rewardsEnabled }` |\n| `useHybridTargets(params)`                               | `{ currentTargets, setHasLoadedTargets, hasLoadedTargets, isLoading, targetsEnabled, isLoadingTargets, setTargetsEnabled }` |\n| `useHybridMilestones(params)`                            | `{ isLoadingMilestones, currentMilestones, milestonesEnabled, setMilestonesEnabled }` |\n| `useHybridPolls(params)`                                 | `{ isLoadingPolls, currentPolls, pollsEnabled, setPollsEnabled }`              |\n| `useHybridCause({ mode, initialCampaign })`              | `{ cause, isLoading }` — single-slot semantics (uses `getFirstCause`).         |\n| `useHybridFundraisingEvent({ mode, initialCampaign })`   | `{ fundraisingEvent, isLoading }` — single-slot semantics.                     |\n| `useHybridFundraisingEventSupportingCampaigns(params)`   | `{ supportingCampaigns, isLoading, setSupportingCampaignsEnabled, supportingCampaignsEnabled, isLoadingSupportingCampaigns, hasLoadedSupportingCampaigns }` |\n| `useHybridAuctionHouses({ mode, enabled, campaignID })`  | `{ auctionHouses, auctionHouseTotal, auctionHousesEnabled, setAuctionHousesEnabled }` — WS-only (no REST fallback yet). |\n| `useLoadingTimeout(params)`                              | `void` — safety-net timer primitive shared by every hook above.                |\n\nReturn shapes are preserved verbatim from the overlay so overlay\ncall-sites can eventually collapse to a re-export.\n\n### Fusion — baseline + WS delta hooks (`/fusion`)\n\n| Hook                                        | Returns                                                                     |\n| ------------------------------------------- | --------------------------------------------------------------------------- |\n| `useCurrentBlockRaised(campaignID, start, end, opts?)` | `{ raised, data, isLoading, isError, refetch }` — REST baseline + WS delta. `opts.demoMode: true` short-circuits to `DemoProvider.getDemoScheduleBlockRaised` / `getDemoBlockRaised` fallback. |\n| `useLiveSchedule(params, opts?)`            | `{ schedule, isLoading, isError, refetch }` — WS-refetch on transition. `opts.demoMode: true` (+ optional `demoIntervalMs`) short-circuits to `DemoProvider.getDemoSchedule` / `buildDemoSchedule` fallback with a live tick timer.     |\n| `useCurrentScheduleItem(schedule, opts?)`   | `{ currentItem, nextItem }` — pure-client rollover projection.              |\n| `useLinkedTeamCampaign(params, opts?)`      | `{ linkedTeamCampaign, … }` — WS-store first, HTTP fallback.                |\n| `useDonorSpotlight(params, opts?)`          | `{ spotlight, isLoading, refetch }` — WS store preferred + REST snapshot.   |\n\n### Legacy aliases (`/legacy`)\n\nSoft-deprecated. Identical behavior to the canonical exports —\nrename the import specifier once and the rest of your code base\nkeeps working unchanged:\n\n| Legacy name            | Canonical replacement                                          |\n| ---------------------- | -------------------------------------------------------------- |\n| `useUDPStore`          | `usePipelineValue` (Tier 1) / typed slice hooks (Tier 2)       |\n| `useUDPStoreApi`       | (use a typed slice hook; or reach for the raw store API)       |\n| `createUDPStore`       | `createPipelineStore` from `@playlive/realtime-pipeline/store` |\n| `UDPStore` (type)      | `PipelineStore` from `@playlive/realtime-pipeline/store`       |\n| `UDPStoreApi` (type)   | `PipelineStoreApi` from `@playlive/realtime-pipeline/store`    |\n| `CharityTypes`         | re-exported from `@playlive/realtime-pipeline/protocol`         |\n\nPlus every Tier 1+2+3 export under its canonical name.\n\n## Migration from `playlive-overlay-data-layer`\n\nOne-shot sed-friendly rewrite of every import specifier (no body\nedits needed for the happy path):\n\n```bash\n# Tier 3 — kitchen-sink (drop-in replacement)\nrg -l '@playlive/overlay-data-layer/websocket' . \\\n  | xargs sed -i '' \\\n    -e 's|@playlive/overlay-data-layer/websocket|@playlive/react-pipeline/provider|g'\n```\n\nFor the soft-deprecated names (`useUDPStore`, `createUDPStore`, …)\nsubstitute `/legacy` instead of `/provider` to opt into the alias\nbundle, then migrate at your leisure (PRD §10 — one-release window).\n\nSee the phase-8 row in the root [MIGRATION.md](../../MIGRATION.md) for\nthe full per-symbol mapping plus call-site walkthroughs.\n\n## Upstream spec\n\nNo external API surface — internal workspace package. All transport\nruns through `@playlive/realtime-pipeline/connection`.\n\n## Twitch Extension URL disclosure\n\n`KNOWN_URLS` enumerates every absolute URL or host this package can\nfetch. **It is empty.** This package opens WebSocket connections only\nto URLs the consumer passes to `<RealtimePipelineProvider url={…}>`;\nthose URLs must be added to your Extension submission's URL\ndisclosure list, not this one.\n\n```ts\nimport { KNOWN_URLS } from \"@playlive/react-pipeline\";\nconsole.log(KNOWN_URLS); // []\n```\n\nSee [`docs/twitch-extension-checklist.md`](../../docs/twitch-extension-checklist.md).\n\n## Examples\n\nLive overlay apps consuming this provider land under `apps/*`\n(phase 10). Until then, the Tier 1 quick-start above is the minimum\nviable wiring.\n\n## Contributing\n\nSee [CONTRIBUTING.md](../../CONTRIBUTING.md). To add new Tier-2\nhooks, mirror an existing slice hook in `src/hooks/index.ts` and\nadd a `it.each` entry in `tests/unit/index.test.ts`.\n\n## License\n\nMIT — see [LICENSE](../../LICENSE). Distributed via Play Live\nCodeArtifact (PRD §6).\n","readmeFilename":"README.md","dist":{"tarball":"https://packages.playlive.experience.stjude.org/@playlive/react-pipeline/-/react-pipeline-0.4.8.tgz","shasum":"d243d56054ab95e37e5e442a787dd113b3c59314","integrity":"sha512-AR/AV8TRf1K6UA0iQ6TvMdvMnHmW2AWc0ply0oraDhEi4ydWgE2N8GvpBkpUG7R24bwaVQ4MyG3/Mcq2QkVBVA=="}},"0.4.9":{"name":"@playlive/react-pipeline","version":"0.4.9","description":"React bindings for @playlive/realtime-pipeline — three usage tiers (single selector / typed per-slice / kitchen-sink). Twitch-Extension safe; no react-use-websocket-lite.","type":"module","sideEffects":false,"main":"./index.js","types":"./index.d.ts","exports":{".":{"import":"./index.js","types":"./index.d.ts"},"./provider":{"import":"./provider/index.js","types":"./provider/index.d.ts"},"./legacy":{"import":"./legacy/index.js","types":"./legacy/index.d.ts"},"./fusion":{"import":"./fusion/index.js","types":"./fusion/index.d.ts"}},"dependencies":{},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","zustand":"^5.0.0","@playlive/realtime-pipeline":"^0.2.7","@playlive/tiltify-core":"^0.4.11","@tanstack/react-query":"^5.0.0","@playlive/react-query":"^0.3.4","@playlive/fundraiser-data":"^0.4.2"},"peerDependenciesMeta":{"@tanstack/react-query":{"optional":true},"@playlive/react-query":{"optional":true},"@playlive/fundraiser-data":{"optional":true}},"playlive":{"target":"browser","frontendEligible":true,"coverageFloor":85},"publishConfig":{"access":"restricted","registry":"https://playlive-767397689694.d.codeartifact.us-east-1.amazonaws.com/npm/playlive/"},"integrity":"sha512-dHR/z29BDxnameVYSi3sMV4rJewjxIrez633h3JaYIzdcGC7OdTuldnypPbBjm/BE5ZabZimcXs8lp91nWQEjA==","shasum":"a7915a00fd19e092ac695bc863407e91032e8fb0","readme":"# @playlive/react-pipeline\n\nReact bindings for [`@playlive/realtime-pipeline`](../realtime-pipeline/).\nDrop-in replacement for `playlive-overlay-data-layer/src/websocket/*`\nwith **zero** dependency on `react-use-websocket-lite` — built directly\non Zustand v5's `useStore` + `useSyncExternalStore`.\n\n![Coverage](./coverage-badge.svg)\n\n## Three tiers — take only what you need\n\nPick the surface that matches how much of the legacy\n`UnifiedDataPipelineProvider` API you actually want. Each tier is\nstrictly larger than the last and lives at its own subpath so unused\ncode is tree-shaken out.\n\n| Tier | Subpath                            | Surface                                                                         | Use when                                                              |\n| ---- | ---------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| 1    | `@playlive/react-pipeline`         | `RealtimePipelineProvider` + `usePipelineValue` (universal selector)            | Greenfield app; you want one selector primitive and nothing else.     |\n| 2    | `@playlive/react-pipeline`         | Tier 1 **plus** 22 typed per-slice hooks (`usePipelineDonations(id)`, …)        | You want stable, typed one-liners for the common reads.               |\n| 3    | `@playlive/react-pipeline/provider`| Tier 2 **plus** `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, etc.   | Porting an existing overlay-data-layer consumer with minimal churn.   |\n| —    | `@playlive/react-pipeline/legacy`  | Tier 3 **plus** `useUDPStore` / `useUDPStoreApi` / `createUDPStore` aliases     | Soft-deprecated shim for the legacy symbol names (one-release window).|\n\n## Install\n\n```bash\nbun add @playlive/react-pipeline\nbun add -d react react-dom zustand\n```\n\nPeer dependencies (jose-style — consumer brings their own):\n`react@^19`, `react-dom@^19`, `zustand@^5`,\n`@playlive/realtime-pipeline` (workspace), `@playlive/tiltify-core`\n(workspace, type-only — stripped at compile time).\n\nNothing else ships in the bundle.\n\n## Quick start — Tier 1\n\n```tsx\nimport { RealtimePipelineProvider, usePipelineCampaigns } from \"@playlive/react-pipeline\";\n\nfunction App() {\n  return (\n    <RealtimePipelineProvider\n      url=\"wss://pipeline.example.com/socket\"\n      charityType=\"tiltify\"\n      autoConnect\n    >\n      <Overlay />\n    </RealtimePipelineProvider>\n  );\n}\n\nfunction Overlay() {\n  const campaigns = usePipelineCampaigns();\n  return <ul>{campaigns.map((c) => <li key={c.id}>{c.name}</li>)}</ul>;\n}\n```\n\n`RealtimePipelineProvider` mounts a Zustand store + opens (or accepts)\na `PipelineConnection`. Children call any Tier 1 / Tier 2 hook to\nsubscribe.\n\n## Subpath exports\n\n| Subpath                              | Description                                                                       |\n| ------------------------------------ | --------------------------------------------------------------------------------- |\n| `@playlive/react-pipeline`           | Default barrel — Tier 1 + Tier 2 (provider + 21 typed slice hooks).               |\n| `@playlive/react-pipeline/provider`  | Tier 3 — `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, add-pipeline\\*. |\n| `@playlive/react-pipeline/fusion`    | Cross-package composition hooks (REST baseline + WS delta) — `useCurrentBlockRaised`, `useLiveSchedule`, `useCurrentScheduleItem`, `useLinkedTeamCampaign`, `useDonorSpotlight`, plus the WS-store-first / HTTP-fallback hybrid slice hooks (`useHybridRewards`, `useHybridTargets`, `useHybridMilestones`, `useHybridPolls`, `useHybridCause`, `useHybridFundraisingEvent`, `useHybridFundraisingEventSupportingCampaigns`, `useHybridAuctionHouses`) + the shared `useLoadingTimeout` primitive. |\n| `@playlive/react-pipeline/legacy`    | Soft-deprecated alias bundle (`useUDPStore`, `createUDPStore`, `UDPStore`, etc.). |\n\nEach subpath ships an ESM bundle, a Bun source condition, and `.d.ts`\ndeclarations. Frontend-eligible bundles are not minified (PRD §6.1)\nso stack traces stay readable in production.\n\n## API reference\n\n### Tier 1 — universal primitive\n\n| Export                          | Signature                                                            |\n| ------------------------------- | -------------------------------------------------------------------- |\n| `RealtimePipelineProvider`      | `(props: RealtimePipelineProviderProps) => JSX.Element`              |\n| `usePipelineValue`              | `<T>(selector: (s: PipelineStore) => T) => T`                        |\n\n`usePipelineValue` is single-arg by design. Zustand v5 dropped the\nthird `equalityFn` parameter on `useStore` to keep the package\nfree of the `use-sync-external-store` shim peer. For shallow / custom\nequality, wrap your selector with `useShallow` from\n`zustand/shallow` (zero-extra-dep):\n\n```tsx\nimport { useShallow } from \"zustand/shallow\";\nimport { usePipelineValue } from \"@playlive/react-pipeline\";\n\nconst { connected, connecting } = usePipelineValue(\n  useShallow((s) => ({ connected: s.connected, connecting: s.connecting })),\n);\n```\n\n### Tier 2 — typed per-slice hooks\n\nAll 22 hooks are stable wrappers over `usePipelineValue` with a typed\nselector + memo-stable empty-array fallbacks (no infinite-render\nchurn from fresh `[]` allocations — Decision §3 in CHANGELOG).\n\n| Hook                              | Returns                                                             |\n| --------------------------------- | ------------------------------------------------------------------- |\n| `usePipelineCampaigns()`          | `TiltifyCampaign[]`                                                 |\n| `usePipelineTeamCampaigns()`      | `TiltifyTeamCampaign[]`                                             |\n| `usePipelineFundraisingEvents()`  | `TiltifyFundraisingEvent[]`                                         |\n| `usePipelineCauses()`             | `TiltifyCause[]`                                                    |\n| `usePipelineDonations()`          | `Record<string, TiltifyDonationWithTestFlag[]>`                     |\n| `usePipelineDonations(id)`        | `TiltifyDonationWithTestFlag[]` (memo-stable `[]` if id unknown)    |\n| `usePipelineDonationSum(id, opts?)` | `PipelineDonationSumResult` — live per-currency sum over `[start, end)` of the WS donation firehose. Pair with the REST `/schedules/campaigns/{id}/raised` `asOf` field to replace polling with a `baseline + WS-delta` view. |\n| `usePipelineDonationTrains(id, withStatus?)` | `DonationTrain[]`                                        |\n| `usePipelineSubathonTimer(id, withStatus?)`  | `SubathonTimer[]`                                        |\n| `usePipelineDonorSpotlight(id)`   | `ComputedSpotlight \\| null`                                         |\n| `usePipelinePolls(id)`            | `TiltifyPoll[]`                                                     |\n| `usePipelineRewards(id)`          | `TiltifyReward[]`                                                   |\n| `usePipelineTargets(id)`          | `TiltifyTarget[]`                                                   |\n| `usePipelineMilestones(id)`       | `TiltifyMilestone[]`                                                |\n| `usePipelineLeaderboardEntries(id)` | `TiltifyLeaderboardEntry[]`                                       |\n| `usePipelineAuctionHouses()`      | `AuctionHouse[]`                                                    |\n| `usePipelineTwitchChat()`         | `TwitchChatMessage[]`                                               |\n| `usePipelineConnectionState()`    | `PipelineConnectionState` (10 split selectors — no false re-renders)|\n| `usePipelineConnectionActions()`  | `PipelineConnectionActions` (stable `connect/disconnect/refresh/…`) |\n| `usePipelineSubscriptions()`      | `Record<string, TiltifyWebhookSubscription>`                        |\n| `usePipelineTiltifySettings()`    | `TiltifyWebSocketSettings`                                          |\n| `usePipelineLastRefreshResponse()`| `RefreshResponseMessage \\| null`                                    |\n| `usePipelineDonorSpotlightSettings()` | `Record<string, DonorSpotlightSettings>`                        |\n\n### Tier 3 — kitchen-sink provider (`/provider`)\n\n| Export                              | Notes                                                                                  |\n| ----------------------------------- | -------------------------------------------------------------------------------------- |\n| `UnifiedDataPipelineProvider`       | Superset of `RealtimePipelineProvider`; adds legacy `reloadOnErrorReconnect` + `debug`.|\n| `useUnifiedDataPipeline()`          | One-call kitchen-sink hook. Returns every slice + actions + legacy field aliases.      |\n| `useAddPipelineCampaignIDs(ids)`    | Effect hook — registers campaign ids with the store on mount.                          |\n| `useAddPipelineCampaigns(items)`    | Effect hook — registers full campaign records on mount.                                |\n| `useAddPipelineFundraisingEventIDs(ids)` | Effect hook — registers fundraising-event ids on mount.                           |\n| `useCommonContextHooks(…)`          | URL / mode / charity-type sync to the store. Same shape as the legacy hook.            |\n| `useIdentify()` / `useIdentify(…11 legacy args)` | Zero-arg returns the store's bound `identify` action. Positional arg form is preserved as an `@deprecated` overload — args are ignored, warns once. |\n\n### Fusion — hybrid slice hooks (`/fusion`)\n\nOverlay-parity `usePipeline*` hooks lifted from\n`playlive-overlay-vite/src/hooks/pipeline/` so every overlay that\nwants the same WS-store-first / HTTP-fallback shape can drop the\nlocal copy. Every hook accepts a `mode: \"ws\" | \"http\"` param that\npicks between:\n\n- `\"ws\"` — read the Tier-2 store selector for the slice, latch a\n  local `hasLoadedX` boolean on the first slice arrival (or on the\n  server-side \"feature disabled\" signal), and fire\n  `updateTiltifySettings({ <sliceKey> })` whenever the local\n  `xEnabled` toggle flips so the pipeline dynamically un/subscribes.\n- `\"http\"` — delegate to the matching `@playlive/react-query` REST\n  hook (`useRewards`, `useTargets`, …) with the WS store slice\n  ignored.\n\nAll hybrid hooks share `useLoadingTimeout` — a 15 s safety-net that\nforce-completes the WS branch when the store never delivers.\n\n| Hook                                                     | Returns                                                                        |\n| -------------------------------------------------------- | ------------------------------------------------------------------------------ |\n| `useHybridRewards(params)`                               | `{ currentRewards, setHasLoadedRewards, hasLoadedRewards, isLoadingRewards, isLoading, setRewardsEnabled, rewardsEnabled }` |\n| `useHybridTargets(params)`                               | `{ currentTargets, setHasLoadedTargets, hasLoadedTargets, isLoading, targetsEnabled, isLoadingTargets, setTargetsEnabled }` |\n| `useHybridMilestones(params)`                            | `{ isLoadingMilestones, currentMilestones, milestonesEnabled, setMilestonesEnabled }` |\n| `useHybridPolls(params)`                                 | `{ isLoadingPolls, currentPolls, pollsEnabled, setPollsEnabled }`              |\n| `useHybridCause({ mode, initialCampaign })`              | `{ cause, isLoading }` — single-slot semantics (uses `getFirstCause`).         |\n| `useHybridFundraisingEvent({ mode, initialCampaign })`   | `{ fundraisingEvent, isLoading }` — single-slot semantics.                     |\n| `useHybridFundraisingEventSupportingCampaigns(params)`   | `{ supportingCampaigns, isLoading, setSupportingCampaignsEnabled, supportingCampaignsEnabled, isLoadingSupportingCampaigns, hasLoadedSupportingCampaigns }` |\n| `useHybridAuctionHouses({ mode, enabled, campaignID })`  | `{ auctionHouses, auctionHouseTotal, auctionHousesEnabled, setAuctionHousesEnabled }` — WS-only (no REST fallback yet). |\n| `useLoadingTimeout(params)`                              | `void` — safety-net timer primitive shared by every hook above.                |\n\nReturn shapes are preserved verbatim from the overlay so overlay\ncall-sites can eventually collapse to a re-export.\n\n### Fusion — baseline + WS delta hooks (`/fusion`)\n\n| Hook                                        | Returns                                                                     |\n| ------------------------------------------- | --------------------------------------------------------------------------- |\n| `useCurrentBlockRaised(campaignID, start, end, opts?)` | `{ raised, data, isLoading, isError, refetch }` — REST baseline + WS delta. `opts.demoMode: true` short-circuits to `DemoProvider.getDemoScheduleBlockRaised` / `getDemoBlockRaised` fallback. |\n| `useLiveSchedule(params, opts?)`            | `{ schedule, isLoading, isError, refetch }` — WS-refetch on transition. `opts.demoMode: true` (+ optional `demoIntervalMs`) short-circuits to `DemoProvider.getDemoSchedule` / `buildDemoSchedule` fallback with a live tick timer.     |\n| `useCurrentScheduleItem(schedule, opts?)`   | `{ currentItem, nextItem }` — pure-client rollover projection.              |\n| `useLinkedTeamCampaign(params, opts?)`      | `{ linkedTeamCampaign, … }` — WS-store first, HTTP fallback.                |\n| `useDonorSpotlight(params, opts?)`          | `{ spotlight, isLoading, refetch }` — WS store preferred + REST snapshot.   |\n\n### Legacy aliases (`/legacy`)\n\nSoft-deprecated. Identical behavior to the canonical exports —\nrename the import specifier once and the rest of your code base\nkeeps working unchanged:\n\n| Legacy name            | Canonical replacement                                          |\n| ---------------------- | -------------------------------------------------------------- |\n| `useUDPStore`          | `usePipelineValue` (Tier 1) / typed slice hooks (Tier 2)       |\n| `useUDPStoreApi`       | (use a typed slice hook; or reach for the raw store API)       |\n| `createUDPStore`       | `createPipelineStore` from `@playlive/realtime-pipeline/store` |\n| `UDPStore` (type)      | `PipelineStore` from `@playlive/realtime-pipeline/store`       |\n| `UDPStoreApi` (type)   | `PipelineStoreApi` from `@playlive/realtime-pipeline/store`    |\n| `CharityTypes`         | re-exported from `@playlive/realtime-pipeline/protocol`         |\n\nPlus every Tier 1+2+3 export under its canonical name.\n\n## Migration from `playlive-overlay-data-layer`\n\nOne-shot sed-friendly rewrite of every import specifier (no body\nedits needed for the happy path):\n\n```bash\n# Tier 3 — kitchen-sink (drop-in replacement)\nrg -l '@playlive/overlay-data-layer/websocket' . \\\n  | xargs sed -i '' \\\n    -e 's|@playlive/overlay-data-layer/websocket|@playlive/react-pipeline/provider|g'\n```\n\nFor the soft-deprecated names (`useUDPStore`, `createUDPStore`, …)\nsubstitute `/legacy` instead of `/provider` to opt into the alias\nbundle, then migrate at your leisure (PRD §10 — one-release window).\n\nSee the phase-8 row in the root [MIGRATION.md](../../MIGRATION.md) for\nthe full per-symbol mapping plus call-site walkthroughs.\n\n## Upstream spec\n\nNo external API surface — internal workspace package. All transport\nruns through `@playlive/realtime-pipeline/connection`.\n\n## Twitch Extension URL disclosure\n\n`KNOWN_URLS` enumerates every absolute URL or host this package can\nfetch. **It is empty.** This package opens WebSocket connections only\nto URLs the consumer passes to `<RealtimePipelineProvider url={…}>`;\nthose URLs must be added to your Extension submission's URL\ndisclosure list, not this one.\n\n```ts\nimport { KNOWN_URLS } from \"@playlive/react-pipeline\";\nconsole.log(KNOWN_URLS); // []\n```\n\nSee [`docs/twitch-extension-checklist.md`](../../docs/twitch-extension-checklist.md).\n\n## Examples\n\nLive overlay apps consuming this provider land under `apps/*`\n(phase 10). Until then, the Tier 1 quick-start above is the minimum\nviable wiring.\n\n## Contributing\n\nSee [CONTRIBUTING.md](../../CONTRIBUTING.md). To add new Tier-2\nhooks, mirror an existing slice hook in `src/hooks/index.ts` and\nadd a `it.each` entry in `tests/unit/index.test.ts`.\n\n## License\n\nMIT — see [LICENSE](../../LICENSE). Distributed via Play Live\nCodeArtifact (PRD §6).\n","readmeFilename":"README.md","dist":{"tarball":"https://packages.playlive.experience.stjude.org/@playlive/react-pipeline/-/react-pipeline-0.4.9.tgz","shasum":"a7915a00fd19e092ac695bc863407e91032e8fb0","integrity":"sha512-dHR/z29BDxnameVYSi3sMV4rJewjxIrez633h3JaYIzdcGC7OdTuldnypPbBjm/BE5ZabZimcXs8lp91nWQEjA=="}},"0.4.10":{"name":"@playlive/react-pipeline","version":"0.4.10","description":"React bindings for @playlive/realtime-pipeline — three usage tiers (single selector / typed per-slice / kitchen-sink). Twitch-Extension safe; no react-use-websocket-lite.","type":"module","sideEffects":false,"main":"./index.js","types":"./index.d.ts","exports":{".":{"import":"./index.js","types":"./index.d.ts"},"./provider":{"import":"./provider/index.js","types":"./provider/index.d.ts"},"./legacy":{"import":"./legacy/index.js","types":"./legacy/index.d.ts"},"./fusion":{"import":"./fusion/index.js","types":"./fusion/index.d.ts"}},"dependencies":{},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","zustand":"^5.0.0","@playlive/realtime-pipeline":"^0.2.8","@playlive/tiltify-core":"^0.4.11","@tanstack/react-query":"^5.0.0","@playlive/react-query":"^0.3.4","@playlive/fundraiser-data":"^0.4.2"},"peerDependenciesMeta":{"@tanstack/react-query":{"optional":true},"@playlive/react-query":{"optional":true},"@playlive/fundraiser-data":{"optional":true}},"playlive":{"target":"browser","frontendEligible":true,"coverageFloor":85},"publishConfig":{"access":"restricted","registry":"https://playlive-767397689694.d.codeartifact.us-east-1.amazonaws.com/npm/playlive/"},"integrity":"sha512-wZY81K4MGJNFcVKnQhNofpL7Bj8TeZQE3nZ+j4+vsfMILGQQjol2et85cAmUzbdBOJ7BydyrKVRFfPDSbBOZmw==","shasum":"aa811bca8629a40c36c2d8c2fb144c21bb615148","readme":"# @playlive/react-pipeline\n\nReact bindings for [`@playlive/realtime-pipeline`](../realtime-pipeline/).\nDrop-in replacement for `playlive-overlay-data-layer/src/websocket/*`\nwith **zero** dependency on `react-use-websocket-lite` — built directly\non Zustand v5's `useStore` + `useSyncExternalStore`.\n\n![Coverage](./coverage-badge.svg)\n\n## Three tiers — take only what you need\n\nPick the surface that matches how much of the legacy\n`UnifiedDataPipelineProvider` API you actually want. Each tier is\nstrictly larger than the last and lives at its own subpath so unused\ncode is tree-shaken out.\n\n| Tier | Subpath                            | Surface                                                                         | Use when                                                              |\n| ---- | ---------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| 1    | `@playlive/react-pipeline`         | `RealtimePipelineProvider` + `usePipelineValue` (universal selector)            | Greenfield app; you want one selector primitive and nothing else.     |\n| 2    | `@playlive/react-pipeline`         | Tier 1 **plus** 22 typed per-slice hooks (`usePipelineDonations(id)`, …)        | You want stable, typed one-liners for the common reads.               |\n| 3    | `@playlive/react-pipeline/provider`| Tier 2 **plus** `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, etc.   | Porting an existing overlay-data-layer consumer with minimal churn.   |\n| —    | `@playlive/react-pipeline/legacy`  | Tier 3 **plus** `useUDPStore` / `useUDPStoreApi` / `createUDPStore` aliases     | Soft-deprecated shim for the legacy symbol names (one-release window).|\n\n## Install\n\n```bash\nbun add @playlive/react-pipeline\nbun add -d react react-dom zustand\n```\n\nPeer dependencies (jose-style — consumer brings their own):\n`react@^19`, `react-dom@^19`, `zustand@^5`,\n`@playlive/realtime-pipeline` (workspace), `@playlive/tiltify-core`\n(workspace, type-only — stripped at compile time).\n\nNothing else ships in the bundle.\n\n## Quick start — Tier 1\n\n```tsx\nimport { RealtimePipelineProvider, usePipelineCampaigns } from \"@playlive/react-pipeline\";\n\nfunction App() {\n  return (\n    <RealtimePipelineProvider\n      url=\"wss://pipeline.example.com/socket\"\n      charityType=\"tiltify\"\n      autoConnect\n    >\n      <Overlay />\n    </RealtimePipelineProvider>\n  );\n}\n\nfunction Overlay() {\n  const campaigns = usePipelineCampaigns();\n  return <ul>{campaigns.map((c) => <li key={c.id}>{c.name}</li>)}</ul>;\n}\n```\n\n`RealtimePipelineProvider` mounts a Zustand store + opens (or accepts)\na `PipelineConnection`. Children call any Tier 1 / Tier 2 hook to\nsubscribe.\n\n## Subpath exports\n\n| Subpath                              | Description                                                                       |\n| ------------------------------------ | --------------------------------------------------------------------------------- |\n| `@playlive/react-pipeline`           | Default barrel — Tier 1 + Tier 2 (provider + 21 typed slice hooks).               |\n| `@playlive/react-pipeline/provider`  | Tier 3 — `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, add-pipeline\\*. |\n| `@playlive/react-pipeline/fusion`    | Cross-package composition hooks (REST baseline + WS delta) — `useCurrentBlockRaised`, `useLiveSchedule`, `useCurrentScheduleItem`, `useLinkedTeamCampaign`, `useDonorSpotlight`, plus the WS-store-first / HTTP-fallback hybrid slice hooks (`useHybridRewards`, `useHybridTargets`, `useHybridMilestones`, `useHybridPolls`, `useHybridCause`, `useHybridFundraisingEvent`, `useHybridFundraisingEventSupportingCampaigns`, `useHybridAuctionHouses`) + the shared `useLoadingTimeout` primitive. |\n| `@playlive/react-pipeline/legacy`    | Soft-deprecated alias bundle (`useUDPStore`, `createUDPStore`, `UDPStore`, etc.). |\n\nEach subpath ships an ESM bundle, a Bun source condition, and `.d.ts`\ndeclarations. Frontend-eligible bundles are not minified (PRD §6.1)\nso stack traces stay readable in production.\n\n## API reference\n\n### Tier 1 — universal primitive\n\n| Export                          | Signature                                                            |\n| ------------------------------- | -------------------------------------------------------------------- |\n| `RealtimePipelineProvider`      | `(props: RealtimePipelineProviderProps) => JSX.Element`              |\n| `usePipelineValue`              | `<T>(selector: (s: PipelineStore) => T) => T`                        |\n\n`usePipelineValue` is single-arg by design. Zustand v5 dropped the\nthird `equalityFn` parameter on `useStore` to keep the package\nfree of the `use-sync-external-store` shim peer. For shallow / custom\nequality, wrap your selector with `useShallow` from\n`zustand/shallow` (zero-extra-dep):\n\n```tsx\nimport { useShallow } from \"zustand/shallow\";\nimport { usePipelineValue } from \"@playlive/react-pipeline\";\n\nconst { connected, connecting } = usePipelineValue(\n  useShallow((s) => ({ connected: s.connected, connecting: s.connecting })),\n);\n```\n\n### Tier 2 — typed per-slice hooks\n\nAll 22 hooks are stable wrappers over `usePipelineValue` with a typed\nselector + memo-stable empty-array fallbacks (no infinite-render\nchurn from fresh `[]` allocations — Decision §3 in CHANGELOG).\n\n| Hook                              | Returns                                                             |\n| --------------------------------- | ------------------------------------------------------------------- |\n| `usePipelineCampaigns()`          | `TiltifyCampaign[]`                                                 |\n| `usePipelineTeamCampaigns()`      | `TiltifyTeamCampaign[]`                                             |\n| `usePipelineFundraisingEvents()`  | `TiltifyFundraisingEvent[]`                                         |\n| `usePipelineCauses()`             | `TiltifyCause[]`                                                    |\n| `usePipelineDonations()`          | `Record<string, TiltifyDonationWithTestFlag[]>`                     |\n| `usePipelineDonations(id)`        | `TiltifyDonationWithTestFlag[]` (memo-stable `[]` if id unknown)    |\n| `usePipelineDonationSum(id, opts?)` | `PipelineDonationSumResult` — live per-currency sum over `[start, end)` of the WS donation firehose. Pair with the REST `/schedules/campaigns/{id}/raised` `asOf` field to replace polling with a `baseline + WS-delta` view. |\n| `usePipelineDonationTrains(id, withStatus?)` | `DonationTrain[]`                                        |\n| `usePipelineSubathonTimer(id, withStatus?)`  | `SubathonTimer[]`                                        |\n| `usePipelineDonorSpotlight(id)`   | `ComputedSpotlight \\| null`                                         |\n| `usePipelinePolls(id)`            | `TiltifyPoll[]`                                                     |\n| `usePipelineRewards(id)`          | `TiltifyReward[]`                                                   |\n| `usePipelineTargets(id)`          | `TiltifyTarget[]`                                                   |\n| `usePipelineMilestones(id)`       | `TiltifyMilestone[]`                                                |\n| `usePipelineLeaderboardEntries(id)` | `TiltifyLeaderboardEntry[]`                                       |\n| `usePipelineAuctionHouses()`      | `AuctionHouse[]`                                                    |\n| `usePipelineTwitchChat()`         | `TwitchChatMessage[]`                                               |\n| `usePipelineConnectionState()`    | `PipelineConnectionState` (10 split selectors — no false re-renders)|\n| `usePipelineConnectionActions()`  | `PipelineConnectionActions` (stable `connect/disconnect/refresh/…`) |\n| `usePipelineSubscriptions()`      | `Record<string, TiltifyWebhookSubscription>`                        |\n| `usePipelineTiltifySettings()`    | `TiltifyWebSocketSettings`                                          |\n| `usePipelineLastRefreshResponse()`| `RefreshResponseMessage \\| null`                                    |\n| `usePipelineDonorSpotlightSettings()` | `Record<string, DonorSpotlightSettings>`                        |\n\n### Tier 3 — kitchen-sink provider (`/provider`)\n\n| Export                              | Notes                                                                                  |\n| ----------------------------------- | -------------------------------------------------------------------------------------- |\n| `UnifiedDataPipelineProvider`       | Superset of `RealtimePipelineProvider`; adds legacy `reloadOnErrorReconnect` + `debug`.|\n| `useUnifiedDataPipeline()`          | One-call kitchen-sink hook. Returns every slice + actions + legacy field aliases.      |\n| `useAddPipelineCampaignIDs(ids)`    | Effect hook — registers campaign ids with the store on mount.                          |\n| `useAddPipelineCampaigns(items)`    | Effect hook — registers full campaign records on mount.                                |\n| `useAddPipelineFundraisingEventIDs(ids)` | Effect hook — registers fundraising-event ids on mount.                           |\n| `useCommonContextHooks(…)`          | URL / mode / charity-type sync to the store. Same shape as the legacy hook.            |\n| `useIdentify()` / `useIdentify(…11 legacy args)` | Zero-arg returns the store's bound `identify` action. Positional arg form is preserved as an `@deprecated` overload — args are ignored, warns once. |\n\n### Fusion — hybrid slice hooks (`/fusion`)\n\nOverlay-parity `usePipeline*` hooks lifted from\n`playlive-overlay-vite/src/hooks/pipeline/` so every overlay that\nwants the same WS-store-first / HTTP-fallback shape can drop the\nlocal copy. Every hook accepts a `mode: \"ws\" | \"http\"` param that\npicks between:\n\n- `\"ws\"` — read the Tier-2 store selector for the slice, latch a\n  local `hasLoadedX` boolean on the first slice arrival (or on the\n  server-side \"feature disabled\" signal), and fire\n  `updateTiltifySettings({ <sliceKey> })` whenever the local\n  `xEnabled` toggle flips so the pipeline dynamically un/subscribes.\n- `\"http\"` — delegate to the matching `@playlive/react-query` REST\n  hook (`useRewards`, `useTargets`, …) with the WS store slice\n  ignored.\n\nAll hybrid hooks share `useLoadingTimeout` — a 15 s safety-net that\nforce-completes the WS branch when the store never delivers.\n\n| Hook                                                     | Returns                                                                        |\n| -------------------------------------------------------- | ------------------------------------------------------------------------------ |\n| `useHybridRewards(params)`                               | `{ currentRewards, setHasLoadedRewards, hasLoadedRewards, isLoadingRewards, isLoading, setRewardsEnabled, rewardsEnabled }` |\n| `useHybridTargets(params)`                               | `{ currentTargets, setHasLoadedTargets, hasLoadedTargets, isLoading, targetsEnabled, isLoadingTargets, setTargetsEnabled }` |\n| `useHybridMilestones(params)`                            | `{ isLoadingMilestones, currentMilestones, milestonesEnabled, setMilestonesEnabled }` |\n| `useHybridPolls(params)`                                 | `{ isLoadingPolls, currentPolls, pollsEnabled, setPollsEnabled }`              |\n| `useHybridCause({ mode, initialCampaign })`              | `{ cause, isLoading }` — single-slot semantics (uses `getFirstCause`).         |\n| `useHybridFundraisingEvent({ mode, initialCampaign })`   | `{ fundraisingEvent, isLoading }` — single-slot semantics.                     |\n| `useHybridFundraisingEventSupportingCampaigns(params)`   | `{ supportingCampaigns, isLoading, setSupportingCampaignsEnabled, supportingCampaignsEnabled, isLoadingSupportingCampaigns, hasLoadedSupportingCampaigns }` |\n| `useHybridAuctionHouses({ mode, enabled, campaignID })`  | `{ auctionHouses, auctionHouseTotal, auctionHousesEnabled, setAuctionHousesEnabled }` — WS-only (no REST fallback yet). |\n| `useLoadingTimeout(params)`                              | `void` — safety-net timer primitive shared by every hook above.                |\n\nReturn shapes are preserved verbatim from the overlay so overlay\ncall-sites can eventually collapse to a re-export.\n\n### Fusion — baseline + WS delta hooks (`/fusion`)\n\n| Hook                                        | Returns                                                                     |\n| ------------------------------------------- | --------------------------------------------------------------------------- |\n| `useCurrentBlockRaised(campaignID, start, end, opts?)` | `{ raised, data, isLoading, isError, refetch }` — REST baseline + WS delta. `opts.demoMode: true` short-circuits to `DemoProvider.getDemoScheduleBlockRaised` / `getDemoBlockRaised` fallback. |\n| `useLiveSchedule(params, opts?)`            | `{ schedule, isLoading, isError, refetch }` — WS-refetch on transition. `opts.demoMode: true` (+ optional `demoIntervalMs`) short-circuits to `DemoProvider.getDemoSchedule` / `buildDemoSchedule` fallback with a live tick timer.     |\n| `useCurrentScheduleItem(schedule, opts?)`   | `{ currentItem, nextItem }` — pure-client rollover projection.              |\n| `useLinkedTeamCampaign(params, opts?)`      | `{ linkedTeamCampaign, … }` — WS-store first, HTTP fallback.                |\n| `useDonorSpotlight(params, opts?)`          | `{ spotlight, isLoading, refetch }` — WS store preferred + REST snapshot.   |\n\n### Legacy aliases (`/legacy`)\n\nSoft-deprecated. Identical behavior to the canonical exports —\nrename the import specifier once and the rest of your code base\nkeeps working unchanged:\n\n| Legacy name            | Canonical replacement                                          |\n| ---------------------- | -------------------------------------------------------------- |\n| `useUDPStore`          | `usePipelineValue` (Tier 1) / typed slice hooks (Tier 2)       |\n| `useUDPStoreApi`       | (use a typed slice hook; or reach for the raw store API)       |\n| `createUDPStore`       | `createPipelineStore` from `@playlive/realtime-pipeline/store` |\n| `UDPStore` (type)      | `PipelineStore` from `@playlive/realtime-pipeline/store`       |\n| `UDPStoreApi` (type)   | `PipelineStoreApi` from `@playlive/realtime-pipeline/store`    |\n| `CharityTypes`         | re-exported from `@playlive/realtime-pipeline/protocol`         |\n\nPlus every Tier 1+2+3 export under its canonical name.\n\n## Migration from `playlive-overlay-data-layer`\n\nOne-shot sed-friendly rewrite of every import specifier (no body\nedits needed for the happy path):\n\n```bash\n# Tier 3 — kitchen-sink (drop-in replacement)\nrg -l '@playlive/overlay-data-layer/websocket' . \\\n  | xargs sed -i '' \\\n    -e 's|@playlive/overlay-data-layer/websocket|@playlive/react-pipeline/provider|g'\n```\n\nFor the soft-deprecated names (`useUDPStore`, `createUDPStore`, …)\nsubstitute `/legacy` instead of `/provider` to opt into the alias\nbundle, then migrate at your leisure (PRD §10 — one-release window).\n\nSee the phase-8 row in the root [MIGRATION.md](../../MIGRATION.md) for\nthe full per-symbol mapping plus call-site walkthroughs.\n\n## Upstream spec\n\nNo external API surface — internal workspace package. All transport\nruns through `@playlive/realtime-pipeline/connection`.\n\n## Twitch Extension URL disclosure\n\n`KNOWN_URLS` enumerates every absolute URL or host this package can\nfetch. **It is empty.** This package opens WebSocket connections only\nto URLs the consumer passes to `<RealtimePipelineProvider url={…}>`;\nthose URLs must be added to your Extension submission's URL\ndisclosure list, not this one.\n\n```ts\nimport { KNOWN_URLS } from \"@playlive/react-pipeline\";\nconsole.log(KNOWN_URLS); // []\n```\n\nSee [`docs/twitch-extension-checklist.md`](../../docs/twitch-extension-checklist.md).\n\n## Examples\n\nLive overlay apps consuming this provider land under `apps/*`\n(phase 10). Until then, the Tier 1 quick-start above is the minimum\nviable wiring.\n\n## Contributing\n\nSee [CONTRIBUTING.md](../../CONTRIBUTING.md). To add new Tier-2\nhooks, mirror an existing slice hook in `src/hooks/index.ts` and\nadd a `it.each` entry in `tests/unit/index.test.ts`.\n\n## License\n\nMIT — see [LICENSE](../../LICENSE). Distributed via Play Live\nCodeArtifact (PRD §6).\n","readmeFilename":"README.md","dist":{"tarball":"https://packages.playlive.experience.stjude.org/@playlive/react-pipeline/-/react-pipeline-0.4.10.tgz","shasum":"aa811bca8629a40c36c2d8c2fb144c21bb615148","integrity":"sha512-wZY81K4MGJNFcVKnQhNofpL7Bj8TeZQE3nZ+j4+vsfMILGQQjol2et85cAmUzbdBOJ7BydyrKVRFfPDSbBOZmw=="}},"0.4.11":{"name":"@playlive/react-pipeline","version":"0.4.11","description":"React bindings for @playlive/realtime-pipeline — three usage tiers (single selector / typed per-slice / kitchen-sink). Twitch-Extension safe; no react-use-websocket-lite.","type":"module","sideEffects":false,"main":"./index.js","types":"./index.d.ts","exports":{".":{"import":"./index.js","types":"./index.d.ts"},"./provider":{"import":"./provider/index.js","types":"./provider/index.d.ts"},"./legacy":{"import":"./legacy/index.js","types":"./legacy/index.d.ts"},"./fusion":{"import":"./fusion/index.js","types":"./fusion/index.d.ts"}},"dependencies":{},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","zustand":"^5.0.0","@playlive/realtime-pipeline":"^0.2.8","@playlive/tiltify-core":"^0.4.11","@tanstack/react-query":"^5.0.0","@playlive/react-query":"^0.3.4","@playlive/fundraiser-data":"^0.4.2"},"peerDependenciesMeta":{"@tanstack/react-query":{"optional":true},"@playlive/react-query":{"optional":true},"@playlive/fundraiser-data":{"optional":true}},"playlive":{"target":"browser","frontendEligible":true,"coverageFloor":85},"publishConfig":{"access":"restricted","registry":"https://playlive-767397689694.d.codeartifact.us-east-1.amazonaws.com/npm/playlive/"},"integrity":"sha512-exnWYyfUImfIkKJHkNT4bQ2gYC4ZUmoGiGJJHmDls3AruiR2rPIL9vYaLTWv0nNdxHA/UnV9CBH5hhR7XxQT4g==","shasum":"f206f2a8632ec3d9514434830407613ea234892b","readme":"# @playlive/react-pipeline\n\nReact bindings for [`@playlive/realtime-pipeline`](../realtime-pipeline/).\nDrop-in replacement for `playlive-overlay-data-layer/src/websocket/*`\nwith **zero** dependency on `react-use-websocket-lite` — built directly\non Zustand v5's `useStore` + `useSyncExternalStore`.\n\n![Coverage](./coverage-badge.svg)\n\n## Three tiers — take only what you need\n\nPick the surface that matches how much of the legacy\n`UnifiedDataPipelineProvider` API you actually want. Each tier is\nstrictly larger than the last and lives at its own subpath so unused\ncode is tree-shaken out.\n\n| Tier | Subpath                            | Surface                                                                         | Use when                                                              |\n| ---- | ---------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| 1    | `@playlive/react-pipeline`         | `RealtimePipelineProvider` + `usePipelineValue` (universal selector)            | Greenfield app; you want one selector primitive and nothing else.     |\n| 2    | `@playlive/react-pipeline`         | Tier 1 **plus** 22 typed per-slice hooks (`usePipelineDonations(id)`, …)        | You want stable, typed one-liners for the common reads.               |\n| 3    | `@playlive/react-pipeline/provider`| Tier 2 **plus** `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, etc.   | Porting an existing overlay-data-layer consumer with minimal churn.   |\n| —    | `@playlive/react-pipeline/legacy`  | Tier 3 **plus** `useUDPStore` / `useUDPStoreApi` / `createUDPStore` aliases     | Soft-deprecated shim for the legacy symbol names (one-release window).|\n\n## Install\n\n```bash\nbun add @playlive/react-pipeline\nbun add -d react react-dom zustand\n```\n\nPeer dependencies (jose-style — consumer brings their own):\n`react@^19`, `react-dom@^19`, `zustand@^5`,\n`@playlive/realtime-pipeline` (workspace), `@playlive/tiltify-core`\n(workspace, type-only — stripped at compile time).\n\nNothing else ships in the bundle.\n\n## Quick start — Tier 1\n\n```tsx\nimport { RealtimePipelineProvider, usePipelineCampaigns } from \"@playlive/react-pipeline\";\n\nfunction App() {\n  return (\n    <RealtimePipelineProvider\n      url=\"wss://pipeline.example.com/socket\"\n      charityType=\"tiltify\"\n      autoConnect\n    >\n      <Overlay />\n    </RealtimePipelineProvider>\n  );\n}\n\nfunction Overlay() {\n  const campaigns = usePipelineCampaigns();\n  return <ul>{campaigns.map((c) => <li key={c.id}>{c.name}</li>)}</ul>;\n}\n```\n\n`RealtimePipelineProvider` mounts a Zustand store + opens (or accepts)\na `PipelineConnection`. Children call any Tier 1 / Tier 2 hook to\nsubscribe.\n\n## Subpath exports\n\n| Subpath                              | Description                                                                       |\n| ------------------------------------ | --------------------------------------------------------------------------------- |\n| `@playlive/react-pipeline`           | Default barrel — Tier 1 + Tier 2 (provider + 21 typed slice hooks).               |\n| `@playlive/react-pipeline/provider`  | Tier 3 — `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, add-pipeline\\*. |\n| `@playlive/react-pipeline/fusion`    | Cross-package composition hooks (REST baseline + WS delta) — `useCurrentBlockRaised`, `useLiveSchedule`, `useCurrentScheduleItem`, `useLinkedTeamCampaign`, `useDonorSpotlight`, plus the WS-store-first / HTTP-fallback hybrid slice hooks (`useHybridRewards`, `useHybridTargets`, `useHybridMilestones`, `useHybridPolls`, `useHybridCause`, `useHybridFundraisingEvent`, `useHybridFundraisingEventSupportingCampaigns`, `useHybridAuctionHouses`) + the shared `useLoadingTimeout` primitive. |\n| `@playlive/react-pipeline/legacy`    | Soft-deprecated alias bundle (`useUDPStore`, `createUDPStore`, `UDPStore`, etc.). |\n\nEach subpath ships an ESM bundle, a Bun source condition, and `.d.ts`\ndeclarations. Frontend-eligible bundles are not minified (PRD §6.1)\nso stack traces stay readable in production.\n\n## API reference\n\n### Tier 1 — universal primitive\n\n| Export                          | Signature                                                            |\n| ------------------------------- | -------------------------------------------------------------------- |\n| `RealtimePipelineProvider`      | `(props: RealtimePipelineProviderProps) => JSX.Element`              |\n| `usePipelineValue`              | `<T>(selector: (s: PipelineStore) => T) => T`                        |\n\n`usePipelineValue` is single-arg by design. Zustand v5 dropped the\nthird `equalityFn` parameter on `useStore` to keep the package\nfree of the `use-sync-external-store` shim peer. For shallow / custom\nequality, wrap your selector with `useShallow` from\n`zustand/shallow` (zero-extra-dep):\n\n```tsx\nimport { useShallow } from \"zustand/shallow\";\nimport { usePipelineValue } from \"@playlive/react-pipeline\";\n\nconst { connected, connecting } = usePipelineValue(\n  useShallow((s) => ({ connected: s.connected, connecting: s.connecting })),\n);\n```\n\n### Tier 2 — typed per-slice hooks\n\nAll 22 hooks are stable wrappers over `usePipelineValue` with a typed\nselector + memo-stable empty-array fallbacks (no infinite-render\nchurn from fresh `[]` allocations — Decision §3 in CHANGELOG).\n\n| Hook                              | Returns                                                             |\n| --------------------------------- | ------------------------------------------------------------------- |\n| `usePipelineCampaigns()`          | `TiltifyCampaign[]`                                                 |\n| `usePipelineTeamCampaigns()`      | `TiltifyTeamCampaign[]`                                             |\n| `usePipelineFundraisingEvents()`  | `TiltifyFundraisingEvent[]`                                         |\n| `usePipelineCauses()`             | `TiltifyCause[]`                                                    |\n| `usePipelineDonations()`          | `Record<string, TiltifyDonationWithTestFlag[]>`                     |\n| `usePipelineDonations(id)`        | `TiltifyDonationWithTestFlag[]` (memo-stable `[]` if id unknown)    |\n| `usePipelineDonationSum(id, opts?)` | `PipelineDonationSumResult` — live per-currency sum over `[start, end)` of the WS donation firehose. Pair with the REST `/schedules/campaigns/{id}/raised` `asOf` field to replace polling with a `baseline + WS-delta` view. |\n| `usePipelineDonationTrains(id, withStatus?)` | `DonationTrain[]`                                        |\n| `usePipelineSubathonTimer(id, withStatus?)`  | `SubathonTimer[]`                                        |\n| `usePipelineDonorSpotlight(id)`   | `ComputedSpotlight \\| null`                                         |\n| `usePipelinePolls(id)`            | `TiltifyPoll[]`                                                     |\n| `usePipelineRewards(id)`          | `TiltifyReward[]`                                                   |\n| `usePipelineTargets(id)`          | `TiltifyTarget[]`                                                   |\n| `usePipelineMilestones(id)`       | `TiltifyMilestone[]`                                                |\n| `usePipelineLeaderboardEntries(id)` | `TiltifyLeaderboardEntry[]`                                       |\n| `usePipelineAuctionHouses()`      | `AuctionHouse[]`                                                    |\n| `usePipelineTwitchChat()`         | `TwitchChatMessage[]`                                               |\n| `usePipelineConnectionState()`    | `PipelineConnectionState` (10 split selectors — no false re-renders)|\n| `usePipelineConnectionActions()`  | `PipelineConnectionActions` (stable `connect/disconnect/refresh/…`) |\n| `usePipelineSubscriptions()`      | `Record<string, TiltifyWebhookSubscription>`                        |\n| `usePipelineTiltifySettings()`    | `TiltifyWebSocketSettings`                                          |\n| `usePipelineLastRefreshResponse()`| `RefreshResponseMessage \\| null`                                    |\n| `usePipelineDonorSpotlightSettings()` | `Record<string, DonorSpotlightSettings>`                        |\n\n### Tier 3 — kitchen-sink provider (`/provider`)\n\n| Export                              | Notes                                                                                  |\n| ----------------------------------- | -------------------------------------------------------------------------------------- |\n| `UnifiedDataPipelineProvider`       | Superset of `RealtimePipelineProvider`; adds legacy `reloadOnErrorReconnect` + `debug`.|\n| `useUnifiedDataPipeline()`          | One-call kitchen-sink hook. Returns every slice + actions + legacy field aliases.      |\n| `useAddPipelineCampaignIDs(ids)`    | Effect hook — registers campaign ids with the store on mount.                          |\n| `useAddPipelineCampaigns(items)`    | Effect hook — registers full campaign records on mount.                                |\n| `useAddPipelineFundraisingEventIDs(ids)` | Effect hook — registers fundraising-event ids on mount.                           |\n| `useCommonContextHooks(…)`          | URL / mode / charity-type sync to the store. Same shape as the legacy hook.            |\n| `useIdentify()` / `useIdentify(…11 legacy args)` | Zero-arg returns the store's bound `identify` action. Positional arg form is preserved as an `@deprecated` overload — args are ignored, warns once. |\n\n### Fusion — hybrid slice hooks (`/fusion`)\n\nOverlay-parity `usePipeline*` hooks lifted from\n`playlive-overlay-vite/src/hooks/pipeline/` so every overlay that\nwants the same WS-store-first / HTTP-fallback shape can drop the\nlocal copy. Every hook accepts a `mode: \"ws\" | \"http\"` param that\npicks between:\n\n- `\"ws\"` — read the Tier-2 store selector for the slice, latch a\n  local `hasLoadedX` boolean on the first slice arrival (or on the\n  server-side \"feature disabled\" signal), and fire\n  `updateTiltifySettings({ <sliceKey> })` whenever the local\n  `xEnabled` toggle flips so the pipeline dynamically un/subscribes.\n- `\"http\"` — delegate to the matching `@playlive/react-query` REST\n  hook (`useRewards`, `useTargets`, …) with the WS store slice\n  ignored.\n\nAll hybrid hooks share `useLoadingTimeout` — a 15 s safety-net that\nforce-completes the WS branch when the store never delivers.\n\n| Hook                                                     | Returns                                                                        |\n| -------------------------------------------------------- | ------------------------------------------------------------------------------ |\n| `useHybridRewards(params)`                               | `{ currentRewards, setHasLoadedRewards, hasLoadedRewards, isLoadingRewards, isLoading, setRewardsEnabled, rewardsEnabled }` |\n| `useHybridTargets(params)`                               | `{ currentTargets, setHasLoadedTargets, hasLoadedTargets, isLoading, targetsEnabled, isLoadingTargets, setTargetsEnabled }` |\n| `useHybridMilestones(params)`                            | `{ isLoadingMilestones, currentMilestones, milestonesEnabled, setMilestonesEnabled }` |\n| `useHybridPolls(params)`                                 | `{ isLoadingPolls, currentPolls, pollsEnabled, setPollsEnabled }`              |\n| `useHybridCause({ mode, initialCampaign })`              | `{ cause, isLoading }` — single-slot semantics (uses `getFirstCause`).         |\n| `useHybridFundraisingEvent({ mode, initialCampaign })`   | `{ fundraisingEvent, isLoading }` — single-slot semantics.                     |\n| `useHybridFundraisingEventSupportingCampaigns(params)`   | `{ supportingCampaigns, isLoading, setSupportingCampaignsEnabled, supportingCampaignsEnabled, isLoadingSupportingCampaigns, hasLoadedSupportingCampaigns }` |\n| `useHybridAuctionHouses({ mode, enabled, campaignID })`  | `{ auctionHouses, auctionHouseTotal, auctionHousesEnabled, setAuctionHousesEnabled }` — WS-only (no REST fallback yet). |\n| `useLoadingTimeout(params)`                              | `void` — safety-net timer primitive shared by every hook above.                |\n\nReturn shapes are preserved verbatim from the overlay so overlay\ncall-sites can eventually collapse to a re-export.\n\n### Fusion — baseline + WS delta hooks (`/fusion`)\n\n| Hook                                        | Returns                                                                     |\n| ------------------------------------------- | --------------------------------------------------------------------------- |\n| `useCurrentBlockRaised(campaignID, start, end, opts?)` | `{ raised, data, isLoading, isError, refetch }` — REST baseline + WS delta. `opts.demoMode: true` short-circuits to `DemoProvider.getDemoScheduleBlockRaised` / `getDemoBlockRaised` fallback. |\n| `useLiveSchedule(params, opts?)`            | `{ schedule, isLoading, isError, refetch }` — WS-refetch on transition. `opts.demoMode: true` (+ optional `demoIntervalMs`) short-circuits to `DemoProvider.getDemoSchedule` / `buildDemoSchedule` fallback with a live tick timer.     |\n| `useCurrentScheduleItem(schedule, opts?)`   | `{ currentItem, nextItem }` — pure-client rollover projection.              |\n| `useLinkedTeamCampaign(params, opts?)`      | `{ linkedTeamCampaign, … }` — WS-store first, HTTP fallback.                |\n| `useDonorSpotlight(params, opts?)`          | `{ spotlight, isLoading, refetch }` — WS store preferred + REST snapshot.   |\n\n### Legacy aliases (`/legacy`)\n\nSoft-deprecated. Identical behavior to the canonical exports —\nrename the import specifier once and the rest of your code base\nkeeps working unchanged:\n\n| Legacy name            | Canonical replacement                                          |\n| ---------------------- | -------------------------------------------------------------- |\n| `useUDPStore`          | `usePipelineValue` (Tier 1) / typed slice hooks (Tier 2)       |\n| `useUDPStoreApi`       | (use a typed slice hook; or reach for the raw store API)       |\n| `createUDPStore`       | `createPipelineStore` from `@playlive/realtime-pipeline/store` |\n| `UDPStore` (type)      | `PipelineStore` from `@playlive/realtime-pipeline/store`       |\n| `UDPStoreApi` (type)   | `PipelineStoreApi` from `@playlive/realtime-pipeline/store`    |\n| `CharityTypes`         | re-exported from `@playlive/realtime-pipeline/protocol`         |\n\nPlus every Tier 1+2+3 export under its canonical name.\n\n## Migration from `playlive-overlay-data-layer`\n\nOne-shot sed-friendly rewrite of every import specifier (no body\nedits needed for the happy path):\n\n```bash\n# Tier 3 — kitchen-sink (drop-in replacement)\nrg -l '@playlive/overlay-data-layer/websocket' . \\\n  | xargs sed -i '' \\\n    -e 's|@playlive/overlay-data-layer/websocket|@playlive/react-pipeline/provider|g'\n```\n\nFor the soft-deprecated names (`useUDPStore`, `createUDPStore`, …)\nsubstitute `/legacy` instead of `/provider` to opt into the alias\nbundle, then migrate at your leisure (PRD §10 — one-release window).\n\nSee the phase-8 row in the root [MIGRATION.md](../../MIGRATION.md) for\nthe full per-symbol mapping plus call-site walkthroughs.\n\n## Upstream spec\n\nNo external API surface — internal workspace package. All transport\nruns through `@playlive/realtime-pipeline/connection`.\n\n## Twitch Extension URL disclosure\n\n`KNOWN_URLS` enumerates every absolute URL or host this package can\nfetch. **It is empty.** This package opens WebSocket connections only\nto URLs the consumer passes to `<RealtimePipelineProvider url={…}>`;\nthose URLs must be added to your Extension submission's URL\ndisclosure list, not this one.\n\n```ts\nimport { KNOWN_URLS } from \"@playlive/react-pipeline\";\nconsole.log(KNOWN_URLS); // []\n```\n\nSee [`docs/twitch-extension-checklist.md`](../../docs/twitch-extension-checklist.md).\n\n## Examples\n\nLive overlay apps consuming this provider land under `apps/*`\n(phase 10). Until then, the Tier 1 quick-start above is the minimum\nviable wiring.\n\n## Contributing\n\nSee [CONTRIBUTING.md](../../CONTRIBUTING.md). To add new Tier-2\nhooks, mirror an existing slice hook in `src/hooks/index.ts` and\nadd a `it.each` entry in `tests/unit/index.test.ts`.\n\n## License\n\nMIT — see [LICENSE](../../LICENSE). Distributed via Play Live\nCodeArtifact (PRD §6).\n","readmeFilename":"README.md","dist":{"tarball":"https://packages.playlive.experience.stjude.org/@playlive/react-pipeline/-/react-pipeline-0.4.11.tgz","shasum":"f206f2a8632ec3d9514434830407613ea234892b","integrity":"sha512-exnWYyfUImfIkKJHkNT4bQ2gYC4ZUmoGiGJJHmDls3AruiR2rPIL9vYaLTWv0nNdxHA/UnV9CBH5hhR7XxQT4g=="}},"0.4.13":{"name":"@playlive/react-pipeline","version":"0.4.13","description":"React bindings for @playlive/realtime-pipeline","type":"module","sideEffects":false,"main":"./index.js","types":"./index.d.ts","exports":{".":{"import":"./index.js","types":"./index.d.ts"},"./provider":{"import":"./provider/index.js","types":"./provider/index.d.ts"},"./legacy":{"import":"./legacy/index.js","types":"./legacy/index.d.ts"},"./fusion":{"import":"./fusion/index.js","types":"./fusion/index.d.ts"}},"dependencies":{},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","zustand":"^5.0.0","@playlive/realtime-pipeline":"^0.2.8","@playlive/tiltify-core":"^0.4.11","@tanstack/react-query":"^5.0.0","@playlive/react-query":"^0.3.4","@playlive/fundraiser-data":"^0.4.2"},"peerDependenciesMeta":{"@tanstack/react-query":{"optional":true},"@playlive/react-query":{"optional":true},"@playlive/fundraiser-data":{"optional":true}},"playlive":{"target":"browser","frontendEligible":true,"coverageFloor":85},"publishConfig":{"access":"restricted","registry":"https://playlive-767397689694.d.codeartifact.us-east-1.amazonaws.com/npm/playlive/"},"integrity":"sha512-WuLc7rPexvWpj2UhYwqDBOeogRPOb9o9V02uWzKjHIVXUP29j3iZDDo7/lFpU/hjuJZnzrt4PHrCHBPRRSL5ug==","shasum":"617b3e892d98a51962ffdaee8044491584849b77","readme":"# @playlive/react-pipeline\n\nReact bindings for [`@playlive/realtime-pipeline`](../realtime-pipeline/).\nDrop-in replacement for `playlive-overlay-data-layer/src/websocket/*`\nwith **zero** dependency on `react-use-websocket-lite` — built directly\non Zustand v5's `useStore` + `useSyncExternalStore`.\n\n![Coverage](./coverage-badge.svg)\n\n## Three tiers — take only what you need\n\nPick the surface that matches how much of the legacy\n`UnifiedDataPipelineProvider` API you actually want. Each tier is\nstrictly larger than the last and lives at its own subpath so unused\ncode is tree-shaken out.\n\n| Tier | Subpath                            | Surface                                                                         | Use when                                                              |\n| ---- | ---------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| 1    | `@playlive/react-pipeline`         | `RealtimePipelineProvider` + `usePipelineValue` (universal selector)            | Greenfield app; you want one selector primitive and nothing else.     |\n| 2    | `@playlive/react-pipeline`         | Tier 1 **plus** 22 typed per-slice hooks (`usePipelineDonations(id)`, …)        | You want stable, typed one-liners for the common reads.               |\n| 3    | `@playlive/react-pipeline/provider`| Tier 2 **plus** `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, etc.   | Porting an existing overlay-data-layer consumer with minimal churn.   |\n| —    | `@playlive/react-pipeline/legacy`  | Tier 3 **plus** `useUDPStore` / `useUDPStoreApi` / `createUDPStore` aliases     | Soft-deprecated shim for the legacy symbol names (one-release window).|\n\n## Install\n\n```bash\nbun add @playlive/react-pipeline\nbun add -d react react-dom zustand\n```\n\nPeer dependencies (jose-style — consumer brings their own):\n`react@^19`, `react-dom@^19`, `zustand@^5`,\n`@playlive/realtime-pipeline` (workspace), `@playlive/tiltify-core`\n(workspace, type-only — stripped at compile time).\n\nNothing else ships in the bundle.\n\n## Quick start — Tier 1\n\n```tsx\nimport { RealtimePipelineProvider, usePipelineCampaigns } from \"@playlive/react-pipeline\";\n\nfunction App() {\n  return (\n    <RealtimePipelineProvider\n      url=\"wss://pipeline.example.com/socket\"\n      charityType=\"tiltify\"\n      autoConnect\n    >\n      <Overlay />\n    </RealtimePipelineProvider>\n  );\n}\n\nfunction Overlay() {\n  const campaigns = usePipelineCampaigns();\n  return <ul>{campaigns.map((c) => <li key={c.id}>{c.name}</li>)}</ul>;\n}\n```\n\n`RealtimePipelineProvider` mounts a Zustand store + opens (or accepts)\na `PipelineConnection`. Children call any Tier 1 / Tier 2 hook to\nsubscribe.\n\n## Subpath exports\n\n| Subpath                              | Description                                                                       |\n| ------------------------------------ | --------------------------------------------------------------------------------- |\n| `@playlive/react-pipeline`           | Default barrel — Tier 1 + Tier 2 (provider + 21 typed slice hooks).               |\n| `@playlive/react-pipeline/provider`  | Tier 3 — `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, add-pipeline\\*. |\n| `@playlive/react-pipeline/fusion`    | Cross-package composition hooks (REST baseline + WS delta) — `useCurrentBlockRaised`, `useLiveSchedule`, `useCurrentScheduleItem`, `useLinkedTeamCampaign`, `useDonorSpotlight`, plus the WS-store-first / HTTP-fallback hybrid slice hooks (`useHybridRewards`, `useHybridTargets`, `useHybridMilestones`, `useHybridPolls`, `useHybridCause`, `useHybridFundraisingEvent`, `useHybridFundraisingEventSupportingCampaigns`, `useHybridAuctionHouses`) + the shared `useLoadingTimeout` primitive. |\n| `@playlive/react-pipeline/legacy`    | Soft-deprecated alias bundle (`useUDPStore`, `createUDPStore`, `UDPStore`, etc.). |\n\nEach subpath ships an ESM bundle, a Bun source condition, and `.d.ts`\ndeclarations. Frontend-eligible bundles are not minified (PRD §6.1)\nso stack traces stay readable in production.\n\n## API reference\n\n### Tier 1 — universal primitive\n\n| Export                          | Signature                                                            |\n| ------------------------------- | -------------------------------------------------------------------- |\n| `RealtimePipelineProvider`      | `(props: RealtimePipelineProviderProps) => JSX.Element`              |\n| `usePipelineValue`              | `<T>(selector: (s: PipelineStore) => T) => T`                        |\n\n`usePipelineValue` is single-arg by design. Zustand v5 dropped the\nthird `equalityFn` parameter on `useStore` to keep the package\nfree of the `use-sync-external-store` shim peer. For shallow / custom\nequality, wrap your selector with `useShallow` from\n`zustand/shallow` (zero-extra-dep):\n\n```tsx\nimport { useShallow } from \"zustand/shallow\";\nimport { usePipelineValue } from \"@playlive/react-pipeline\";\n\nconst { connected, connecting } = usePipelineValue(\n  useShallow((s) => ({ connected: s.connected, connecting: s.connecting })),\n);\n```\n\n### Tier 2 — typed per-slice hooks\n\nAll 22 hooks are stable wrappers over `usePipelineValue` with a typed\nselector + memo-stable empty-array fallbacks (no infinite-render\nchurn from fresh `[]` allocations — Decision §3 in CHANGELOG).\n\n| Hook                              | Returns                                                             |\n| --------------------------------- | ------------------------------------------------------------------- |\n| `usePipelineCampaigns()`          | `TiltifyCampaign[]`                                                 |\n| `usePipelineTeamCampaigns()`      | `TiltifyTeamCampaign[]`                                             |\n| `usePipelineFundraisingEvents()`  | `TiltifyFundraisingEvent[]`                                         |\n| `usePipelineCauses()`             | `TiltifyCause[]`                                                    |\n| `usePipelineDonations()`          | `Record<string, TiltifyDonationWithTestFlag[]>`                     |\n| `usePipelineDonations(id)`        | `TiltifyDonationWithTestFlag[]` (memo-stable `[]` if id unknown)    |\n| `usePipelineDonationSum(id, opts?)` | `PipelineDonationSumResult` — live per-currency sum over `[start, end)` of the WS donation firehose. Pair with the REST `/schedules/campaigns/{id}/raised` `asOf` field to replace polling with a `baseline + WS-delta` view. |\n| `usePipelineDonationTrains(id, withStatus?)` | `DonationTrain[]`                                        |\n| `usePipelineSubathonTimer(id, withStatus?)`  | `SubathonTimer[]`                                        |\n| `usePipelineDonorSpotlight(id)`   | `ComputedSpotlight \\| null`                                         |\n| `usePipelinePolls(id)`            | `TiltifyPoll[]`                                                     |\n| `usePipelineRewards(id)`          | `TiltifyReward[]`                                                   |\n| `usePipelineTargets(id)`          | `TiltifyTarget[]`                                                   |\n| `usePipelineMilestones(id)`       | `TiltifyMilestone[]`                                                |\n| `usePipelineLeaderboardEntries(id)` | `TiltifyLeaderboardEntry[]`                                       |\n| `usePipelineAuctionHouses()`      | `AuctionHouse[]`                                                    |\n| `usePipelineTwitchChat()`         | `TwitchChatMessage[]`                                               |\n| `usePipelineConnectionState()`    | `PipelineConnectionState` (10 split selectors — no false re-renders)|\n| `usePipelineConnectionActions()`  | `PipelineConnectionActions` (stable `connect/disconnect/refresh/…`) |\n| `usePipelineSubscriptions()`      | `Record<string, TiltifyWebhookSubscription>`                        |\n| `usePipelineTiltifySettings()`    | `TiltifyWebSocketSettings`                                          |\n| `usePipelineLastRefreshResponse()`| `RefreshResponseMessage \\| null`                                    |\n| `usePipelineDonorSpotlightSettings()` | `Record<string, DonorSpotlightSettings>`                        |\n\n### Tier 3 — kitchen-sink provider (`/provider`)\n\n| Export                              | Notes                                                                                  |\n| ----------------------------------- | -------------------------------------------------------------------------------------- |\n| `UnifiedDataPipelineProvider`       | Superset of `RealtimePipelineProvider`; adds legacy `reloadOnErrorReconnect` + `debug`.|\n| `useUnifiedDataPipeline()`          | One-call kitchen-sink hook. Returns every slice + actions + legacy field aliases.      |\n| `useAddPipelineCampaignIDs(ids)`    | Effect hook — registers campaign ids with the store on mount.                          |\n| `useAddPipelineCampaigns(items)`    | Effect hook — registers full campaign records on mount.                                |\n| `useAddPipelineFundraisingEventIDs(ids)` | Effect hook — registers fundraising-event ids on mount.                           |\n| `useCommonContextHooks(…)`          | URL / mode / charity-type sync to the store. Same shape as the legacy hook.            |\n| `useIdentify()` / `useIdentify(…11 legacy args)` | Zero-arg returns the store's bound `identify` action. Positional arg form is preserved as an `@deprecated` overload — args are ignored, warns once. |\n\n### Fusion — hybrid slice hooks (`/fusion`)\n\nOverlay-parity `usePipeline*` hooks lifted from\n`playlive-overlay-vite/src/hooks/pipeline/` so every overlay that\nwants the same WS-store-first / HTTP-fallback shape can drop the\nlocal copy. Every hook accepts a `mode: \"ws\" | \"http\"` param that\npicks between:\n\n- `\"ws\"` — read the Tier-2 store selector for the slice, latch a\n  local `hasLoadedX` boolean on the first slice arrival (or on the\n  server-side \"feature disabled\" signal), and fire\n  `updateTiltifySettings({ <sliceKey> })` whenever the local\n  `xEnabled` toggle flips so the pipeline dynamically un/subscribes.\n- `\"http\"` — delegate to the matching `@playlive/react-query` REST\n  hook (`useRewards`, `useTargets`, …) with the WS store slice\n  ignored.\n\nAll hybrid hooks share `useLoadingTimeout` — a 15 s safety-net that\nforce-completes the WS branch when the store never delivers.\n\n| Hook                                                     | Returns                                                                        |\n| -------------------------------------------------------- | ------------------------------------------------------------------------------ |\n| `useHybridRewards(params)`                               | `{ currentRewards, setHasLoadedRewards, hasLoadedRewards, isLoadingRewards, isLoading, setRewardsEnabled, rewardsEnabled }` |\n| `useHybridTargets(params)`                               | `{ currentTargets, setHasLoadedTargets, hasLoadedTargets, isLoading, targetsEnabled, isLoadingTargets, setTargetsEnabled }` |\n| `useHybridMilestones(params)`                            | `{ isLoadingMilestones, currentMilestones, milestonesEnabled, setMilestonesEnabled }` |\n| `useHybridPolls(params)`                                 | `{ isLoadingPolls, currentPolls, pollsEnabled, setPollsEnabled }`              |\n| `useHybridCause({ mode, initialCampaign })`              | `{ cause, isLoading }` — single-slot semantics (uses `getFirstCause`).         |\n| `useHybridFundraisingEvent({ mode, initialCampaign })`   | `{ fundraisingEvent, isLoading }` — single-slot semantics.                     |\n| `useHybridFundraisingEventSupportingCampaigns(params)`   | `{ supportingCampaigns, isLoading, setSupportingCampaignsEnabled, supportingCampaignsEnabled, isLoadingSupportingCampaigns, hasLoadedSupportingCampaigns }` |\n| `useHybridAuctionHouses({ mode, enabled, campaignID })`  | `{ auctionHouses, auctionHouseTotal, auctionHousesEnabled, setAuctionHousesEnabled }` — WS-only (no REST fallback yet). |\n| `useLoadingTimeout(params)`                              | `void` — safety-net timer primitive shared by every hook above.                |\n\nReturn shapes are preserved verbatim from the overlay so overlay\ncall-sites can eventually collapse to a re-export.\n\n### Fusion — baseline + WS delta hooks (`/fusion`)\n\n| Hook                                        | Returns                                                                     |\n| ------------------------------------------- | --------------------------------------------------------------------------- |\n| `useCurrentBlockRaised(campaignID, start, end, opts?)` | `{ raised, data, isLoading, isError, refetch }` — REST baseline + WS delta. `opts.demoMode: true` short-circuits to `DemoProvider.getDemoScheduleBlockRaised` / `getDemoBlockRaised` fallback. |\n| `useLiveSchedule(params, opts?)`            | `{ schedule, isLoading, isError, refetch }` — WS-refetch on transition. `opts.demoMode: true` (+ optional `demoIntervalMs`) short-circuits to `DemoProvider.getDemoSchedule` / `buildDemoSchedule` fallback with a live tick timer.     |\n| `useCurrentScheduleItem(schedule, opts?)`   | `{ currentItem, nextItem }` — pure-client rollover projection.              |\n| `useLinkedTeamCampaign(params, opts?)`      | `{ linkedTeamCampaign, … }` — WS-store first, HTTP fallback.                |\n| `useDonorSpotlight(params, opts?)`          | `{ spotlight, isLoading, refetch }` — WS store preferred + REST snapshot.   |\n\n### Legacy aliases (`/legacy`)\n\nSoft-deprecated. Identical behavior to the canonical exports —\nrename the import specifier once and the rest of your code base\nkeeps working unchanged:\n\n| Legacy name            | Canonical replacement                                          |\n| ---------------------- | -------------------------------------------------------------- |\n| `useUDPStore`          | `usePipelineValue` (Tier 1) / typed slice hooks (Tier 2)       |\n| `useUDPStoreApi`       | (use a typed slice hook; or reach for the raw store API)       |\n| `createUDPStore`       | `createPipelineStore` from `@playlive/realtime-pipeline/store` |\n| `UDPStore` (type)      | `PipelineStore` from `@playlive/realtime-pipeline/store`       |\n| `UDPStoreApi` (type)   | `PipelineStoreApi` from `@playlive/realtime-pipeline/store`    |\n| `CharityTypes`         | re-exported from `@playlive/realtime-pipeline/protocol`         |\n\nPlus every Tier 1+2+3 export under its canonical name.\n\n## Migration from `playlive-overlay-data-layer`\n\nOne-shot sed-friendly rewrite of every import specifier (no body\nedits needed for the happy path):\n\n```bash\n# Tier 3 — kitchen-sink (drop-in replacement)\nrg -l '@playlive/overlay-data-layer/websocket' . \\\n  | xargs sed -i '' \\\n    -e 's|@playlive/overlay-data-layer/websocket|@playlive/react-pipeline/provider|g'\n```\n\nFor the soft-deprecated names (`useUDPStore`, `createUDPStore`, …)\nsubstitute `/legacy` instead of `/provider` to opt into the alias\nbundle, then migrate at your leisure (PRD §10 — one-release window).\n\nSee the phase-8 row in the root [MIGRATION.md](../../MIGRATION.md) for\nthe full per-symbol mapping plus call-site walkthroughs.\n\n## Upstream spec\n\nNo external API surface — internal workspace package. All transport\nruns through `@playlive/realtime-pipeline/connection`.\n\n## Twitch Extension URL disclosure\n\n`KNOWN_URLS` enumerates every absolute URL or host this package can\nfetch. **It is empty.** This package opens WebSocket connections only\nto URLs the consumer passes to `<RealtimePipelineProvider url={…}>`;\nthose URLs must be added to your Extension submission's URL\ndisclosure list, not this one.\n\n```ts\nimport { KNOWN_URLS } from \"@playlive/react-pipeline\";\nconsole.log(KNOWN_URLS); // []\n```\n\nSee [`docs/twitch-extension-checklist.md`](../../docs/twitch-extension-checklist.md).\n\n## Examples\n\nLive overlay apps consuming this provider land under `apps/*`\n(phase 10). Until then, the Tier 1 quick-start above is the minimum\nviable wiring.\n\n## Contributing\n\nSee [CONTRIBUTING.md](../../CONTRIBUTING.md). To add new Tier-2\nhooks, mirror an existing slice hook in `src/hooks/index.ts` and\nadd a `it.each` entry in `tests/unit/index.test.ts`.\n\n## License\n\nMIT — see [LICENSE](../../LICENSE). Distributed via Play Live\nCodeArtifact (PRD §6).\n","readmeFilename":"README.md","dist":{"tarball":"https://packages.playlive.experience.stjude.org/@playlive/react-pipeline/-/react-pipeline-0.4.13.tgz","shasum":"617b3e892d98a51962ffdaee8044491584849b77","integrity":"sha512-WuLc7rPexvWpj2UhYwqDBOeogRPOb9o9V02uWzKjHIVXUP29j3iZDDo7/lFpU/hjuJZnzrt4PHrCHBPRRSL5ug=="}},"0.4.14":{"name":"@playlive/react-pipeline","version":"0.4.14","description":"React bindings for @playlive/realtime-pipeline","type":"module","sideEffects":false,"main":"./index.js","types":"./index.d.ts","exports":{".":{"import":"./index.js","types":"./index.d.ts"},"./provider":{"import":"./provider/index.js","types":"./provider/index.d.ts"},"./legacy":{"import":"./legacy/index.js","types":"./legacy/index.d.ts"},"./fusion":{"import":"./fusion/index.js","types":"./fusion/index.d.ts"}},"dependencies":{},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","zustand":"^5.0.0","@playlive/realtime-pipeline":"^0.2.8","@playlive/tiltify-core":"^0.4.11","@tanstack/react-query":"^5.0.0","@playlive/react-query":"^0.3.4","@playlive/fundraiser-data":"^0.4.2"},"peerDependenciesMeta":{"@tanstack/react-query":{"optional":true},"@playlive/react-query":{"optional":true},"@playlive/fundraiser-data":{"optional":true}},"playlive":{"target":"browser","frontendEligible":true,"coverageFloor":85},"publishConfig":{"access":"restricted","registry":"https://playlive-767397689694.d.codeartifact.us-east-1.amazonaws.com/npm/playlive/"},"integrity":"sha512-PQKT8ubvPUgm8RZPwPIr4frD13qXEu5MxYrHo5wc24en3OSvT3Sz4kMI5ls8VOtms0YtWhluy8x13Z5yx8KswA==","shasum":"7b0ed186b2655aa8004633ae0cc448864aef346e","readme":"# @playlive/react-pipeline\n\nReact bindings for [`@playlive/realtime-pipeline`](../realtime-pipeline/).\nDrop-in replacement for `playlive-overlay-data-layer/src/websocket/*`\nwith **zero** dependency on `react-use-websocket-lite` — built directly\non Zustand v5's `useStore` + `useSyncExternalStore`.\n\n![Coverage](./coverage-badge.svg)\n\n## Three tiers — take only what you need\n\nPick the surface that matches how much of the legacy\n`UnifiedDataPipelineProvider` API you actually want. Each tier is\nstrictly larger than the last and lives at its own subpath so unused\ncode is tree-shaken out.\n\n| Tier | Subpath                            | Surface                                                                         | Use when                                                              |\n| ---- | ---------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| 1    | `@playlive/react-pipeline`         | `RealtimePipelineProvider` + `usePipelineValue` (universal selector)            | Greenfield app; you want one selector primitive and nothing else.     |\n| 2    | `@playlive/react-pipeline`         | Tier 1 **plus** 22 typed per-slice hooks (`usePipelineDonations(id)`, …)        | You want stable, typed one-liners for the common reads.               |\n| 3    | `@playlive/react-pipeline/provider`| Tier 2 **plus** `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, etc.   | Porting an existing overlay-data-layer consumer with minimal churn.   |\n| —    | `@playlive/react-pipeline/legacy`  | Tier 3 **plus** `useUDPStore` / `useUDPStoreApi` / `createUDPStore` aliases     | Soft-deprecated shim for the legacy symbol names (one-release window).|\n\n## Install\n\n```bash\nbun add @playlive/react-pipeline\nbun add -d react react-dom zustand\n```\n\nPeer dependencies (jose-style — consumer brings their own):\n`react@^19`, `react-dom@^19`, `zustand@^5`,\n`@playlive/realtime-pipeline` (workspace), `@playlive/tiltify-core`\n(workspace, type-only — stripped at compile time).\n\nNothing else ships in the bundle.\n\n## Quick start — Tier 1\n\n```tsx\nimport { RealtimePipelineProvider, usePipelineCampaigns } from \"@playlive/react-pipeline\";\n\nfunction App() {\n  return (\n    <RealtimePipelineProvider\n      url=\"wss://pipeline.example.com/socket\"\n      charityType=\"tiltify\"\n      autoConnect\n    >\n      <Overlay />\n    </RealtimePipelineProvider>\n  );\n}\n\nfunction Overlay() {\n  const campaigns = usePipelineCampaigns();\n  return <ul>{campaigns.map((c) => <li key={c.id}>{c.name}</li>)}</ul>;\n}\n```\n\n`RealtimePipelineProvider` mounts a Zustand store + opens (or accepts)\na `PipelineConnection`. Children call any Tier 1 / Tier 2 hook to\nsubscribe.\n\n## Subpath exports\n\n| Subpath                              | Description                                                                       |\n| ------------------------------------ | --------------------------------------------------------------------------------- |\n| `@playlive/react-pipeline`           | Default barrel — Tier 1 + Tier 2 (provider + 21 typed slice hooks).               |\n| `@playlive/react-pipeline/provider`  | Tier 3 — `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, add-pipeline\\*. |\n| `@playlive/react-pipeline/fusion`    | Cross-package composition hooks (REST baseline + WS delta) — `useCurrentBlockRaised`, `useLiveSchedule`, `useCurrentScheduleItem`, `useLinkedTeamCampaign`, `useDonorSpotlight`, plus the WS-store-first / HTTP-fallback hybrid slice hooks (`useHybridRewards`, `useHybridTargets`, `useHybridMilestones`, `useHybridPolls`, `useHybridCause`, `useHybridFundraisingEvent`, `useHybridFundraisingEventSupportingCampaigns`, `useHybridAuctionHouses`) + the shared `useLoadingTimeout` primitive. |\n| `@playlive/react-pipeline/legacy`    | Soft-deprecated alias bundle (`useUDPStore`, `createUDPStore`, `UDPStore`, etc.). |\n\nEach subpath ships an ESM bundle, a Bun source condition, and `.d.ts`\ndeclarations. Frontend-eligible bundles are not minified (PRD §6.1)\nso stack traces stay readable in production.\n\n## API reference\n\n### Tier 1 — universal primitive\n\n| Export                          | Signature                                                            |\n| ------------------------------- | -------------------------------------------------------------------- |\n| `RealtimePipelineProvider`      | `(props: RealtimePipelineProviderProps) => JSX.Element`              |\n| `usePipelineValue`              | `<T>(selector: (s: PipelineStore) => T) => T`                        |\n\n`usePipelineValue` is single-arg by design. Zustand v5 dropped the\nthird `equalityFn` parameter on `useStore` to keep the package\nfree of the `use-sync-external-store` shim peer. For shallow / custom\nequality, wrap your selector with `useShallow` from\n`zustand/shallow` (zero-extra-dep):\n\n```tsx\nimport { useShallow } from \"zustand/shallow\";\nimport { usePipelineValue } from \"@playlive/react-pipeline\";\n\nconst { connected, connecting } = usePipelineValue(\n  useShallow((s) => ({ connected: s.connected, connecting: s.connecting })),\n);\n```\n\n### Tier 2 — typed per-slice hooks\n\nAll 22 hooks are stable wrappers over `usePipelineValue` with a typed\nselector + memo-stable empty-array fallbacks (no infinite-render\nchurn from fresh `[]` allocations — Decision §3 in CHANGELOG).\n\n| Hook                              | Returns                                                             |\n| --------------------------------- | ------------------------------------------------------------------- |\n| `usePipelineCampaigns()`          | `TiltifyCampaign[]`                                                 |\n| `usePipelineTeamCampaigns()`      | `TiltifyTeamCampaign[]`                                             |\n| `usePipelineFundraisingEvents()`  | `TiltifyFundraisingEvent[]`                                         |\n| `usePipelineCauses()`             | `TiltifyCause[]`                                                    |\n| `usePipelineDonations()`          | `Record<string, TiltifyDonationWithTestFlag[]>`                     |\n| `usePipelineDonations(id)`        | `TiltifyDonationWithTestFlag[]` (memo-stable `[]` if id unknown)    |\n| `usePipelineDonationSum(id, opts?)` | `PipelineDonationSumResult` — live per-currency sum over `[start, end)` of the WS donation firehose. Pair with the REST `/schedules/campaigns/{id}/raised` `asOf` field to replace polling with a `baseline + WS-delta` view. |\n| `usePipelineDonationTrains(id, withStatus?)` | `DonationTrain[]`                                        |\n| `usePipelineSubathonTimer(id, withStatus?)`  | `SubathonTimer[]`                                        |\n| `usePipelineDonorSpotlight(id)`   | `ComputedSpotlight \\| null`                                         |\n| `usePipelinePolls(id)`            | `TiltifyPoll[]`                                                     |\n| `usePipelineRewards(id)`          | `TiltifyReward[]`                                                   |\n| `usePipelineTargets(id)`          | `TiltifyTarget[]`                                                   |\n| `usePipelineMilestones(id)`       | `TiltifyMilestone[]`                                                |\n| `usePipelineLeaderboardEntries(id)` | `TiltifyLeaderboardEntry[]`                                       |\n| `usePipelineAuctionHouses()`      | `AuctionHouse[]`                                                    |\n| `usePipelineTwitchChat()`         | `TwitchChatMessage[]`                                               |\n| `usePipelineConnectionState()`    | `PipelineConnectionState` (10 split selectors — no false re-renders)|\n| `usePipelineConnectionActions()`  | `PipelineConnectionActions` (stable `connect/disconnect/refresh/…`) |\n| `usePipelineSubscriptions()`      | `Record<string, TiltifyWebhookSubscription>`                        |\n| `usePipelineTiltifySettings()`    | `TiltifyWebSocketSettings`                                          |\n| `usePipelineLastRefreshResponse()`| `RefreshResponseMessage \\| null`                                    |\n| `usePipelineDonorSpotlightSettings()` | `Record<string, DonorSpotlightSettings>`                        |\n\n### Tier 3 — kitchen-sink provider (`/provider`)\n\n| Export                              | Notes                                                                                  |\n| ----------------------------------- | -------------------------------------------------------------------------------------- |\n| `UnifiedDataPipelineProvider`       | Superset of `RealtimePipelineProvider`; adds legacy `reloadOnErrorReconnect` + `debug`.|\n| `useUnifiedDataPipeline()`          | One-call kitchen-sink hook. Returns every slice + actions + legacy field aliases.      |\n| `useAddPipelineCampaignIDs(ids)`    | Effect hook — registers campaign ids with the store on mount.                          |\n| `useAddPipelineCampaigns(items)`    | Effect hook — registers full campaign records on mount.                                |\n| `useAddPipelineFundraisingEventIDs(ids)` | Effect hook — registers fundraising-event ids on mount.                           |\n| `useCommonContextHooks(…)`          | URL / mode / charity-type sync to the store. Same shape as the legacy hook.            |\n| `useIdentify()` / `useIdentify(…11 legacy args)` | Zero-arg returns the store's bound `identify` action. Positional arg form is preserved as an `@deprecated` overload — args are ignored, warns once. |\n\n### Fusion — hybrid slice hooks (`/fusion`)\n\nOverlay-parity `usePipeline*` hooks lifted from\n`playlive-overlay-vite/src/hooks/pipeline/` so every overlay that\nwants the same WS-store-first / HTTP-fallback shape can drop the\nlocal copy. Every hook accepts a `mode: \"ws\" | \"http\"` param that\npicks between:\n\n- `\"ws\"` — read the Tier-2 store selector for the slice, latch a\n  local `hasLoadedX` boolean on the first slice arrival (or on the\n  server-side \"feature disabled\" signal), and fire\n  `updateTiltifySettings({ <sliceKey> })` whenever the local\n  `xEnabled` toggle flips so the pipeline dynamically un/subscribes.\n- `\"http\"` — delegate to the matching `@playlive/react-query` REST\n  hook (`useRewards`, `useTargets`, …) with the WS store slice\n  ignored.\n\nAll hybrid hooks share `useLoadingTimeout` — a 15 s safety-net that\nforce-completes the WS branch when the store never delivers.\n\n| Hook                                                     | Returns                                                                        |\n| -------------------------------------------------------- | ------------------------------------------------------------------------------ |\n| `useHybridRewards(params)`                               | `{ currentRewards, setHasLoadedRewards, hasLoadedRewards, isLoadingRewards, isLoading, setRewardsEnabled, rewardsEnabled }` |\n| `useHybridTargets(params)`                               | `{ currentTargets, setHasLoadedTargets, hasLoadedTargets, isLoading, targetsEnabled, isLoadingTargets, setTargetsEnabled }` |\n| `useHybridMilestones(params)`                            | `{ isLoadingMilestones, currentMilestones, milestonesEnabled, setMilestonesEnabled }` |\n| `useHybridPolls(params)`                                 | `{ isLoadingPolls, currentPolls, pollsEnabled, setPollsEnabled }`              |\n| `useHybridCause({ mode, initialCampaign })`              | `{ cause, isLoading }` — single-slot semantics (uses `getFirstCause`).         |\n| `useHybridFundraisingEvent({ mode, initialCampaign })`   | `{ fundraisingEvent, isLoading }` — single-slot semantics.                     |\n| `useHybridFundraisingEventSupportingCampaigns(params)`   | `{ supportingCampaigns, isLoading, setSupportingCampaignsEnabled, supportingCampaignsEnabled, isLoadingSupportingCampaigns, hasLoadedSupportingCampaigns }` |\n| `useHybridAuctionHouses({ mode, enabled, campaignID })`  | `{ auctionHouses, auctionHouseTotal, auctionHousesEnabled, setAuctionHousesEnabled }` — WS-only (no REST fallback yet). |\n| `useLoadingTimeout(params)`                              | `void` — safety-net timer primitive shared by every hook above.                |\n\nReturn shapes are preserved verbatim from the overlay so overlay\ncall-sites can eventually collapse to a re-export.\n\n### Fusion — baseline + WS delta hooks (`/fusion`)\n\n| Hook                                        | Returns                                                                     |\n| ------------------------------------------- | --------------------------------------------------------------------------- |\n| `useCurrentBlockRaised(campaignID, start, end, opts?)` | `{ raised, data, isLoading, isError, refetch }` — REST baseline + WS delta. `opts.demoMode: true` short-circuits to `DemoProvider.getDemoScheduleBlockRaised` / `getDemoBlockRaised` fallback. |\n| `useLiveSchedule(params, opts?)`            | `{ schedule, isLoading, isError, refetch }` — WS-refetch on transition. `opts.demoMode: true` (+ optional `demoIntervalMs`) short-circuits to `DemoProvider.getDemoSchedule` / `buildDemoSchedule` fallback with a live tick timer.     |\n| `useCurrentScheduleItem(schedule, opts?)`   | `{ currentItem, nextItem }` — pure-client rollover projection.              |\n| `useLinkedTeamCampaign(params, opts?)`      | `{ linkedTeamCampaign, … }` — WS-store first, HTTP fallback.                |\n| `useDonorSpotlight(params, opts?)`          | `{ spotlight, isLoading, refetch }` — WS store preferred + REST snapshot.   |\n\n### Legacy aliases (`/legacy`)\n\nSoft-deprecated. Identical behavior to the canonical exports —\nrename the import specifier once and the rest of your code base\nkeeps working unchanged:\n\n| Legacy name            | Canonical replacement                                          |\n| ---------------------- | -------------------------------------------------------------- |\n| `useUDPStore`          | `usePipelineValue` (Tier 1) / typed slice hooks (Tier 2)       |\n| `useUDPStoreApi`       | (use a typed slice hook; or reach for the raw store API)       |\n| `createUDPStore`       | `createPipelineStore` from `@playlive/realtime-pipeline/store` |\n| `UDPStore` (type)      | `PipelineStore` from `@playlive/realtime-pipeline/store`       |\n| `UDPStoreApi` (type)   | `PipelineStoreApi` from `@playlive/realtime-pipeline/store`    |\n| `CharityTypes`         | re-exported from `@playlive/realtime-pipeline/protocol`         |\n\nPlus every Tier 1+2+3 export under its canonical name.\n\n## Migration from `playlive-overlay-data-layer`\n\nOne-shot sed-friendly rewrite of every import specifier (no body\nedits needed for the happy path):\n\n```bash\n# Tier 3 — kitchen-sink (drop-in replacement)\nrg -l '@playlive/overlay-data-layer/websocket' . \\\n  | xargs sed -i '' \\\n    -e 's|@playlive/overlay-data-layer/websocket|@playlive/react-pipeline/provider|g'\n```\n\nFor the soft-deprecated names (`useUDPStore`, `createUDPStore`, …)\nsubstitute `/legacy` instead of `/provider` to opt into the alias\nbundle, then migrate at your leisure (PRD §10 — one-release window).\n\nSee the phase-8 row in the root [MIGRATION.md](../../MIGRATION.md) for\nthe full per-symbol mapping plus call-site walkthroughs.\n\n## Upstream spec\n\nNo external API surface — internal workspace package. All transport\nruns through `@playlive/realtime-pipeline/connection`.\n\n## Twitch Extension URL disclosure\n\n`KNOWN_URLS` enumerates every absolute URL or host this package can\nfetch. **It is empty.** This package opens WebSocket connections only\nto URLs the consumer passes to `<RealtimePipelineProvider url={…}>`;\nthose URLs must be added to your Extension submission's URL\ndisclosure list, not this one.\n\n```ts\nimport { KNOWN_URLS } from \"@playlive/react-pipeline\";\nconsole.log(KNOWN_URLS); // []\n```\n\nSee [`docs/twitch-extension-checklist.md`](../../docs/twitch-extension-checklist.md).\n\n## Examples\n\nLive overlay apps consuming this provider land under `apps/*`\n(phase 10). Until then, the Tier 1 quick-start above is the minimum\nviable wiring.\n\n## Contributing\n\nSee [CONTRIBUTING.md](../../CONTRIBUTING.md). To add new Tier-2\nhooks, mirror an existing slice hook in `src/hooks/index.ts` and\nadd a `it.each` entry in `tests/unit/index.test.ts`.\n\n## License\n\nMIT — see [LICENSE](../../LICENSE). Distributed via Play Live\nCodeArtifact (PRD §6).\n","readmeFilename":"README.md","dist":{"tarball":"https://packages.playlive.experience.stjude.org/@playlive/react-pipeline/-/react-pipeline-0.4.14.tgz","shasum":"7b0ed186b2655aa8004633ae0cc448864aef346e","integrity":"sha512-PQKT8ubvPUgm8RZPwPIr4frD13qXEu5MxYrHo5wc24en3OSvT3Sz4kMI5ls8VOtms0YtWhluy8x13Z5yx8KswA=="}},"0.4.15":{"name":"@playlive/react-pipeline","version":"0.4.15","description":"React bindings for @playlive/realtime-pipeline","type":"module","sideEffects":false,"main":"./index.js","types":"./index.d.ts","exports":{".":{"import":"./index.js","types":"./index.d.ts"},"./provider":{"import":"./provider/index.js","types":"./provider/index.d.ts"},"./legacy":{"import":"./legacy/index.js","types":"./legacy/index.d.ts"},"./fusion":{"import":"./fusion/index.js","types":"./fusion/index.d.ts"}},"dependencies":{},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","zustand":"^5.0.0","@playlive/realtime-pipeline":"^0.2.8","@playlive/tiltify-core":"^0.4.11","@tanstack/react-query":"^5.0.0","@playlive/react-query":"^0.3.4","@playlive/fundraiser-data":"^0.4.2"},"peerDependenciesMeta":{"@tanstack/react-query":{"optional":true},"@playlive/react-query":{"optional":true},"@playlive/fundraiser-data":{"optional":true}},"playlive":{"target":"browser","frontendEligible":true,"coverageFloor":85},"publishConfig":{"access":"restricted","registry":"https://playlive-767397689694.d.codeartifact.us-east-1.amazonaws.com/npm/playlive/"},"integrity":"sha512-1AMzoHbqGz5rN594drfANEzDccay4/m7OlKpCT92F0HGml/9cu9FaFPSK5lwdQNnXrHjo2P0sa1+FeuPSx6/tA==","shasum":"1bcf973729c975c81ffd987251a405b90cdc1cab","readme":"# @playlive/react-pipeline\n\nReact bindings for [`@playlive/realtime-pipeline`](../realtime-pipeline/).\nDrop-in replacement for `playlive-overlay-data-layer/src/websocket/*`\nwith **zero** dependency on `react-use-websocket-lite` — built directly\non Zustand v5's `useStore` + `useSyncExternalStore`.\n\n![Coverage](./coverage-badge.svg)\n\n## Three tiers — take only what you need\n\nPick the surface that matches how much of the legacy\n`UnifiedDataPipelineProvider` API you actually want. Each tier is\nstrictly larger than the last and lives at its own subpath so unused\ncode is tree-shaken out.\n\n| Tier | Subpath                            | Surface                                                                         | Use when                                                              |\n| ---- | ---------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| 1    | `@playlive/react-pipeline`         | `RealtimePipelineProvider` + `usePipelineValue` (universal selector)            | Greenfield app; you want one selector primitive and nothing else.     |\n| 2    | `@playlive/react-pipeline`         | Tier 1 **plus** 22 typed per-slice hooks (`usePipelineDonations(id)`, …)        | You want stable, typed one-liners for the common reads.               |\n| 3    | `@playlive/react-pipeline/provider`| Tier 2 **plus** `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, etc.   | Porting an existing overlay-data-layer consumer with minimal churn.   |\n| —    | `@playlive/react-pipeline/legacy`  | Tier 3 **plus** `useUDPStore` / `useUDPStoreApi` / `createUDPStore` aliases     | Soft-deprecated shim for the legacy symbol names (one-release window).|\n\n## Install\n\n```bash\nbun add @playlive/react-pipeline\nbun add -d react react-dom zustand\n```\n\nPeer dependencies (jose-style — consumer brings their own):\n`react@^19`, `react-dom@^19`, `zustand@^5`,\n`@playlive/realtime-pipeline` (workspace), `@playlive/tiltify-core`\n(workspace, type-only — stripped at compile time).\n\nNothing else ships in the bundle.\n\n## Quick start — Tier 1\n\n```tsx\nimport { RealtimePipelineProvider, usePipelineCampaigns } from \"@playlive/react-pipeline\";\n\nfunction App() {\n  return (\n    <RealtimePipelineProvider\n      url=\"wss://pipeline.example.com/socket\"\n      charityType=\"tiltify\"\n      autoConnect\n    >\n      <Overlay />\n    </RealtimePipelineProvider>\n  );\n}\n\nfunction Overlay() {\n  const campaigns = usePipelineCampaigns();\n  return <ul>{campaigns.map((c) => <li key={c.id}>{c.name}</li>)}</ul>;\n}\n```\n\n`RealtimePipelineProvider` mounts a Zustand store + opens (or accepts)\na `PipelineConnection`. Children call any Tier 1 / Tier 2 hook to\nsubscribe.\n\n## Subpath exports\n\n| Subpath                              | Description                                                                       |\n| ------------------------------------ | --------------------------------------------------------------------------------- |\n| `@playlive/react-pipeline`           | Default barrel — Tier 1 + Tier 2 (provider + 21 typed slice hooks).               |\n| `@playlive/react-pipeline/provider`  | Tier 3 — `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, add-pipeline\\*. |\n| `@playlive/react-pipeline/fusion`    | Cross-package composition hooks (REST baseline + WS delta) — `useCurrentBlockRaised`, `useLiveSchedule`, `useCurrentScheduleItem`, `useLinkedTeamCampaign`, `useDonorSpotlight`, plus the WS-store-first / HTTP-fallback hybrid slice hooks (`useHybridRewards`, `useHybridTargets`, `useHybridMilestones`, `useHybridPolls`, `useHybridCause`, `useHybridFundraisingEvent`, `useHybridFundraisingEventSupportingCampaigns`, `useHybridAuctionHouses`) + the shared `useLoadingTimeout` primitive. |\n| `@playlive/react-pipeline/legacy`    | Soft-deprecated alias bundle (`useUDPStore`, `createUDPStore`, `UDPStore`, etc.). |\n\nEach subpath ships an ESM bundle, a Bun source condition, and `.d.ts`\ndeclarations. Frontend-eligible bundles are not minified (PRD §6.1)\nso stack traces stay readable in production.\n\n## API reference\n\n### Tier 1 — universal primitive\n\n| Export                          | Signature                                                            |\n| ------------------------------- | -------------------------------------------------------------------- |\n| `RealtimePipelineProvider`      | `(props: RealtimePipelineProviderProps) => JSX.Element`              |\n| `usePipelineValue`              | `<T>(selector: (s: PipelineStore) => T) => T`                        |\n\n`usePipelineValue` is single-arg by design. Zustand v5 dropped the\nthird `equalityFn` parameter on `useStore` to keep the package\nfree of the `use-sync-external-store` shim peer. For shallow / custom\nequality, wrap your selector with `useShallow` from\n`zustand/shallow` (zero-extra-dep):\n\n```tsx\nimport { useShallow } from \"zustand/shallow\";\nimport { usePipelineValue } from \"@playlive/react-pipeline\";\n\nconst { connected, connecting } = usePipelineValue(\n  useShallow((s) => ({ connected: s.connected, connecting: s.connecting })),\n);\n```\n\n### Tier 2 — typed per-slice hooks\n\nAll 22 hooks are stable wrappers over `usePipelineValue` with a typed\nselector + memo-stable empty-array fallbacks (no infinite-render\nchurn from fresh `[]` allocations — Decision §3 in CHANGELOG).\n\n| Hook                              | Returns                                                             |\n| --------------------------------- | ------------------------------------------------------------------- |\n| `usePipelineCampaigns()`          | `TiltifyCampaign[]`                                                 |\n| `usePipelineTeamCampaigns()`      | `TiltifyTeamCampaign[]`                                             |\n| `usePipelineFundraisingEvents()`  | `TiltifyFundraisingEvent[]`                                         |\n| `usePipelineCauses()`             | `TiltifyCause[]`                                                    |\n| `usePipelineDonations()`          | `Record<string, TiltifyDonationWithTestFlag[]>`                     |\n| `usePipelineDonations(id)`        | `TiltifyDonationWithTestFlag[]` (memo-stable `[]` if id unknown)    |\n| `usePipelineDonationSum(id, opts?)` | `PipelineDonationSumResult` — live per-currency sum over `[start, end)` of the WS donation firehose. Pair with the REST `/schedules/campaigns/{id}/raised` `asOf` field to replace polling with a `baseline + WS-delta` view. |\n| `usePipelineDonationTrains(id, withStatus?)` | `DonationTrain[]`                                        |\n| `usePipelineSubathonTimer(id, withStatus?)`  | `SubathonTimer[]`                                        |\n| `usePipelineDonorSpotlight(id)`   | `ComputedSpotlight \\| null`                                         |\n| `usePipelinePolls(id)`            | `TiltifyPoll[]`                                                     |\n| `usePipelineRewards(id)`          | `TiltifyReward[]`                                                   |\n| `usePipelineTargets(id)`          | `TiltifyTarget[]`                                                   |\n| `usePipelineMilestones(id)`       | `TiltifyMilestone[]`                                                |\n| `usePipelineLeaderboardEntries(id)` | `TiltifyLeaderboardEntry[]`                                       |\n| `usePipelineAuctionHouses()`      | `AuctionHouse[]`                                                    |\n| `usePipelineTwitchChat()`         | `TwitchChatMessage[]`                                               |\n| `usePipelineConnectionState()`    | `PipelineConnectionState` (10 split selectors — no false re-renders)|\n| `usePipelineConnectionActions()`  | `PipelineConnectionActions` (stable `connect/disconnect/refresh/…`) |\n| `usePipelineSubscriptions()`      | `Record<string, TiltifyWebhookSubscription>`                        |\n| `usePipelineTiltifySettings()`    | `TiltifyWebSocketSettings`                                          |\n| `usePipelineLastRefreshResponse()`| `RefreshResponseMessage \\| null`                                    |\n| `usePipelineDonorSpotlightSettings()` | `Record<string, DonorSpotlightSettings>`                        |\n\n### Tier 3 — kitchen-sink provider (`/provider`)\n\n| Export                              | Notes                                                                                  |\n| ----------------------------------- | -------------------------------------------------------------------------------------- |\n| `UnifiedDataPipelineProvider`       | Superset of `RealtimePipelineProvider`; adds legacy `reloadOnErrorReconnect` + `debug`.|\n| `useUnifiedDataPipeline()`          | One-call kitchen-sink hook. Returns every slice + actions + legacy field aliases.      |\n| `useAddPipelineCampaignIDs(ids)`    | Effect hook — registers campaign ids with the store on mount.                          |\n| `useAddPipelineCampaigns(items)`    | Effect hook — registers full campaign records on mount.                                |\n| `useAddPipelineFundraisingEventIDs(ids)` | Effect hook — registers fundraising-event ids on mount.                           |\n| `useCommonContextHooks(…)`          | URL / mode / charity-type sync to the store. Same shape as the legacy hook.            |\n| `useIdentify()` / `useIdentify(…11 legacy args)` | Zero-arg returns the store's bound `identify` action. Positional arg form is preserved as an `@deprecated` overload — args are ignored, warns once. |\n\n### Fusion — hybrid slice hooks (`/fusion`)\n\nOverlay-parity `usePipeline*` hooks lifted from\n`playlive-overlay-vite/src/hooks/pipeline/` so every overlay that\nwants the same WS-store-first / HTTP-fallback shape can drop the\nlocal copy. Every hook accepts a `mode: \"ws\" | \"http\"` param that\npicks between:\n\n- `\"ws\"` — read the Tier-2 store selector for the slice, latch a\n  local `hasLoadedX` boolean on the first slice arrival (or on the\n  server-side \"feature disabled\" signal), and fire\n  `updateTiltifySettings({ <sliceKey> })` whenever the local\n  `xEnabled` toggle flips so the pipeline dynamically un/subscribes.\n- `\"http\"` — delegate to the matching `@playlive/react-query` REST\n  hook (`useRewards`, `useTargets`, …) with the WS store slice\n  ignored.\n\nAll hybrid hooks share `useLoadingTimeout` — a 15 s safety-net that\nforce-completes the WS branch when the store never delivers.\n\n| Hook                                                     | Returns                                                                        |\n| -------------------------------------------------------- | ------------------------------------------------------------------------------ |\n| `useHybridRewards(params)`                               | `{ currentRewards, setHasLoadedRewards, hasLoadedRewards, isLoadingRewards, isLoading, setRewardsEnabled, rewardsEnabled }` |\n| `useHybridTargets(params)`                               | `{ currentTargets, setHasLoadedTargets, hasLoadedTargets, isLoading, targetsEnabled, isLoadingTargets, setTargetsEnabled }` |\n| `useHybridMilestones(params)`                            | `{ isLoadingMilestones, currentMilestones, milestonesEnabled, setMilestonesEnabled }` |\n| `useHybridPolls(params)`                                 | `{ isLoadingPolls, currentPolls, pollsEnabled, setPollsEnabled }`              |\n| `useHybridCause({ mode, initialCampaign })`              | `{ cause, isLoading }` — single-slot semantics (uses `getFirstCause`).         |\n| `useHybridFundraisingEvent({ mode, initialCampaign })`   | `{ fundraisingEvent, isLoading }` — single-slot semantics.                     |\n| `useHybridFundraisingEventSupportingCampaigns(params)`   | `{ supportingCampaigns, isLoading, setSupportingCampaignsEnabled, supportingCampaignsEnabled, isLoadingSupportingCampaigns, hasLoadedSupportingCampaigns }` |\n| `useHybridAuctionHouses({ mode, enabled, campaignID })`  | `{ auctionHouses, auctionHouseTotal, auctionHousesEnabled, setAuctionHousesEnabled }` — WS-only (no REST fallback yet). |\n| `useLoadingTimeout(params)`                              | `void` — safety-net timer primitive shared by every hook above.                |\n\nReturn shapes are preserved verbatim from the overlay so overlay\ncall-sites can eventually collapse to a re-export.\n\n### Fusion — baseline + WS delta hooks (`/fusion`)\n\n| Hook                                        | Returns                                                                     |\n| ------------------------------------------- | --------------------------------------------------------------------------- |\n| `useCurrentBlockRaised(campaignID, start, end, opts?)` | `{ raised, data, isLoading, isError, refetch }` — REST baseline + WS delta. `opts.demoMode: true` short-circuits to `DemoProvider.getDemoScheduleBlockRaised` / `getDemoBlockRaised` fallback. |\n| `useLiveSchedule(params, opts?)`            | `{ schedule, isLoading, isError, refetch }` — WS-refetch on transition. `opts.demoMode: true` (+ optional `demoIntervalMs`) short-circuits to `DemoProvider.getDemoSchedule` / `buildDemoSchedule` fallback with a live tick timer.     |\n| `useCurrentScheduleItem(schedule, opts?)`   | `{ currentItem, nextItem }` — pure-client rollover projection.              |\n| `useLinkedTeamCampaign(params, opts?)`      | `{ linkedTeamCampaign, … }` — WS-store first, HTTP fallback.                |\n| `useDonorSpotlight(params, opts?)`          | `{ spotlight, isLoading, refetch }` — WS store preferred + REST snapshot.   |\n\n### Legacy aliases (`/legacy`)\n\nSoft-deprecated. Identical behavior to the canonical exports —\nrename the import specifier once and the rest of your code base\nkeeps working unchanged:\n\n| Legacy name            | Canonical replacement                                          |\n| ---------------------- | -------------------------------------------------------------- |\n| `useUDPStore`          | `usePipelineValue` (Tier 1) / typed slice hooks (Tier 2)       |\n| `useUDPStoreApi`       | (use a typed slice hook; or reach for the raw store API)       |\n| `createUDPStore`       | `createPipelineStore` from `@playlive/realtime-pipeline/store` |\n| `UDPStore` (type)      | `PipelineStore` from `@playlive/realtime-pipeline/store`       |\n| `UDPStoreApi` (type)   | `PipelineStoreApi` from `@playlive/realtime-pipeline/store`    |\n| `CharityTypes`         | re-exported from `@playlive/realtime-pipeline/protocol`         |\n\nPlus every Tier 1+2+3 export under its canonical name.\n\n## Migration from `playlive-overlay-data-layer`\n\nOne-shot sed-friendly rewrite of every import specifier (no body\nedits needed for the happy path):\n\n```bash\n# Tier 3 — kitchen-sink (drop-in replacement)\nrg -l '@playlive/overlay-data-layer/websocket' . \\\n  | xargs sed -i '' \\\n    -e 's|@playlive/overlay-data-layer/websocket|@playlive/react-pipeline/provider|g'\n```\n\nFor the soft-deprecated names (`useUDPStore`, `createUDPStore`, …)\nsubstitute `/legacy` instead of `/provider` to opt into the alias\nbundle, then migrate at your leisure (PRD §10 — one-release window).\n\nSee the phase-8 row in the root [MIGRATION.md](../../MIGRATION.md) for\nthe full per-symbol mapping plus call-site walkthroughs.\n\n## Upstream spec\n\nNo external API surface — internal workspace package. All transport\nruns through `@playlive/realtime-pipeline/connection`.\n\n## Twitch Extension URL disclosure\n\n`KNOWN_URLS` enumerates every absolute URL or host this package can\nfetch. **It is empty.** This package opens WebSocket connections only\nto URLs the consumer passes to `<RealtimePipelineProvider url={…}>`;\nthose URLs must be added to your Extension submission's URL\ndisclosure list, not this one.\n\n```ts\nimport { KNOWN_URLS } from \"@playlive/react-pipeline\";\nconsole.log(KNOWN_URLS); // []\n```\n\nSee [`docs/twitch-extension-checklist.md`](../../docs/twitch-extension-checklist.md).\n\n## Examples\n\nLive overlay apps consuming this provider land under `apps/*`\n(phase 10). Until then, the Tier 1 quick-start above is the minimum\nviable wiring.\n\n## Contributing\n\nSee [CONTRIBUTING.md](../../CONTRIBUTING.md). To add new Tier-2\nhooks, mirror an existing slice hook in `src/hooks/index.ts` and\nadd a `it.each` entry in `tests/unit/index.test.ts`.\n\n## License\n\nMIT — see [LICENSE](../../LICENSE). Distributed via Play Live\nCodeArtifact (PRD §6).\n","readmeFilename":"README.md","dist":{"tarball":"https://packages.playlive.experience.stjude.org/@playlive/react-pipeline/-/react-pipeline-0.4.15.tgz","shasum":"1bcf973729c975c81ffd987251a405b90cdc1cab","integrity":"sha512-1AMzoHbqGz5rN594drfANEzDccay4/m7OlKpCT92F0HGml/9cu9FaFPSK5lwdQNnXrHjo2P0sa1+FeuPSx6/tA=="}},"0.4.16":{"name":"@playlive/react-pipeline","version":"0.4.16","description":"React bindings for @playlive/realtime-pipeline","type":"module","sideEffects":false,"main":"./index.js","types":"./index.d.ts","exports":{".":{"import":"./index.js","types":"./index.d.ts"},"./provider":{"import":"./provider/index.js","types":"./provider/index.d.ts"},"./legacy":{"import":"./legacy/index.js","types":"./legacy/index.d.ts"},"./fusion":{"import":"./fusion/index.js","types":"./fusion/index.d.ts"}},"dependencies":{},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","zustand":"^5.0.0","@playlive/realtime-pipeline":"^0.2.8","@playlive/tiltify-core":"^0.4.11","@tanstack/react-query":"^5.0.0","@playlive/react-query":"^0.3.4","@playlive/fundraiser-data":"^0.4.2"},"peerDependenciesMeta":{"@tanstack/react-query":{"optional":true},"@playlive/react-query":{"optional":true},"@playlive/fundraiser-data":{"optional":true}},"playlive":{"target":"browser","frontendEligible":true,"coverageFloor":85},"publishConfig":{"access":"restricted","registry":"https://playlive-767397689694.d.codeartifact.us-east-1.amazonaws.com/npm/playlive/"},"integrity":"sha512-DSCt6tRiWVXNlaO+oG4q79oJhrZSVVSU05SNCAlw+ZUuCCAbW4HFYFxHQ3bqIpkK4ShYQkFQqmxrd8hXMV18Xw==","shasum":"28d9d10f68239d3fae203387dcd4e01b700eee69","readme":"# @playlive/react-pipeline\n\nReact bindings for [`@playlive/realtime-pipeline`](../realtime-pipeline/).\nDrop-in replacement for `playlive-overlay-data-layer/src/websocket/*`\nwith **zero** dependency on `react-use-websocket-lite` — built directly\non Zustand v5's `useStore` + `useSyncExternalStore`.\n\n![Coverage](./coverage-badge.svg)\n\n## Three tiers — take only what you need\n\nPick the surface that matches how much of the legacy\n`UnifiedDataPipelineProvider` API you actually want. Each tier is\nstrictly larger than the last and lives at its own subpath so unused\ncode is tree-shaken out.\n\n| Tier | Subpath                            | Surface                                                                         | Use when                                                              |\n| ---- | ---------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| 1    | `@playlive/react-pipeline`         | `RealtimePipelineProvider` + `usePipelineValue` (universal selector)            | Greenfield app; you want one selector primitive and nothing else.     |\n| 2    | `@playlive/react-pipeline`         | Tier 1 **plus** 22 typed per-slice hooks (`usePipelineDonations(id)`, …)        | You want stable, typed one-liners for the common reads.               |\n| 3    | `@playlive/react-pipeline/provider`| Tier 2 **plus** `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, etc.   | Porting an existing overlay-data-layer consumer with minimal churn.   |\n| —    | `@playlive/react-pipeline/legacy`  | Tier 3 **plus** `useUDPStore` / `useUDPStoreApi` / `createUDPStore` aliases     | Soft-deprecated shim for the legacy symbol names (one-release window).|\n\n## Install\n\n```bash\nbun add @playlive/react-pipeline\nbun add -d react react-dom zustand\n```\n\nPeer dependencies (jose-style — consumer brings their own):\n`react@^19`, `react-dom@^19`, `zustand@^5`,\n`@playlive/realtime-pipeline` (workspace), `@playlive/tiltify-core`\n(workspace, type-only — stripped at compile time).\n\nNothing else ships in the bundle.\n\n## Quick start — Tier 1\n\n```tsx\nimport { RealtimePipelineProvider, usePipelineCampaigns } from \"@playlive/react-pipeline\";\n\nfunction App() {\n  return (\n    <RealtimePipelineProvider\n      url=\"wss://pipeline.example.com/socket\"\n      charityType=\"tiltify\"\n      autoConnect\n    >\n      <Overlay />\n    </RealtimePipelineProvider>\n  );\n}\n\nfunction Overlay() {\n  const campaigns = usePipelineCampaigns();\n  return <ul>{campaigns.map((c) => <li key={c.id}>{c.name}</li>)}</ul>;\n}\n```\n\n`RealtimePipelineProvider` mounts a Zustand store + opens (or accepts)\na `PipelineConnection`. Children call any Tier 1 / Tier 2 hook to\nsubscribe.\n\n## Subpath exports\n\n| Subpath                              | Description                                                                       |\n| ------------------------------------ | --------------------------------------------------------------------------------- |\n| `@playlive/react-pipeline`           | Default barrel — Tier 1 + Tier 2 (provider + 21 typed slice hooks).               |\n| `@playlive/react-pipeline/provider`  | Tier 3 — `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, add-pipeline\\*. |\n| `@playlive/react-pipeline/fusion`    | Cross-package composition hooks (REST baseline + WS delta) — `useCurrentBlockRaised`, `useLiveSchedule`, `useCurrentScheduleItem`, `useLinkedTeamCampaign`, `useDonorSpotlight`, plus the WS-store-first / HTTP-fallback hybrid slice hooks (`useHybridRewards`, `useHybridTargets`, `useHybridMilestones`, `useHybridPolls`, `useHybridCause`, `useHybridFundraisingEvent`, `useHybridFundraisingEventSupportingCampaigns`, `useHybridAuctionHouses`) + the shared `useLoadingTimeout` primitive. |\n| `@playlive/react-pipeline/legacy`    | Soft-deprecated alias bundle (`useUDPStore`, `createUDPStore`, `UDPStore`, etc.). |\n\nEach subpath ships an ESM bundle, a Bun source condition, and `.d.ts`\ndeclarations. Frontend-eligible bundles are not minified (PRD §6.1)\nso stack traces stay readable in production.\n\n## API reference\n\n### Tier 1 — universal primitive\n\n| Export                          | Signature                                                            |\n| ------------------------------- | -------------------------------------------------------------------- |\n| `RealtimePipelineProvider`      | `(props: RealtimePipelineProviderProps) => JSX.Element`              |\n| `usePipelineValue`              | `<T>(selector: (s: PipelineStore) => T) => T`                        |\n\n`usePipelineValue` is single-arg by design. Zustand v5 dropped the\nthird `equalityFn` parameter on `useStore` to keep the package\nfree of the `use-sync-external-store` shim peer. For shallow / custom\nequality, wrap your selector with `useShallow` from\n`zustand/shallow` (zero-extra-dep):\n\n```tsx\nimport { useShallow } from \"zustand/shallow\";\nimport { usePipelineValue } from \"@playlive/react-pipeline\";\n\nconst { connected, connecting } = usePipelineValue(\n  useShallow((s) => ({ connected: s.connected, connecting: s.connecting })),\n);\n```\n\n### Tier 2 — typed per-slice hooks\n\nAll 22 hooks are stable wrappers over `usePipelineValue` with a typed\nselector + memo-stable empty-array fallbacks (no infinite-render\nchurn from fresh `[]` allocations — Decision §3 in CHANGELOG).\n\n| Hook                              | Returns                                                             |\n| --------------------------------- | ------------------------------------------------------------------- |\n| `usePipelineCampaigns()`          | `TiltifyCampaign[]`                                                 |\n| `usePipelineTeamCampaigns()`      | `TiltifyTeamCampaign[]`                                             |\n| `usePipelineFundraisingEvents()`  | `TiltifyFundraisingEvent[]`                                         |\n| `usePipelineCauses()`             | `TiltifyCause[]`                                                    |\n| `usePipelineDonations()`          | `Record<string, TiltifyDonationWithTestFlag[]>`                     |\n| `usePipelineDonations(id)`        | `TiltifyDonationWithTestFlag[]` (memo-stable `[]` if id unknown)    |\n| `usePipelineDonationSum(id, opts?)` | `PipelineDonationSumResult` — live per-currency sum over `[start, end)` of the WS donation firehose. Pair with the REST `/schedules/campaigns/{id}/raised` `asOf` field to replace polling with a `baseline + WS-delta` view. |\n| `usePipelineDonationTrains(id, withStatus?)` | `DonationTrain[]`                                        |\n| `usePipelineSubathonTimer(id, withStatus?)`  | `SubathonTimer[]`                                        |\n| `usePipelineDonorSpotlight(id)`   | `ComputedSpotlight \\| null`                                         |\n| `usePipelinePolls(id)`            | `TiltifyPoll[]`                                                     |\n| `usePipelineRewards(id)`          | `TiltifyReward[]`                                                   |\n| `usePipelineTargets(id)`          | `TiltifyTarget[]`                                                   |\n| `usePipelineMilestones(id)`       | `TiltifyMilestone[]`                                                |\n| `usePipelineLeaderboardEntries(id)` | `TiltifyLeaderboardEntry[]`                                       |\n| `usePipelineAuctionHouses()`      | `AuctionHouse[]`                                                    |\n| `usePipelineTwitchChat()`         | `TwitchChatMessage[]`                                               |\n| `usePipelineConnectionState()`    | `PipelineConnectionState` (10 split selectors — no false re-renders)|\n| `usePipelineConnectionActions()`  | `PipelineConnectionActions` (stable `connect/disconnect/refresh/…`) |\n| `usePipelineSubscriptions()`      | `Record<string, TiltifyWebhookSubscription>`                        |\n| `usePipelineTiltifySettings()`    | `TiltifyWebSocketSettings`                                          |\n| `usePipelineLastRefreshResponse()`| `RefreshResponseMessage \\| null`                                    |\n| `usePipelineDonorSpotlightSettings()` | `Record<string, DonorSpotlightSettings>`                        |\n\n### Tier 3 — kitchen-sink provider (`/provider`)\n\n| Export                              | Notes                                                                                  |\n| ----------------------------------- | -------------------------------------------------------------------------------------- |\n| `UnifiedDataPipelineProvider`       | Superset of `RealtimePipelineProvider`; adds legacy `reloadOnErrorReconnect` + `debug`.|\n| `useUnifiedDataPipeline()`          | One-call kitchen-sink hook. Returns every slice + actions + legacy field aliases.      |\n| `useAddPipelineCampaignIDs(ids)`    | Effect hook — registers campaign ids with the store on mount.                          |\n| `useAddPipelineCampaigns(items)`    | Effect hook — registers full campaign records on mount.                                |\n| `useAddPipelineFundraisingEventIDs(ids)` | Effect hook — registers fundraising-event ids on mount.                           |\n| `useCommonContextHooks(…)`          | URL / mode / charity-type sync to the store. Same shape as the legacy hook.            |\n| `useIdentify()` / `useIdentify(…11 legacy args)` | Zero-arg returns the store's bound `identify` action. Positional arg form is preserved as an `@deprecated` overload — args are ignored, warns once. |\n\n### Fusion — hybrid slice hooks (`/fusion`)\n\nOverlay-parity `usePipeline*` hooks lifted from\n`playlive-overlay-vite/src/hooks/pipeline/` so every overlay that\nwants the same WS-store-first / HTTP-fallback shape can drop the\nlocal copy. Every hook accepts a `mode: \"ws\" | \"http\"` param that\npicks between:\n\n- `\"ws\"` — read the Tier-2 store selector for the slice, latch a\n  local `hasLoadedX` boolean on the first slice arrival (or on the\n  server-side \"feature disabled\" signal), and fire\n  `updateTiltifySettings({ <sliceKey> })` whenever the local\n  `xEnabled` toggle flips so the pipeline dynamically un/subscribes.\n- `\"http\"` — delegate to the matching `@playlive/react-query` REST\n  hook (`useRewards`, `useTargets`, …) with the WS store slice\n  ignored.\n\nAll hybrid hooks share `useLoadingTimeout` — a 15 s safety-net that\nforce-completes the WS branch when the store never delivers.\n\n| Hook                                                     | Returns                                                                        |\n| -------------------------------------------------------- | ------------------------------------------------------------------------------ |\n| `useHybridRewards(params)`                               | `{ currentRewards, setHasLoadedRewards, hasLoadedRewards, isLoadingRewards, isLoading, setRewardsEnabled, rewardsEnabled }` |\n| `useHybridTargets(params)`                               | `{ currentTargets, setHasLoadedTargets, hasLoadedTargets, isLoading, targetsEnabled, isLoadingTargets, setTargetsEnabled }` |\n| `useHybridMilestones(params)`                            | `{ isLoadingMilestones, currentMilestones, milestonesEnabled, setMilestonesEnabled }` |\n| `useHybridPolls(params)`                                 | `{ isLoadingPolls, currentPolls, pollsEnabled, setPollsEnabled }`              |\n| `useHybridCause({ mode, initialCampaign })`              | `{ cause, isLoading }` — single-slot semantics (uses `getFirstCause`).         |\n| `useHybridFundraisingEvent({ mode, initialCampaign })`   | `{ fundraisingEvent, isLoading }` — single-slot semantics.                     |\n| `useHybridFundraisingEventSupportingCampaigns(params)`   | `{ supportingCampaigns, isLoading, setSupportingCampaignsEnabled, supportingCampaignsEnabled, isLoadingSupportingCampaigns, hasLoadedSupportingCampaigns }` |\n| `useHybridAuctionHouses({ mode, enabled, campaignID })`  | `{ auctionHouses, auctionHouseTotal, auctionHousesEnabled, setAuctionHousesEnabled }` — WS-only (no REST fallback yet). |\n| `useLoadingTimeout(params)`                              | `void` — safety-net timer primitive shared by every hook above.                |\n\nReturn shapes are preserved verbatim from the overlay so overlay\ncall-sites can eventually collapse to a re-export.\n\n### Fusion — baseline + WS delta hooks (`/fusion`)\n\n| Hook                                        | Returns                                                                     |\n| ------------------------------------------- | --------------------------------------------------------------------------- |\n| `useCurrentBlockRaised(campaignID, start, end, opts?)` | `{ raised, data, isLoading, isError, refetch }` — REST baseline + WS delta. `opts.demoMode: true` short-circuits to `DemoProvider.getDemoScheduleBlockRaised` / `getDemoBlockRaised` fallback. |\n| `useLiveSchedule(params, opts?)`            | `{ schedule, isLoading, isError, refetch }` — WS-refetch on transition. `opts.demoMode: true` (+ optional `demoIntervalMs`) short-circuits to `DemoProvider.getDemoSchedule` / `buildDemoSchedule` fallback with a live tick timer.     |\n| `useCurrentScheduleItem(schedule, opts?)`   | `{ currentItem, nextItem }` — pure-client rollover projection.              |\n| `useLinkedTeamCampaign(params, opts?)`      | `{ linkedTeamCampaign, … }` — WS-store first, HTTP fallback.                |\n| `useDonorSpotlight(params, opts?)`          | `{ spotlight, isLoading, refetch }` — WS store preferred + REST snapshot.   |\n\n### Legacy aliases (`/legacy`)\n\nSoft-deprecated. Identical behavior to the canonical exports —\nrename the import specifier once and the rest of your code base\nkeeps working unchanged:\n\n| Legacy name            | Canonical replacement                                          |\n| ---------------------- | -------------------------------------------------------------- |\n| `useUDPStore`          | `usePipelineValue` (Tier 1) / typed slice hooks (Tier 2)       |\n| `useUDPStoreApi`       | (use a typed slice hook; or reach for the raw store API)       |\n| `createUDPStore`       | `createPipelineStore` from `@playlive/realtime-pipeline/store` |\n| `UDPStore` (type)      | `PipelineStore` from `@playlive/realtime-pipeline/store`       |\n| `UDPStoreApi` (type)   | `PipelineStoreApi` from `@playlive/realtime-pipeline/store`    |\n| `CharityTypes`         | re-exported from `@playlive/realtime-pipeline/protocol`         |\n\nPlus every Tier 1+2+3 export under its canonical name.\n\n## Migration from `playlive-overlay-data-layer`\n\nOne-shot sed-friendly rewrite of every import specifier (no body\nedits needed for the happy path):\n\n```bash\n# Tier 3 — kitchen-sink (drop-in replacement)\nrg -l '@playlive/overlay-data-layer/websocket' . \\\n  | xargs sed -i '' \\\n    -e 's|@playlive/overlay-data-layer/websocket|@playlive/react-pipeline/provider|g'\n```\n\nFor the soft-deprecated names (`useUDPStore`, `createUDPStore`, …)\nsubstitute `/legacy` instead of `/provider` to opt into the alias\nbundle, then migrate at your leisure (PRD §10 — one-release window).\n\nSee the phase-8 row in the root [MIGRATION.md](../../MIGRATION.md) for\nthe full per-symbol mapping plus call-site walkthroughs.\n\n## Upstream spec\n\nNo external API surface — internal workspace package. All transport\nruns through `@playlive/realtime-pipeline/connection`.\n\n## Twitch Extension URL disclosure\n\n`KNOWN_URLS` enumerates every absolute URL or host this package can\nfetch. **It is empty.** This package opens WebSocket connections only\nto URLs the consumer passes to `<RealtimePipelineProvider url={…}>`;\nthose URLs must be added to your Extension submission's URL\ndisclosure list, not this one.\n\n```ts\nimport { KNOWN_URLS } from \"@playlive/react-pipeline\";\nconsole.log(KNOWN_URLS); // []\n```\n\nSee [`docs/twitch-extension-checklist.md`](../../docs/twitch-extension-checklist.md).\n\n## Examples\n\nLive overlay apps consuming this provider land under `apps/*`\n(phase 10). Until then, the Tier 1 quick-start above is the minimum\nviable wiring.\n\n## Contributing\n\nSee [CONTRIBUTING.md](../../CONTRIBUTING.md). To add new Tier-2\nhooks, mirror an existing slice hook in `src/hooks/index.ts` and\nadd a `it.each` entry in `tests/unit/index.test.ts`.\n\n## License\n\nMIT — see [LICENSE](../../LICENSE). Distributed via Play Live\nCodeArtifact (PRD §6).\n","readmeFilename":"README.md","dist":{"tarball":"https://packages.playlive.experience.stjude.org/@playlive/react-pipeline/-/react-pipeline-0.4.16.tgz","shasum":"28d9d10f68239d3fae203387dcd4e01b700eee69","integrity":"sha512-DSCt6tRiWVXNlaO+oG4q79oJhrZSVVSU05SNCAlw+ZUuCCAbW4HFYFxHQ3bqIpkK4ShYQkFQqmxrd8hXMV18Xw=="}},"0.4.17":{"name":"@playlive/react-pipeline","version":"0.4.17","description":"React bindings for @playlive/realtime-pipeline","type":"module","sideEffects":false,"main":"./index.js","types":"./index.d.ts","exports":{".":{"import":"./index.js","types":"./index.d.ts"},"./provider":{"import":"./provider/index.js","types":"./provider/index.d.ts"},"./legacy":{"import":"./legacy/index.js","types":"./legacy/index.d.ts"},"./fusion":{"import":"./fusion/index.js","types":"./fusion/index.d.ts"}},"dependencies":{},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","zustand":"^5.0.0","@playlive/realtime-pipeline":"^0.2.10","@playlive/tiltify-core":"^0.4.13","@tanstack/react-query":"^5.0.0","@playlive/react-query":"^0.3.4","@playlive/fundraiser-data":"^0.4.3"},"peerDependenciesMeta":{"@tanstack/react-query":{"optional":true},"@playlive/react-query":{"optional":true},"@playlive/fundraiser-data":{"optional":true}},"playlive":{"target":"browser","frontendEligible":true,"coverageFloor":85},"publishConfig":{"access":"restricted","registry":"https://playlive-767397689694.d.codeartifact.us-east-1.amazonaws.com/npm/playlive/"},"integrity":"sha512-GcBkvxda4uiVT7agkchXyx0jv5fPDZDqDpMwwoEnjpENpB5Vc+IWYKoKrAZh3OA37gWTi6Up+QTk53a2915SZA==","shasum":"02a498e90bb0daf6cbe9810d45c60fa25faa4719","readme":"# @playlive/react-pipeline\n\nReact bindings for [`@playlive/realtime-pipeline`](../realtime-pipeline/).\nDrop-in replacement for `playlive-overlay-data-layer/src/websocket/*`\nwith **zero** dependency on `react-use-websocket-lite` — built directly\non Zustand v5's `useStore` + `useSyncExternalStore`.\n\n![Coverage](./coverage-badge.svg)\n\n## Three tiers — take only what you need\n\nPick the surface that matches how much of the legacy\n`UnifiedDataPipelineProvider` API you actually want. Each tier is\nstrictly larger than the last and lives at its own subpath so unused\ncode is tree-shaken out.\n\n| Tier | Subpath                            | Surface                                                                         | Use when                                                              |\n| ---- | ---------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| 1    | `@playlive/react-pipeline`         | `RealtimePipelineProvider` + `usePipelineValue` (universal selector)            | Greenfield app; you want one selector primitive and nothing else.     |\n| 2    | `@playlive/react-pipeline`         | Tier 1 **plus** 22 typed per-slice hooks (`usePipelineDonations(id)`, …)        | You want stable, typed one-liners for the common reads.               |\n| 3    | `@playlive/react-pipeline/provider`| Tier 2 **plus** `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, etc.   | Porting an existing overlay-data-layer consumer with minimal churn.   |\n| —    | `@playlive/react-pipeline/legacy`  | Tier 3 **plus** `useUDPStore` / `useUDPStoreApi` / `createUDPStore` aliases     | Soft-deprecated shim for the legacy symbol names (one-release window).|\n\n## Install\n\n```bash\nbun add @playlive/react-pipeline\nbun add -d react react-dom zustand\n```\n\nPeer dependencies (jose-style — consumer brings their own):\n`react@^19`, `react-dom@^19`, `zustand@^5`,\n`@playlive/realtime-pipeline` (workspace), `@playlive/tiltify-core`\n(workspace, type-only — stripped at compile time).\n\nNothing else ships in the bundle.\n\n## Quick start — Tier 1\n\n```tsx\nimport { RealtimePipelineProvider, usePipelineCampaigns } from \"@playlive/react-pipeline\";\n\nfunction App() {\n  return (\n    <RealtimePipelineProvider\n      url=\"wss://pipeline.example.com/socket\"\n      charityType=\"tiltify\"\n      autoConnect\n    >\n      <Overlay />\n    </RealtimePipelineProvider>\n  );\n}\n\nfunction Overlay() {\n  const campaigns = usePipelineCampaigns();\n  return <ul>{campaigns.map((c) => <li key={c.id}>{c.name}</li>)}</ul>;\n}\n```\n\n`RealtimePipelineProvider` mounts a Zustand store + opens (or accepts)\na `PipelineConnection`. Children call any Tier 1 / Tier 2 hook to\nsubscribe.\n\n## Subpath exports\n\n| Subpath                              | Description                                                                       |\n| ------------------------------------ | --------------------------------------------------------------------------------- |\n| `@playlive/react-pipeline`           | Default barrel — Tier 1 + Tier 2 (provider + 21 typed slice hooks).               |\n| `@playlive/react-pipeline/provider`  | Tier 3 — `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, add-pipeline\\*. |\n| `@playlive/react-pipeline/fusion`    | Cross-package composition hooks (REST baseline + WS delta) — `useCurrentBlockRaised`, `useLiveSchedule`, `useCurrentScheduleItem`, `useLinkedTeamCampaign`, `useDonorSpotlight`, plus the WS-store-first / HTTP-fallback hybrid slice hooks (`useHybridRewards`, `useHybridTargets`, `useHybridMilestones`, `useHybridPolls`, `useHybridCause`, `useHybridFundraisingEvent`, `useHybridFundraisingEventSupportingCampaigns`, `useHybridAuctionHouses`) + the shared `useLoadingTimeout` primitive. |\n| `@playlive/react-pipeline/legacy`    | Soft-deprecated alias bundle (`useUDPStore`, `createUDPStore`, `UDPStore`, etc.). |\n\nEach subpath ships an ESM bundle, a Bun source condition, and `.d.ts`\ndeclarations. Frontend-eligible bundles are not minified (PRD §6.1)\nso stack traces stay readable in production.\n\n## API reference\n\n### Tier 1 — universal primitive\n\n| Export                          | Signature                                                            |\n| ------------------------------- | -------------------------------------------------------------------- |\n| `RealtimePipelineProvider`      | `(props: RealtimePipelineProviderProps) => JSX.Element`              |\n| `usePipelineValue`              | `<T>(selector: (s: PipelineStore) => T) => T`                        |\n\n`usePipelineValue` is single-arg by design. Zustand v5 dropped the\nthird `equalityFn` parameter on `useStore` to keep the package\nfree of the `use-sync-external-store` shim peer. For shallow / custom\nequality, wrap your selector with `useShallow` from\n`zustand/shallow` (zero-extra-dep):\n\n```tsx\nimport { useShallow } from \"zustand/shallow\";\nimport { usePipelineValue } from \"@playlive/react-pipeline\";\n\nconst { connected, connecting } = usePipelineValue(\n  useShallow((s) => ({ connected: s.connected, connecting: s.connecting })),\n);\n```\n\n### Tier 2 — typed per-slice hooks\n\nAll 22 hooks are stable wrappers over `usePipelineValue` with a typed\nselector + memo-stable empty-array fallbacks (no infinite-render\nchurn from fresh `[]` allocations — Decision §3 in CHANGELOG).\n\n| Hook                              | Returns                                                             |\n| --------------------------------- | ------------------------------------------------------------------- |\n| `usePipelineCampaigns()`          | `TiltifyCampaign[]`                                                 |\n| `usePipelineTeamCampaigns()`      | `TiltifyTeamCampaign[]`                                             |\n| `usePipelineFundraisingEvents()`  | `TiltifyFundraisingEvent[]`                                         |\n| `usePipelineCauses()`             | `TiltifyCause[]`                                                    |\n| `usePipelineDonations()`          | `Record<string, TiltifyDonationWithTestFlag[]>`                     |\n| `usePipelineDonations(id)`        | `TiltifyDonationWithTestFlag[]` (memo-stable `[]` if id unknown)    |\n| `usePipelineDonationSum(id, opts?)` | `PipelineDonationSumResult` — live per-currency sum over `[start, end)` of the WS donation firehose. Pair with the REST `/schedules/campaigns/{id}/raised` `asOf` field to replace polling with a `baseline + WS-delta` view. |\n| `usePipelineDonationTrains(id, withStatus?)` | `DonationTrain[]`                                        |\n| `usePipelineSubathonTimer(id, withStatus?)`  | `SubathonTimer[]`                                        |\n| `usePipelineDonorSpotlight(id)`   | `ComputedSpotlight \\| null`                                         |\n| `usePipelinePolls(id)`            | `TiltifyPoll[]`                                                     |\n| `usePipelineRewards(id)`          | `TiltifyReward[]`                                                   |\n| `usePipelineTargets(id)`          | `TiltifyTarget[]`                                                   |\n| `usePipelineMilestones(id)`       | `TiltifyMilestone[]`                                                |\n| `usePipelineLeaderboardEntries(id)` | `TiltifyLeaderboardEntry[]`                                       |\n| `usePipelineAuctionHouses()`      | `AuctionHouse[]`                                                    |\n| `usePipelineTwitchChat()`         | `TwitchChatMessage[]`                                               |\n| `usePipelineConnectionState()`    | `PipelineConnectionState` (10 split selectors — no false re-renders)|\n| `usePipelineConnectionActions()`  | `PipelineConnectionActions` (stable `connect/disconnect/refresh/…`) |\n| `usePipelineSubscriptions()`      | `Record<string, TiltifyWebhookSubscription>`                        |\n| `usePipelineTiltifySettings()`    | `TiltifyWebSocketSettings`                                          |\n| `usePipelineLastRefreshResponse()`| `RefreshResponseMessage \\| null`                                    |\n| `usePipelineDonorSpotlightSettings()` | `Record<string, DonorSpotlightSettings>`                        |\n\n### Tier 3 — kitchen-sink provider (`/provider`)\n\n| Export                              | Notes                                                                                  |\n| ----------------------------------- | -------------------------------------------------------------------------------------- |\n| `UnifiedDataPipelineProvider`       | Superset of `RealtimePipelineProvider`; adds legacy `reloadOnErrorReconnect` + `debug`.|\n| `useUnifiedDataPipeline()`          | One-call kitchen-sink hook. Returns every slice + actions + legacy field aliases.      |\n| `useAddPipelineCampaignIDs(ids)`    | Effect hook — registers campaign ids with the store on mount.                          |\n| `useAddPipelineCampaigns(items)`    | Effect hook — registers full campaign records on mount.                                |\n| `useAddPipelineFundraisingEventIDs(ids)` | Effect hook — registers fundraising-event ids on mount.                           |\n| `useCommonContextHooks(…)`          | URL / mode / charity-type sync to the store. Same shape as the legacy hook.            |\n| `useIdentify()` / `useIdentify(…11 legacy args)` | Zero-arg returns the store's bound `identify` action. Positional arg form is preserved as an `@deprecated` overload — args are ignored, warns once. |\n\n### Fusion — hybrid slice hooks (`/fusion`)\n\nOverlay-parity `usePipeline*` hooks lifted from\n`playlive-overlay-vite/src/hooks/pipeline/` so every overlay that\nwants the same WS-store-first / HTTP-fallback shape can drop the\nlocal copy. Every hook accepts a `mode: \"ws\" | \"http\"` param that\npicks between:\n\n- `\"ws\"` — read the Tier-2 store selector for the slice, latch a\n  local `hasLoadedX` boolean on the first slice arrival (or on the\n  server-side \"feature disabled\" signal), and fire\n  `updateTiltifySettings({ <sliceKey> })` whenever the local\n  `xEnabled` toggle flips so the pipeline dynamically un/subscribes.\n- `\"http\"` — delegate to the matching `@playlive/react-query` REST\n  hook (`useRewards`, `useTargets`, …) with the WS store slice\n  ignored.\n\nAll hybrid hooks share `useLoadingTimeout` — a 15 s safety-net that\nforce-completes the WS branch when the store never delivers.\n\n| Hook                                                     | Returns                                                                        |\n| -------------------------------------------------------- | ------------------------------------------------------------------------------ |\n| `useHybridRewards(params)`                               | `{ currentRewards, setHasLoadedRewards, hasLoadedRewards, isLoadingRewards, isLoading, setRewardsEnabled, rewardsEnabled }` |\n| `useHybridTargets(params)`                               | `{ currentTargets, setHasLoadedTargets, hasLoadedTargets, isLoading, targetsEnabled, isLoadingTargets, setTargetsEnabled }` |\n| `useHybridMilestones(params)`                            | `{ isLoadingMilestones, currentMilestones, milestonesEnabled, setMilestonesEnabled }` |\n| `useHybridPolls(params)`                                 | `{ isLoadingPolls, currentPolls, pollsEnabled, setPollsEnabled }`              |\n| `useHybridCause({ mode, initialCampaign })`              | `{ cause, isLoading }` — single-slot semantics (uses `getFirstCause`).         |\n| `useHybridFundraisingEvent({ mode, initialCampaign })`   | `{ fundraisingEvent, isLoading }` — single-slot semantics.                     |\n| `useHybridFundraisingEventSupportingCampaigns(params)`   | `{ supportingCampaigns, isLoading, setSupportingCampaignsEnabled, supportingCampaignsEnabled, isLoadingSupportingCampaigns, hasLoadedSupportingCampaigns }` |\n| `useHybridAuctionHouses({ mode, enabled, campaignID })`  | `{ auctionHouses, auctionHouseTotal, auctionHousesEnabled, setAuctionHousesEnabled }` — WS-only (no REST fallback yet). |\n| `useLoadingTimeout(params)`                              | `void` — safety-net timer primitive shared by every hook above.                |\n\nReturn shapes are preserved verbatim from the overlay so overlay\ncall-sites can eventually collapse to a re-export.\n\n### Fusion — baseline + WS delta hooks (`/fusion`)\n\n| Hook                                        | Returns                                                                     |\n| ------------------------------------------- | --------------------------------------------------------------------------- |\n| `useCurrentBlockRaised(campaignID, start, end, opts?)` | `{ raised, data, isLoading, isError, refetch }` — REST baseline + WS delta. `opts.demoMode: true` short-circuits to `DemoProvider.getDemoScheduleBlockRaised` / `getDemoBlockRaised` fallback. |\n| `useLiveSchedule(params, opts?)`            | `{ schedule, isLoading, isError, refetch }` — WS-refetch on transition. `opts.demoMode: true` (+ optional `demoIntervalMs`) short-circuits to `DemoProvider.getDemoSchedule` / `buildDemoSchedule` fallback with a live tick timer.     |\n| `useCurrentScheduleItem(schedule, opts?)`   | `{ currentItem, nextItem }` — pure-client rollover projection.              |\n| `useLinkedTeamCampaign(params, opts?)`      | `{ linkedTeamCampaign, … }` — WS-store first, HTTP fallback.                |\n| `useDonorSpotlight(params, opts?)`          | `{ spotlight, isLoading, refetch }` — WS store preferred + REST snapshot.   |\n\n### Legacy aliases (`/legacy`)\n\nSoft-deprecated. Identical behavior to the canonical exports —\nrename the import specifier once and the rest of your code base\nkeeps working unchanged:\n\n| Legacy name            | Canonical replacement                                          |\n| ---------------------- | -------------------------------------------------------------- |\n| `useUDPStore`          | `usePipelineValue` (Tier 1) / typed slice hooks (Tier 2)       |\n| `useUDPStoreApi`       | (use a typed slice hook; or reach for the raw store API)       |\n| `createUDPStore`       | `createPipelineStore` from `@playlive/realtime-pipeline/store` |\n| `UDPStore` (type)      | `PipelineStore` from `@playlive/realtime-pipeline/store`       |\n| `UDPStoreApi` (type)   | `PipelineStoreApi` from `@playlive/realtime-pipeline/store`    |\n| `CharityTypes`         | re-exported from `@playlive/realtime-pipeline/protocol`         |\n\nPlus every Tier 1+2+3 export under its canonical name.\n\n## Migration from `playlive-overlay-data-layer`\n\nOne-shot sed-friendly rewrite of every import specifier (no body\nedits needed for the happy path):\n\n```bash\n# Tier 3 — kitchen-sink (drop-in replacement)\nrg -l '@playlive/overlay-data-layer/websocket' . \\\n  | xargs sed -i '' \\\n    -e 's|@playlive/overlay-data-layer/websocket|@playlive/react-pipeline/provider|g'\n```\n\nFor the soft-deprecated names (`useUDPStore`, `createUDPStore`, …)\nsubstitute `/legacy` instead of `/provider` to opt into the alias\nbundle, then migrate at your leisure (PRD §10 — one-release window).\n\nSee the phase-8 row in the root [MIGRATION.md](../../MIGRATION.md) for\nthe full per-symbol mapping plus call-site walkthroughs.\n\n## Upstream spec\n\nNo external API surface — internal workspace package. All transport\nruns through `@playlive/realtime-pipeline/connection`.\n\n## Twitch Extension URL disclosure\n\n`KNOWN_URLS` enumerates every absolute URL or host this package can\nfetch. **It is empty.** This package opens WebSocket connections only\nto URLs the consumer passes to `<RealtimePipelineProvider url={…}>`;\nthose URLs must be added to your Extension submission's URL\ndisclosure list, not this one.\n\n```ts\nimport { KNOWN_URLS } from \"@playlive/react-pipeline\";\nconsole.log(KNOWN_URLS); // []\n```\n\nSee [`docs/twitch-extension-checklist.md`](../../docs/twitch-extension-checklist.md).\n\n## Examples\n\nLive overlay apps consuming this provider land under `apps/*`\n(phase 10). Until then, the Tier 1 quick-start above is the minimum\nviable wiring.\n\n## Contributing\n\nSee [CONTRIBUTING.md](../../CONTRIBUTING.md). To add new Tier-2\nhooks, mirror an existing slice hook in `src/hooks/index.ts` and\nadd a `it.each` entry in `tests/unit/index.test.ts`.\n\n## License\n\nMIT — see [LICENSE](../../LICENSE). Distributed via Play Live\nCodeArtifact (PRD §6).\n","readmeFilename":"README.md","dist":{"tarball":"https://packages.playlive.experience.stjude.org/@playlive/react-pipeline/-/react-pipeline-0.4.17.tgz","shasum":"02a498e90bb0daf6cbe9810d45c60fa25faa4719","integrity":"sha512-GcBkvxda4uiVT7agkchXyx0jv5fPDZDqDpMwwoEnjpENpB5Vc+IWYKoKrAZh3OA37gWTi6Up+QTk53a2915SZA=="}},"0.4.19":{"name":"@playlive/react-pipeline","version":"0.4.19","description":"React bindings for @playlive/realtime-pipeline","type":"module","sideEffects":false,"main":"./index.js","types":"./index.d.ts","exports":{".":{"import":"./index.js","types":"./index.d.ts"},"./provider":{"import":"./provider/index.js","types":"./provider/index.d.ts"},"./legacy":{"import":"./legacy/index.js","types":"./legacy/index.d.ts"},"./fusion":{"import":"./fusion/index.js","types":"./fusion/index.d.ts"}},"dependencies":{},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","zustand":"^5.0.0","@playlive/realtime-pipeline":"^0.3.1","@playlive/tiltify-core":"^0.4.18","@tanstack/react-query":"^5.0.0","@playlive/react-query":"^0.4.3","@playlive/fundraiser-data":"^0.5.3"},"peerDependenciesMeta":{"@tanstack/react-query":{"optional":true},"@playlive/react-query":{"optional":true},"@playlive/fundraiser-data":{"optional":true}},"playlive":{"target":"browser","frontendEligible":true,"coverageFloor":85},"publishConfig":{"access":"restricted","registry":"https://playlive-767397689694.d.codeartifact.us-east-1.amazonaws.com/npm/playlive/"},"integrity":"sha512-DyNpzI3qJ6CC8YVn6jbVN9GV/gGvJi2IKcSbipIZgiSfPScMdip3lpIqGZG5pqx9L10Sd3M0h9zsyPoouW9O5Q==","shasum":"ee39e04467399a832ca13454e13d85063f466f3f","readme":"# @playlive/react-pipeline\n\nReact bindings for [`@playlive/realtime-pipeline`](../realtime-pipeline/).\nSuccessor to `playlive-overlay-data-layer/src/websocket/*` with **zero**\ndependency on `react-use-websocket-lite` — built directly on Zustand v5's\n`useStore` + `useSyncExternalStore`.\n\n![Coverage](./coverage-badge.svg)\n\n## Three tiers — take only what you need\n\nPick the surface that matches how much of the legacy\n`UnifiedDataPipelineProvider` API you actually want. Each tier is\nstrictly larger than the last and lives at its own subpath so unused\ncode is tree-shaken out.\n\n| Tier | Subpath                             | Surface                                                                       | Use when                                                               |\n| ---- | ----------------------------------- | ----------------------------------------------------------------------------- | ---------------------------------------------------------------------- |\n| 1    | `@playlive/react-pipeline`          | `RealtimePipelineProvider` + `usePipelineValue` (universal selector)          | Greenfield app; you want one selector primitive and nothing else.      |\n| 2    | `@playlive/react-pipeline`          | Tier 1 **plus** the typed per-slice hooks (`usePipelineDonations(id)`, …)     | You want stable, typed one-liners for the common reads.                |\n| 3    | `@playlive/react-pipeline/provider` | Tier 2 **plus** `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, etc. | Porting an existing overlay-data-layer consumer with minimal churn.    |\n| —    | `@playlive/react-pipeline/fusion`   | REST + WS composition hooks (needs the optional TanStack peers)              | You want \"REST baseline + live WS delta\" without hand-rolling it.      |\n| —    | `@playlive/react-pipeline/legacy`   | Tier 3 **plus** `useUDPStore` / `useUDPStoreApi` / `createUDPStore` aliases   | Soft-deprecated shim for the legacy symbol names (one-release window). |\n\n## Install\n\n```bash\nbun add @playlive/react-pipeline\nbun add react react-dom zustand @playlive/realtime-pipeline @playlive/tiltify-core\n```\n\nPeer dependencies (jose-style — the consumer brings their own):\n\n| Peer                          | Range          | Required?                                 |\n| ----------------------------- | -------------- | ----------------------------------------- |\n| `react`                       | `^19.0.0`      | yes                                       |\n| `react-dom`                   | `^19.0.0`      | yes                                       |\n| `zustand`                     | `^5.0.0`       | yes                                       |\n| `@playlive/realtime-pipeline` | `workspace:*`  | yes — store, connection, protocol types    |\n| `@playlive/tiltify-core`      | `workspace:*`  | yes (type-only — stripped at compile time) |\n| `@tanstack/react-query`       | `^5.0.0`       | **optional** — only for `/fusion`          |\n| `@playlive/react-query`       | `workspace:*`  | **optional** — only for `/fusion`          |\n| `@playlive/fundraiser-data`   | `workspace:*`  | **optional** — only for `/fusion`          |\n\nThe three optional peers are declared `optional: true` in\n`peerDependenciesMeta`; you only need them if you import from\n[`@playlive/react-pipeline/fusion`](#fusion--rest-baseline--ws-delta-hooks-fusion).\n\nThis package has **no runtime `dependencies`** — nothing but your own\npeers ends up in the bundle.\n\n## Quick start\n\n```tsx\nimport {\n  RealtimePipelineProvider,\n  usePipelineCampaigns,\n  usePipelineConnectionState,\n  usePipelineDonations,\n} from \"@playlive/react-pipeline\";\n\nconst CAMPAIGN_ID = \"5d4b0d3c-9f31-4a5c-8fd1-0a2b3c4d5e6f\";\n\nfunction App() {\n  return (\n    <RealtimePipelineProvider\n      url=\"wss://main.playlive.ws.api.experience.stjude.org\"\n      initialCampaignIDs={[CAMPAIGN_ID]}\n      initialOverlayName=\"donation-bar\"\n      initialTeamUserSlug=\"@playliver\"\n      initialOverlayPath=\"/overlays/donation-bar\"\n      autoConnect\n    >\n      <DonationBar campaignID={CAMPAIGN_ID} />\n    </RealtimePipelineProvider>\n  );\n}\n\nfunction DonationBar({ campaignID }: { campaignID: string }) {\n  const { connected, connecting, hasReceivedInitialData } = usePipelineConnectionState();\n  const campaigns = usePipelineCampaigns();\n  const donations = usePipelineDonations(campaignID);\n\n  if (connecting) return <p>Connecting…</p>;\n  if (!connected) return <p>Disconnected — reconnecting…</p>;\n  if (!hasReceivedInitialData) return <p>Waiting for first tick…</p>;\n\n  const campaign = campaigns.find((c) => c.id === campaignID);\n  const raised = Number(campaign?.total_amount_raised?.value ?? 0);\n  const goal = Number(campaign?.goal?.value ?? 0);\n\n  return (\n    <section>\n      <progress value={raised} max={goal || 1} />\n      <p>\n        ${raised.toFixed(2)} of ${goal.toFixed(2)}\n      </p>\n      <ul>\n        {donations.slice(0, 5).map((d) => (\n          <li key={d.id}>\n            {d.donor_name} — ${d.amount.value}\n          </li>\n        ))}\n      </ul>\n    </section>\n  );\n}\n```\n\n`RealtimePipelineProvider` builds one Zustand store per provider\ninstance (from the `initial*` props, which are read **once** at first\nconstruction) and opens a single `PipelineConnection`. `connection.destroy()`\nruns on unmount. Children subscribe with any Tier 1 / Tier 2 hook.\n\nEvery hook that reads the store throws when no provider is mounted\nabove it; the four `useOn*` observer hooks deliberately soft-fail\n(they no-op) so you can drop them into components that sometimes render\noutside a pipeline tree.\n\n## Subpath exports\n\n| Subpath                             | Description                                                                                             |\n| ----------------------------------- | ------------------------------------------------------------------------------------------------------- |\n| `@playlive/react-pipeline`          | Default barrel — Tier 1 + Tier 2 (provider, `usePipelineValue`, typed slice hooks, observers).           |\n| `@playlive/react-pipeline/provider` | Tier 3 — `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, `useAddPipeline*`, `useIdentify`, …    |\n| `@playlive/react-pipeline/fusion`   | REST + WS composition hooks (`useCurrentBlockRaised`, `useLiveSchedule`, the `useHybrid*` family, …).    |\n| `@playlive/react-pipeline/legacy`   | Soft-deprecated alias bundle (`useUDPStore`, `useUDPStoreApi`, `createUDPStore`, `UDPStore`, …).         |\n\nEach subpath ships an ESM bundle, a `bun` source condition, and `.d.ts`\ndeclarations. Frontend-eligible bundles are not minified (PRD §6.1) so\nstack traces stay readable in production.\n\n## API reference\n\nRun `bun run docs:build` inside this package to emit the TypeDoc site at\n`dist/docs/`.\n\n### Tier 1 — universal primitive\n\n| Export                          | Signature                                                     |\n| ------------------------------- | ------------------------------------------------------------- |\n| `RealtimePipelineProvider`      | `(props: RealtimePipelineProviderProps) => ReactNode`          |\n| `RealtimePipelineProviderProps` | Type. `InitialProps` + `children`, `url?`, `autoConnect?` (default `true`), `webSocketCtor?`, `log?`, `store?`, `connection?`. |\n| `usePipelineValue`              | `<T>(selector: (s: PipelineStore) => T) => T`                  |\n| `PACKAGE_NAME`                  | `\"@playlive/react-pipeline\"` — for runtime version-pinning.    |\n| `KNOWN_URLS`                    | `readonly string[]` — frozen, empty. See the disclosure section. |\n\n`RealtimePipelineProviderProps` extends\n[`InitialProps`](../realtime-pipeline/), so every `initial*` key\n(`initialCampaignIDs`, `initialTeamCampaignIDs`, `initialCauseIDs`,\n`initialFundraisingEventIDs`, `initialOverlayName`, `initialTeamUserSlug`,\n`initialOverlayPath`, `initialOverlayConfig`, `initialAdminApiKey`,\n`initialDonationTrainsEnabled`, …) is a valid prop. There is **no**\n`charityType` prop — set the charity type through the store's\n`setCurrentType` action or via `useCommonContextHooks` (Tier 3).\n\n`usePipelineValue` is single-arg by design. Zustand v5 dropped the third\n`equalityFn` parameter on `useStore` to keep the package free of the\n`use-sync-external-store` shim peer. For shallow / custom equality, wrap\nyour selector with `useShallow` from `zustand/shallow` (zero extra dep):\n\n```tsx\nimport { useShallow } from \"zustand/shallow\";\nimport { usePipelineValue } from \"@playlive/react-pipeline\";\n\nconst { connected, connecting } = usePipelineValue(\n  useShallow((s) => ({ connected: s.connected, connecting: s.connecting })),\n);\n```\n\n### Tier 2 — typed per-slice hooks\n\nStable wrappers over `usePipelineValue` with a typed selector +\nmemo-stable empty-array/object fallbacks (no infinite-render churn from\nfresh `[]` allocations — Decision §3 in [CHANGELOG.md](./CHANGELOG.md)).\n\n| Hook                                          | Returns                                                     |\n| --------------------------------------------- | ----------------------------------------------------------- |\n| `usePipelineCampaigns()`                      | `TiltifyCampaign[]`                                          |\n| `usePipelineTeamCampaigns()`                  | `TiltifyTeamCampaign[]`                                      |\n| `usePipelineFundraisingEvents()`              | `TiltifyFundraisingEvent[]`                                  |\n| `usePipelineCauses()`                         | `TiltifyCause[]`                                             |\n| `usePipelineDonations()`                      | `Record<string, TiltifyDonationWithTestFlag[]>`               |\n| `usePipelineDonations(id)`                    | `TiltifyDonationWithTestFlag[]` (memo-stable `[]` if unknown) |\n| `usePipelineDonationTrains(id, withStatus?)`  | `DonationTrain[]` — `withStatus` is `\"ACTIVE\" \\| \"ENDED\" \\| \"ENDING\" \\| \"PENDING\"` |\n| `usePipelineSubathonTimer(id, withStatus?)`   | `SubathonTimer[]` — `withStatus` is `\"ACTIVE\" \\| \"INACTIVE\"`  |\n| `usePipelineDonorSpotlight(id)`               | `ComputedSpotlight \\| null`                                  |\n| `usePipelineDonorSpotlightSettings()`         | `Record<string, DonorSpotlightSettings>`                     |\n| `usePipelinePolls(id)`                        | `TiltifyPoll[]`                                              |\n| `usePipelinePollDeltas(pollID)`               | `PollOptionDelta[]` — server-computed \"+$X on option Y\" deltas |\n| `usePipelineRewards(id)`                      | `TiltifyReward[]`                                            |\n| `usePipelineTargets(id)`                      | `TiltifyTarget[]`                                            |\n| `usePipelineMilestones(id)`                   | `TiltifyMilestone[]`                                         |\n| `usePipelineLeaderboardEntries(id)`           | `TiltifyLeaderboardEntry[]`                                  |\n| `usePipelineLeaderboardExclusions(id)`        | `LeaderboardExclusion[]`                                     |\n| `usePipelineLeaderboardExclusionsMap()`       | `Record<string, LeaderboardExclusion[]>`                     |\n| `usePipelineSchedule(id)`                     | `ScheduleTransitionBlock[]` — full `updateSchedule` WS push  |\n| `usePipelineSchedules()`                      | `Record<string, ScheduleTransitionBlock[]>`                  |\n| `usePipelineLastScheduleTransition(id)`       | `ScheduleTransitionPayload \\| null` — latest block crossing  |\n| `usePipelineLastScheduleTransitions()`        | `Record<string, ScheduleTransitionPayload>`                  |\n| `usePipelineDonationSum(id, options?)`        | `PipelineDonationSumResult` — live per-currency sum over `[start, end)` |\n| `usePipelineAuctionHouses()`                  | `AuctionHouse[]`                                             |\n| `usePipelineTwitchChat()`                     | `TwitchChatMessage[]`                                        |\n| `usePipelineConnectionState()`                | `PipelineConnectionState` (10 split selectors — no false re-renders) |\n| `usePipelineConnectionActions()`              | `PipelineConnectionActions` (stable `connect`/`disconnect`/…) |\n| `usePipelineSubscriptions()`                  | `Record<string, TiltifyWebhookSubscription>`                 |\n| `usePipelineTiltifySettings()`                | `TiltifyWebSocketSettings`                                   |\n| `usePipelineLastRefreshResponse()`            | `RefreshResponseMessage \\| null`                             |\n\nCompanion types exported alongside them: `PipelineConnectionState`\n(`connected`, `connecting`, `charityType`, `connectionType`, `wsURL`,\n`hasReceivedInitialData`, `lastIdentified`, `lastIdentityMessage`,\n`lastFirehoseMessage`, `lastMessage`), `PipelineConnectionActions`\n(`connect`, `disconnect`, `identify`, `refresh`, `reset`,\n`setAutoConnect`, `setAdminApiKey`), `PipelineDonationSumOptions`,\n`PipelineDonationSumByCurrency`, `PipelineDonationSumResult`.\n\n#### Change observers\n\nPush-style subscriptions that diff the store outside React's render\npath. Each stashes `handler` in a ref, subscribes exactly once, and\ntreats whatever is already on the store at mount as the baseline (so\nthey never fire for pre-existing data). All four **soft-fail** without\na provider.\n\n| Hook                                                | Handler payload                                        |\n| --------------------------------------------------- | ------------------------------------------------------ |\n| `useOnScheduleTransition(handler, options?)`        | `ScheduleTransitionPayload`                             |\n| `useOnScheduleUpdate(handler, options?)`            | `{ campaignID, schedule: ScheduleTransitionBlock[] }`   |\n| `useOnLeaderboardEntriesChange(handler, options?)`  | `{ campaignID, entries: TiltifyLeaderboardEntry[] }`    |\n| `useOnLeaderboardExclusionsChange(handler, options?)` | `{ campaignID, exclusions: LeaderboardExclusion[] }`  |\n\n`options` is `{ campaignIDs?: readonly string[] }` — omit it (or pass an\nempty list) to observe every campaign.\n\n```tsx\nuseOnLeaderboardEntriesChange(\n  ({ campaignID }) => {\n    queryClient.invalidateQueries({\n      queryKey: [\"leaderboard-with-exclusions\", \"tiltify\", campaignID],\n    });\n  },\n  { campaignIDs: [campaignID] },\n);\n```\n\n### Tier 3 — kitchen-sink provider (`/provider`)\n\n| Export                                        | Notes                                                                                          |\n| --------------------------------------------- | ---------------------------------------------------------------------------------------------- |\n| `UnifiedDataPipelineProvider`                 | Superset of `RealtimePipelineProvider`; adds legacy `reloadOnErrorReconnect` (default `true`) + `debug` (wires `console.debug` logging). |\n| `UnifiedDataPipelineProviderProps`            | Type. `Omit<RealtimePipelineProviderProps, \"children\"> & { children, reloadOnErrorReconnect?, debug? }`. |\n| `useUnifiedDataPipeline()`                    | One-call kitchen-sink hook → `UnifiedDataPipelineState`. Re-renders on **every** store change.  |\n| `UnifiedDataPipelineState`                    | Type. `PipelineStore` + `allRewards` / `allMilestones` / `allTargets` / `allPolls` / `allFundraisingEventSupportingCampaigns` / `tiltifySubscriptions` legacy aliases. |\n| `useAddPipelineCampaignIDs(params)`           | Layout-effect hook. `{ mode: \"ws\" \\| \"http\"; initialCampaign: { id }; isTeam: boolean; causeID? }` — no-op in `\"http\"` mode. |\n| `useAddPipelineCampaigns(params)`             | Layout-effect hook. `{ campaign?: TiltifyCampaign \\| null; teamCampaign?: TiltifyTeamCampaign \\| null }`. Idempotent by id. |\n| `useAddPipelineFundraisingEventIDs(params)`   | Layout-effect hook. `{ mode; initialCampaign: { id; fundraising_event_id: string \\| null } }`.   |\n| `useCommonContextHooks(params)`               | `{ url: string; mode: \"ws\" \\| \"http\"; charityType: CharityType }` — syncs type + URL and connects when `mode === \"ws\"`. |\n| `useIdentify()`                               | Returns the store-bound `identify` action. The 11-positional-arg legacy overload is kept as `@deprecated`; the args are ignored and a one-shot `console.warn` fires. |\n\n`RealtimePipelineProvider` / `RealtimePipelineProviderProps` are also\nre-exported from `/provider` so a Tier-3 consumer needs exactly one\nimport specifier.\n\n### Fusion — REST baseline + WS delta hooks (`/fusion`)\n\nThese fuse the WebSocket firehose with REST reads from\n[`@playlive/react-query`](../react-query/) /\n[`@playlive/fundraiser-data`](../fundraiser-data/). Importing this\nsubpath means you must also install the three optional peers.\n\n| Hook                                                   | Returns                                                                    |\n| ------------------------------------------------------ | -------------------------------------------------------------------------- |\n| `useCurrentBlockRaised(campaignID, start, end, opts?)` | `{ raised, data, isLoading, isError, refetch }` — REST baseline `+` WS delta over `[start, end)`. `opts`: `{ enabled?, queryOptions?, demoMode? }`. |\n| `useLiveSchedule(params, opts?)`                       | `{ schedule, isLoading, isError, refetch }`. `params`: `{ charityType, campaignID, isTeam? }`; `opts`: `{ enabled?, queryOptions?, demoMode?, demoIntervalMs? }`. |\n| `useCurrentScheduleItem(schedule, opts?)`              | `{ currentItem, nextItem }` — pure client-side rollover projection with a self-scheduling boundary timer. |\n| `useLinkedTeamCampaign(params, opts?)`                 | `{ shouldFetchLinkedTeamCampaign, resolvedLinkedTeamCampaign, linkedTeamTotalAmount, linkedTeamGoalAmount, isLoadingLinkedTeamCampaign, debug }`. |\n| `useDonorSpotlight(params, opts?)`                     | `{ spotlight, isLoading, refetch }`. `params`: `{ campaignID, isTeam, live, mode }`; `opts`: `{ pollingIntervalMs?, enabled?, queryOptions? }`. |\n\n`useLiveSchedule` resolves in a strict fallback order: full\n`updateSchedule` WS push → `lastScheduleTransitions` synthesis (demo\nonly) → REST `useSchedule` → demo fixture.\n\n### Fusion — hybrid slice hooks (`/fusion`)\n\nOverlay-parity hooks lifted from `playlive-overlay-vite/src/hooks/pipeline/`\nso every overlay can drop its local copy. Each takes a\n`mode: \"ws\" | \"http\"` that picks between:\n\n- `\"ws\"` — read the Tier-2 store selector, latch a local `hasLoadedX`\n  boolean on first arrival (or on the server's \"feature disabled\"\n  signal), and call `updateTiltifySettings({ <sliceKey> })` whenever the\n  local `xEnabled` toggle flips so the pipeline dynamically un/subscribes.\n- `\"http\"` — delegate to the matching `@playlive/react-query` REST hook\n  (`useRewards`, `useTargets`, …) and ignore the WS slice.\n\nAll of them share `useLoadingTimeout` — a 15 s safety net that\nforce-completes the WS branch when the store never delivers.\n\n| Hook                                                    | Params → Returns                                                              |\n| -------------------------------------------------------- | ----------------------------------------------------------------------------- |\n| `useHybridRewards({ campaignID, isTeam, mode, initialRewardsEnabled? })`   | `{ currentRewards, hasLoadedRewards, setHasLoadedRewards, isLoadingRewards, isLoading, rewardsEnabled, setRewardsEnabled }` |\n| `useHybridTargets({ campaignID, isTeam, mode })`         | `{ currentTargets, hasLoadedTargets, setHasLoadedTargets, isLoadingTargets, isLoading, targetsEnabled, setTargetsEnabled }` |\n| `useHybridMilestones({ campaignID, isTeam, mode })`      | `{ currentMilestones, isLoadingMilestones, milestonesEnabled, setMilestonesEnabled }` |\n| `useHybridPolls({ campaignID, isTeam, mode })`           | `{ currentPolls, isLoadingPolls, pollsEnabled, setPollsEnabled }`              |\n| `useHybridCause({ mode, initialCampaign })`              | `{ cause, isLoading }` — single-slot semantics.                                |\n| `useHybridFundraisingEvent({ mode, initialCampaign })`   | `{ fundraisingEvent, isLoading }` — single-slot semantics.                     |\n| `useHybridFundraisingEventSupportingCampaigns({ mode, initialFundraisingEvent, initialSupportingCampaignsEnabled? })` | `{ supportingCampaigns, isLoading, isLoadingSupportingCampaigns, hasLoadedSupportingCampaigns, supportingCampaignsEnabled, setSupportingCampaignsEnabled }` |\n| `useHybridDonationTrains({ campaignID, mode, initialDonationTrainsEnabled?, withStatus?, http?, auth? })` | `{ currentDonationTrains, hasLoadedDonationTrains, setHasLoadedDonationTrains, isLoadingDonationTrains, isLoading, donationTrainsEnabled, setDonationTrainsEnabled }` |\n| `useHybridAuctionHouses({ mode, enabled, campaignID })`  | `{ auctionHouses, auctionHouseTotal, auctionHousesEnabled, setAuctionHousesEnabled }` — WS-only (no REST fallback yet). |\n| `useHybridLeaderboardWithExclusions({ charityType, campaignID, isTeam?, timeType?, count?, startDate?, endDate?, enabled?, continuouslyUpdate? })` | `{ entries, isLoading }` — WS-driven invalidation over the REST leaderboard, with a `LEADERBOARD_SAFETY_POLL_MS` (60 s) backstop. |\n| `useLoadingTimeout({ mode, isLoading, onFinishedLoading, timeoutAmount? })` | `void` — the shared safety-net timer primitive.                    |\n\n`LEADERBOARD_SAFETY_POLL_MS` is exported alongside the hooks. Return\nshapes are preserved verbatim from the overlay so overlay call sites can\neventually collapse to a re-export.\n\n### Legacy aliases (`/legacy`)\n\nSoft-deprecated. Behavior is identical to the canonical exports — rename\nthe import specifier once and the rest of your code base keeps working:\n\n| Legacy name          | Canonical replacement                                            |\n| -------------------- | ---------------------------------------------------------------- |\n| `useUDPStore`        | `usePipelineValue` (Tier 1) / typed slice hooks (Tier 2)          |\n| `useUDPStoreApi`     | Use a typed slice hook, or reach for the raw store API            |\n| `createUDPStore`     | `createPipelineStore` from `@playlive/realtime-pipeline/store`    |\n| `UDPStore` (type)    | `PipelineStore` from `@playlive/realtime-pipeline/store`          |\n| `UDPStoreApi` (type) | `PipelineStoreApi` from `@playlive/realtime-pipeline/store`       |\n| `CharityTypes`       | Re-exported from `@playlive/realtime-pipeline/protocol`           |\n\n`useUDPStore` / `useUDPStoreApi` throw with a\n`\"must be called inside <UnifiedDataPipelineProvider>\"` message when no\nprovider is mounted. The subpath also re-exports every Tier 1 + 2 + 3\nsymbol under its canonical name, plus the `PipelineStore` /\n`PipelineStoreApi` types.\n\n## Migration from `playlive-overlay-data-layer`\n\nOne-shot, sed-friendly rewrite of every import specifier (no body edits\nneeded for the happy path):\n\n```bash\n# Tier 3 — kitchen-sink (drop-in replacement)\nrg -l '@playlive/overlay-data-layer/websocket' . \\\n  | xargs sed -i '' \\\n    -e 's|@playlive/overlay-data-layer/websocket|@playlive/react-pipeline/provider|g'\n```\n\nFor the soft-deprecated names (`useUDPStore`, `createUDPStore`, …)\nsubstitute `/legacy` instead of `/provider` to opt into the alias\nbundle, then migrate at your leisure (PRD §10 — one-release window).\n\nTwo call-site changes are **not** covered by the sed:\n\n- `useIdentify(sendJsonMessage, campaignIDs, …)` — the positional args\n  are now ignored (the connection reads identify inputs off the store).\n  Drop them; the deprecated overload still compiles and warns once.\n- `useWebSocket` from `react-use-websocket-lite` — replaced by\n  `createPipelineConnection` in `@playlive/realtime-pipeline/connection`,\n  which `RealtimePipelineProvider` mounts for you. Use the\n  `/migrate:rusw` agent skill for the codemod.\n\nSee the phase-8 row in the root [MIGRATION.md](../../MIGRATION.md) and\n[`docs/overlay-data-layer-migration.md`](../../docs/overlay-data-layer-migration.md)\nfor the full per-symbol mapping.\n\n## Upstream spec\n\nNo external API surface — this package fetches nothing itself. Every\nbyte on the wire is framed by `@playlive/realtime-pipeline`; the message\ncatalogue lives in\n[`docs/realtime-protocol.md`](../../docs/realtime-protocol.md) and the\ntypes in [`@playlive/realtime-pipeline/protocol`](../realtime-pipeline/).\n\n## Twitch Extension URL disclosure\n\n`KNOWN_URLS` enumerates every absolute URL or host this package can\nreach. **It is empty.** This package only opens the WebSocket URL the\nconsumer hands to `<RealtimePipelineProvider url={…}>` (or the store's\n`DEFAULT_WEBSOCKET_URL`, `wss://main.playlive.ws.api.experience.stjude.org`).\nDisclose that URL — plus the `KNOWN_URLS` of `@playlive/realtime-pipeline`\nand, if you import `/fusion`, `@playlive/fundraiser-data` — on your\nExtension submission.\n\n```ts\nimport { KNOWN_URLS } from \"@playlive/react-pipeline\";\nconsole.log(KNOWN_URLS); // []\n```\n\nSee [`docs/twitch-extension-checklist.md`](../../docs/twitch-extension-checklist.md).\n\n## Examples\n\n### Live schedule-block total without polling\n\nThe canonical fusion use case: fetch a REST baseline once, then add the\nWebSocket donation delta on top so the number ticks up on every donation\ninstead of every 30-second poll. Requires the optional peers plus a\n`<QueryClientProvider>` above the pipeline provider.\n\n```tsx\nimport { configure } from \"@playlive/fundraiser-data/config\";\nimport { getConfigForEnv } from \"@playlive/fundraiser-data/environments\";\nimport { RealtimePipelineProvider } from \"@playlive/react-pipeline\";\nimport {\n  useCurrentBlockRaised,\n  useCurrentScheduleItem,\n  useLiveSchedule,\n} from \"@playlive/react-pipeline/fusion\";\nimport { makeQueryClient } from \"@playlive/react-query\";\nimport { QueryClientProvider } from \"@tanstack/react-query\";\n\nconfigure(getConfigForEnv(\"prod\", { tiltifyProxyUrl: import.meta.env.VITE_TILTIFY_PROXY_URL }));\nconst queryClient = makeQueryClient();\n\nconst CAMPAIGN_ID = \"5d4b0d3c-9f31-4a5c-8fd1-0a2b3c4d5e6f\";\n\nexport function App() {\n  return (\n    <QueryClientProvider client={queryClient}>\n      <RealtimePipelineProvider\n        url=\"wss://main.playlive.ws.api.experience.stjude.org\"\n        initialCampaignIDs={[CAMPAIGN_ID]}\n        initialOverlayName=\"schedule-block\"\n        initialTeamUserSlug=\"@playliver\"\n      >\n        <BlockTotal campaignID={CAMPAIGN_ID} />\n      </RealtimePipelineProvider>\n    </QueryClientProvider>\n  );\n}\n\nfunction BlockTotal({ campaignID }: { campaignID: string }) {\n  const { schedule, isLoading: scheduleLoading, isError } = useLiveSchedule({\n    charityType: \"tiltify\",\n    campaignID,\n  });\n  const { currentItem, nextItem } = useCurrentScheduleItem(schedule);\n\n  const { raised, isLoading } = useCurrentBlockRaised(\n    campaignID,\n    currentItem?.starts_at,\n    currentItem?.ends_at,\n  );\n\n  if (scheduleLoading) return <p>Loading schedule…</p>;\n  if (isError) return <p>Schedule unavailable.</p>;\n  if (!currentItem) return <p>Up next: {nextItem?.name ?? \"nothing scheduled\"}</p>;\n\n  return (\n    <section>\n      <h2>{currentItem.name}</h2>\n      <p>{isLoading ? \"…\" : `$${raised.toFixed(2)} raised this block`}</p>\n    </section>\n  );\n}\n```\n\n### Raw baseline + delta with the Tier-2 primitive\n\nIf you already have the baseline (from a route loader, say), skip the\nfusion hook and reduce the firehose yourself:\n\n```tsx\nimport { usePipelineDonationSum } from \"@playlive/react-pipeline\";\n\nfunction BlockDelta({\n  campaignID,\n  baseline,\n}: {\n  campaignID: string;\n  // `GET /schedules/campaigns/{id}/raised`\n  baseline: { raised: number; currency: string; asOf: string };\n}) {\n  const delta = usePipelineDonationSum(campaignID, {\n    start: baseline.asOf,\n    end: \"2025-09-20T23:00:00.000Z\",\n    currency: baseline.currency,\n  });\n\n  const total = baseline.raised + (delta.raised ?? 0);\n  return (\n    <p>\n      ${total.toFixed(2)} ({delta.donationCount} live donations since baseline)\n    </p>\n  );\n}\n```\n\n`usePipelineDonationSum` matches `start <= completed_at < end`, dedupes\nby donation id (last write wins, so post-edit corrections propagate),\nand excludes `test: true` donations unless you pass\n`includeTest: true` — which keeps it consistent with the REST baseline.\n\n### Driving it locally\n\n[`dev/greenroom`](../../dev/greenroom/) boots an in-process pipeline\nserver you can point the provider at, then inject donations, schedule\ntransitions, and leaderboard updates over its `/_admin` knobs. The\npackage's own integration suite (`tests/integration/index.test.tsx`)\ndoes exactly that and is a good copy-paste starting point. See\n[`docs/greenroom-cookbook.md`](../../docs/greenroom-cookbook.md).\n\n## Contributing\n\nSee [CONTRIBUTING.md](../../CONTRIBUTING.md). To add a Tier-2 hook,\nmirror an existing slice hook in `src/hooks/index.ts` and add an\n`it.each` entry in `tests/unit/hooks.test.tsx`; the barrel-surface guard\nin `tests/unit/index.test.ts` also needs the new name. Fusion hooks live\nin `src/fusion/` with one file per slice.\n\n## License\n\nMIT — see [LICENSE](../../LICENSE). Distributed via Play Live\nCodeArtifact (PRD §6).\n","readmeFilename":"README.md","dist":{"tarball":"https://packages.playlive.experience.stjude.org/@playlive/react-pipeline/-/react-pipeline-0.4.19.tgz","shasum":"ee39e04467399a832ca13454e13d85063f466f3f","integrity":"sha512-DyNpzI3qJ6CC8YVn6jbVN9GV/gGvJi2IKcSbipIZgiSfPScMdip3lpIqGZG5pqx9L10Sd3M0h9zsyPoouW9O5Q=="}},"0.4.20":{"name":"@playlive/react-pipeline","version":"0.4.20","description":"React bindings for @playlive/realtime-pipeline","type":"module","sideEffects":false,"main":"./index.js","types":"./index.d.ts","exports":{".":{"import":"./index.js","types":"./index.d.ts"},"./provider":{"import":"./provider/index.js","types":"./provider/index.d.ts"},"./legacy":{"import":"./legacy/index.js","types":"./legacy/index.d.ts"},"./fusion":{"import":"./fusion/index.js","types":"./fusion/index.d.ts"}},"dependencies":{},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0","zustand":"^5.0.0","@playlive/realtime-pipeline":"^0.3.2","@playlive/tiltify-core":"^0.4.19","@tanstack/react-query":"^5.0.0","@playlive/react-query":"^0.4.4","@playlive/fundraiser-data":"^0.5.4"},"peerDependenciesMeta":{"@tanstack/react-query":{"optional":true},"@playlive/react-query":{"optional":true},"@playlive/fundraiser-data":{"optional":true}},"playlive":{"target":"browser","frontendEligible":true,"coverageFloor":85},"publishConfig":{"access":"restricted","registry":"https://playlive-767397689694.d.codeartifact.us-east-1.amazonaws.com/npm/playlive/"},"integrity":"sha512-pp0cpzIiJf4pxAZ4jMddqMVS9+jo6Unpc9bpHkoLIshtZJWdVl4Diu5Vvga8Reyz4ZTmrzHd0ECNwV/oVZn86A==","shasum":"d576619a9d3ecdbb92e29ec76ec67404eb777eeb","readme":"# @playlive/react-pipeline\n\nReact bindings for [`@playlive/realtime-pipeline`](../realtime-pipeline/).\nSuccessor to `playlive-overlay-data-layer/src/websocket/*` with **zero**\ndependency on `react-use-websocket-lite` — built directly on Zustand v5's\n`useStore` + `useSyncExternalStore`.\n\n![Coverage](./coverage-badge.svg)\n\n## Three tiers — take only what you need\n\nPick the surface that matches how much of the legacy\n`UnifiedDataPipelineProvider` API you actually want. Each tier is\nstrictly larger than the last and lives at its own subpath so unused\ncode is tree-shaken out.\n\n| Tier | Subpath                             | Surface                                                                       | Use when                                                               |\n| ---- | ----------------------------------- | ----------------------------------------------------------------------------- | ---------------------------------------------------------------------- |\n| 1    | `@playlive/react-pipeline`          | `RealtimePipelineProvider` + `usePipelineValue` (universal selector)          | Greenfield app; you want one selector primitive and nothing else.      |\n| 2    | `@playlive/react-pipeline`          | Tier 1 **plus** the typed per-slice hooks (`usePipelineDonations(id)`, …)     | You want stable, typed one-liners for the common reads.                |\n| 3    | `@playlive/react-pipeline/provider` | Tier 2 **plus** `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, etc. | Porting an existing overlay-data-layer consumer with minimal churn.    |\n| —    | `@playlive/react-pipeline/fusion`   | REST + WS composition hooks (needs the optional TanStack peers)              | You want \"REST baseline + live WS delta\" without hand-rolling it.      |\n| —    | `@playlive/react-pipeline/legacy`   | Tier 3 **plus** `useUDPStore` / `useUDPStoreApi` / `createUDPStore` aliases   | Soft-deprecated shim for the legacy symbol names (one-release window). |\n\n## Install\n\n```bash\nbun add @playlive/react-pipeline\nbun add react react-dom zustand @playlive/realtime-pipeline @playlive/tiltify-core\n```\n\nPeer dependencies (jose-style — the consumer brings their own):\n\n| Peer                          | Range          | Required?                                 |\n| ----------------------------- | -------------- | ----------------------------------------- |\n| `react`                       | `^19.0.0`      | yes                                       |\n| `react-dom`                   | `^19.0.0`      | yes                                       |\n| `zustand`                     | `^5.0.0`       | yes                                       |\n| `@playlive/realtime-pipeline` | `workspace:*`  | yes — store, connection, protocol types    |\n| `@playlive/tiltify-core`      | `workspace:*`  | yes (type-only — stripped at compile time) |\n| `@tanstack/react-query`       | `^5.0.0`       | **optional** — only for `/fusion`          |\n| `@playlive/react-query`       | `workspace:*`  | **optional** — only for `/fusion`          |\n| `@playlive/fundraiser-data`   | `workspace:*`  | **optional** — only for `/fusion`          |\n\nThe three optional peers are declared `optional: true` in\n`peerDependenciesMeta`; you only need them if you import from\n[`@playlive/react-pipeline/fusion`](#fusion--rest-baseline--ws-delta-hooks-fusion).\n\nThis package has **no runtime `dependencies`** — nothing but your own\npeers ends up in the bundle.\n\n## Quick start\n\n```tsx\nimport {\n  RealtimePipelineProvider,\n  usePipelineCampaigns,\n  usePipelineConnectionState,\n  usePipelineDonations,\n} from \"@playlive/react-pipeline\";\n\nconst CAMPAIGN_ID = \"5d4b0d3c-9f31-4a5c-8fd1-0a2b3c4d5e6f\";\n\nfunction App() {\n  return (\n    <RealtimePipelineProvider\n      url=\"wss://main.playlive.ws.api.experience.stjude.org\"\n      initialCampaignIDs={[CAMPAIGN_ID]}\n      initialOverlayName=\"donation-bar\"\n      initialTeamUserSlug=\"@playliver\"\n      initialOverlayPath=\"/overlays/donation-bar\"\n      autoConnect\n    >\n      <DonationBar campaignID={CAMPAIGN_ID} />\n    </RealtimePipelineProvider>\n  );\n}\n\nfunction DonationBar({ campaignID }: { campaignID: string }) {\n  const { connected, connecting, hasReceivedInitialData } = usePipelineConnectionState();\n  const campaigns = usePipelineCampaigns();\n  const donations = usePipelineDonations(campaignID);\n\n  if (connecting) return <p>Connecting…</p>;\n  if (!connected) return <p>Disconnected — reconnecting…</p>;\n  if (!hasReceivedInitialData) return <p>Waiting for first tick…</p>;\n\n  const campaign = campaigns.find((c) => c.id === campaignID);\n  const raised = Number(campaign?.total_amount_raised?.value ?? 0);\n  const goal = Number(campaign?.goal?.value ?? 0);\n\n  return (\n    <section>\n      <progress value={raised} max={goal || 1} />\n      <p>\n        ${raised.toFixed(2)} of ${goal.toFixed(2)}\n      </p>\n      <ul>\n        {donations.slice(0, 5).map((d) => (\n          <li key={d.id}>\n            {d.donor_name} — ${d.amount.value}\n          </li>\n        ))}\n      </ul>\n    </section>\n  );\n}\n```\n\n`RealtimePipelineProvider` builds one Zustand store per provider\ninstance (from the `initial*` props, which are read **once** at first\nconstruction) and opens a single `PipelineConnection`. `connection.destroy()`\nruns on unmount. Children subscribe with any Tier 1 / Tier 2 hook.\n\nEvery hook that reads the store throws when no provider is mounted\nabove it; the four `useOn*` observer hooks deliberately soft-fail\n(they no-op) so you can drop them into components that sometimes render\noutside a pipeline tree.\n\n## Subpath exports\n\n| Subpath                             | Description                                                                                             |\n| ----------------------------------- | ------------------------------------------------------------------------------------------------------- |\n| `@playlive/react-pipeline`          | Default barrel — Tier 1 + Tier 2 (provider, `usePipelineValue`, typed slice hooks, observers).           |\n| `@playlive/react-pipeline/provider` | Tier 3 — `UnifiedDataPipelineProvider`, `useUnifiedDataPipeline`, `useAddPipeline*`, `useIdentify`, …    |\n| `@playlive/react-pipeline/fusion`   | REST + WS composition hooks (`useCurrentBlockRaised`, `useLiveSchedule`, the `useHybrid*` family, …).    |\n| `@playlive/react-pipeline/legacy`   | Soft-deprecated alias bundle (`useUDPStore`, `useUDPStoreApi`, `createUDPStore`, `UDPStore`, …).         |\n\nEach subpath ships an ESM bundle, a `bun` source condition, and `.d.ts`\ndeclarations. Frontend-eligible bundles are not minified so stack\ntraces stay readable in production.\n\n## API reference\n\nFull generated API documentation:\n<https://packages.playlive.experience.stjude.org/p/@playlive/react-pipeline/docs/>\n\n### Tier 1 — universal primitive\n\n| Export                          | Signature                                                     |\n| ------------------------------- | ------------------------------------------------------------- |\n| `RealtimePipelineProvider`      | `(props: RealtimePipelineProviderProps) => ReactNode`          |\n| `RealtimePipelineProviderProps` | Type. `InitialProps` + `children`, `url?`, `autoConnect?` (default `true`), `webSocketCtor?`, `log?`, `store?`, `connection?`. |\n| `usePipelineValue`              | `<T>(selector: (s: PipelineStore) => T) => T`                  |\n| `PACKAGE_NAME`                  | `\"@playlive/react-pipeline\"` — for runtime version-pinning.    |\n| `KNOWN_URLS`                    | `readonly string[]` — frozen, empty. See the disclosure section. |\n\n`RealtimePipelineProviderProps` extends\n[`InitialProps`](../realtime-pipeline/), so every `initial*` key\n(`initialCampaignIDs`, `initialTeamCampaignIDs`, `initialCauseIDs`,\n`initialFundraisingEventIDs`, `initialOverlayName`, `initialTeamUserSlug`,\n`initialOverlayPath`, `initialOverlayConfig`, `initialAdminApiKey`,\n`initialDonationTrainsEnabled`, …) is a valid prop. There is **no**\n`charityType` prop — set the charity type through the store's\n`setCurrentType` action or via `useCommonContextHooks` (Tier 3).\n\n`usePipelineValue` is single-arg by design. Zustand v5 dropped the third\n`equalityFn` parameter on `useStore` to keep the package free of the\n`use-sync-external-store` shim peer. For shallow / custom equality, wrap\nyour selector with `useShallow` from `zustand/shallow` (zero extra dep):\n\n```tsx\nimport { useShallow } from \"zustand/shallow\";\nimport { usePipelineValue } from \"@playlive/react-pipeline\";\n\nconst { connected, connecting } = usePipelineValue(\n  useShallow((s) => ({ connected: s.connected, connecting: s.connecting })),\n);\n```\n\n### Tier 2 — typed per-slice hooks\n\nStable wrappers over `usePipelineValue` with a typed selector +\nmemo-stable empty-array/object fallbacks (no infinite-render churn from\nfresh `[]` allocations — Decision §3 in [CHANGELOG.md](./CHANGELOG.md)).\n\n| Hook                                          | Returns                                                     |\n| --------------------------------------------- | ----------------------------------------------------------- |\n| `usePipelineCampaigns()`                      | `TiltifyCampaign[]`                                          |\n| `usePipelineTeamCampaigns()`                  | `TiltifyTeamCampaign[]`                                      |\n| `usePipelineFundraisingEvents()`              | `TiltifyFundraisingEvent[]`                                  |\n| `usePipelineCauses()`                         | `TiltifyCause[]`                                             |\n| `usePipelineDonations()`                      | `Record<string, TiltifyDonationWithTestFlag[]>`               |\n| `usePipelineDonations(id)`                    | `TiltifyDonationWithTestFlag[]` (memo-stable `[]` if unknown) |\n| `usePipelineDonationTrains(id, withStatus?)`  | `DonationTrain[]` — `withStatus` is `\"ACTIVE\" \\| \"ENDED\" \\| \"ENDING\" \\| \"PENDING\"` |\n| `usePipelineSubathonTimer(id, withStatus?)`   | `SubathonTimer[]` — `withStatus` is `\"ACTIVE\" \\| \"INACTIVE\"`  |\n| `usePipelineDonorSpotlight(id)`               | `ComputedSpotlight \\| null`                                  |\n| `usePipelineDonorSpotlightSettings()`         | `Record<string, DonorSpotlightSettings>`                     |\n| `usePipelinePolls(id)`                        | `TiltifyPoll[]`                                              |\n| `usePipelinePollDeltas(pollID)`               | `PollOptionDelta[]` — server-computed \"+$X on option Y\" deltas |\n| `usePipelineRewards(id)`                      | `TiltifyReward[]`                                            |\n| `usePipelineTargets(id)`                      | `TiltifyTarget[]`                                            |\n| `usePipelineMilestones(id)`                   | `TiltifyMilestone[]`                                         |\n| `usePipelineLeaderboardEntries(id)`           | `TiltifyLeaderboardEntry[]`                                  |\n| `usePipelineLeaderboardExclusions(id)`        | `LeaderboardExclusion[]`                                     |\n| `usePipelineLeaderboardExclusionsMap()`       | `Record<string, LeaderboardExclusion[]>`                     |\n| `usePipelineSchedule(id)`                     | `ScheduleTransitionBlock[]` — full `updateSchedule` WS push  |\n| `usePipelineSchedules()`                      | `Record<string, ScheduleTransitionBlock[]>`                  |\n| `usePipelineLastScheduleTransition(id)`       | `ScheduleTransitionPayload \\| null` — latest block crossing  |\n| `usePipelineLastScheduleTransitions()`        | `Record<string, ScheduleTransitionPayload>`                  |\n| `usePipelineDonationSum(id, options?)`        | `PipelineDonationSumResult` — live per-currency sum over `[start, end)` |\n| `usePipelineAuctionHouses()`                  | `AuctionHouse[]`                                             |\n| `usePipelineTwitchChat()`                     | `TwitchChatMessage[]`                                        |\n| `usePipelineConnectionState()`                | `PipelineConnectionState` (10 split selectors — no false re-renders) |\n| `usePipelineConnectionActions()`              | `PipelineConnectionActions` (stable `connect`/`disconnect`/…) |\n| `usePipelineSubscriptions()`                  | `Record<string, TiltifyWebhookSubscription>`                 |\n| `usePipelineTiltifySettings()`                | `TiltifyWebSocketSettings`                                   |\n| `usePipelineLastRefreshResponse()`            | `RefreshResponseMessage \\| null`                             |\n\nCompanion types exported alongside them: `PipelineConnectionState`\n(`connected`, `connecting`, `charityType`, `connectionType`, `wsURL`,\n`hasReceivedInitialData`, `lastIdentified`, `lastIdentityMessage`,\n`lastFirehoseMessage`, `lastMessage`), `PipelineConnectionActions`\n(`connect`, `disconnect`, `identify`, `refresh`, `reset`,\n`setAutoConnect`, `setAdminApiKey`), `PipelineDonationSumOptions`,\n`PipelineDonationSumByCurrency`, `PipelineDonationSumResult`.\n\n#### Change observers\n\nPush-style subscriptions that diff the store outside React's render\npath. Each stashes `handler` in a ref, subscribes exactly once, and\ntreats whatever is already on the store at mount as the baseline (so\nthey never fire for pre-existing data). All four **soft-fail** without\na provider.\n\n| Hook                                                | Handler payload                                        |\n| --------------------------------------------------- | ------------------------------------------------------ |\n| `useOnScheduleTransition(handler, options?)`        | `ScheduleTransitionPayload`                             |\n| `useOnScheduleUpdate(handler, options?)`            | `{ campaignID, schedule: ScheduleTransitionBlock[] }`   |\n| `useOnLeaderboardEntriesChange(handler, options?)`  | `{ campaignID, entries: TiltifyLeaderboardEntry[] }`    |\n| `useOnLeaderboardExclusionsChange(handler, options?)` | `{ campaignID, exclusions: LeaderboardExclusion[] }`  |\n\n`options` is `{ campaignIDs?: readonly string[] }` — omit it (or pass an\nempty list) to observe every campaign.\n\n```tsx\nuseOnLeaderboardEntriesChange(\n  ({ campaignID }) => {\n    queryClient.invalidateQueries({\n      queryKey: [\"leaderboard-with-exclusions\", \"tiltify\", campaignID],\n    });\n  },\n  { campaignIDs: [campaignID] },\n);\n```\n\n### Tier 3 — kitchen-sink provider (`/provider`)\n\n| Export                                        | Notes                                                                                          |\n| --------------------------------------------- | ---------------------------------------------------------------------------------------------- |\n| `UnifiedDataPipelineProvider`                 | Superset of `RealtimePipelineProvider`; adds legacy `reloadOnErrorReconnect` (default `true`) + `debug` (wires `console.debug` logging). |\n| `UnifiedDataPipelineProviderProps`            | Type. `Omit<RealtimePipelineProviderProps, \"children\"> & { children, reloadOnErrorReconnect?, debug? }`. |\n| `useUnifiedDataPipeline()`                    | One-call kitchen-sink hook → `UnifiedDataPipelineState`. Re-renders on **every** store change.  |\n| `UnifiedDataPipelineState`                    | Type. `PipelineStore` + `allRewards` / `allMilestones` / `allTargets` / `allPolls` / `allFundraisingEventSupportingCampaigns` / `tiltifySubscriptions` legacy aliases. |\n| `useAddPipelineCampaignIDs(params)`           | Layout-effect hook. `{ mode: \"ws\" \\| \"http\"; initialCampaign: { id }; isTeam: boolean; causeID? }` — no-op in `\"http\"` mode. |\n| `useAddPipelineCampaigns(params)`             | Layout-effect hook. `{ campaign?: TiltifyCampaign \\| null; teamCampaign?: TiltifyTeamCampaign \\| null }`. Idempotent by id. |\n| `useAddPipelineFundraisingEventIDs(params)`   | Layout-effect hook. `{ mode; initialCampaign: { id; fundraising_event_id: string \\| null } }`.   |\n| `useCommonContextHooks(params)`               | `{ url: string; mode: \"ws\" \\| \"http\"; charityType: CharityType }` — syncs type + URL and connects when `mode === \"ws\"`. |\n| `useIdentify()`                               | Returns the store-bound `identify` action. The 11-positional-arg legacy overload is kept as `@deprecated`; the args are ignored and a one-shot `console.warn` fires. |\n\n`RealtimePipelineProvider` / `RealtimePipelineProviderProps` are also\nre-exported from `/provider` so a Tier-3 consumer needs exactly one\nimport specifier.\n\n### Fusion — REST baseline + WS delta hooks (`/fusion`)\n\nThese fuse the WebSocket firehose with REST reads from\n[`@playlive/react-query`](../react-query/) /\n[`@playlive/fundraiser-data`](../fundraiser-data/). Importing this\nsubpath means you must also install the three optional peers.\n\n| Hook                                                   | Returns                                                                    |\n| ------------------------------------------------------ | -------------------------------------------------------------------------- |\n| `useCurrentBlockRaised(campaignID, start, end, opts?)` | `{ raised, data, isLoading, isError, refetch }` — REST baseline `+` WS delta over `[start, end)`. `opts`: `{ enabled?, queryOptions?, demoMode? }`. |\n| `useLiveSchedule(params, opts?)`                       | `{ schedule, isLoading, isError, refetch }`. `params`: `{ charityType, campaignID, isTeam? }`; `opts`: `{ enabled?, queryOptions?, demoMode?, demoIntervalMs? }`. |\n| `useCurrentScheduleItem(schedule, opts?)`              | `{ currentItem, nextItem }` — pure client-side rollover projection with a self-scheduling boundary timer. |\n| `useLinkedTeamCampaign(params, opts?)`                 | `{ shouldFetchLinkedTeamCampaign, resolvedLinkedTeamCampaign, linkedTeamTotalAmount, linkedTeamGoalAmount, isLoadingLinkedTeamCampaign, debug }`. |\n| `useDonorSpotlight(params, opts?)`                     | `{ spotlight, isLoading, refetch }`. `params`: `{ campaignID, isTeam, live, mode }`; `opts`: `{ pollingIntervalMs?, enabled?, queryOptions? }`. |\n\n`useLiveSchedule` resolves in a strict fallback order: full\n`updateSchedule` WS push → `lastScheduleTransitions` synthesis (demo\nonly) → REST `useSchedule` → demo fixture.\n\n### Fusion — hybrid slice hooks (`/fusion`)\n\nOverlay-parity hooks — the ones every Play Live overlay used to keep a\nlocal copy of — shipped here so they can be imported instead. Each takes a\n`mode: \"ws\" | \"http\"` that picks between:\n\n- `\"ws\"` — read the Tier-2 store selector, latch a local `hasLoadedX`\n  boolean on first arrival (or on the server's \"feature disabled\"\n  signal), and call `updateTiltifySettings({ <sliceKey> })` whenever the\n  local `xEnabled` toggle flips so the pipeline dynamically un/subscribes.\n- `\"http\"` — delegate to the matching `@playlive/react-query` REST hook\n  (`useRewards`, `useTargets`, …) and ignore the WS slice.\n\nAll of them share `useLoadingTimeout` — a 15 s safety net that\nforce-completes the WS branch when the store never delivers.\n\n| Hook                                                    | Params → Returns                                                              |\n| -------------------------------------------------------- | ----------------------------------------------------------------------------- |\n| `useHybridRewards({ campaignID, isTeam, mode, initialRewardsEnabled? })`   | `{ currentRewards, hasLoadedRewards, setHasLoadedRewards, isLoadingRewards, isLoading, rewardsEnabled, setRewardsEnabled }` |\n| `useHybridTargets({ campaignID, isTeam, mode })`         | `{ currentTargets, hasLoadedTargets, setHasLoadedTargets, isLoadingTargets, isLoading, targetsEnabled, setTargetsEnabled }` |\n| `useHybridMilestones({ campaignID, isTeam, mode })`      | `{ currentMilestones, isLoadingMilestones, milestonesEnabled, setMilestonesEnabled }` |\n| `useHybridPolls({ campaignID, isTeam, mode })`           | `{ currentPolls, isLoadingPolls, pollsEnabled, setPollsEnabled }`              |\n| `useHybridCause({ mode, initialCampaign })`              | `{ cause, isLoading }` — single-slot semantics.                                |\n| `useHybridFundraisingEvent({ mode, initialCampaign })`   | `{ fundraisingEvent, isLoading }` — single-slot semantics.                     |\n| `useHybridFundraisingEventSupportingCampaigns({ mode, initialFundraisingEvent, initialSupportingCampaignsEnabled? })` | `{ supportingCampaigns, isLoading, isLoadingSupportingCampaigns, hasLoadedSupportingCampaigns, supportingCampaignsEnabled, setSupportingCampaignsEnabled }` |\n| `useHybridDonationTrains({ campaignID, mode, initialDonationTrainsEnabled?, withStatus?, http?, auth? })` | `{ currentDonationTrains, hasLoadedDonationTrains, setHasLoadedDonationTrains, isLoadingDonationTrains, isLoading, donationTrainsEnabled, setDonationTrainsEnabled }` |\n| `useHybridAuctionHouses({ mode, enabled, campaignID })`  | `{ auctionHouses, auctionHouseTotal, auctionHousesEnabled, setAuctionHousesEnabled }` — WS-only (no REST fallback yet). |\n| `useHybridLeaderboardWithExclusions({ charityType, campaignID, isTeam?, timeType?, count?, startDate?, endDate?, enabled?, continuouslyUpdate? })` | `{ entries, isLoading }` — WS-driven invalidation over the REST leaderboard, with a `LEADERBOARD_SAFETY_POLL_MS` (60 s) backstop. |\n| `useLoadingTimeout({ mode, isLoading, onFinishedLoading, timeoutAmount? })` | `void` — the shared safety-net timer primitive.                    |\n\n`LEADERBOARD_SAFETY_POLL_MS` is exported alongside the hooks. Return\nshapes are preserved verbatim from the original overlay hooks, so\nexisting call sites can collapse to a re-export.\n\n### Legacy aliases (`/legacy`)\n\nSoft-deprecated. Behavior is identical to the canonical exports — rename\nthe import specifier once and the rest of your code base keeps working:\n\n| Legacy name          | Canonical replacement                                            |\n| -------------------- | ---------------------------------------------------------------- |\n| `useUDPStore`        | `usePipelineValue` (Tier 1) / typed slice hooks (Tier 2)          |\n| `useUDPStoreApi`     | Use a typed slice hook, or reach for the raw store API            |\n| `createUDPStore`     | `createPipelineStore` from `@playlive/realtime-pipeline/store`    |\n| `UDPStore` (type)    | `PipelineStore` from `@playlive/realtime-pipeline/store`          |\n| `UDPStoreApi` (type) | `PipelineStoreApi` from `@playlive/realtime-pipeline/store`       |\n| `CharityTypes`       | Re-exported from `@playlive/realtime-pipeline/protocol`           |\n\n`useUDPStore` / `useUDPStoreApi` throw with a\n`\"must be called inside <UnifiedDataPipelineProvider>\"` message when no\nprovider is mounted. The subpath also re-exports every Tier 1 + 2 + 3\nsymbol under its canonical name, plus the `PipelineStore` /\n`PipelineStoreApi` types.\n\n## Migration from `playlive-overlay-data-layer`\n\nOne-shot, sed-friendly rewrite of every import specifier (no body edits\nneeded for the happy path):\n\n```bash\n# Tier 3 — kitchen-sink (drop-in replacement)\nrg -l '@playlive/overlay-data-layer/websocket' . \\\n  | xargs sed -i '' \\\n    -e 's|@playlive/overlay-data-layer/websocket|@playlive/react-pipeline/provider|g'\n```\n\nFor the soft-deprecated names (`useUDPStore`, `createUDPStore`, …)\nsubstitute `/legacy` instead of `/provider` to opt into the alias\nbundle, then migrate at your leisure — the aliases are kept for a\none-release window.\n\nTwo call-site changes are **not** covered by the sed:\n\n- `useIdentify(sendJsonMessage, campaignIDs, …)` — the positional args\n  are now ignored (the connection reads identify inputs off the store).\n  Drop them; the deprecated overload still compiles and warns once.\n- `useWebSocket` from `react-use-websocket-lite` — replaced by\n  `createPipelineConnection` in `@playlive/realtime-pipeline/connection`,\n  which `RealtimePipelineProvider` mounts for you.\n\nThe [Legacy aliases](#legacy-aliases-legacy) table above is the full\nper-symbol mapping.\n\n## Upstream spec\n\nNo external API surface — this package fetches nothing itself. It binds\nReact to [`@playlive/realtime-pipeline`](../realtime-pipeline/), which\nowns the WebSocket connection and frames every byte on the wire; the\nmessage catalogue and its types are exported from\n`@playlive/realtime-pipeline/protocol`. The `/fusion` hooks additionally\nread REST baselines through\n[`@playlive/react-query`](../react-query/) and\n[`@playlive/fundraiser-data`](../fundraiser-data/) — see those packages\nfor the endpoints and URL knobs involved.\n\n## Twitch Extension URL disclosure\n\n`KNOWN_URLS` enumerates every absolute URL or host this package can\nreach. **It is empty.** This package only opens the WebSocket URL the\nconsumer hands to `<RealtimePipelineProvider url={…}>` (or the store's\n`DEFAULT_WEBSOCKET_URL`, `wss://main.playlive.ws.api.experience.stjude.org`).\nDisclose that URL — plus the `KNOWN_URLS` of `@playlive/realtime-pipeline`\nand, if you import `/fusion`, `@playlive/fundraiser-data` — on your\nExtension submission.\n\n```ts\nimport { KNOWN_URLS } from \"@playlive/react-pipeline\";\nconsole.log(KNOWN_URLS); // []\n```\n\n## Examples\n\n### Live schedule-block total without polling\n\nThe canonical fusion use case: fetch a REST baseline once, then add the\nWebSocket donation delta on top so the number ticks up on every donation\ninstead of every 30-second poll. Requires the optional peers plus a\n`<QueryClientProvider>` above the pipeline provider.\n\n```tsx\nimport { configure } from \"@playlive/fundraiser-data/config\";\nimport { getConfigForEnv } from \"@playlive/fundraiser-data/environments\";\nimport { RealtimePipelineProvider } from \"@playlive/react-pipeline\";\nimport {\n  useCurrentBlockRaised,\n  useCurrentScheduleItem,\n  useLiveSchedule,\n} from \"@playlive/react-pipeline/fusion\";\nimport { makeQueryClient } from \"@playlive/react-query\";\nimport { QueryClientProvider } from \"@tanstack/react-query\";\n\nconfigure(getConfigForEnv(\"prod\", { tiltifyProxyUrl: import.meta.env.VITE_TILTIFY_PROXY_URL }));\nconst queryClient = makeQueryClient();\n\nconst CAMPAIGN_ID = \"5d4b0d3c-9f31-4a5c-8fd1-0a2b3c4d5e6f\";\n\nexport function App() {\n  return (\n    <QueryClientProvider client={queryClient}>\n      <RealtimePipelineProvider\n        url=\"wss://main.playlive.ws.api.experience.stjude.org\"\n        initialCampaignIDs={[CAMPAIGN_ID]}\n        initialOverlayName=\"schedule-block\"\n        initialTeamUserSlug=\"@playliver\"\n      >\n        <BlockTotal campaignID={CAMPAIGN_ID} />\n      </RealtimePipelineProvider>\n    </QueryClientProvider>\n  );\n}\n\nfunction BlockTotal({ campaignID }: { campaignID: string }) {\n  const { schedule, isLoading: scheduleLoading, isError } = useLiveSchedule({\n    charityType: \"tiltify\",\n    campaignID,\n  });\n  const { currentItem, nextItem } = useCurrentScheduleItem(schedule);\n\n  const { raised, isLoading } = useCurrentBlockRaised(\n    campaignID,\n    currentItem?.starts_at,\n    currentItem?.ends_at,\n  );\n\n  if (scheduleLoading) return <p>Loading schedule…</p>;\n  if (isError) return <p>Schedule unavailable.</p>;\n  if (!currentItem) return <p>Up next: {nextItem?.name ?? \"nothing scheduled\"}</p>;\n\n  return (\n    <section>\n      <h2>{currentItem.name}</h2>\n      <p>{isLoading ? \"…\" : `$${raised.toFixed(2)} raised this block`}</p>\n    </section>\n  );\n}\n```\n\n### Raw baseline + delta with the Tier-2 primitive\n\nIf you already have the baseline (from a route loader, say), skip the\nfusion hook and reduce the firehose yourself:\n\n```tsx\nimport { usePipelineDonationSum } from \"@playlive/react-pipeline\";\n\nfunction BlockDelta({\n  campaignID,\n  baseline,\n}: {\n  campaignID: string;\n  // `GET /schedules/campaigns/{id}/raised`\n  baseline: { raised: number; currency: string; asOf: string };\n}) {\n  const delta = usePipelineDonationSum(campaignID, {\n    start: baseline.asOf,\n    end: \"2025-09-20T23:00:00.000Z\",\n    currency: baseline.currency,\n  });\n\n  const total = baseline.raised + (delta.raised ?? 0);\n  return (\n    <p>\n      ${total.toFixed(2)} ({delta.donationCount} live donations since baseline)\n    </p>\n  );\n}\n```\n\n`usePipelineDonationSum` matches `start <= completed_at < end`, dedupes\nby donation id (last write wins, so post-edit corrections propagate),\nand excludes `test: true` donations unless you pass\n`includeTest: true` — which keeps it consistent with the REST baseline.\n\nBoth examples above are complete and runnable: point\n`<RealtimePipelineProvider url={…}>` at your pipeline WebSocket and the\nrest is copy-paste.\n\n## License\n\nMIT © St. Jude Children's Research Hospital\n","readmeFilename":"README.md","dist":{"tarball":"https://packages.playlive.experience.stjude.org/@playlive/react-pipeline/-/react-pipeline-0.4.20.tgz","shasum":"d576619a9d3ecdbb92e29ec76ec67404eb777eeb","integrity":"sha512-pp0cpzIiJf4pxAZ4jMddqMVS9+jo6Unpc9bpHkoLIshtZJWdVl4Diu5Vvga8Reyz4ZTmrzHd0ECNwV/oVZn86A=="}}},"time":{"0.4.18":"2026-08-26T18:10:02.807Z","modified":"2026-08-26T20:10:10.405Z","0.1.0":"2026-08-26T18:15:13.047Z","0.1.1":"2026-08-26T18:15:13.685Z","0.2.0":"2026-08-26T18:15:14.243Z","0.2.1":"2026-08-26T18:15:14.728Z","0.2.2":"2026-08-26T18:15:15.260Z","0.2.3":"2026-08-26T18:15:15.936Z","0.3.0":"2026-08-26T18:15:16.443Z","0.3.1":"2026-08-26T18:15:16.982Z","0.3.2":"2026-08-26T18:15:17.569Z","0.3.3":"2026-08-26T18:15:18.058Z","0.3.4":"2026-08-26T18:15:18.614Z","0.4.0":"2026-08-26T18:15:19.134Z","0.4.1":"2026-08-26T18:15:19.597Z","0.4.2":"2026-08-26T18:15:20.088Z","0.4.3":"2026-08-26T18:15:20.648Z","0.4.4":"2026-08-26T18:15:21.230Z","0.4.5":"2026-08-26T18:15:21.962Z","0.4.6":"2026-08-26T18:15:22.657Z","0.4.7":"2026-08-26T18:15:23.155Z","0.4.8":"2026-08-26T18:15:23.697Z","0.4.9":"2026-08-26T18:15:24.384Z","0.4.10":"2026-08-26T18:15:25.111Z","0.4.11":"2026-08-26T18:15:25.636Z","0.4.13":"2026-08-26T18:15:26.286Z","0.4.14":"2026-08-26T18:15:26.998Z","0.4.15":"2026-08-26T18:15:27.632Z","0.4.16":"2026-08-26T18:15:28.419Z","0.4.17":"2026-08-26T18:15:29.056Z","0.4.19":"2026-08-26T19:46:21.270Z","0.4.20":"2026-08-26T20:10:10.405Z"}}