@vivix-ai/ivi-frontend-sdk 0.3.7 → 0.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var iviSdkTs = require('@vivix-ai/ivi-sdk-ts');
4
+ var websocket = require('@vivix-ai/ivi-sdk-ts/transports/websocket');
4
5
  var react = require('react');
5
6
  var jsxRuntime = require('react/jsx-runtime');
6
7
 
@@ -622,7 +623,9 @@ var SourceManager = class {
622
623
  durationMs: previous?.durationMs,
623
624
  hasAudio: previous?.hasAudio,
624
625
  error: previous?.status === "failed" ? previous.error : void 0,
625
- preload: previous?.preload
626
+ preload: previous?.preload,
627
+ origin: previous?.origin,
628
+ provisional: previous?.provisional ? false : previous?.provisional
626
629
  });
627
630
  this.sources = next;
628
631
  }
@@ -637,7 +640,38 @@ var SourceManager = class {
637
640
  height: payload.height,
638
641
  durationMs: payload.durationMs,
639
642
  hasAudio: payload.hasAudio,
640
- preload: previous?.preload
643
+ preload: previous?.preload,
644
+ origin: previous?.origin,
645
+ provisional: previous?.provisional ? false : previous?.provisional
646
+ });
647
+ this.sources = next;
648
+ }
649
+ upsertBootstrapReady(payload) {
650
+ const previous = this.sources.get(payload.sourceId);
651
+ const next = new Map(this.sources);
652
+ const source = {
653
+ source_id: payload.sourceId,
654
+ kind: payload.source?.kind ?? (payload.streamId ? "generation_stream" : "stream"),
655
+ stream_id: payload.source?.stream_id ?? payload.streamId,
656
+ asset_type: payload.source?.asset_type ?? "video",
657
+ url: payload.source?.url,
658
+ generation: payload.source?.generation,
659
+ metadata: {
660
+ ...payload.source?.metadata ?? {},
661
+ label: payload.source?.metadata?.label ?? "prebuilt"
662
+ }
663
+ };
664
+ next.set(payload.sourceId, {
665
+ source,
666
+ status: "ready",
667
+ playback: previous?.playback ?? payload.playback,
668
+ width: previous?.width ?? payload.width,
669
+ height: previous?.height ?? payload.height,
670
+ durationMs: previous?.durationMs ?? payload.durationMs,
671
+ hasAudio: previous?.hasAudio ?? payload.hasAudio,
672
+ preload: previous?.preload ?? { autoclearAfterPlay: true },
673
+ origin: previous?.origin ?? "prebuilt-session-response",
674
+ provisional: previous?.provisional ?? true
641
675
  });
642
676
  this.sources = next;
643
677
  }
@@ -671,11 +705,14 @@ var SourceManager = class {
671
705
  const next = /* @__PURE__ */ new Map();
672
706
  sources.forEach((source) => {
673
707
  const previous = this.sources.get(source.source_id);
708
+ const playback = source.playback ?? previous?.playback;
674
709
  next.set(source.source_id, {
675
710
  source,
676
- status: source.playback ? "ready" : "created",
677
- playback: source.playback,
678
- preload: previous?.preload
711
+ status: playback ? "ready" : "created",
712
+ playback,
713
+ preload: previous?.preload,
714
+ origin: previous?.origin,
715
+ provisional: source.playback && previous?.provisional ? false : previous?.provisional
679
716
  });
680
717
  });
681
718
  this.sources = next;
@@ -1107,6 +1144,8 @@ var ConversationManager = class {
1107
1144
 
1108
1145
  // src/runtime/managers/trtc-source-manager.ts
1109
1146
  var TAG = "[IVI-TRTC]";
1147
+ var TRTC_VIEW_ATTR = "data-ivi-trtc-view";
1148
+ var TRTC_MEDIA_STYLE_ID = "ivi-trtc-media-style";
1110
1149
  var DEFAULT_DENOISER_OPTIONS = {
1111
1150
  enabled: true,
1112
1151
  mode: "normal"
@@ -1268,6 +1307,8 @@ var TrtcSourceManager = class {
1268
1307
  if (!session) {
1269
1308
  throw new Error(`TRTC source session not found: ${sourceId}`);
1270
1309
  }
1310
+ ensureTrtcMediaStyleSheet();
1311
+ container.setAttribute(TRTC_VIEW_ATTR, "");
1271
1312
  session.views.set(viewId, {
1272
1313
  sourceId,
1273
1314
  container,
@@ -1275,6 +1316,7 @@ var TrtcSourceManager = class {
1275
1316
  startedVideoKeys: /* @__PURE__ */ new Set(),
1276
1317
  startingVideoKeys: /* @__PURE__ */ new Set()
1277
1318
  });
1319
+ this.log("info", `\u7ED1\u5B9A\u89C6\u56FE source=${sourceId} view=${viewId} views=${session.views.size}`);
1278
1320
  await this.ensureConnected(session);
1279
1321
  const binding = session.views.get(viewId);
1280
1322
  if (!binding) {
@@ -1290,6 +1332,8 @@ var TrtcSourceManager = class {
1290
1332
  }
1291
1333
  const binding = session.views.get(viewId);
1292
1334
  if (binding?.sourceId === sourceId) {
1335
+ this.log("info", `\u89E3\u7ED1\u89C6\u56FE source=${sourceId} view=${viewId}`);
1336
+ binding.container.removeAttribute(TRTC_VIEW_ATTR);
1293
1337
  session.views.delete(viewId);
1294
1338
  }
1295
1339
  void this.applyAudioPolicy(session);
@@ -1306,11 +1350,30 @@ var TrtcSourceManager = class {
1306
1350
  binding.muted = muted;
1307
1351
  void this.applyAudioPolicy(session);
1308
1352
  }
1353
+ reassignView(fromSourceId, toSourceId, viewId) {
1354
+ if (fromSourceId === toSourceId) {
1355
+ return true;
1356
+ }
1357
+ const fromSession = this.getSession(fromSourceId);
1358
+ const toSession = this.getSession(toSourceId);
1359
+ if (!fromSession || !toSession || fromSession !== toSession) {
1360
+ return false;
1361
+ }
1362
+ const binding = fromSession.views.get(viewId);
1363
+ if (!binding || binding.sourceId !== fromSourceId) {
1364
+ return false;
1365
+ }
1366
+ this.log("info", `\u8FC1\u79FB\u89C6\u56FE source=${fromSourceId}->${toSourceId} view=${viewId}`);
1367
+ binding.sourceId = toSourceId;
1368
+ void this.applyAudioPolicy(fromSession);
1369
+ return true;
1370
+ }
1309
1371
  unlinkSourceFromSession(sourceId, session) {
1310
1372
  session.sourceIds.delete(sourceId);
1311
1373
  this.sourceSessionKeys.delete(sourceId);
1312
1374
  session.views.forEach((binding, viewId) => {
1313
1375
  if (binding.sourceId === sourceId) {
1376
+ binding.container.removeAttribute(TRTC_VIEW_ATTR);
1314
1377
  session.views.delete(viewId);
1315
1378
  }
1316
1379
  });
@@ -1479,6 +1542,7 @@ var TrtcSourceManager = class {
1479
1542
  if (!parsed) {
1480
1543
  continue;
1481
1544
  }
1545
+ this.log("info", `\u56DE\u653E\u5DF2\u77E5\u8FDC\u7AEF\u89C6\u9891 source=${binding.sourceId} userId=${parsed.userId} streamType=${parsed.streamType}`);
1482
1546
  await this.startRemoteVideoForBinding(
1483
1547
  client,
1484
1548
  parsed.userId,
@@ -1494,12 +1558,14 @@ var TrtcSourceManager = class {
1494
1558
  }
1495
1559
  binding.startingVideoKeys.add(remoteVideoKey);
1496
1560
  try {
1561
+ this.log("info", `\u5F00\u59CB\u6E32\u67D3\u8FDC\u7AEF\u89C6\u9891 source=${binding.sourceId} view=${remoteVideoKey} userId=${userId} streamType=${streamType}`);
1497
1562
  await client.startRemoteVideo({
1498
1563
  userId,
1499
1564
  streamType,
1500
1565
  view: binding.container,
1501
1566
  option: { fillMode: "contain" }
1502
1567
  });
1568
+ this.log("info", `\u8FDC\u7AEF\u89C6\u9891\u6E32\u67D3\u5B8C\u6210 source=${binding.sourceId} userId=${userId} streamType=${streamType}`);
1503
1569
  binding.startedVideoKeys.add(remoteVideoKey);
1504
1570
  enforceContainMedia(binding.container);
1505
1571
  } catch (error) {
@@ -1630,11 +1696,43 @@ var TrtcSourceManager = class {
1630
1696
  this.onTrtcEvent?.(event);
1631
1697
  }
1632
1698
  };
1699
+ function ensureTrtcMediaStyleSheet() {
1700
+ if (typeof document === "undefined") {
1701
+ return;
1702
+ }
1703
+ if (document.getElementById(TRTC_MEDIA_STYLE_ID)) {
1704
+ return;
1705
+ }
1706
+ const style = document.createElement("style");
1707
+ style.id = TRTC_MEDIA_STYLE_ID;
1708
+ style.textContent = `
1709
+ [${TRTC_VIEW_ATTR}] [id^="player_"],
1710
+ [${TRTC_VIEW_ATTR}] [id^="video_"],
1711
+ [${TRTC_VIEW_ATTR}] [id^="audio_"],
1712
+ [${TRTC_VIEW_ATTR}] video,
1713
+ [${TRTC_VIEW_ATTR}] canvas {
1714
+ width: 100% !important;
1715
+ height: 100% !important;
1716
+ max-width: 100% !important;
1717
+ max-height: 100% !important;
1718
+ background: transparent !important;
1719
+ background-color: transparent !important;
1720
+ background-image: none !important;
1721
+ }
1722
+
1723
+ [${TRTC_VIEW_ATTR}] video,
1724
+ [${TRTC_VIEW_ATTR}] canvas {
1725
+ object-fit: contain !important;
1726
+ display: block !important;
1727
+ }
1728
+ `;
1729
+ document.head.appendChild(style);
1730
+ }
1633
1731
  function isRuntimeTrtcSource(source) {
1634
1732
  return source.status === "ready" && source.playback?.type === "trtc" && typeof source.playback.trtc === "object" && source.playback.trtc !== null;
1635
1733
  }
1636
1734
  function isSameTrtcConfig(a, b) {
1637
- return a.app_id === b.app_id && a.user_id === b.user_id && a.user_sig === b.user_sig && a.room_id === b.room_id;
1735
+ return a.app_id === b.app_id && a.user_id === b.user_id && a.room_id === b.room_id;
1638
1736
  }
1639
1737
  function buildTrtcSessionKey(trtc, aiDenoiser) {
1640
1738
  const denoiser = resolveAIDenoiserOptions(aiDenoiser);
@@ -1642,7 +1740,6 @@ function buildTrtcSessionKey(trtc, aiDenoiser) {
1642
1740
  appId: trtc.app_id,
1643
1741
  roomId: trtc.room_id,
1644
1742
  userId: trtc.user_id,
1645
- userSig: trtc.user_sig,
1646
1743
  aiDenoiser: {
1647
1744
  enabled: denoiser.enabled,
1648
1745
  mode: denoiser.mode,
@@ -1721,6 +1818,9 @@ function enforceContainMedia(container) {
1721
1818
  element.style.setProperty("max-height", "100%", "important");
1722
1819
  element.style.setProperty("object-fit", "contain", "important");
1723
1820
  element.style.setProperty("display", "block", "important");
1821
+ element.style.setProperty("background", "transparent", "important");
1822
+ element.style.setProperty("background-color", "transparent", "important");
1823
+ element.style.setProperty("background-image", "none", "important");
1724
1824
  });
1725
1825
  }
1726
1826
 
@@ -1748,8 +1848,9 @@ function describeLivekitRoom(livekit) {
1748
1848
  // src/runtime/managers/livekit-source-manager.ts
1749
1849
  var TAG2 = "[IVI-LIVEKIT]";
1750
1850
  var LivekitSourceManager = class {
1751
- constructor(onLog) {
1851
+ constructor(onLog, onRemoteVideoAvailable) {
1752
1852
  this.onLog = onLog;
1853
+ this.onRemoteVideoAvailable = onRemoteVideoAvailable;
1753
1854
  this.sessions = /* @__PURE__ */ new Map();
1754
1855
  this.listeners = /* @__PURE__ */ new Map();
1755
1856
  }
@@ -1976,6 +2077,7 @@ var LivekitSourceManager = class {
1976
2077
  waiter(true);
1977
2078
  }
1978
2079
  session.remoteVideoWaiters.length = 0;
2080
+ this.onRemoteVideoAvailable?.(session.sourceId);
1979
2081
  }
1980
2082
  session.views.forEach((binding) => {
1981
2083
  this.attachTrackToView(track, kind, trackKey, binding);
@@ -2171,6 +2273,7 @@ var IviRuntimeCoordinator = class {
2171
2273
  this.userTextFlowCounter = 0;
2172
2274
  this.waitingTracksListValidation = false;
2173
2275
  this.waitingSourcesListValidation = false;
2276
+ this.bootstrapSource = null;
2174
2277
  this.state = {
2175
2278
  status: "idle",
2176
2279
  session: null,
@@ -2179,7 +2282,8 @@ var IviRuntimeCoordinator = class {
2179
2282
  sources: /* @__PURE__ */ new Map(),
2180
2283
  streams: /* @__PURE__ */ new Map(),
2181
2284
  conversationItems: /* @__PURE__ */ new Map(),
2182
- conversations: []
2285
+ conversations: [],
2286
+ bootstrap: null
2183
2287
  };
2184
2288
  this.client = client;
2185
2289
  this.config = {
@@ -2188,10 +2292,12 @@ var IviRuntimeCoordinator = class {
2188
2292
  };
2189
2293
  this.trtcSourceManager = new TrtcSourceManager(
2190
2294
  this.config.onLog,
2191
- (event) => this.emitTrtcEvent(event),
2295
+ (event) => this.onTrtcManagerEvent(event),
2192
2296
  this.config.trtcAIDenoiser
2193
2297
  );
2194
- this.livekitSourceManager = new LivekitSourceManager(this.config.onLog);
2298
+ this.livekitSourceManager = new LivekitSourceManager(this.config.onLog, () => {
2299
+ this.recomposeBootstrapState();
2300
+ });
2195
2301
  this.sessionHandler = new SessionEventHandler(this.sessionManager, {
2196
2302
  onSessionCreated: (event) => this.onSessionCreated(event),
2197
2303
  onSessionEnded: (event) => this.onSessionEnded(event)
@@ -2272,6 +2378,22 @@ var IviRuntimeCoordinator = class {
2272
2378
  emitLog(entry) {
2273
2379
  this.config.onLog?.(entry);
2274
2380
  }
2381
+ setBootstrapSource(source) {
2382
+ const previous = this.bootstrapSource;
2383
+ if (previous && (!source || previous.sourceId !== source.sourceId)) {
2384
+ const existing = this.sourceManager.get(previous.sourceId);
2385
+ if (existing?.origin === "prebuilt-session-response" && existing.provisional) {
2386
+ this.sourceManager.remove(previous.sourceId);
2387
+ }
2388
+ }
2389
+ this.bootstrapSource = source;
2390
+ this.ensureBootstrapSourceRegistered();
2391
+ this.syncPlaybackManagers();
2392
+ this.setState({
2393
+ ...this.state,
2394
+ sources: this.sourceManager.getAll()
2395
+ });
2396
+ }
2275
2397
  /**
2276
2398
  * 编排一次"用户文本输入 -> 触发模型回复"链路。
2277
2399
  *
@@ -2392,10 +2514,10 @@ var IviRuntimeCoordinator = class {
2392
2514
  this.trackManager.reset();
2393
2515
  this.sourceManager.reset();
2394
2516
  this.streamManager.reset();
2395
- this.trtcSourceManager.reset();
2396
- this.livekitSourceManager.reset();
2397
2517
  this.conversationManager.reset();
2398
2518
  this.reset();
2519
+ this.ensureBootstrapSourceRegistered();
2520
+ this.syncPlaybackManagers();
2399
2521
  const nextStatus = this.config.syncStageOnSessionCreated !== false ? "syncing" : "running";
2400
2522
  this.setState({
2401
2523
  status: nextStatus,
@@ -2433,8 +2555,8 @@ var IviRuntimeCoordinator = class {
2433
2555
  onTracksChanged(listRefreshed) {
2434
2556
  const nextStage = this.stageManager.getStage();
2435
2557
  this.sourceManager.syncWithTracks(this.trackManager.getAll());
2436
- this.trtcSourceManager.syncRuntimeSources(this.sourceManager.getAll());
2437
- this.livekitSourceManager.syncRuntimeSources(this.sourceManager.getAll());
2558
+ this.ensureBootstrapSourceRegistered();
2559
+ this.syncPlaybackManagers();
2438
2560
  this.setState({
2439
2561
  ...this.state,
2440
2562
  tracks: this.trackManager.getAll(),
@@ -2448,8 +2570,8 @@ var IviRuntimeCoordinator = class {
2448
2570
  applyLocalTrackTake(trackId) {
2449
2571
  this.trackManager.applyTrackTook(trackId);
2450
2572
  this.sourceManager.syncWithTracks(this.trackManager.getAll());
2451
- this.trtcSourceManager.syncRuntimeSources(this.sourceManager.getAll());
2452
- this.livekitSourceManager.syncRuntimeSources(this.sourceManager.getAll());
2573
+ this.ensureBootstrapSourceRegistered();
2574
+ this.syncPlaybackManagers();
2453
2575
  this.setState({
2454
2576
  ...this.state,
2455
2577
  tracks: this.trackManager.getAll(),
@@ -2489,8 +2611,8 @@ var IviRuntimeCoordinator = class {
2489
2611
  }
2490
2612
  onSourcesChanged(listRefreshed) {
2491
2613
  this.sourceManager.syncWithTracks(this.trackManager.getAll());
2492
- this.trtcSourceManager.syncRuntimeSources(this.sourceManager.getAll());
2493
- this.livekitSourceManager.syncRuntimeSources(this.sourceManager.getAll());
2614
+ this.ensureBootstrapSourceRegistered();
2615
+ this.syncPlaybackManagers();
2494
2616
  this.setState({
2495
2617
  ...this.state,
2496
2618
  sources: this.sourceManager.getAll()
@@ -2513,12 +2635,101 @@ var IviRuntimeCoordinator = class {
2513
2635
  });
2514
2636
  }
2515
2637
  setState(nextState) {
2516
- if (this.state.status === nextState.status && this.state.session === nextState.session && this.state.stage === nextState.stage && this.state.tracks === nextState.tracks && this.state.sources === nextState.sources && this.state.streams === nextState.streams && this.state.conversationItems === nextState.conversationItems && this.state.conversations === nextState.conversations) {
2638
+ let resolvedNextState = nextState;
2639
+ if (this.bootstrapSource && nextState.status !== "stopped" && !nextState.sources.has(this.bootstrapSource.sourceId)) {
2640
+ this.ensureBootstrapSourceRegistered();
2641
+ resolvedNextState = {
2642
+ ...nextState,
2643
+ sources: this.sourceManager.getAll()
2644
+ };
2645
+ }
2646
+ const composedNextState = this.composeBootstrapState(resolvedNextState);
2647
+ if (this.state.status === composedNextState.status && this.state.session === composedNextState.session && this.state.stage === composedNextState.stage && this.state.tracks === composedNextState.tracks && this.state.sources === composedNextState.sources && this.state.streams === composedNextState.streams && this.state.conversationItems === composedNextState.conversationItems && this.state.conversations === composedNextState.conversations && isSameBootstrapState(this.state.bootstrap, composedNextState.bootstrap)) {
2517
2648
  return;
2518
2649
  }
2519
- this.state = nextState;
2650
+ this.state = composedNextState;
2520
2651
  this.stateListeners.forEach((listener) => listener(this.state));
2521
2652
  }
2653
+ composeBootstrapState(nextState) {
2654
+ const bootstrap = this.bootstrapSource;
2655
+ if (!bootstrap) {
2656
+ return nextState.bootstrap === null || nextState.bootstrap === void 0 ? nextState : { ...nextState, bootstrap: null };
2657
+ }
2658
+ const shouldExpose = this.shouldExposeBootstrap(nextState, bootstrap);
2659
+ const bootstrapState = {
2660
+ active: shouldExpose,
2661
+ slot: bootstrap.slot,
2662
+ trackId: bootstrap.trackId,
2663
+ sourceId: bootstrap.sourceId,
2664
+ streamId: bootstrap.streamId
2665
+ };
2666
+ if (!shouldExpose) {
2667
+ return {
2668
+ ...nextState,
2669
+ bootstrap: bootstrapState
2670
+ };
2671
+ }
2672
+ const tracks = new Map(nextState.tracks);
2673
+ tracks.set(bootstrap.trackId, {
2674
+ track_id: bootstrap.trackId,
2675
+ active_source_id: bootstrap.sourceId
2676
+ });
2677
+ return {
2678
+ ...nextState,
2679
+ tracks,
2680
+ bootstrap: bootstrapState
2681
+ };
2682
+ }
2683
+ shouldExposeBootstrap(state, bootstrap) {
2684
+ if (state.status === "stopped") {
2685
+ return false;
2686
+ }
2687
+ const bootstrapRuntimeSource = state.sources.get(bootstrap.sourceId);
2688
+ if (!isReadyRenderableSource(bootstrapRuntimeSource)) {
2689
+ return false;
2690
+ }
2691
+ const layer = (state.stage?.composition ?? []).find((item) => item.slot === bootstrap.slot);
2692
+ if (!layer) {
2693
+ return true;
2694
+ }
2695
+ const track = state.tracks.get(layer.track_id);
2696
+ const activeSourceId = track?.active_source_id;
2697
+ if (!activeSourceId || activeSourceId === bootstrap.sourceId) {
2698
+ return true;
2699
+ }
2700
+ return !this.isReadyForBootstrapHandoff(activeSourceId, state.sources.get(activeSourceId));
2701
+ }
2702
+ isReadyForBootstrapHandoff(sourceId, source) {
2703
+ if (!isReadyRenderableSource(source)) {
2704
+ return false;
2705
+ }
2706
+ if (isTrtcPlaybackSource(source)) {
2707
+ return this.trtcSourceManager.hasRemoteVideoAvailable(sourceId);
2708
+ }
2709
+ if (isLivekitPlaybackSource(source)) {
2710
+ return this.livekitSourceManager.hasRemoteVideoAvailable(sourceId);
2711
+ }
2712
+ return true;
2713
+ }
2714
+ recomposeBootstrapState() {
2715
+ if (!this.bootstrapSource) {
2716
+ return;
2717
+ }
2718
+ this.setState({ ...this.state });
2719
+ }
2720
+ ensureBootstrapSourceRegistered() {
2721
+ if (!this.bootstrapSource) {
2722
+ return;
2723
+ }
2724
+ if (this.sourceManager.has(this.bootstrapSource.sourceId)) {
2725
+ return;
2726
+ }
2727
+ this.sourceManager.upsertBootstrapReady(this.bootstrapSource);
2728
+ }
2729
+ syncPlaybackManagers() {
2730
+ this.trtcSourceManager.syncRuntimeSources(this.sourceManager.getAll());
2731
+ this.livekitSourceManager.syncRuntimeSources(this.sourceManager.getAll());
2732
+ }
2522
2733
  emitEvent(event) {
2523
2734
  this.progressUserTextToResponseFlows(event);
2524
2735
  this.eventListeners.forEach((listener) => listener(event, this.state));
@@ -2527,6 +2738,12 @@ var IviRuntimeCoordinator = class {
2527
2738
  this.config.onTrtcEvent?.(event);
2528
2739
  this.trtcEventListeners.forEach((listener) => listener(event));
2529
2740
  }
2741
+ onTrtcManagerEvent(event) {
2742
+ this.emitTrtcEvent(event);
2743
+ if (event.type === "remote_video_available") {
2744
+ this.recomposeBootstrapState();
2745
+ }
2746
+ }
2530
2747
  resetStoppedState() {
2531
2748
  this.rejectPendingUserTextToResponseFlows(
2532
2749
  new Error("Runtime stopped before user text to response flow completed.")
@@ -2691,7 +2908,331 @@ function isLivekitPlaybackSource(source) {
2691
2908
  if (!source || !source.playback) return false;
2692
2909
  return isLivekitSourcePlayback(source.playback);
2693
2910
  }
2911
+ function isReadyRenderableSource(source) {
2912
+ return Boolean(source && source.status === "ready" && source.playback);
2913
+ }
2914
+ function isSameBootstrapState(a, b) {
2915
+ if (!a && !b) return true;
2916
+ if (!a || !b) return false;
2917
+ return a.active === b.active && a.slot === b.slot && a.trackId === b.trackId && a.sourceId === b.sourceId && a.streamId === b.streamId;
2918
+ }
2919
+ var IviCreateIVISessionError = class extends Error {
2920
+ constructor(response, body) {
2921
+ super(`CreateIVISession failed with ${response.status} ${response.statusText}`);
2922
+ this.name = "IviCreateIVISessionError";
2923
+ this.status = response.status;
2924
+ this.statusText = response.statusText;
2925
+ this.body = body;
2926
+ }
2927
+ };
2928
+ async function createIVISession(request, options) {
2929
+ const fetchImpl = options.fetch ?? globalThis.fetch;
2930
+ if (!fetchImpl) {
2931
+ throw new Error("fetch is not available. Pass options.fetch to createIVISession.");
2932
+ }
2933
+ const requestBody = toCpCreateIVISessionRequestBody(request);
2934
+ const response = await fetchImpl(resolveCreateIVISessionUrl(options), {
2935
+ method: "POST",
2936
+ headers: await buildHeaders(options.headers),
2937
+ body: JSON.stringify(requestBody),
2938
+ credentials: options.credentials,
2939
+ signal: options.signal
2940
+ });
2941
+ const responseBody = await readJsonResponse(response);
2942
+ if (!response.ok) {
2943
+ throw new IviCreateIVISessionError(response, responseBody);
2944
+ }
2945
+ return normalizeCreateIVISessionResponse(responseBody, request, requestBody);
2946
+ }
2947
+ function createRuntimeFromSession(session, options = {}) {
2948
+ const websocketUrl = buildWebSocketUrl(session.endpoint, options.websocket);
2949
+ const client = new iviSdkTs.IviClient({
2950
+ ...options.clientConfig ?? {},
2951
+ transport: new websocket.WebSocketTransport({
2952
+ url: websocketUrl,
2953
+ sessionId: session.iviSessionId,
2954
+ protocols: options.websocket?.protocols,
2955
+ socketFactory: options.websocket?.socketFactory
2956
+ })
2957
+ });
2958
+ const runtime = new IviRuntimeCoordinator(client, options.runtimeConfig);
2959
+ if (options.fastStart !== false && session.prebuiltPlayback) {
2960
+ runtime.setBootstrapSource(toBootstrapSource(session.prebuiltPlayback, options));
2961
+ }
2962
+ return {
2963
+ session,
2964
+ client,
2965
+ runtime
2966
+ };
2967
+ }
2968
+ async function createIVISessionRuntime(request, options) {
2969
+ const session = await createIVISession(request, options.http);
2970
+ return createRuntimeFromSession(session, options);
2971
+ }
2972
+ function toCpCreateIVISessionRequestBody(request) {
2973
+ const body = { ...request };
2974
+ delete body.idempotencyKey;
2975
+ delete body.streamType;
2976
+ delete body.iviVersion;
2977
+ delete body.sessionParameters;
2978
+ delete body.enableDecoderPublish;
2979
+ delete body.sessionRecording;
2980
+ delete body.prebuiltCharacters;
2981
+ delete body.prebuiltStream;
2982
+ if (request.idempotencyKey !== void 0) body.idempotency_key = request.idempotencyKey;
2983
+ if (request.streamType !== void 0) body.stream_type = request.streamType;
2984
+ if (request.iviVersion !== void 0) body.ivi_version = request.iviVersion;
2985
+ if (request.sessionParameters !== void 0) body.session_parameters = request.sessionParameters;
2986
+ if (request.enableDecoderPublish !== void 0) body.enable_decoder_publish = request.enableDecoderPublish;
2987
+ if (request.sessionRecording !== void 0) {
2988
+ body.session_recording = {
2989
+ enabled: request.sessionRecording.enabled,
2990
+ aspect_ratio: request.sessionRecording.aspectRatio
2991
+ };
2992
+ }
2993
+ if (request.prebuiltCharacters !== void 0) {
2994
+ body.prebuilt_characters = request.prebuiltCharacters.map((character) => ({
2995
+ character_id: character.characterId,
2996
+ character_payload: serializePrebuiltPayload(character.characterPayload)
2997
+ }));
2998
+ }
2999
+ if (request.prebuiltStream !== void 0) {
3000
+ body.prebuilt_stream = {
3001
+ stream_id: request.prebuiltStream.streamId,
3002
+ mode: request.prebuiltStream.mode,
3003
+ stream_config: serializePrebuiltPayload(request.prebuiltStream.streamConfig)
3004
+ };
3005
+ }
3006
+ return body;
3007
+ }
3008
+ function normalizeCreateIVISessionResponse(responseBody, request = {}, requestBody = toCpCreateIVISessionRequestBody(request)) {
3009
+ const payload = unwrapResponsePayload(responseBody);
3010
+ const iviSessionId = getString(payload, "ivi_session_id") ?? getString(payload, "iviSessionId");
3011
+ const endpoint = getString(payload, "endpoint");
3012
+ if (!iviSessionId) {
3013
+ throw new Error("CreateIVISession response missing ivi_session_id.");
3014
+ }
3015
+ if (!endpoint) {
3016
+ throw new Error("CreateIVISession response missing endpoint.");
3017
+ }
3018
+ const trtcInfo = normalizeTrtcInfo(getObjectValue(payload, "trtc_info") ?? getObjectValue(payload, "trtcInfo"));
3019
+ const livekitInfo = normalizeLivekitInfo(getObjectValue(payload, "livekit_info") ?? getObjectValue(payload, "livekitInfo"));
3020
+ const prebuildInfo = getObjectValue(payload, "prebuild_trtc_info") ?? getObjectValue(payload, "prebuildTrtcInfo");
3021
+ const prebuildTrtcInfo = normalizeTrtcInfo(
3022
+ getObjectValue(prebuildInfo, "trtc_info") ?? getObjectValue(prebuildInfo, "trtcInfo")
3023
+ );
3024
+ const prebuildLivekitInfo = normalizeLivekitInfo(
3025
+ getObjectValue(prebuildInfo, "livekit_info") ?? getObjectValue(prebuildInfo, "livekitInfo")
3026
+ );
3027
+ const prebuiltPlayback = buildPrebuiltPlayback(
3028
+ request,
3029
+ requestBody,
3030
+ prebuildTrtcInfo,
3031
+ prebuildLivekitInfo
3032
+ );
3033
+ return {
3034
+ iviSessionId,
3035
+ endpoint,
3036
+ tokenInfo: getObjectValue(payload, "token_info") ?? getObjectValue(payload, "tokenInfo"),
3037
+ trtcInfo,
3038
+ livekitInfo,
3039
+ prebuildTrtcInfo: prebuildTrtcInfo || prebuildLivekitInfo ? {
3040
+ trtcInfo: prebuildTrtcInfo,
3041
+ livekitInfo: prebuildLivekitInfo
3042
+ } : void 0,
3043
+ prebuiltPlayback,
3044
+ raw: responseBody,
3045
+ requestBody
3046
+ };
3047
+ }
3048
+ function resolveCreateIVISessionUrl(options) {
3049
+ if (options.url) return options.url;
3050
+ if (!options.baseUrl) {
3051
+ throw new Error("CreateIVISession requires options.baseUrl or options.url.");
3052
+ }
3053
+ const path = options.path ?? "/v1/ivi/sessions";
3054
+ return new URL(path, ensureTrailingSlash(options.baseUrl)).toString();
3055
+ }
3056
+ async function buildHeaders(headers) {
3057
+ const resolvedHeaders = typeof headers === "function" ? await headers() : headers;
3058
+ const next = new Headers(resolvedHeaders);
3059
+ if (!next.has("content-type")) {
3060
+ next.set("content-type", "application/json");
3061
+ }
3062
+ return next;
3063
+ }
3064
+ async function readJsonResponse(response) {
3065
+ const text = await response.text();
3066
+ if (!text) {
3067
+ return null;
3068
+ }
3069
+ try {
3070
+ return JSON.parse(text);
3071
+ } catch {
3072
+ return text;
3073
+ }
3074
+ }
3075
+ function buildWebSocketUrl(endpoint, options) {
3076
+ const rawUrl = options?.url ?? endpoint;
3077
+ if (!options?.query) {
3078
+ return rawUrl;
3079
+ }
3080
+ const url = new URL(rawUrl);
3081
+ Object.entries(options.query).forEach(([key, value]) => {
3082
+ if (value === void 0 || value === null) return;
3083
+ url.searchParams.set(key, value);
3084
+ });
3085
+ return url.toString();
3086
+ }
3087
+ function toBootstrapSource(prebuiltPlayback, options) {
3088
+ return {
3089
+ sourceId: prebuiltPlayback.sourceId,
3090
+ streamId: prebuiltPlayback.streamId,
3091
+ slot: options.bootstrapSlot ?? "main",
3092
+ trackId: options.bootstrapTrackId ?? "__ivi_bootstrap_track",
3093
+ playback: prebuiltPlayback.playback,
3094
+ source: {
3095
+ source_id: prebuiltPlayback.sourceId,
3096
+ kind: prebuiltPlayback.streamId ? "generation_stream" : "stream",
3097
+ stream_id: prebuiltPlayback.streamId,
3098
+ asset_type: "video",
3099
+ metadata: {
3100
+ label: "prebuilt"
3101
+ }
3102
+ }
3103
+ };
3104
+ }
3105
+ function buildPrebuiltPlayback(request, requestBody, trtcInfo, livekitInfo) {
3106
+ if (!trtcInfo && !livekitInfo) {
3107
+ return void 0;
3108
+ }
3109
+ const streamId = resolvePrebuiltStreamId(request, requestBody, trtcInfo, livekitInfo);
3110
+ const sourceId = request.prebuiltStream?.sourceId ?? streamId;
3111
+ if (!sourceId) {
3112
+ return void 0;
3113
+ }
3114
+ if (trtcInfo) {
3115
+ return {
3116
+ sourceId,
3117
+ streamId,
3118
+ kind: "trtc",
3119
+ playback: {
3120
+ type: "trtc",
3121
+ trtc: trtcInfo
3122
+ },
3123
+ provisional: true
3124
+ };
3125
+ }
3126
+ if (livekitInfo) {
3127
+ return {
3128
+ sourceId,
3129
+ streamId,
3130
+ kind: "livekit",
3131
+ playback: {
3132
+ type: "livekit",
3133
+ livekit: livekitInfo
3134
+ },
3135
+ provisional: true
3136
+ };
3137
+ }
3138
+ return void 0;
3139
+ }
3140
+ function resolvePrebuiltStreamId(request, requestBody, trtcInfo, livekitInfo) {
3141
+ const prebuiltStream = getObjectValue(requestBody, "prebuilt_stream");
3142
+ return request.prebuiltStream?.streamId ?? getString(prebuiltStream, "stream_id") ?? getString(prebuiltStream, "streamId") ?? trtcInfo?.room_id ?? livekitInfo?.room;
3143
+ }
3144
+ function normalizeTrtcInfo(value) {
3145
+ if (!isRecord(value)) {
3146
+ return void 0;
3147
+ }
3148
+ const appId = getString(value, "app_id") ?? getString(value, "appId");
3149
+ const roomId = getString(value, "room_id") ?? getString(value, "roomId");
3150
+ const userId = getString(value, "user_id") ?? getString(value, "userId");
3151
+ const userSig = getString(value, "user_sig") ?? getString(value, "userSig");
3152
+ if (!appId || !roomId || !userId || !userSig) {
3153
+ return void 0;
3154
+ }
3155
+ const normalized = {
3156
+ ...value,
3157
+ app_id: appId,
3158
+ room_id: roomId,
3159
+ user_id: userId,
3160
+ user_sig: userSig
3161
+ };
3162
+ delete normalized.secret_key;
3163
+ delete normalized.secretKey;
3164
+ return normalized;
3165
+ }
3166
+ function normalizeLivekitInfo(value) {
3167
+ if (!isRecord(value)) {
3168
+ return void 0;
3169
+ }
3170
+ const wsUrl = getString(value, "ws_url") ?? getString(value, "url") ?? getString(value, "wsUrl");
3171
+ const token = getString(value, "token") ?? getString(value, "access_token") ?? getString(value, "accessToken");
3172
+ if (!wsUrl || !token) {
3173
+ return void 0;
3174
+ }
3175
+ return {
3176
+ ws_url: wsUrl,
3177
+ token,
3178
+ room: getString(value, "room") ?? getString(value, "room_name") ?? getString(value, "roomName"),
3179
+ identity: getString(value, "identity")
3180
+ };
3181
+ }
3182
+ function serializePrebuiltPayload(value) {
3183
+ if (typeof value === "string") {
3184
+ return value;
3185
+ }
3186
+ return encodeJsonBase64(value);
3187
+ }
3188
+ function encodeJsonBase64(value) {
3189
+ const bytes = new TextEncoder().encode(JSON.stringify(value));
3190
+ let binary = "";
3191
+ bytes.forEach((byte) => {
3192
+ binary += String.fromCharCode(byte);
3193
+ });
3194
+ if (typeof btoa === "function") {
3195
+ return btoa(binary);
3196
+ }
3197
+ const maybeBuffer = globalThis.Buffer;
3198
+ if (maybeBuffer) {
3199
+ return maybeBuffer.from(bytes).toString("base64");
3200
+ }
3201
+ throw new Error("Base64 encoder is not available in this runtime.");
3202
+ }
3203
+ function unwrapResponsePayload(value) {
3204
+ if (!isRecord(value)) {
3205
+ return {};
3206
+ }
3207
+ const data = getObjectValue(value, "data");
3208
+ return data ?? value;
3209
+ }
3210
+ function getObjectValue(value, key) {
3211
+ if (!isRecord(value)) {
3212
+ return void 0;
3213
+ }
3214
+ const nested = value[key];
3215
+ return isRecord(nested) ? nested : void 0;
3216
+ }
3217
+ function getString(value, key) {
3218
+ if (!isRecord(value)) {
3219
+ return void 0;
3220
+ }
3221
+ const nested = value[key];
3222
+ return typeof nested === "string" && nested.trim().length > 0 ? nested.trim() : void 0;
3223
+ }
3224
+ function isRecord(value) {
3225
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3226
+ }
3227
+ function ensureTrailingSlash(value) {
3228
+ return value.endsWith("/") ? value : `${value}/`;
3229
+ }
3230
+
3231
+ // src/sdk.ts
2694
3232
  var IviFrontendSdk = class {
3233
+ constructor(config = {}) {
3234
+ this.config = config;
3235
+ }
2695
3236
  createClient(config) {
2696
3237
  return new iviSdkTs.IviClient(config);
2697
3238
  }
@@ -2699,6 +3240,25 @@ var IviFrontendSdk = class {
2699
3240
  const client = this.createClient(clientConfig);
2700
3241
  return new IviRuntimeCoordinator(client, runtimeConfig);
2701
3242
  }
3243
+ createIVISession(request, options) {
3244
+ return createIVISession(request, {
3245
+ ...this.config.http ?? {},
3246
+ ...options ?? {}
3247
+ });
3248
+ }
3249
+ createRuntimeFromSession(session, options) {
3250
+ return createRuntimeFromSession(session, options);
3251
+ }
3252
+ createIVISessionRuntime(request, options) {
3253
+ const http = {
3254
+ ...this.config.http ?? {},
3255
+ ...options?.http ?? {}
3256
+ };
3257
+ return createIVISessionRuntime(request, {
3258
+ ...options ?? {},
3259
+ http
3260
+ });
3261
+ }
2702
3262
  };
2703
3263
  var IviStageViewContext = react.createContext(null);
2704
3264
  var EMPTY_RUNTIME_STATE = {
@@ -2709,7 +3269,8 @@ var EMPTY_RUNTIME_STATE = {
2709
3269
  sources: /* @__PURE__ */ new Map(),
2710
3270
  streams: /* @__PURE__ */ new Map(),
2711
3271
  conversationItems: /* @__PURE__ */ new Map(),
2712
- conversations: []
3272
+ conversations: [],
3273
+ bootstrap: null
2713
3274
  };
2714
3275
  function useRuntimeState(runtime) {
2715
3276
  const [state, setState] = react.useState(() => runtime?.getState() ?? EMPTY_RUNTIME_STATE);
@@ -2737,10 +3298,10 @@ function IVIStageView(props) {
2737
3298
  const state = useRuntimeState(runtime);
2738
3299
  const slotTrackMap = react.useMemo(() => {
2739
3300
  return buildSlotTrackMapFromState(state);
2740
- }, [state.stage]);
3301
+ }, [state.stage, state.bootstrap]);
2741
3302
  const slotBindings = react.useMemo(() => {
2742
3303
  return buildSlotBindingsFromState(state);
2743
- }, [state.stage, state.tracks, state.sources]);
3304
+ }, [state.stage, state.tracks, state.sources, state.bootstrap]);
2744
3305
  react.useEffect(() => {
2745
3306
  onBindingsChange?.(slotBindings);
2746
3307
  }, [slotBindings, onBindingsChange]);
@@ -2780,15 +3341,18 @@ function buildSlotTrackMapFromState(state) {
2780
3341
  (state.stage?.composition ?? []).forEach((item) => {
2781
3342
  map.set(item.slot, item.track_id);
2782
3343
  });
3344
+ if (state.bootstrap?.active) {
3345
+ map.set(state.bootstrap.slot, state.bootstrap.trackId);
3346
+ }
2783
3347
  return map;
2784
3348
  }
2785
3349
  function buildSlotBindingsFromState(state) {
2786
- const bindings = [];
3350
+ const bindingsBySlot = /* @__PURE__ */ new Map();
2787
3351
  (state.stage?.composition ?? []).forEach((item) => {
2788
3352
  const track = state.tracks.get(item.track_id);
2789
3353
  const sourceId = track?.active_source_id ?? null;
2790
3354
  const source = sourceId ? state.sources.get(sourceId) : void 0;
2791
- bindings.push({
3355
+ bindingsBySlot.set(item.slot, {
2792
3356
  slot: item.slot,
2793
3357
  trackId: item.track_id,
2794
3358
  track,
@@ -2796,7 +3360,18 @@ function buildSlotBindingsFromState(state) {
2796
3360
  source
2797
3361
  });
2798
3362
  });
2799
- return bindings;
3363
+ if (state.bootstrap?.active) {
3364
+ const track = state.tracks.get(state.bootstrap.trackId);
3365
+ const source = state.sources.get(state.bootstrap.sourceId);
3366
+ bindingsBySlot.set(state.bootstrap.slot, {
3367
+ slot: state.bootstrap.slot,
3368
+ trackId: state.bootstrap.trackId,
3369
+ track,
3370
+ sourceId: state.bootstrap.sourceId,
3371
+ source
3372
+ });
3373
+ }
3374
+ return Array.from(bindingsBySlot.values());
2800
3375
  }
2801
3376
  var VOLUME_STORAGE_KEY = "ivi-volume-preferences";
2802
3377
  var DEFAULT_VOLUME = 80;
@@ -3564,23 +4139,25 @@ function IVITrtcPlayer(props) {
3564
4139
  const [loading, setLoading] = react.useState(true);
3565
4140
  const [error, setError] = react.useState(null);
3566
4141
  const mutedRef = react.useRef(muted);
4142
+ const attachedSourceIdRef = react.useRef(null);
3567
4143
  mutedRef.current = muted;
4144
+ react.useEffect(() => {
4145
+ const unsubscribe = manager.subscribe(resolvedSourceId, (snapshot) => {
4146
+ setLoading(snapshot.status === "idle" || snapshot.status === "connecting");
4147
+ setError(snapshot.status === "error" ? snapshot.error ?? "TRTC \u62C9\u6D41\u5931\u8D25" : null);
4148
+ });
4149
+ return unsubscribe;
4150
+ }, [manager, resolvedSourceId]);
3568
4151
  react.useEffect(() => {
3569
4152
  const container = containerRef.current;
3570
4153
  if (!container) {
3571
4154
  return;
3572
4155
  }
3573
4156
  let disposed = false;
4157
+ attachedSourceIdRef.current = resolvedSourceId;
3574
4158
  if (shouldManageSourceLifecycle) {
3575
4159
  manager.upsertSource(resolvedSourceId, trtc, trtcAIDenoiser);
3576
4160
  }
3577
- const unsubscribe = manager.subscribe(resolvedSourceId, (snapshot) => {
3578
- if (disposed) {
3579
- return;
3580
- }
3581
- setLoading(snapshot.status === "idle" || snapshot.status === "connecting");
3582
- setError(snapshot.status === "error" ? snapshot.error ?? "TRTC \u62C9\u6D41\u5931\u8D25" : null);
3583
- });
3584
4161
  void manager.attachView(resolvedSourceId, viewIdRef.current, container, mutedRef.current).catch((caughtError) => {
3585
4162
  if (disposed) {
3586
4163
  return;
@@ -3590,15 +4167,15 @@ function IVITrtcPlayer(props) {
3590
4167
  });
3591
4168
  return () => {
3592
4169
  disposed = true;
3593
- unsubscribe();
3594
- manager.detachView(resolvedSourceId, viewIdRef.current);
4170
+ const attachedSourceId = attachedSourceIdRef.current ?? resolvedSourceId;
4171
+ manager.detachView(attachedSourceId, viewIdRef.current);
3595
4172
  if (shouldManageSourceLifecycle) {
3596
- manager.removeSource(resolvedSourceId);
4173
+ manager.removeSource(attachedSourceId);
3597
4174
  }
4175
+ attachedSourceIdRef.current = null;
3598
4176
  };
3599
4177
  }, [
3600
4178
  manager,
3601
- resolvedSourceId,
3602
4179
  shouldManageSourceLifecycle,
3603
4180
  trtc.app_id,
3604
4181
  trtc.room_id,
@@ -3606,6 +4183,15 @@ function IVITrtcPlayer(props) {
3606
4183
  trtc.user_sig,
3607
4184
  trtcAIDenoiser
3608
4185
  ]);
4186
+ react.useEffect(() => {
4187
+ const previousSourceId = attachedSourceIdRef.current;
4188
+ if (!previousSourceId || previousSourceId === resolvedSourceId) {
4189
+ return;
4190
+ }
4191
+ if (manager.reassignView(previousSourceId, resolvedSourceId, viewIdRef.current)) {
4192
+ attachedSourceIdRef.current = resolvedSourceId;
4193
+ }
4194
+ }, [manager, resolvedSourceId]);
3609
4195
  react.useEffect(() => {
3610
4196
  manager.updateViewMuted(resolvedSourceId, viewIdRef.current, muted);
3611
4197
  }, [manager, muted, resolvedSourceId]);
@@ -3618,7 +4204,6 @@ function IVITrtcPlayer(props) {
3618
4204
  height: "100%",
3619
4205
  minWidth: 0,
3620
4206
  minHeight: 0,
3621
- backgroundColor: "#000",
3622
4207
  position: "relative",
3623
4208
  ...style
3624
4209
  },
@@ -4114,13 +4699,7 @@ function getSourceRenderKey(sourceId, source) {
4114
4699
  if (!trtc) {
4115
4700
  return sourceId;
4116
4701
  }
4117
- return [
4118
- "trtc",
4119
- trtc.app_id ?? "",
4120
- trtc.room_id ?? "",
4121
- trtc.user_id ?? "",
4122
- trtc.user_sig ?? ""
4123
- ].join(":");
4702
+ return ["trtc", trtc.app_id ?? "", trtc.room_id ?? ""].join(":");
4124
4703
  }
4125
4704
  function TrackSlotMediaContent(props) {
4126
4705
  const {
@@ -4576,6 +5155,82 @@ function getClientLogTag(category) {
4576
5155
  if (category === "reconnect") return "[IVI-RECONNECT]";
4577
5156
  return "[IVI-CLIENT]";
4578
5157
  }
5158
+ function useIviSessionRuntime(config) {
5159
+ const [status, setStatus] = react.useState("idle");
5160
+ const [session, setSession] = react.useState(null);
5161
+ const [runtime, setRuntime] = react.useState(null);
5162
+ const [client, setClient] = react.useState(null);
5163
+ const [error, setError] = react.useState(null);
5164
+ react.useEffect(() => {
5165
+ const {
5166
+ request,
5167
+ enabled = true,
5168
+ autoStart = true,
5169
+ onCreated,
5170
+ onRuntimeReady,
5171
+ onError,
5172
+ ...options
5173
+ } = config;
5174
+ if (!enabled || !request) {
5175
+ setStatus("idle");
5176
+ setSession(null);
5177
+ setRuntime(null);
5178
+ setClient(null);
5179
+ setError(null);
5180
+ return;
5181
+ }
5182
+ let disposed = false;
5183
+ let activeRuntime = null;
5184
+ setStatus("creating");
5185
+ setSession(null);
5186
+ setRuntime(null);
5187
+ setClient(null);
5188
+ setError(null);
5189
+ void createIVISessionRuntime(request, options).then(async (created) => {
5190
+ if (disposed) {
5191
+ created.runtime.stop();
5192
+ return;
5193
+ }
5194
+ activeRuntime = created.runtime;
5195
+ setSession(created.session);
5196
+ setRuntime(created.runtime);
5197
+ setClient(created.client);
5198
+ onCreated?.(created.session);
5199
+ onRuntimeReady?.(created.runtime, created.client);
5200
+ if (!autoStart) {
5201
+ setStatus("idle");
5202
+ return;
5203
+ }
5204
+ setStatus("connecting");
5205
+ await created.runtime.start();
5206
+ if (disposed) {
5207
+ created.runtime.stop();
5208
+ return;
5209
+ }
5210
+ setStatus("running");
5211
+ }).catch((caughtError) => {
5212
+ if (disposed) {
5213
+ return;
5214
+ }
5215
+ const normalizedError = caughtError instanceof Error ? caughtError : new Error(String(caughtError));
5216
+ setError(normalizedError);
5217
+ setStatus("error");
5218
+ onError?.(caughtError);
5219
+ });
5220
+ return () => {
5221
+ disposed = true;
5222
+ activeRuntime?.stop();
5223
+ setStatus("stopped");
5224
+ };
5225
+ }, [config]);
5226
+ return {
5227
+ status,
5228
+ session,
5229
+ runtime,
5230
+ client,
5231
+ error
5232
+ };
5233
+ }
4579
5234
 
4580
5235
  exports.DEFAULT_HIDE_COMPLETED_SUBTITLE_AFTER_MS = DEFAULT_HIDE_COMPLETED_SUBTITLE_AFTER_MS;
4581
5236
  exports.EMPTY_RUNTIME_STATE = EMPTY_RUNTIME_STATE;
@@ -4584,14 +5239,21 @@ exports.IVIStageView = IVIStageView;
4584
5239
  exports.IVISubtitleOverlay = IVISubtitleOverlay;
4585
5240
  exports.IVITrackSlot = IVITrackSlot;
4586
5241
  exports.IVITrtcPlayer = IVITrtcPlayer;
5242
+ exports.IviCreateIVISessionError = IviCreateIVISessionError;
4587
5243
  exports.IviFrontendSdk = IviFrontendSdk;
4588
5244
  exports.IviRuntimeCoordinator = IviRuntimeCoordinator;
4589
5245
  exports.IviRuntimeDispatcher = IviRuntimeDispatcher;
4590
5246
  exports.LivekitSourceManager = LivekitSourceManager;
4591
5247
  exports.TrtcSourceManager = TrtcSourceManager;
5248
+ exports.createIVISession = createIVISession;
5249
+ exports.createIVISessionRuntime = createIVISessionRuntime;
5250
+ exports.createRuntimeFromSession = createRuntimeFromSession;
4592
5251
  exports.isLivekitSourcePlayback = isLivekitSourcePlayback;
4593
5252
  exports.isReadyLivekitRuntimeSource = isReadyLivekitRuntimeSource;
4594
5253
  exports.isSameLivekitConfig = isSameLivekitConfig;
5254
+ exports.normalizeCreateIVISessionResponse = normalizeCreateIVISessionResponse;
5255
+ exports.toCpCreateIVISessionRequestBody = toCpCreateIVISessionRequestBody;
5256
+ exports.useIviSessionRuntime = useIviSessionRuntime;
4595
5257
  exports.useIviStageView = useIviStageView;
4596
5258
  exports.useIviSubtitles = useIviSubtitles;
4597
5259
  exports.useManagedIviRuntime = useManagedIviRuntime;