@videojs/spf 10.0.0-beta.24 → 10.0.0-beta.26

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.
Files changed (375) hide show
  1. package/README.md +5 -0
  2. package/dist/default/background-video.js +3 -0
  3. package/dist/default/core/actors/create-machine-actor.js.map +1 -1
  4. package/dist/default/core/actors/create-transition-actor.js.map +1 -1
  5. package/dist/default/core/composition/create-composition.js +2 -2
  6. package/dist/default/core/composition/create-composition.js.map +1 -1
  7. package/dist/default/core/composition/share-signals.js +13 -11
  8. package/dist/default/core/composition/share-signals.js.map +1 -1
  9. package/dist/default/core/machine.js.map +1 -1
  10. package/dist/default/core/reactors/create-machine-reactor.js.map +1 -1
  11. package/dist/default/core/signals/effect.js.map +1 -1
  12. package/dist/default/core/signals/primitives.js.map +1 -1
  13. package/dist/default/core/tasks/task.js.map +1 -1
  14. package/dist/default/hls.js +4 -1
  15. package/dist/default/media/abr/quality-selection.js +6 -68
  16. package/dist/default/media/abr/quality-selection.js.map +1 -1
  17. package/dist/default/media/buffer/back-buffer.js.map +1 -1
  18. package/dist/default/media/buffer/forward-buffer.js +37 -3
  19. package/dist/default/media/buffer/forward-buffer.js.map +1 -1
  20. package/dist/default/media/dom/capabilities.js +70 -0
  21. package/dist/default/media/dom/capabilities.js.map +1 -0
  22. package/dist/default/media/dom/mse/append-segment.js.map +1 -1
  23. package/dist/default/media/dom/mse/buffer-flusher.js.map +1 -1
  24. package/dist/default/media/dom/mse/duration.js +27 -10
  25. package/dist/default/media/dom/mse/duration.js.map +1 -1
  26. package/dist/default/media/dom/mse/end-of-stream.js.map +1 -1
  27. package/dist/default/media/dom/mse/mediasource-setup.js +68 -17
  28. package/dist/default/media/dom/mse/mediasource-setup.js.map +1 -1
  29. package/dist/default/media/dom/text/resolve-vtt-segment.js +1 -7
  30. package/dist/default/media/dom/text/resolve-vtt-segment.js.map +1 -1
  31. package/dist/default/media/dom/text/text-track-slots.js +3 -3
  32. package/dist/default/media/dom/text/text-track-slots.js.map +1 -1
  33. package/dist/default/media/hls/parse-attributes.js +17 -3
  34. package/dist/default/media/hls/parse-attributes.js.map +1 -1
  35. package/dist/default/media/hls/parse-media-playlist.js +29 -1
  36. package/dist/default/media/hls/parse-media-playlist.js.map +1 -1
  37. package/dist/default/media/hls/parse-multivariant.js +15 -6
  38. package/dist/default/media/hls/parse-multivariant.js.map +1 -1
  39. package/dist/default/media/hls/resolve-url.js.map +1 -1
  40. package/dist/default/media/media-tracks/media-tracks.js +101 -0
  41. package/dist/default/media/media-tracks/media-tracks.js.map +1 -0
  42. package/dist/default/media/mp4/box.js +58 -0
  43. package/dist/default/media/mp4/box.js.map +1 -0
  44. package/dist/default/media/mp4/timestamp-origin.js +107 -0
  45. package/dist/default/media/mp4/timestamp-origin.js.map +1 -0
  46. package/dist/default/media/primitives/select-tracks.js +98 -13
  47. package/dist/default/media/primitives/select-tracks.js.map +1 -1
  48. package/dist/default/media/text/parse-vtt-timestamp-map.js +42 -0
  49. package/dist/default/media/text/parse-vtt-timestamp-map.js.map +1 -0
  50. package/dist/default/media/text/resolve-vtt-metadata.js +23 -0
  51. package/dist/default/media/text/resolve-vtt-metadata.js.map +1 -0
  52. package/dist/default/media/types/index.js.map +1 -1
  53. package/dist/default/media/utils/cdn.js +56 -0
  54. package/dist/default/media/utils/cdn.js.map +1 -0
  55. package/dist/default/media/utils/preload.js.map +1 -1
  56. package/dist/default/media/utils/track-selection.js +1 -1
  57. package/dist/default/media/utils/track-selection.js.map +1 -1
  58. package/dist/default/media/utils/tracks.js +31 -4
  59. package/dist/default/media/utils/tracks.js.map +1 -1
  60. package/dist/default/media-tracks.js +2 -0
  61. package/dist/default/network/bandwidth-estimator.js.map +1 -1
  62. package/dist/default/network/chunked-stream-iterable.js.map +1 -1
  63. package/dist/default/network/ewma.js +1 -1
  64. package/dist/default/network/ewma.js.map +1 -1
  65. package/dist/default/network/fetch.js +7 -1
  66. package/dist/default/network/fetch.js.map +1 -1
  67. package/dist/default/playback/actors/dom/segment-loader.js +103 -106
  68. package/dist/default/playback/actors/dom/segment-loader.js.map +1 -1
  69. package/dist/default/playback/actors/dom/source-buffer.js +3 -1
  70. package/dist/default/playback/actors/dom/source-buffer.js.map +1 -1
  71. package/dist/default/playback/actors/dom/text-tracks.js.map +1 -1
  72. package/dist/default/playback/actors/text-track-segment-loader.js +30 -16
  73. package/dist/default/playback/actors/text-track-segment-loader.js.map +1 -1
  74. package/dist/default/playback/behaviors/calculate-presentation-duration.js +21 -43
  75. package/dist/default/playback/behaviors/calculate-presentation-duration.js.map +1 -1
  76. package/dist/default/playback/behaviors/derive-cdn-priority.js +68 -0
  77. package/dist/default/playback/behaviors/derive-cdn-priority.js.map +1 -0
  78. package/dist/default/playback/behaviors/dom/airplay.js +211 -0
  79. package/dist/default/playback/behaviors/dom/airplay.js.map +1 -0
  80. package/dist/default/playback/behaviors/dom/apply-start-position.js +93 -0
  81. package/dist/default/playback/behaviors/dom/apply-start-position.js.map +1 -0
  82. package/dist/default/playback/behaviors/dom/end-of-stream.js +25 -102
  83. package/dist/default/playback/behaviors/dom/end-of-stream.js.map +1 -1
  84. package/dist/default/playback/behaviors/dom/load-segments.js +6 -2
  85. package/dist/default/playback/behaviors/dom/load-segments.js.map +1 -1
  86. package/dist/default/playback/behaviors/dom/recover-end-stall.js +77 -0
  87. package/dist/default/playback/behaviors/dom/recover-end-stall.js.map +1 -0
  88. package/dist/default/playback/behaviors/dom/setup-buffer-actors.js +50 -18
  89. package/dist/default/playback/behaviors/dom/setup-buffer-actors.js.map +1 -1
  90. package/dist/default/playback/behaviors/dom/setup-mediasource.js +36 -11
  91. package/dist/default/playback/behaviors/dom/setup-mediasource.js.map +1 -1
  92. package/dist/default/playback/behaviors/dom/setup-text-track-actors.js +11 -25
  93. package/dist/default/playback/behaviors/dom/setup-text-track-actors.js.map +1 -1
  94. package/dist/default/playback/behaviors/dom/sync-text-tracks.js +43 -18
  95. package/dist/default/playback/behaviors/dom/sync-text-tracks.js.map +1 -1
  96. package/dist/default/playback/behaviors/dom/track-current-time.js +6 -0
  97. package/dist/default/playback/behaviors/dom/track-current-time.js.map +1 -1
  98. package/dist/default/playback/behaviors/dom/track-load-triggers.js +4 -4
  99. package/dist/default/playback/behaviors/dom/track-load-triggers.js.map +1 -1
  100. package/dist/default/playback/behaviors/dom/track-playback-rate.js.map +1 -1
  101. package/dist/default/playback/behaviors/dom/update-mediasource-duration.js +3 -2
  102. package/dist/default/playback/behaviors/dom/update-mediasource-duration.js.map +1 -1
  103. package/dist/default/playback/behaviors/establish-start-media-time.js +123 -0
  104. package/dist/default/playback/behaviors/establish-start-media-time.js.map +1 -0
  105. package/dist/default/playback/behaviors/resolve-presentation.js.map +1 -1
  106. package/dist/default/playback/behaviors/resolve-track.js +45 -22
  107. package/dist/default/playback/behaviors/resolve-track.js.map +1 -1
  108. package/dist/default/playback/behaviors/select-tracks.js +46 -77
  109. package/dist/default/playback/behaviors/select-tracks.js.map +1 -1
  110. package/dist/default/playback/behaviors/setup-failover-monitor.js +66 -0
  111. package/dist/default/playback/behaviors/setup-failover-monitor.js.map +1 -0
  112. package/dist/default/playback/behaviors/sync-preload.js.map +1 -1
  113. package/dist/default/playback/behaviors/track-switching.js +403 -0
  114. package/dist/default/playback/behaviors/track-switching.js.map +1 -0
  115. package/dist/default/playback/engines/background-video/adapter.js +158 -0
  116. package/dist/default/playback/engines/background-video/adapter.js.map +1 -0
  117. package/dist/default/playback/engines/background-video/engine.js +75 -0
  118. package/dist/default/playback/engines/background-video/engine.js.map +1 -0
  119. package/dist/default/playback/engines/hls/adapter-audio-only.js +124 -0
  120. package/dist/default/playback/engines/hls/adapter-audio-only.js.map +1 -0
  121. package/dist/default/playback/engines/hls/adapter.js +20 -11
  122. package/dist/default/playback/engines/hls/adapter.js.map +1 -1
  123. package/dist/default/playback/engines/hls/engine-audio-only.js +94 -0
  124. package/dist/default/playback/engines/hls/engine-audio-only.js.map +1 -0
  125. package/dist/default/playback/engines/hls/engine.js +38 -8
  126. package/dist/default/playback/engines/hls/engine.js.map +1 -1
  127. package/dist/default/playback/primitives/failover-fetch.js +42 -0
  128. package/dist/default/playback/primitives/failover-fetch.js.map +1 -0
  129. package/dist/default/playback/primitives/head-peek.js +44 -0
  130. package/dist/default/playback/primitives/head-peek.js.map +1 -0
  131. package/dist/default/playback/primitives/relocation-pipelines.js +203 -0
  132. package/dist/default/playback/primitives/relocation-pipelines.js.map +1 -0
  133. package/dist/default/playback/primitives/segment-load-pipeline.js +96 -0
  134. package/dist/default/playback/primitives/segment-load-pipeline.js.map +1 -0
  135. package/dist/default/playback/primitives/text-segment-load-pipeline.js +37 -0
  136. package/dist/default/playback/primitives/text-segment-load-pipeline.js.map +1 -0
  137. package/dist/{dev/playback/behaviors → default/playback/primitives}/track-types.js +1 -1
  138. package/dist/default/playback/primitives/track-types.js.map +1 -0
  139. package/dist/dev/background-video.d.ts +3 -0
  140. package/dist/dev/background-video.js +3 -0
  141. package/dist/dev/core/actors/actor.d.ts +0 -1
  142. package/dist/dev/core/actors/actor.d.ts.map +1 -1
  143. package/dist/dev/core/actors/create-machine-actor.d.ts +9 -6
  144. package/dist/dev/core/actors/create-machine-actor.d.ts.map +1 -1
  145. package/dist/dev/core/actors/create-machine-actor.js.map +1 -1
  146. package/dist/dev/core/actors/create-transition-actor.d.ts +0 -1
  147. package/dist/dev/core/actors/create-transition-actor.d.ts.map +1 -1
  148. package/dist/dev/core/actors/create-transition-actor.js.map +1 -1
  149. package/dist/dev/core/composition/create-composition.d.ts +10 -11
  150. package/dist/dev/core/composition/create-composition.d.ts.map +1 -1
  151. package/dist/dev/core/composition/create-composition.js +2 -2
  152. package/dist/dev/core/composition/create-composition.js.map +1 -1
  153. package/dist/dev/core/composition/share-signals.d.ts +11 -10
  154. package/dist/dev/core/composition/share-signals.d.ts.map +1 -1
  155. package/dist/dev/core/composition/share-signals.js +13 -11
  156. package/dist/dev/core/composition/share-signals.js.map +1 -1
  157. package/dist/dev/core/machine.d.ts +1 -0
  158. package/dist/dev/core/machine.d.ts.map +1 -1
  159. package/dist/dev/core/machine.js.map +1 -1
  160. package/dist/dev/core/reactors/create-machine-reactor.d.ts +2 -2
  161. package/dist/dev/core/reactors/create-machine-reactor.d.ts.map +1 -1
  162. package/dist/dev/core/reactors/create-machine-reactor.js.map +1 -1
  163. package/dist/dev/core/signals/effect.d.ts.map +1 -1
  164. package/dist/dev/core/signals/effect.js.map +1 -1
  165. package/dist/dev/core/signals/primitives.d.ts +1 -2
  166. package/dist/dev/core/signals/primitives.d.ts.map +1 -1
  167. package/dist/dev/core/signals/primitives.js.map +1 -1
  168. package/dist/dev/core/tasks/task.d.ts +2 -2
  169. package/dist/dev/core/tasks/task.d.ts.map +1 -1
  170. package/dist/dev/core/tasks/task.js.map +1 -1
  171. package/dist/dev/hls.d.ts +5 -1
  172. package/dist/dev/hls.js +4 -1
  173. package/dist/dev/index.d.ts +0 -1
  174. package/dist/dev/index.d.ts.map +1 -1
  175. package/dist/dev/media/abr/quality-selection.d.ts.map +1 -1
  176. package/dist/dev/media/abr/quality-selection.js +6 -68
  177. package/dist/dev/media/abr/quality-selection.js.map +1 -1
  178. package/dist/dev/media/buffer/back-buffer.d.ts.map +1 -1
  179. package/dist/dev/media/buffer/back-buffer.js.map +1 -1
  180. package/dist/dev/media/buffer/forward-buffer.d.ts.map +1 -1
  181. package/dist/dev/media/buffer/forward-buffer.js +37 -3
  182. package/dist/dev/media/buffer/forward-buffer.js.map +1 -1
  183. package/dist/dev/media/dom/capabilities.js +70 -0
  184. package/dist/dev/media/dom/capabilities.js.map +1 -0
  185. package/dist/dev/media/dom/mse/append-segment.d.ts +3 -9
  186. package/dist/dev/media/dom/mse/append-segment.d.ts.map +1 -1
  187. package/dist/dev/media/dom/mse/append-segment.js.map +1 -1
  188. package/dist/dev/media/dom/mse/buffer-flusher.d.ts.map +1 -1
  189. package/dist/dev/media/dom/mse/buffer-flusher.js.map +1 -1
  190. package/dist/dev/media/dom/mse/duration.js +27 -10
  191. package/dist/dev/media/dom/mse/duration.js.map +1 -1
  192. package/dist/dev/media/dom/mse/end-of-stream.js.map +1 -1
  193. package/dist/dev/media/dom/mse/mediasource-setup.js +68 -17
  194. package/dist/dev/media/dom/mse/mediasource-setup.js.map +1 -1
  195. package/dist/dev/media/dom/text/resolve-vtt-segment.d.ts +0 -6
  196. package/dist/dev/media/dom/text/resolve-vtt-segment.d.ts.map +1 -1
  197. package/dist/dev/media/dom/text/resolve-vtt-segment.js +1 -7
  198. package/dist/dev/media/dom/text/resolve-vtt-segment.js.map +1 -1
  199. package/dist/dev/media/dom/text/text-track-slots.d.ts +0 -1
  200. package/dist/dev/media/dom/text/text-track-slots.d.ts.map +1 -1
  201. package/dist/dev/media/dom/text/text-track-slots.js +3 -3
  202. package/dist/dev/media/dom/text/text-track-slots.js.map +1 -1
  203. package/dist/dev/media/hls/parse-attributes.js +17 -3
  204. package/dist/dev/media/hls/parse-attributes.js.map +1 -1
  205. package/dist/dev/media/hls/parse-media-playlist.js +29 -1
  206. package/dist/dev/media/hls/parse-media-playlist.js.map +1 -1
  207. package/dist/dev/media/hls/parse-multivariant.js +15 -6
  208. package/dist/dev/media/hls/parse-multivariant.js.map +1 -1
  209. package/dist/dev/media/hls/resolve-url.js.map +1 -1
  210. package/dist/dev/media/media-tracks/media-tracks.d.ts +51 -0
  211. package/dist/dev/media/media-tracks/media-tracks.d.ts.map +1 -0
  212. package/dist/dev/media/media-tracks/media-tracks.js +101 -0
  213. package/dist/dev/media/media-tracks/media-tracks.js.map +1 -0
  214. package/dist/dev/media/mp4/box.js +58 -0
  215. package/dist/dev/media/mp4/box.js.map +1 -0
  216. package/dist/dev/media/mp4/timestamp-origin.js +107 -0
  217. package/dist/dev/media/mp4/timestamp-origin.js.map +1 -0
  218. package/dist/dev/media/primitives/select-tracks.d.ts +35 -0
  219. package/dist/dev/media/primitives/select-tracks.d.ts.map +1 -0
  220. package/dist/dev/media/primitives/select-tracks.js +98 -13
  221. package/dist/dev/media/primitives/select-tracks.js.map +1 -1
  222. package/dist/dev/media/text/parse-vtt-timestamp-map.js +42 -0
  223. package/dist/dev/media/text/parse-vtt-timestamp-map.js.map +1 -0
  224. package/dist/dev/media/text/resolve-vtt-metadata.js +23 -0
  225. package/dist/dev/media/text/resolve-vtt-metadata.js.map +1 -0
  226. package/dist/dev/media/types/index.d.ts +69 -2
  227. package/dist/dev/media/types/index.d.ts.map +1 -1
  228. package/dist/dev/media/types/index.js.map +1 -1
  229. package/dist/dev/media/utils/cdn.d.ts +13 -0
  230. package/dist/dev/media/utils/cdn.d.ts.map +1 -0
  231. package/dist/dev/media/utils/cdn.js +56 -0
  232. package/dist/dev/media/utils/cdn.js.map +1 -0
  233. package/dist/dev/media/utils/preload.js.map +1 -1
  234. package/dist/dev/media/utils/track-selection.js +1 -1
  235. package/dist/dev/media/utils/track-selection.js.map +1 -1
  236. package/dist/dev/media/utils/tracks.js +31 -4
  237. package/dist/dev/media/utils/tracks.js.map +1 -1
  238. package/dist/dev/media-tracks.d.ts +3 -0
  239. package/dist/dev/media-tracks.js +2 -0
  240. package/dist/dev/network/bandwidth-estimator.d.ts.map +1 -1
  241. package/dist/dev/network/bandwidth-estimator.js.map +1 -1
  242. package/dist/dev/network/chunked-stream-iterable.js.map +1 -1
  243. package/dist/dev/network/ewma.js +1 -1
  244. package/dist/dev/network/ewma.js.map +1 -1
  245. package/dist/dev/network/fetch.js +7 -1
  246. package/dist/dev/network/fetch.js.map +1 -1
  247. package/dist/dev/playback/actors/dom/segment-loader.d.ts +16 -4
  248. package/dist/dev/playback/actors/dom/segment-loader.d.ts.map +1 -1
  249. package/dist/dev/playback/actors/dom/segment-loader.js +103 -106
  250. package/dist/dev/playback/actors/dom/segment-loader.js.map +1 -1
  251. package/dist/dev/playback/actors/dom/source-buffer.d.ts +10 -25
  252. package/dist/dev/playback/actors/dom/source-buffer.d.ts.map +1 -1
  253. package/dist/dev/playback/actors/dom/source-buffer.js +3 -1
  254. package/dist/dev/playback/actors/dom/source-buffer.js.map +1 -1
  255. package/dist/dev/playback/actors/dom/text-tracks.d.ts +3 -1
  256. package/dist/dev/playback/actors/dom/text-tracks.js.map +1 -1
  257. package/dist/dev/playback/actors/text-track-segment-loader.d.ts +7 -12
  258. package/dist/dev/playback/actors/text-track-segment-loader.d.ts.map +1 -1
  259. package/dist/dev/playback/actors/text-track-segment-loader.js +30 -16
  260. package/dist/dev/playback/actors/text-track-segment-loader.js.map +1 -1
  261. package/dist/dev/playback/actors/text-tracks.d.ts +3 -12
  262. package/dist/dev/playback/actors/text-tracks.d.ts.map +1 -1
  263. package/dist/dev/playback/behaviors/calculate-presentation-duration.d.ts +11 -2
  264. package/dist/dev/playback/behaviors/calculate-presentation-duration.d.ts.map +1 -1
  265. package/dist/dev/playback/behaviors/calculate-presentation-duration.js +21 -43
  266. package/dist/dev/playback/behaviors/calculate-presentation-duration.js.map +1 -1
  267. package/dist/dev/playback/behaviors/derive-cdn-priority.js +68 -0
  268. package/dist/dev/playback/behaviors/derive-cdn-priority.js.map +1 -0
  269. package/dist/dev/playback/behaviors/dom/airplay.js +211 -0
  270. package/dist/dev/playback/behaviors/dom/airplay.js.map +1 -0
  271. package/dist/dev/playback/behaviors/dom/apply-start-position.js +93 -0
  272. package/dist/dev/playback/behaviors/dom/apply-start-position.js.map +1 -0
  273. package/dist/dev/playback/behaviors/dom/end-of-stream.js +25 -102
  274. package/dist/dev/playback/behaviors/dom/end-of-stream.js.map +1 -1
  275. package/dist/dev/playback/behaviors/dom/load-segments.d.ts +14 -4
  276. package/dist/dev/playback/behaviors/dom/load-segments.d.ts.map +1 -1
  277. package/dist/dev/playback/behaviors/dom/load-segments.js +6 -2
  278. package/dist/dev/playback/behaviors/dom/load-segments.js.map +1 -1
  279. package/dist/dev/playback/behaviors/dom/recover-end-stall.js +77 -0
  280. package/dist/dev/playback/behaviors/dom/recover-end-stall.js.map +1 -0
  281. package/dist/dev/playback/behaviors/dom/setup-buffer-actors.js +50 -18
  282. package/dist/dev/playback/behaviors/dom/setup-buffer-actors.js.map +1 -1
  283. package/dist/dev/playback/behaviors/dom/setup-mediasource.js +36 -11
  284. package/dist/dev/playback/behaviors/dom/setup-mediasource.js.map +1 -1
  285. package/dist/dev/playback/behaviors/dom/setup-text-track-actors.d.ts +16 -19
  286. package/dist/dev/playback/behaviors/dom/setup-text-track-actors.d.ts.map +1 -1
  287. package/dist/dev/playback/behaviors/dom/setup-text-track-actors.js +11 -25
  288. package/dist/dev/playback/behaviors/dom/setup-text-track-actors.js.map +1 -1
  289. package/dist/dev/playback/behaviors/dom/sync-text-tracks.js +43 -18
  290. package/dist/dev/playback/behaviors/dom/sync-text-tracks.js.map +1 -1
  291. package/dist/dev/playback/behaviors/dom/track-current-time.d.ts +1 -2
  292. package/dist/dev/playback/behaviors/dom/track-current-time.d.ts.map +1 -1
  293. package/dist/dev/playback/behaviors/dom/track-current-time.js +6 -0
  294. package/dist/dev/playback/behaviors/dom/track-current-time.js.map +1 -1
  295. package/dist/dev/playback/behaviors/dom/track-load-triggers.d.ts +1 -2
  296. package/dist/dev/playback/behaviors/dom/track-load-triggers.d.ts.map +1 -1
  297. package/dist/dev/playback/behaviors/dom/track-load-triggers.js +4 -4
  298. package/dist/dev/playback/behaviors/dom/track-load-triggers.js.map +1 -1
  299. package/dist/dev/playback/behaviors/dom/track-playback-rate.d.ts +1 -2
  300. package/dist/dev/playback/behaviors/dom/track-playback-rate.d.ts.map +1 -1
  301. package/dist/dev/playback/behaviors/dom/track-playback-rate.js.map +1 -1
  302. package/dist/dev/playback/behaviors/dom/update-mediasource-duration.js +3 -2
  303. package/dist/dev/playback/behaviors/dom/update-mediasource-duration.js.map +1 -1
  304. package/dist/dev/playback/behaviors/establish-start-media-time.d.ts +31 -0
  305. package/dist/dev/playback/behaviors/establish-start-media-time.d.ts.map +1 -0
  306. package/dist/dev/playback/behaviors/establish-start-media-time.js +123 -0
  307. package/dist/dev/playback/behaviors/establish-start-media-time.js.map +1 -0
  308. package/dist/dev/playback/behaviors/resolve-presentation.d.ts +2 -1
  309. package/dist/dev/playback/behaviors/resolve-presentation.d.ts.map +1 -1
  310. package/dist/dev/playback/behaviors/resolve-presentation.js.map +1 -1
  311. package/dist/dev/playback/behaviors/resolve-track.js +45 -22
  312. package/dist/dev/playback/behaviors/resolve-track.js.map +1 -1
  313. package/dist/dev/playback/behaviors/select-tracks.d.ts +14 -0
  314. package/dist/dev/playback/behaviors/select-tracks.d.ts.map +1 -0
  315. package/dist/dev/playback/behaviors/select-tracks.js +46 -77
  316. package/dist/dev/playback/behaviors/select-tracks.js.map +1 -1
  317. package/dist/dev/playback/behaviors/setup-failover-monitor.d.ts +14 -0
  318. package/dist/dev/playback/behaviors/setup-failover-monitor.d.ts.map +1 -0
  319. package/dist/dev/playback/behaviors/setup-failover-monitor.js +66 -0
  320. package/dist/dev/playback/behaviors/setup-failover-monitor.js.map +1 -0
  321. package/dist/dev/playback/behaviors/sync-preload.js.map +1 -1
  322. package/dist/dev/playback/behaviors/track-switching.js +403 -0
  323. package/dist/dev/playback/behaviors/track-switching.js.map +1 -0
  324. package/dist/dev/playback/engines/background-video/adapter.d.ts +59 -0
  325. package/dist/dev/playback/engines/background-video/adapter.d.ts.map +1 -0
  326. package/dist/dev/playback/engines/background-video/adapter.js +158 -0
  327. package/dist/dev/playback/engines/background-video/adapter.js.map +1 -0
  328. package/dist/dev/playback/engines/background-video/engine.d.ts +106 -0
  329. package/dist/dev/playback/engines/background-video/engine.d.ts.map +1 -0
  330. package/dist/dev/playback/engines/background-video/engine.js +75 -0
  331. package/dist/dev/playback/engines/background-video/engine.js.map +1 -0
  332. package/dist/dev/playback/engines/hls/adapter-audio-only.d.ts +46 -0
  333. package/dist/dev/playback/engines/hls/adapter-audio-only.d.ts.map +1 -0
  334. package/dist/dev/playback/engines/hls/adapter-audio-only.js +124 -0
  335. package/dist/dev/playback/engines/hls/adapter-audio-only.js.map +1 -0
  336. package/dist/dev/playback/engines/hls/adapter.d.ts +3 -5
  337. package/dist/dev/playback/engines/hls/adapter.d.ts.map +1 -1
  338. package/dist/dev/playback/engines/hls/adapter.js +20 -11
  339. package/dist/dev/playback/engines/hls/adapter.js.map +1 -1
  340. package/dist/dev/playback/engines/hls/engine-audio-only.d.ts +156 -0
  341. package/dist/dev/playback/engines/hls/engine-audio-only.d.ts.map +1 -0
  342. package/dist/dev/playback/engines/hls/engine-audio-only.js +94 -0
  343. package/dist/dev/playback/engines/hls/engine-audio-only.js.map +1 -0
  344. package/dist/dev/playback/engines/hls/engine.d.ts +109 -4
  345. package/dist/dev/playback/engines/hls/engine.d.ts.map +1 -1
  346. package/dist/dev/playback/engines/hls/engine.js +38 -8
  347. package/dist/dev/playback/engines/hls/engine.js.map +1 -1
  348. package/dist/dev/playback/primitives/derive-start-media-time.d.ts +16 -0
  349. package/dist/dev/playback/primitives/derive-start-media-time.d.ts.map +1 -0
  350. package/dist/dev/playback/primitives/failover-fetch.js +42 -0
  351. package/dist/dev/playback/primitives/failover-fetch.js.map +1 -0
  352. package/dist/dev/playback/primitives/head-peek.js +44 -0
  353. package/dist/dev/playback/primitives/head-peek.js.map +1 -0
  354. package/dist/dev/playback/primitives/relocation-pipelines.js +203 -0
  355. package/dist/dev/playback/primitives/relocation-pipelines.js.map +1 -0
  356. package/dist/dev/playback/primitives/segment-load-pipeline.d.ts +2 -0
  357. package/dist/dev/playback/primitives/segment-load-pipeline.js +96 -0
  358. package/dist/dev/playback/primitives/segment-load-pipeline.js.map +1 -0
  359. package/dist/dev/playback/primitives/source-buffer-messages.d.ts +43 -0
  360. package/dist/dev/playback/primitives/source-buffer-messages.d.ts.map +1 -0
  361. package/dist/dev/playback/primitives/text-segment-load-pipeline.d.ts +65 -0
  362. package/dist/dev/playback/primitives/text-segment-load-pipeline.d.ts.map +1 -0
  363. package/dist/dev/playback/primitives/text-segment-load-pipeline.js +37 -0
  364. package/dist/dev/playback/primitives/text-segment-load-pipeline.js.map +1 -0
  365. package/dist/dev/playback/primitives/text-track-messages.d.ts +14 -0
  366. package/dist/dev/playback/primitives/text-track-messages.d.ts.map +1 -0
  367. package/dist/{default/playback/behaviors → dev/playback/primitives}/track-types.js +1 -1
  368. package/dist/dev/playback/primitives/track-types.js.map +1 -0
  369. package/package.json +17 -7
  370. package/dist/default/playback/behaviors/quality-switching.js +0 -96
  371. package/dist/default/playback/behaviors/quality-switching.js.map +0 -1
  372. package/dist/default/playback/behaviors/track-types.js.map +0 -1
  373. package/dist/dev/playback/behaviors/quality-switching.js +0 -96
  374. package/dist/dev/playback/behaviors/quality-switching.js.map +0 -1
  375. package/dist/dev/playback/behaviors/track-types.js.map +0 -1
@@ -0,0 +1,203 @@
1
+ import { peek, update } from "../../core/signals/primitives.js";
2
+ import { effect } from "../../core/signals/effect.js";
3
+ import { resolveVttSegmentMetadata } from "../../media/text/resolve-vtt-metadata.js";
4
+ import { findTrackById, getTracksByType } from "../../media/utils/tracks.js";
5
+ import { dispatchCuesStep, textStepWiring } from "./text-segment-load-pipeline.js";
6
+ import { dispatchStep, fetchStep } from "./segment-load-pipeline.js";
7
+ import { findMediaTrack, readBaseMediaDecodeTime } from "../../media/mp4/timestamp-origin.js";
8
+ import { peekHead } from "./head-peek.js";
9
+ //#region src/playback/primitives/relocation-pipelines.ts
10
+ /** Assert the relocation state view from the opaque step deps — this module knows the slots the composition provides. */
11
+ function relocationState(deps) {
12
+ return deps.state;
13
+ }
14
+ function containerSlot(deps) {
15
+ return relocationState(deps).mediaContainerData;
16
+ }
17
+ /** Synchronous RMW of the per-type entry — disjoint keys across producers, so no lost update. */
18
+ function writeContainer(slot, trackType, patch) {
19
+ update(slot, (current) => ({
20
+ ...current,
21
+ [trackType]: {
22
+ ...current?.[trackType],
23
+ ...patch
24
+ }
25
+ }));
26
+ }
27
+ /**
28
+ * Resolve once `read()` returns a number. Shared by the A/V stamp (waits for the
29
+ * `derive`d origin — immediate for per-type, the shared-`min` barrier for coordinated)
30
+ * and the text step (waits for the primary A/V origin). No bound: for fMP4 the origin
31
+ * always establishes.
32
+ */
33
+ function awaitDefined(read) {
34
+ return new Promise((resolve) => {
35
+ let stop;
36
+ stop = effect(() => {
37
+ const value = read();
38
+ if (value !== void 0) {
39
+ stop?.();
40
+ resolve(value);
41
+ }
42
+ });
43
+ });
44
+ }
45
+ /**
46
+ * Relocation pipelines for one track type — a plain config `messagePipelines`.
47
+ * Keyed by **track type** (`'video'` / `'audio'`), so ABR rungs of a type share the
48
+ * origin (discover skips once the type's value is present). The steps read/write
49
+ * `state.mediaContainerData[trackType]` via their call-time `deps`; the stamp applies
50
+ * the same `derive` seam the reactor uses (pass the composition's resolved
51
+ * `deriveStartMediaTime` so the buffer offset and the model's `startMediaTime` agree).
52
+ */
53
+ function relocationPipelinesFor(trackType, derive) {
54
+ const handlerType = trackType === "video" ? "vide" : "soun";
55
+ /**
56
+ * Init step: head-peek the buffered media track's `track_id` + `mdhd` timescale into
57
+ * `mediaContainerData[trackType]`. Matching by handler (`vide`/`soun`) skips a muxed
58
+ * `clcp` caption track, and the `track_id` lets `readSegmentOrigin` read *this*
59
+ * track's `tfdt` rather than the first `traf` in the segment.
60
+ */
61
+ const readInitTrackInfo = async (frame, _signal, deps) => {
62
+ const { op } = frame;
63
+ if (op.type !== "append-init" || !frame.data) return;
64
+ const slot = containerSlot(deps);
65
+ if (peek(slot)?.[trackType]?.timescale !== void 0) return;
66
+ frame.data = await peekHead(frame.data, (bytes) => {
67
+ const track = findMediaTrack(bytes, handlerType);
68
+ if (track === void 0) return false;
69
+ writeContainer(slot, trackType, {
70
+ trackId: track.trackId,
71
+ timescale: track.timescale
72
+ });
73
+ return true;
74
+ });
75
+ };
76
+ /**
77
+ * Media-segment step: head-peek the `tfdt` baseMediaDecodeTime of the media track's
78
+ * `traf` (matched by the `track_id` discovered from the init), recording the
79
+ * segment's 0-based `startTime` with it — the origin is `bmdt/ts − segmentStartTime`,
80
+ * so the first *loaded* segment need not be the 0th. Without a discovered `track_id`
81
+ * (non-fMP4 / mock init) there's no media track to relocate, so the step no-ops and
82
+ * the append stays native.
83
+ */
84
+ const readSegmentOrigin = async (frame, _signal, deps) => {
85
+ const { op } = frame;
86
+ if (op.type !== "append-segment" || !frame.data) return;
87
+ const slot = containerSlot(deps);
88
+ const container = peek(slot)?.[trackType];
89
+ if (container?.baseMediaDecodeTime !== void 0) return;
90
+ const { trackId } = container ?? {};
91
+ if (trackId === void 0) return;
92
+ const segmentStartTime = op.meta.startTime;
93
+ frame.data = await peekHead(frame.data, (bytes) => {
94
+ const baseMediaDecodeTime = readBaseMediaDecodeTime(bytes, trackId);
95
+ if (baseMediaDecodeTime === void 0) return false;
96
+ writeContainer(slot, trackType, {
97
+ baseMediaDecodeTime,
98
+ segmentStartTime
99
+ });
100
+ return true;
101
+ });
102
+ };
103
+ /**
104
+ * Stamp step — tier-agnostic apply. Relocate by the `derive`d `startMediaTime` for
105
+ * this type (`offset = −startMediaTime`). Applies the **same** `derive` the reactor
106
+ * uses, over the shared `mediaContainerData` slot — so the buffer offset matches the
107
+ * model's stamped `startMediaTime`, and it's robust to `established` + late tracks
108
+ * (the slot persists; the model value may not be re-stamped after the reactor goes
109
+ * sticky). Awaited: per-type resolves at once (own origin discovered earlier in this
110
+ * pipeline); shared-`min` waits until every selected A/V origin is in — the barrier,
111
+ * filled by the other type's discover step. A derived `0` (0-PTS / below threshold)
112
+ * leaves the append native — setting `timestampOffset` at all can ripple.
113
+ */
114
+ const stampStartMediaTime = async (frame, signal, deps) => {
115
+ if (frame.op.type !== "append-segment") return;
116
+ const state = relocationState(deps);
117
+ const own = peek(state.mediaContainerData)?.[trackType];
118
+ if (own?.timescale === void 0 || own.baseMediaDecodeTime === void 0 || own.segmentStartTime === void 0) return;
119
+ const startMediaTime = await awaitDefined(() => {
120
+ const containerData = state.mediaContainerData.get();
121
+ if (!containerData) return void 0;
122
+ return derive(containerData, {
123
+ selectedVideoTrackId: state.selectedVideoTrackId?.get(),
124
+ selectedAudioTrackId: state.selectedAudioTrackId?.get()
125
+ })[trackType];
126
+ });
127
+ if (signal.aborted || startMediaTime === 0) return;
128
+ frame.meta = {
129
+ ...frame.meta ?? frame.op.meta,
130
+ timestampOffset: -startMediaTime
131
+ };
132
+ };
133
+ return () => ({
134
+ remove: [dispatchStep],
135
+ "append-init": [
136
+ fetchStep,
137
+ readInitTrackInfo,
138
+ dispatchStep
139
+ ],
140
+ "append-segment": [
141
+ fetchStep,
142
+ readSegmentOrigin,
143
+ stampStartMediaTime,
144
+ dispatchStep
145
+ ]
146
+ });
147
+ }
148
+ /**
149
+ * Resolve step for the relocation text pipeline. Reuses the injected host resolver
150
+ * (the loader's folded `resolveSegment`, via `textStepWiring`) for cues and fetches
151
+ * the `X-TIMESTAMP-MAP` header in parallel, stashing it on `frame.metadata` for
152
+ * `relocateCuesStep`. Replaces the
153
+ * base `resolveCuesStep` (which fetches cues only) — text's native `<track>` parser
154
+ * discards the header, so the map needs its own raw-bytes fetch.
155
+ */
156
+ const resolveWithMetadataStep = async (frame, signal, deps) => {
157
+ const [cues, metadata] = await Promise.all([textStepWiring(deps).resolveSegment(frame.op.segment.url), resolveVttSegmentMetadata(frame.op.segment.url)]);
158
+ if (signal.aborted) return;
159
+ frame.cues = cues;
160
+ frame.metadata = metadata;
161
+ };
162
+ /**
163
+ * Relocate step — shifts each cue onto the 0-based presentation timeline:
164
+ * `cueFinal = cueNative − startMediaTime`, where `startMediaTime` is the primary
165
+ * A/V track's origin (selected **video**, else **audio** — the single-anchor rule,
166
+ * and defensive like the reactor's optional selection) and `cueNative` folds in the
167
+ * `X-TIMESTAMP-MAP` correction (`mpegts/90000 − local`) for map-bearing VTT (Apple)
168
+ * or is the absolute cue time (no map, e.g. Mux). Text can resolve before A/V
169
+ * establishes, so the origin is awaited; fMP4 always establishes it (0-PTS → 0),
170
+ * and a text-only source (no A/V selected) simply gets offset 0.
171
+ */
172
+ const relocateCuesStep = async (frame, signal, deps) => {
173
+ if (!frame.cues?.length) return;
174
+ const state = deps.state;
175
+ const startMediaTime = await awaitDefined(() => {
176
+ const presentation = state.presentation.get();
177
+ if (!presentation) return void 0;
178
+ const primaryId = state.selectedVideoTrackId.get() ?? state.selectedAudioTrackId.get();
179
+ if (primaryId !== void 0) return findTrackById(presentation, primaryId)?.startMediaTime;
180
+ return getTracksByType(presentation, "video").length > 0 || getTracksByType(presentation, "audio").length > 0 ? void 0 : 0;
181
+ });
182
+ if (signal.aborted) return;
183
+ const { timestampMap } = frame.metadata ?? {};
184
+ const delta = (timestampMap ? timestampMap.mpegts / 9e4 - timestampMap.local : 0) - startMediaTime;
185
+ if (delta !== 0) for (const cue of frame.cues) {
186
+ cue.startTime += delta;
187
+ cue.endTime += delta;
188
+ }
189
+ };
190
+ /**
191
+ * Relocation text pipeline — the text analog of `relocationPipelinesFor(type)`.
192
+ * `resolveWithMetadata` (cues + `X-TIMESTAMP-MAP`) → `relocateCues` (shift by the
193
+ * primary A/V origin) → `dispatchCues`.
194
+ */
195
+ const relocatingTextPipelines = () => [
196
+ resolveWithMetadataStep,
197
+ relocateCuesStep,
198
+ dispatchCuesStep
199
+ ];
200
+ //#endregion
201
+ export { relocatingTextPipelines, relocationPipelinesFor };
202
+
203
+ //# sourceMappingURL=relocation-pipelines.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relocation-pipelines.js","names":[],"sources":["../../../../src/playback/primitives/relocation-pipelines.ts"],"sourcesContent":["/**\n * Non-zero-PTS relocation pipelines — the config-supplied, loader-facing half of the\n * `establishStartMediaTime` behavior (`../behaviors/establish-start-media-time`). The\n * reactor there owns the lifecycle and the `derive` coordination seam; this file supplies\n * the `messagePipelines` the segment/text loaders run to *fill and act on* that behavior's\n * state. It's the relocation analog of `track-switching`'s config-supplied constraint/rule\n * chain — a pluggable strategy handed to the machinery via config, not applied inline —\n * supplied to the loaders rather than applied by the behavior, so the two coordinate\n * through the shared `mediaContainerData` / `startMediaTime` slots alone, never by import.\n *\n * DOM-free: it composes the loader vocabularies (`segment-load-pipeline`,\n * `text-segment-load-pipeline`) through their structural sink seams and shifts cues via\n * the structural `Cue` type, so it names no `SourceBuffer`, `TextTracksActor`, or\n * `VTTCue`. The A/V pipeline is a plain `messagePipelines` array:\n * - `discover` — init `track_id` + `mdhd` timescale for the buffered media track, then\n * that same track's `tfdt` baseMediaDecodeTime, matched by `track_id` so a muxed\n * segment reads the media track's origin, not the first `traf` — writes\n * `state.mediaContainerData`.\n * - `stamp` — reads that track's derived origin back and relocates via\n * `timestampOffset = −startMediaTime`.\n * Steps read composition `state` from their call-time `deps` (no closures, no context).\n *\n * The text half (`relocatingTextPipelines`) is the same idea for the text-segment loader:\n * a `resolveWithMetadata → relocateCues → dispatchCues` pipeline that shifts cues onto the\n * same 0-based timeline, reading the primary A/V track's `startMediaTime` (the reactor's\n * consumed value) via `deps`.\n */\nimport type { StateSignals } from '../../core/composition/create-composition';\nimport { effect } from '../../core/signals/effect';\nimport { peek, type Signal, update } from '../../core/signals/primitives';\nimport { findMediaTrack, type MediaHandlerType, readBaseMediaDecodeTime } from '../../media/mp4/timestamp-origin';\nimport { resolveVttSegmentMetadata, type TextSegmentMetadata } from '../../media/text/resolve-vtt-metadata';\nimport type { Cue, MaybeResolvedPresentation, MediaContainerData } from '../../media/types';\nimport { findTrackById, getTracksByType } from '../../media/utils/tracks';\nimport type { DeriveStartMediaTime } from './derive-start-media-time';\nimport { peekHead } from './head-peek';\nimport { dispatchStep, fetchStep, type LoadStep, type MessagePipelines, type StepDeps } from './segment-load-pipeline';\nimport {\n dispatchCuesStep,\n type TextFrame,\n type TextLoadStep,\n type TextStepDeps,\n textStepWiring,\n} from './text-segment-load-pipeline';\n\n/** The composition slots these steps read — the view asserted from the opaque `deps.state`. */\ninterface RelocationSlots {\n presentation?: MaybeResolvedPresentation;\n mediaContainerData?: Record<string, MediaContainerData>;\n selectedVideoTrackId?: string;\n selectedAudioTrackId?: string;\n}\n\ntype ContainerSlot = Signal<Record<string, MediaContainerData> | undefined>;\n\n/** Assert the relocation state view from the opaque step deps — this module knows the slots the composition provides. */\nfunction relocationState(deps: StepDeps): StateSignals<RelocationSlots> {\n return deps.state as unknown as StateSignals<RelocationSlots>;\n}\n\nfunction containerSlot(deps: StepDeps): ContainerSlot {\n return relocationState(deps).mediaContainerData;\n}\n\n/** Synchronous RMW of the per-type entry — disjoint keys across producers, so no lost update. */\nfunction writeContainer(slot: ContainerSlot, trackType: string, patch: Partial<MediaContainerData>): void {\n update(slot, (current) => ({ ...current, [trackType]: { ...current?.[trackType], ...patch } }));\n}\n\n/**\n * Resolve once `read()` returns a number. Shared by the A/V stamp (waits for the\n * `derive`d origin — immediate for per-type, the shared-`min` barrier for coordinated)\n * and the text step (waits for the primary A/V origin). No bound: for fMP4 the origin\n * always establishes.\n */\nfunction awaitDefined(read: () => number | undefined): Promise<number> {\n return new Promise((resolve) => {\n let stop: (() => void) | undefined;\n stop = effect(() => {\n const value = read();\n if (value !== undefined) {\n stop?.();\n resolve(value);\n }\n });\n });\n}\n\n/**\n * Relocation pipelines for one track type — a plain config `messagePipelines`.\n * Keyed by **track type** (`'video'` / `'audio'`), so ABR rungs of a type share the\n * origin (discover skips once the type's value is present). The steps read/write\n * `state.mediaContainerData[trackType]` via their call-time `deps`; the stamp applies\n * the same `derive` seam the reactor uses (pass the composition's resolved\n * `deriveStartMediaTime` so the buffer offset and the model's `startMediaTime` agree).\n */\nexport function relocationPipelinesFor(trackType: 'video' | 'audio', derive: DeriveStartMediaTime): MessagePipelines {\n const handlerType: MediaHandlerType = trackType === 'video' ? 'vide' : 'soun';\n\n /**\n * Init step: head-peek the buffered media track's `track_id` + `mdhd` timescale into\n * `mediaContainerData[trackType]`. Matching by handler (`vide`/`soun`) skips a muxed\n * `clcp` caption track, and the `track_id` lets `readSegmentOrigin` read *this*\n * track's `tfdt` rather than the first `traf` in the segment.\n */\n const readInitTrackInfo: LoadStep = async (frame, _signal, deps) => {\n const { op } = frame;\n if (op.type !== 'append-init' || !frame.data) return;\n const slot = containerSlot(deps);\n if (peek(slot)?.[trackType]?.timescale !== undefined) return; // already have it (any rung of this type)\n frame.data = await peekHead(frame.data, (bytes) => {\n const track = findMediaTrack(bytes, handlerType);\n if (track === undefined) return false;\n writeContainer(slot, trackType, { trackId: track.trackId, timescale: track.timescale });\n return true;\n });\n };\n\n /**\n * Media-segment step: head-peek the `tfdt` baseMediaDecodeTime of the media track's\n * `traf` (matched by the `track_id` discovered from the init), recording the\n * segment's 0-based `startTime` with it — the origin is `bmdt/ts − segmentStartTime`,\n * so the first *loaded* segment need not be the 0th. Without a discovered `track_id`\n * (non-fMP4 / mock init) there's no media track to relocate, so the step no-ops and\n * the append stays native.\n */\n const readSegmentOrigin: LoadStep = async (frame, _signal, deps) => {\n const { op } = frame;\n if (op.type !== 'append-segment' || !frame.data) return;\n const slot = containerSlot(deps);\n const container = peek(slot)?.[trackType];\n if (container?.baseMediaDecodeTime !== undefined) return; // established\n const { trackId } = container ?? {};\n if (trackId === undefined) return; // init didn't identify a media track — nothing to match\n const segmentStartTime = op.meta.startTime;\n frame.data = await peekHead(frame.data, (bytes) => {\n const baseMediaDecodeTime = readBaseMediaDecodeTime(bytes, trackId);\n if (baseMediaDecodeTime === undefined) return false;\n writeContainer(slot, trackType, { baseMediaDecodeTime, segmentStartTime });\n return true;\n });\n };\n\n /**\n * Stamp step — tier-agnostic apply. Relocate by the `derive`d `startMediaTime` for\n * this type (`offset = −startMediaTime`). Applies the **same** `derive` the reactor\n * uses, over the shared `mediaContainerData` slot — so the buffer offset matches the\n * model's stamped `startMediaTime`, and it's robust to `established` + late tracks\n * (the slot persists; the model value may not be re-stamped after the reactor goes\n * sticky). Awaited: per-type resolves at once (own origin discovered earlier in this\n * pipeline); shared-`min` waits until every selected A/V origin is in — the barrier,\n * filled by the other type's discover step. A derived `0` (0-PTS / below threshold)\n * leaves the append native — setting `timestampOffset` at all can ripple.\n */\n const stampStartMediaTime: LoadStep = async (frame, signal, deps) => {\n if (frame.op.type !== 'append-segment') return;\n const state = relocationState(deps);\n // Liveness guard: if THIS type's own origin wasn't discovered — `readSegmentOrigin`\n // ran earlier in this pipeline and found no `tfdt` (mock / TS / containerless) — the\n // source isn't relocatable, so leave the append native and DON'T wait. Only a\n // discoverable type awaits the derived origin (which for shared-`min` legitimately\n // blocks on the other selected type — the barrier).\n const own = peek(state.mediaContainerData)?.[trackType];\n if (own?.timescale === undefined || own.baseMediaDecodeTime === undefined || own.segmentStartTime === undefined) {\n return;\n }\n const startMediaTime = await awaitDefined(() => {\n const containerData = state.mediaContainerData.get();\n if (!containerData) return undefined;\n return derive(containerData, {\n selectedVideoTrackId: state.selectedVideoTrackId?.get(),\n selectedAudioTrackId: state.selectedAudioTrackId?.get(),\n })[trackType];\n });\n if (signal.aborted || startMediaTime === 0) return;\n frame.meta = { ...(frame.meta ?? frame.op.meta), timestampOffset: -startMediaTime };\n };\n\n return () => ({\n remove: [dispatchStep],\n 'append-init': [fetchStep, readInitTrackInfo, dispatchStep],\n 'append-segment': [fetchStep, readSegmentOrigin, stampStartMediaTime, dispatchStep],\n });\n}\n\n// ============================================================================\n// TEXT (cue relocation)\n// ============================================================================\n\n/**\n * Resolve step for the relocation text pipeline. Reuses the injected host resolver\n * (the loader's folded `resolveSegment`, via `textStepWiring`) for cues and fetches\n * the `X-TIMESTAMP-MAP` header in parallel, stashing it on `frame.metadata` for\n * `relocateCuesStep`. Replaces the\n * base `resolveCuesStep` (which fetches cues only) — text's native `<track>` parser\n * discards the header, so the map needs its own raw-bytes fetch.\n */\nconst resolveWithMetadataStep = async <C extends Cue>(\n frame: TextFrame<C>,\n signal: AbortSignal,\n deps: TextStepDeps\n): Promise<void> => {\n const [cues, metadata] = await Promise.all([\n textStepWiring<C>(deps).resolveSegment(frame.op.segment.url),\n resolveVttSegmentMetadata(frame.op.segment.url),\n ]);\n if (signal.aborted) return;\n frame.cues = cues;\n frame.metadata = metadata;\n};\n\n/**\n * Relocate step — shifts each cue onto the 0-based presentation timeline:\n * `cueFinal = cueNative − startMediaTime`, where `startMediaTime` is the primary\n * A/V track's origin (selected **video**, else **audio** — the single-anchor rule,\n * and defensive like the reactor's optional selection) and `cueNative` folds in the\n * `X-TIMESTAMP-MAP` correction (`mpegts/90000 − local`) for map-bearing VTT (Apple)\n * or is the absolute cue time (no map, e.g. Mux). Text can resolve before A/V\n * establishes, so the origin is awaited; fMP4 always establishes it (0-PTS → 0),\n * and a text-only source (no A/V selected) simply gets offset 0.\n */\nconst relocateCuesStep = async <C extends Cue>(\n frame: TextFrame<C>,\n signal: AbortSignal,\n deps: TextStepDeps\n): Promise<void> => {\n if (!frame.cues?.length) return;\n const state = deps.state as unknown as StateSignals<RelocationSlots>;\n const startMediaTime = await awaitDefined(() => {\n const presentation = state.presentation.get();\n if (!presentation) return undefined;\n const primaryId = state.selectedVideoTrackId.get() ?? state.selectedAudioTrackId.get();\n if (primaryId !== undefined) {\n // A/V selected: use its origin once stamped (undefined until then → keep waiting).\n return findTrackById(presentation, primaryId)?.startMediaTime;\n }\n // No A/V selected. Distinguish \"not selected YET\" from \"text-only source\". If the\n // presentation has A/V tracks, one WILL be selected and stamped — wait for it, or the\n // cues get shifted by the full `mapCorrection` (e.g. +10s on Apple X-TIMESTAMP-MAP\n // sources) when text relocation wins the race against A/V selection. Only a genuinely\n // text-only source (no A/V origin ever coming) legitimately relocates by offset 0.\n const hasAv =\n getTracksByType(presentation, 'video').length > 0 || getTracksByType(presentation, 'audio').length > 0;\n return hasAv ? undefined : 0;\n });\n if (signal.aborted) return;\n const { timestampMap } = (frame.metadata as TextSegmentMetadata | undefined) ?? {};\n const mapCorrection = timestampMap ? timestampMap.mpegts / 90000 - timestampMap.local : 0;\n const delta = mapCorrection - startMediaTime;\n if (delta !== 0) {\n for (const cue of frame.cues) {\n cue.startTime += delta;\n cue.endTime += delta;\n }\n }\n};\n\n/**\n * Relocation text pipeline — the text analog of `relocationPipelinesFor(type)`.\n * `resolveWithMetadata` (cues + `X-TIMESTAMP-MAP`) → `relocateCues` (shift by the\n * primary A/V origin) → `dispatchCues`.\n */\nexport const relocatingTextPipelines = <C extends Cue>(): TextLoadStep<C>[] => [\n resolveWithMetadataStep,\n relocateCuesStep,\n dispatchCuesStep,\n];\n"],"mappings":";;;;;;;;;;AAwDA,SAAS,gBAAgB,MAA+C;CACtE,OAAO,KAAK;AACd;AAEA,SAAS,cAAc,MAA+B;CACpD,OAAO,gBAAgB,IAAI,CAAC,CAAC;AAC/B;;AAGA,SAAS,eAAe,MAAqB,WAAmB,OAA0C;CACxG,OAAO,OAAO,aAAa;EAAE,GAAG;GAAU,YAAY;GAAE,GAAG,UAAU;GAAY,GAAG;EAAM;CAAE,EAAE;AAChG;;;;;;;AAQA,SAAS,aAAa,MAAiD;CACrE,OAAO,IAAI,SAAS,YAAY;EAC9B,IAAI;EACJ,OAAO,aAAa;GAClB,MAAM,QAAQ,KAAK;GACnB,IAAI,UAAU,KAAA,GAAW;IACvB,OAAO;IACP,QAAQ,KAAK;GACf;EACF,CAAC;CACH,CAAC;AACH;;;;;;;;;AAUA,SAAgB,uBAAuB,WAA8B,QAAgD;CACnH,MAAM,cAAgC,cAAc,UAAU,SAAS;;;;;;;CAQvE,MAAM,oBAA8B,OAAO,OAAO,SAAS,SAAS;EAClE,MAAM,EAAE,OAAO;EACf,IAAI,GAAG,SAAS,iBAAiB,CAAC,MAAM,MAAM;EAC9C,MAAM,OAAO,cAAc,IAAI;EAC/B,IAAI,KAAK,IAAI,CAAC,GAAG,UAAU,EAAE,cAAc,KAAA,GAAW;EACtD,MAAM,OAAO,MAAM,SAAS,MAAM,OAAO,UAAU;GACjD,MAAM,QAAQ,eAAe,OAAO,WAAW;GAC/C,IAAI,UAAU,KAAA,GAAW,OAAO;GAChC,eAAe,MAAM,WAAW;IAAE,SAAS,MAAM;IAAS,WAAW,MAAM;GAAU,CAAC;GACtF,OAAO;EACT,CAAC;CACH;;;;;;;;;CAUA,MAAM,oBAA8B,OAAO,OAAO,SAAS,SAAS;EAClE,MAAM,EAAE,OAAO;EACf,IAAI,GAAG,SAAS,oBAAoB,CAAC,MAAM,MAAM;EACjD,MAAM,OAAO,cAAc,IAAI;EAC/B,MAAM,YAAY,KAAK,IAAI,CAAC,GAAG;EAC/B,IAAI,WAAW,wBAAwB,KAAA,GAAW;EAClD,MAAM,EAAE,YAAY,aAAa,CAAC;EAClC,IAAI,YAAY,KAAA,GAAW;EAC3B,MAAM,mBAAmB,GAAG,KAAK;EACjC,MAAM,OAAO,MAAM,SAAS,MAAM,OAAO,UAAU;GACjD,MAAM,sBAAsB,wBAAwB,OAAO,OAAO;GAClE,IAAI,wBAAwB,KAAA,GAAW,OAAO;GAC9C,eAAe,MAAM,WAAW;IAAE;IAAqB;GAAiB,CAAC;GACzE,OAAO;EACT,CAAC;CACH;;;;;;;;;;;;CAaA,MAAM,sBAAgC,OAAO,OAAO,QAAQ,SAAS;EACnE,IAAI,MAAM,GAAG,SAAS,kBAAkB;EACxC,MAAM,QAAQ,gBAAgB,IAAI;EAMlC,MAAM,MAAM,KAAK,MAAM,kBAAkB,CAAC,GAAG;EAC7C,IAAI,KAAK,cAAc,KAAA,KAAa,IAAI,wBAAwB,KAAA,KAAa,IAAI,qBAAqB,KAAA,GACpG;EAEF,MAAM,iBAAiB,MAAM,mBAAmB;GAC9C,MAAM,gBAAgB,MAAM,mBAAmB,IAAI;GACnD,IAAI,CAAC,eAAe,OAAO,KAAA;GAC3B,OAAO,OAAO,eAAe;IAC3B,sBAAsB,MAAM,sBAAsB,IAAI;IACtD,sBAAsB,MAAM,sBAAsB,IAAI;GACxD,CAAC,CAAC,CAAC;EACL,CAAC;EACD,IAAI,OAAO,WAAW,mBAAmB,GAAG;EAC5C,MAAM,OAAO;GAAE,GAAI,MAAM,QAAQ,MAAM,GAAG;GAAO,iBAAiB,CAAC;EAAe;CACpF;CAEA,cAAc;EACZ,QAAQ,CAAC,YAAY;EACrB,eAAe;GAAC;GAAW;GAAmB;EAAY;EAC1D,kBAAkB;GAAC;GAAW;GAAmB;GAAqB;EAAY;CACpF;AACF;;;;;;;;;AAcA,MAAM,0BAA0B,OAC9B,OACA,QACA,SACkB;CAClB,MAAM,CAAC,MAAM,YAAY,MAAM,QAAQ,IAAI,CACzC,eAAkB,IAAI,CAAC,CAAC,eAAe,MAAM,GAAG,QAAQ,GAAG,GAC3D,0BAA0B,MAAM,GAAG,QAAQ,GAAG,CAChD,CAAC;CACD,IAAI,OAAO,SAAS;CACpB,MAAM,OAAO;CACb,MAAM,WAAW;AACnB;;;;;;;;;;;AAYA,MAAM,mBAAmB,OACvB,OACA,QACA,SACkB;CAClB,IAAI,CAAC,MAAM,MAAM,QAAQ;CACzB,MAAM,QAAQ,KAAK;CACnB,MAAM,iBAAiB,MAAM,mBAAmB;EAC9C,MAAM,eAAe,MAAM,aAAa,IAAI;EAC5C,IAAI,CAAC,cAAc,OAAO,KAAA;EAC1B,MAAM,YAAY,MAAM,qBAAqB,IAAI,KAAK,MAAM,qBAAqB,IAAI;EACrF,IAAI,cAAc,KAAA,GAEhB,OAAO,cAAc,cAAc,SAAS,CAAC,EAAE;EASjD,OADE,gBAAgB,cAAc,OAAO,CAAC,CAAC,SAAS,KAAK,gBAAgB,cAAc,OAAO,CAAC,CAAC,SAAS,IACxF,KAAA,IAAY;CAC7B,CAAC;CACD,IAAI,OAAO,SAAS;CACpB,MAAM,EAAE,iBAAkB,MAAM,YAAgD,CAAC;CAEjF,MAAM,SADgB,eAAe,aAAa,SAAS,MAAQ,aAAa,QAAQ,KAC1D;CAC9B,IAAI,UAAU,GACZ,KAAK,MAAM,OAAO,MAAM,MAAM;EAC5B,IAAI,aAAa;EACjB,IAAI,WAAW;CACjB;AAEJ;;;;;;AAOA,MAAa,gCAAkE;CAC7E;CACA;CACA;AACF"}
@@ -0,0 +1,96 @@
1
+ import { effect } from "../../core/signals/effect.js";
2
+ //#region src/playback/primitives/segment-load-pipeline.ts
3
+ /**
4
+ * Base-step view of the loader's own wiring. `createSegmentLoaderActor` folds its
5
+ * `sourceBufferActor` + `fetch` into the threaded `config` so base steps read them
6
+ * from the uniform passthrough — present whether the loader runs inside a composition
7
+ * or standalone. `config` is loose (`object`), so assert the shape here (one cast, like
8
+ * relocation's `containerSlot`).
9
+ */
10
+ function stepWiring(deps) {
11
+ return deps.config;
12
+ }
13
+ /**
14
+ * Resolves when the SourceBufferActor snapshot reaches 'idle'.
15
+ * Rejects if the signal is aborted or the actor is destroyed.
16
+ *
17
+ * Used to sequence SourceBufferActor operations without awaiting send()
18
+ * directly — send() is fire-and-forget; callers observe completion via
19
+ * state transition.
20
+ */
21
+ function waitForIdle(snapshot, signal) {
22
+ return new Promise((resolve, reject) => {
23
+ if (snapshot.get().value === "idle") {
24
+ resolve();
25
+ return;
26
+ }
27
+ if (snapshot.get().value === "destroyed") {
28
+ reject(new DOMException("Aborted", "AbortError"));
29
+ return;
30
+ }
31
+ if (signal.aborted) {
32
+ reject(signal.reason);
33
+ return;
34
+ }
35
+ let stop;
36
+ const cleanup = (fn) => {
37
+ stop?.();
38
+ signal.removeEventListener("abort", onAbort);
39
+ fn();
40
+ };
41
+ const onAbort = () => cleanup(() => reject(signal.reason));
42
+ stop = effect(() => {
43
+ const value = snapshot.get().value;
44
+ if (value === "idle") cleanup(resolve);
45
+ else if (value === "destroyed") cleanup(() => reject(new DOMException("Aborted", "AbortError")));
46
+ });
47
+ signal.addEventListener("abort", onAbort, { once: true });
48
+ });
49
+ }
50
+ /** Build the SourceBuffer message a completed frame dispatches. `fetchStep` always precedes `dispatchStep` in append pipelines, so `data` is set by now. */
51
+ function toMessage({ op, data, meta }) {
52
+ switch (op.type) {
53
+ case "remove": return op;
54
+ case "append-init": return {
55
+ type: "append-init",
56
+ data,
57
+ meta: op.meta
58
+ };
59
+ case "append-segment": return {
60
+ type: "append-segment",
61
+ data,
62
+ meta: meta ?? op.meta
63
+ };
64
+ }
65
+ }
66
+ /**
67
+ * Fetch this op's bytes into the frame. Init segments need the full body
68
+ * (`minChunkSize: Infinity`) before appending; media segments stream so chunks
69
+ * append as they arrive. Awaiting headers eagerly also starts the HTTP
70
+ * connection (and records the fetch in observers like tests).
71
+ */
72
+ const fetchStep = async (frame, signal, deps) => {
73
+ const { op } = frame;
74
+ if (op.type === "remove") return;
75
+ const { fetch } = stepWiring(deps);
76
+ frame.data = await fetch(op, op.type === "append-init" ? {
77
+ signal,
78
+ minChunkSize: Infinity
79
+ } : { signal });
80
+ };
81
+ /** Dispatch the frame's message to the SourceBufferActor and await its return to idle. */
82
+ const dispatchStep = async (frame, signal, deps) => {
83
+ const { sourceBufferActor } = stepWiring(deps);
84
+ sourceBufferActor.send(toMessage(frame));
85
+ await waitForIdle(sourceBufferActor.snapshot, signal);
86
+ };
87
+ /** Tier 0 default: fetch (for ops that carry bytes) then dispatch. No relocation vocabulary. */
88
+ const DEFAULT_MESSAGE_PIPELINES = () => ({
89
+ remove: [dispatchStep],
90
+ "append-init": [fetchStep, dispatchStep],
91
+ "append-segment": [fetchStep, dispatchStep]
92
+ });
93
+ //#endregion
94
+ export { DEFAULT_MESSAGE_PIPELINES, dispatchStep, fetchStep };
95
+
96
+ //# sourceMappingURL=segment-load-pipeline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"segment-load-pipeline.js","names":[],"sources":["../../../../src/playback/primitives/segment-load-pipeline.ts"],"sourcesContent":["/**\n * The v/a segment **load pipeline** — the step vocabulary and base steps a segment\n * loader runs, extracted from the loader actor so the \"what\" (the composable steps)\n * is separable from the \"how\" (the actor's scheduling). A pipeline is a per-op list\n * of {@link LoadStep}s the loader drains in order; the default is `fetch → dispatch`,\n * and a composition can insert its own steps between them (e.g. non-zero-PTS\n * relocation) without the loader knowing.\n *\n * The base steps reach their SourceBuffer sink through the structural {@link AppendSink}\n * seam rather than the concrete actor type — the loader folds the real actor into\n * `config` at construction, and it satisfies the seam by structure — so this module\n * carries no dependency on the actor implementation, and no dependency on the DOM.\n */\nimport type { AnySlotMap } from '../../core/composition/create-composition';\nimport { effect } from '../../core/signals/effect';\nimport type { ReadonlySignal } from '../../core/signals/primitives';\nimport type { AddressableObject } from '../../media/types';\nimport type {\n AppendInitMessage,\n AppendSegmentMessage,\n IndividualSourceBufferMessage,\n RemoveMessage,\n} from './source-buffer-messages';\n\n// ============================================================================\n// SINK SEAM\n// ============================================================================\n\n/**\n * The structural view of a SourceBuffer sink the base steps use — just the ops\n * {@link dispatchStep} needs (`send` + a `snapshot` to await idle). The concrete\n * `SourceBufferActor` satisfies it by structure, so the pipeline names no actor.\n */\nexport interface AppendSink {\n send(message: IndividualSourceBufferMessage): void;\n snapshot: ReadonlySignal<{ value: string }>;\n}\n\n// ============================================================================\n// STEP MODEL\n// ============================================================================\n\n/**\n * A LoadTask is the intent to perform one unit of SegmentLoader work.\n * Unlike SourceBufferMessage, fetch-based tasks carry a URL rather than\n * pre-fetched data — the runner fetches and appends them in sequence.\n *\n * Derived from SourceBufferMessage types by removing `data` and adding\n * a fetch URL via AddressableObject.\n *\n * @todo Rename — \"LoadTask\" risks confusion with the `Task` class used for\n * SourceBufferActor scheduling. These are closer to operation descriptors or\n * messages than tasks in that sense.\n */\nexport type LoadTask =\n | (Omit<AppendInitMessage, 'data'> & AddressableObject)\n | (Omit<AppendSegmentMessage, 'data'> & AddressableObject)\n | RemoveMessage;\n\n/**\n * A {@link LoadTask} mid-reassembly into its append message. `LoadTask` is a\n * message with `data` omitted and a URL added; the pipeline reverses that —\n * `fetchStep` produces `data`, `dispatchStep` reassembles the message. `data` is\n * the omitted payload (kept as the fetched stream — the loader never produces the\n * `ArrayBuffer` arm of `SegmentData` — widened back at dispatch). `meta` overrides\n * `op.meta` for `append-segment` (e.g. a stamped `timestampOffset`); an\n * `append-init` dispatches `op.meta` directly.\n */\nexport interface Frame {\n readonly op: LoadTask;\n data?: AsyncIterable<Uint8Array>;\n meta?: AppendSegmentMessage['meta'];\n}\n\n/**\n * One stage of a message pipeline. Mutates the {@link Frame} in place and may be\n * async; the runner checks `signal.aborted` before each step and passes the\n * actor's {@link StepDeps} on every call, so a stateless step (e.g.\n * {@link fetchStep}) is a plain value, and a step that needs composition signals\n * (relocation's discover/stamp) reads them from `deps` at call time.\n */\nexport type LoadStep = (frame: Frame, signal: AbortSignal, deps: StepDeps) => void | Promise<void>;\n\n/**\n * The uniform passthrough handed to every {@link LoadStep} on every call — the\n * composition triple, nothing more. `state`/`context` are the full composition signal\n * maps; `config` is the threaded config with the loader's own wiring folded in (see\n * {@link stepWiring} + `createSegmentLoaderActor`). All three are typed loose: the\n * loader is a conduit and never reads them; a step asserts the slots it knows are\n * present (composition steps read `state`; base steps read the folded wiring off\n * `config`).\n */\nexport interface StepDeps {\n state: AnySlotMap;\n context: AnySlotMap;\n config: object;\n}\n\n/**\n * Base-step view of the loader's own wiring. `createSegmentLoaderActor` folds its\n * `sourceBufferActor` + `fetch` into the threaded `config` so base steps read them\n * from the uniform passthrough — present whether the loader runs inside a composition\n * or standalone. `config` is loose (`object`), so assert the shape here (one cast, like\n * relocation's `containerSlot`).\n */\nfunction stepWiring(deps: StepDeps): { sourceBufferActor: AppendSink; fetch: FetchBytes } {\n return deps.config as { sourceBufferActor: AppendSink; fetch: FetchBytes };\n}\n\n/**\n * Builds the ordered step list for each message type. Called **once per actor**\n * — its role is per-actor instantiation, so stateful steps (relocation's origin\n * discoverer) get fresh state per source reset; deps arrive at step-call time,\n * not here. The default ({@link DEFAULT_MESSAGE_PIPELINES}) is `fetch → dispatch`;\n * a non-zero-PTS composition returns a map that inserts its own discover/stamp\n * steps between them (see `establishStartMediaTime`), so the loader stays oblivious\n * to relocation and the Tier 0 pipeline carries no relocation vocabulary at all.\n */\nexport type MessagePipelines = () => Record<LoadTask['type'], LoadStep[]>;\n\n// ============================================================================\n// BASE STEPS\n// ============================================================================\n\nexport type FetchBytes = (\n addressable: AddressableObject,\n options?: RequestInit & { minChunkSize?: number }\n) => Promise<AsyncIterable<Uint8Array>>;\n\n/**\n * Resolves when the SourceBufferActor snapshot reaches 'idle'.\n * Rejects if the signal is aborted or the actor is destroyed.\n *\n * Used to sequence SourceBufferActor operations without awaiting send()\n * directly — send() is fire-and-forget; callers observe completion via\n * state transition.\n */\nfunction waitForIdle(snapshot: AppendSink['snapshot'], signal: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (snapshot.get().value === 'idle') {\n resolve();\n return;\n }\n if (snapshot.get().value === 'destroyed') {\n reject(new DOMException('Aborted', 'AbortError'));\n return;\n }\n if (signal.aborted) {\n reject(signal.reason);\n return;\n }\n\n let stop: (() => void) | undefined;\n\n const cleanup = (fn: () => void) => {\n stop?.();\n signal.removeEventListener('abort', onAbort);\n fn();\n };\n\n const onAbort = () => cleanup(() => reject(signal.reason));\n\n stop = effect(() => {\n const value = snapshot.get().value;\n if (value === 'idle') cleanup(resolve);\n else if (value === 'destroyed') cleanup(() => reject(new DOMException('Aborted', 'AbortError')));\n });\n\n signal.addEventListener('abort', onAbort, { once: true });\n });\n}\n\n/** Build the SourceBuffer message a completed frame dispatches. `fetchStep` always precedes `dispatchStep` in append pipelines, so `data` is set by now. */\nfunction toMessage({ op, data, meta }: Frame): IndividualSourceBufferMessage {\n switch (op.type) {\n case 'remove':\n return op;\n case 'append-init':\n return { type: 'append-init', data: data!, meta: op.meta };\n case 'append-segment':\n return { type: 'append-segment', data: data!, meta: meta ?? op.meta };\n }\n}\n\n/**\n * Fetch this op's bytes into the frame. Init segments need the full body\n * (`minChunkSize: Infinity`) before appending; media segments stream so chunks\n * append as they arrive. Awaiting headers eagerly also starts the HTTP\n * connection (and records the fetch in observers like tests).\n */\nexport const fetchStep: LoadStep = async (frame, signal, deps) => {\n const { op } = frame;\n if (op.type === 'remove') return; // fetchStep only appears in append pipelines\n const { fetch } = stepWiring(deps);\n frame.data = await fetch(op, op.type === 'append-init' ? { signal, minChunkSize: Infinity } : { signal });\n};\n\n/** Dispatch the frame's message to the SourceBufferActor and await its return to idle. */\nexport const dispatchStep: LoadStep = async (frame, signal, deps) => {\n const { sourceBufferActor } = stepWiring(deps);\n sourceBufferActor.send(toMessage(frame));\n await waitForIdle(sourceBufferActor.snapshot, signal);\n};\n\n/** Tier 0 default: fetch (for ops that carry bytes) then dispatch. No relocation vocabulary. */\nexport const DEFAULT_MESSAGE_PIPELINES: MessagePipelines = () => ({\n remove: [dispatchStep],\n 'append-init': [fetchStep, dispatchStep],\n 'append-segment': [fetchStep, dispatchStep],\n});\n"],"mappings":";;;;;;;;;AAyGA,SAAS,WAAW,MAAsE;CACxF,OAAO,KAAK;AACd;;;;;;;;;AA8BA,SAAS,YAAY,UAAkC,QAAoC;CACzF,OAAO,IAAI,SAAS,SAAS,WAAW;EACtC,IAAI,SAAS,IAAI,CAAC,CAAC,UAAU,QAAQ;GACnC,QAAQ;GACR;EACF;EACA,IAAI,SAAS,IAAI,CAAC,CAAC,UAAU,aAAa;GACxC,OAAO,IAAI,aAAa,WAAW,YAAY,CAAC;GAChD;EACF;EACA,IAAI,OAAO,SAAS;GAClB,OAAO,OAAO,MAAM;GACpB;EACF;EAEA,IAAI;EAEJ,MAAM,WAAW,OAAmB;GAClC,OAAO;GACP,OAAO,oBAAoB,SAAS,OAAO;GAC3C,GAAG;EACL;EAEA,MAAM,gBAAgB,cAAc,OAAO,OAAO,MAAM,CAAC;EAEzD,OAAO,aAAa;GAClB,MAAM,QAAQ,SAAS,IAAI,CAAC,CAAC;GAC7B,IAAI,UAAU,QAAQ,QAAQ,OAAO;QAChC,IAAI,UAAU,aAAa,cAAc,OAAO,IAAI,aAAa,WAAW,YAAY,CAAC,CAAC;EACjG,CAAC;EAED,OAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;CAC1D,CAAC;AACH;;AAGA,SAAS,UAAU,EAAE,IAAI,MAAM,QAA8C;CAC3E,QAAQ,GAAG,MAAX;EACE,KAAK,UACH,OAAO;EACT,KAAK,eACH,OAAO;GAAE,MAAM;GAAqB;GAAO,MAAM,GAAG;EAAK;EAC3D,KAAK,kBACH,OAAO;GAAE,MAAM;GAAwB;GAAO,MAAM,QAAQ,GAAG;EAAK;CACxE;AACF;;;;;;;AAQA,MAAa,YAAsB,OAAO,OAAO,QAAQ,SAAS;CAChE,MAAM,EAAE,OAAO;CACf,IAAI,GAAG,SAAS,UAAU;CAC1B,MAAM,EAAE,UAAU,WAAW,IAAI;CACjC,MAAM,OAAO,MAAM,MAAM,IAAI,GAAG,SAAS,gBAAgB;EAAE;EAAQ,cAAc;CAAS,IAAI,EAAE,OAAO,CAAC;AAC1G;;AAGA,MAAa,eAAyB,OAAO,OAAO,QAAQ,SAAS;CACnE,MAAM,EAAE,sBAAsB,WAAW,IAAI;CAC7C,kBAAkB,KAAK,UAAU,KAAK,CAAC;CACvC,MAAM,YAAY,kBAAkB,UAAU,MAAM;AACtD;;AAGA,MAAa,mCAAqD;CAChE,QAAQ,CAAC,YAAY;CACrB,eAAe,CAAC,WAAW,YAAY;CACvC,kBAAkB,CAAC,WAAW,YAAY;AAC5C"}
@@ -0,0 +1,37 @@
1
+ //#region src/playback/primitives/text-segment-load-pipeline.ts
2
+ /**
3
+ * Base-step view of the loader's wiring, folded into `config` by
4
+ * `createTextTrackSegmentLoaderActor` so base steps read it from the uniform passthrough
5
+ * — present in both composition and standalone use. `config` is loose (`object`), so
6
+ * assert the shape here (mirrors the v/a loader's `stepWiring`). The sink is the
7
+ * structural {@link CueSink}, not the concrete actor.
8
+ */
9
+ function textStepWiring(deps) {
10
+ return deps.config;
11
+ }
12
+ /** Resolve the op's cues (via the injected host primitive) into the frame. The text analog of `fetchStep`. */
13
+ const resolveCuesStep = async (frame, signal, deps) => {
14
+ const cues = await textStepWiring(deps).resolveSegment(frame.op.segment.url);
15
+ if (signal.aborted) return;
16
+ frame.cues = cues;
17
+ };
18
+ /** Dispatch the frame's cues to the TextTracksActor as `add-cues`. The text analog of `dispatchStep`. */
19
+ const dispatchCuesStep = (frame, _signal, deps) => {
20
+ const { op } = frame;
21
+ textStepWiring(deps).textTracksActor.send({
22
+ type: "add-cues",
23
+ meta: {
24
+ trackId: op.trackId,
25
+ id: op.segment.id,
26
+ startTime: op.segment.startTime,
27
+ duration: op.segment.duration
28
+ },
29
+ cues: frame.cues ?? []
30
+ });
31
+ };
32
+ /** Tier 0 default: resolve then dispatch. No relocation vocabulary. */
33
+ const DEFAULT_TEXT_MESSAGE_PIPELINES = () => [resolveCuesStep, dispatchCuesStep];
34
+ //#endregion
35
+ export { DEFAULT_TEXT_MESSAGE_PIPELINES, dispatchCuesStep, resolveCuesStep, textStepWiring };
36
+
37
+ //# sourceMappingURL=text-segment-load-pipeline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-segment-load-pipeline.js","names":[],"sources":["../../../../src/playback/primitives/text-segment-load-pipeline.ts"],"sourcesContent":["/**\n * The text-track **load pipeline** — the step vocabulary and base steps a text-segment\n * loader runs, the text mirror of `segment-load-pipeline`. Extracted from the loader\n * actor so the \"what\" (the composable steps) is separable from the \"how\" (scheduling).\n * A pipeline is a flat, ordered list of {@link TextLoadStep}s (text has a single op\n * type, so no per-type `Record`); the default is `resolveCues → dispatchCues`, and a\n * composition can insert its own steps (e.g. non-zero-PTS cue rebase) without the\n * loader knowing.\n *\n * The dispatch step reaches its cue sink through the structural {@link CueSink} seam\n * rather than the concrete `TextTracksActor` type — the loader folds the real actor\n * into `config`, and it satisfies the seam by structure — so this module names no\n * actor and stays DOM-free (generic over the cue type `C`).\n */\nimport type { AnySlotMap } from '../../core/composition/create-composition';\nimport type { Cue, Segment } from '../../media/types';\nimport type { AddCuesMessage } from './text-track-messages';\n\n// ============================================================================\n// SINK SEAM\n// ============================================================================\n\n/**\n * The structural view of a cue sink the base steps use — just the `send`\n * {@link dispatchCuesStep} needs. The concrete `TextTracksActor` satisfies it by\n * structure, so the pipeline names no actor.\n */\nexport interface CueSink<C extends Cue = Cue> {\n send(message: AddCuesMessage<C>): void;\n}\n\n// ============================================================================\n// STEP MODEL\n// ============================================================================\n\n/**\n * Resolves a text-track segment URL into the array of cues it contains.\n *\n * \"Resolve\" because the fn covers both network fetch and parse into the\n * domain model. Host-agnostic — the concrete resolver (e.g. the\n * browser's native VTT resolver) is supplied at engine-assembly time,\n * so this stays DOM-free. A pure `url → cues` primitive (the text\n * analog of the v/a loader's `fetchBytes`); composition-awareness lives in\n * the injected {@link TextLoadStep}s, not here.\n */\nexport type TextTrackSegmentResolver<C extends Cue = Cue> = (url: string) => Promise<C[]>;\n\n/** Internal load-task descriptor — one segment fetch + dispatch unit. */\nexport interface TextLoadTask {\n segment: Segment;\n trackId: string;\n}\n\n/**\n * A text load in mid-pipeline — the text analog of the v/a loader's `Frame`.\n * `resolveCuesStep` fills `cues`; `dispatchCuesStep` sends them. `metadata` is\n * opaque header metadata a resolve step may attach for a later step to read\n * (e.g. relocation stashes the `X-TIMESTAMP-MAP` correlation here for its rebase\n * step). Typed `unknown` so the generic loader stays host-agnostic — the step\n * that reads it knows its concrete shape (mirrors `StepDeps.state`).\n */\nexport interface TextFrame<C extends Cue = Cue> {\n readonly op: TextLoadTask;\n cues?: C[];\n metadata?: unknown;\n}\n\n/**\n * One stage of a text message pipeline — the text analog of the v/a loader's\n * `LoadStep`. Mutates the {@link TextFrame} in place and may be async; the runner\n * checks `signal.aborted` before each step and passes the actor's\n * {@link TextStepDeps} on every call, so a stateless step (`resolveCuesStep`) is a\n * plain value and a step that needs composition signals (relocation's cue rebase)\n * reads them from `deps` at call time.\n */\nexport type TextLoadStep<C extends Cue = Cue> = (\n frame: TextFrame<C>,\n signal: AbortSignal,\n deps: TextStepDeps\n) => void | Promise<void>;\n\n/**\n * The uniform passthrough handed to each {@link TextLoadStep} — the composition triple,\n * the text analog of `StepDeps`. `state`/`context` are the composition signal maps;\n * `config` is the threaded config with the loader's wiring folded in (see\n * {@link textStepWiring} + `createTextTrackSegmentLoaderActor`). Typed loose: composition\n * steps read `state`; base steps read the folded wiring off `config`.\n */\nexport interface TextStepDeps {\n state: AnySlotMap;\n context: AnySlotMap;\n config: object;\n}\n\n/**\n * Base-step view of the loader's wiring, folded into `config` by\n * `createTextTrackSegmentLoaderActor` so base steps read it from the uniform passthrough\n * — present in both composition and standalone use. `config` is loose (`object`), so\n * assert the shape here (mirrors the v/a loader's `stepWiring`). The sink is the\n * structural {@link CueSink}, not the concrete actor.\n */\nexport function textStepWiring<C extends Cue>(\n deps: TextStepDeps\n): { textTracksActor: CueSink<C>; resolveSegment: TextTrackSegmentResolver<C> } {\n return deps.config as { textTracksActor: CueSink<C>; resolveSegment: TextTrackSegmentResolver<C> };\n}\n\n/**\n * Builds the ordered step list, called **once per actor** (mirrors the v/a loader's\n * `MessagePipelines`, but text has a single op type so it's a flat array, not a\n * `Record`). The default ({@link DEFAULT_TEXT_MESSAGE_PIPELINES}) is\n * `resolveCues → dispatchCues`; a non-zero-PTS composition returns a list that\n * inserts a cue-rebase step (see `relocatingTextPipelines`), so the loader stays\n * oblivious to relocation.\n */\nexport type TextMessagePipelines<C extends Cue = Cue> = () => TextLoadStep<C>[];\n\n// ============================================================================\n// BASE STEPS\n// ============================================================================\n\n// Base steps are generic over the cue type `C` (generic arrow consts, not\n// `TextLoadStep<Cue>` values): each touches `C` — `frame.cues: C[]` and the\n// `C`-typed `textStepWiring<C>` — so a `Cue`-typed const wouldn't slot into a\n// `VTTCue` pipeline. A generic function assigns to any `TextLoadStep<C>`.\n\n/** Resolve the op's cues (via the injected host primitive) into the frame. The text analog of `fetchStep`. */\nexport const resolveCuesStep = async <C extends Cue>(\n frame: TextFrame<C>,\n signal: AbortSignal,\n deps: TextStepDeps\n): Promise<void> => {\n const cues = await textStepWiring<C>(deps).resolveSegment(frame.op.segment.url);\n if (signal.aborted) return;\n frame.cues = cues;\n};\n\n/** Dispatch the frame's cues to the TextTracksActor as `add-cues`. The text analog of `dispatchStep`. */\nexport const dispatchCuesStep = <C extends Cue>(\n frame: TextFrame<C>,\n _signal: AbortSignal,\n deps: TextStepDeps\n): void => {\n const { op } = frame;\n textStepWiring<C>(deps).textTracksActor.send({\n type: 'add-cues',\n meta: {\n trackId: op.trackId,\n id: op.segment.id,\n startTime: op.segment.startTime,\n duration: op.segment.duration,\n },\n cues: frame.cues ?? [],\n });\n};\n\n/** Tier 0 default: resolve then dispatch. No relocation vocabulary. */\nexport const DEFAULT_TEXT_MESSAGE_PIPELINES = <C extends Cue>(): TextLoadStep<C>[] => [\n resolveCuesStep,\n dispatchCuesStep,\n];\n"],"mappings":";;;;;;;;AAqGA,SAAgB,eACd,MAC8E;CAC9E,OAAO,KAAK;AACd;;AAsBA,MAAa,kBAAkB,OAC7B,OACA,QACA,SACkB;CAClB,MAAM,OAAO,MAAM,eAAkB,IAAI,CAAC,CAAC,eAAe,MAAM,GAAG,QAAQ,GAAG;CAC9E,IAAI,OAAO,SAAS;CACpB,MAAM,OAAO;AACf;;AAGA,MAAa,oBACX,OACA,SACA,SACS;CACT,MAAM,EAAE,OAAO;CACf,eAAkB,IAAI,CAAC,CAAC,gBAAgB,KAAK;EAC3C,MAAM;EACN,MAAM;GACJ,SAAS,GAAG;GACZ,IAAI,GAAG,QAAQ;GACf,WAAW,GAAG,QAAQ;GACtB,UAAU,GAAG,QAAQ;EACvB;EACA,MAAM,MAAM,QAAQ,CAAC;CACvB,CAAC;AACH;;AAGA,MAAa,uCAAyE,CACpF,iBACA,gBACF"}
@@ -1,4 +1,4 @@
1
- //#region src/playback/behaviors/track-types.ts
1
+ //#region src/playback/primitives/track-types.ts
2
2
  /**
3
3
  * **Per-type config bundles for per-type behavior variants.**
4
4
  *
@@ -0,0 +1 @@
1
+ {"version":3,"file":"track-types.js","names":[],"sources":["../../../../src/playback/primitives/track-types.ts"],"sourcesContent":["/**\n * **Per-type config bundles for per-type behavior variants.**\n *\n * Each `*_TYPE_CONFIG` constant names the per-type slot keys that\n * per-type-variant setup-shape helpers consume. Variants reference the\n * shared constant at their `defineBehavior` setup body rather than\n * constructing the config inline, so the per-type identity (which state\n * slot, which context slot, which discriminant) lives in one place per\n * type.\n *\n * Variants spread their composition-time `config` over the defaults so\n * engines can layer composition-supplied additions on top (e.g. a\n * custom fetch closure, a non-default segment resolver). Defaults\n * first, engine config second.\n *\n * Helpers continue to consume their slice via the existing typed-key\n * generics (`<K extends SelectedTrackKey>` etc.); the extra fields on\n * the config object (carrying facets other helpers care about) are fine\n * under structural typing.\n */\n\n/**\n * Type-identity bundle for video-track-typed behaviors.\n *\n * @see setupVideoBufferActors, loadVideoSegments\n */\nexport const VIDEO_TYPE_CONFIG = {\n type: 'video',\n selectedKey: 'selectedVideoTrackId',\n actorKey: 'videoBufferActor',\n loaderKey: 'videoSegmentLoaderActor',\n} as const;\n\n/**\n * Type-identity bundle for audio-track-typed behaviors.\n *\n * @see setupAudioBufferActors, loadAudioSegments\n */\nexport const AUDIO_TYPE_CONFIG = {\n type: 'audio',\n selectedKey: 'selectedAudioTrackId',\n actorKey: 'audioBufferActor',\n loaderKey: 'audioSegmentLoaderActor',\n} as const;\n\n/**\n * Type-identity bundle for text-track-typed behaviors. Text tracks have\n * no `SourceBufferActor` (MSE doesn't apply), so this bundle omits\n * `actorKey`. The `loaderKey` points at the text-track-segment-loader\n * actor (a `MessageActor` with continue-vs-preempt semantics, parallel\n * to v/a's `SegmentLoaderActor`).\n *\n * @see loadTextTrackSegments, setupTextTrackActors\n */\nexport const TEXT_TYPE_CONFIG = {\n type: 'text',\n selectedKey: 'selectedTextTrackId',\n loaderKey: 'textTrackSegmentLoaderActor',\n} as const;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,MAAa,oBAAoB;CAC/B,MAAM;CACN,aAAa;CACb,UAAU;CACV,WAAW;AACb;;;;;;AAOA,MAAa,oBAAoB;CAC/B,MAAM;CACN,aAAa;CACb,UAAU;CACV,WAAW;AACb;;;;;;;;;;AAWA,MAAa,mBAAmB;CAC9B,MAAM;CACN,aAAa;CACb,WAAW;AACb"}
@@ -0,0 +1,3 @@
1
+ import { BackgroundVideoEngineConfig, BackgroundVideoEngineContext, BackgroundVideoEngineSignals, BackgroundVideoEngineState, createBackgroundVideoEngine } from "./playback/engines/background-video/engine.js";
2
+ import { BackgroundVideoMediaAPI, BackgroundVideoMediaElement, BackgroundVideoMediaMixin, BackgroundVideoMediaProps, backgroundVideoMediaDefaultProps } from "./playback/engines/background-video/adapter.js";
3
+ export { type BackgroundVideoEngineConfig, type BackgroundVideoEngineContext, type BackgroundVideoEngineSignals, type BackgroundVideoEngineState, type BackgroundVideoMediaAPI, BackgroundVideoMediaElement, BackgroundVideoMediaMixin, type BackgroundVideoMediaProps, backgroundVideoMediaDefaultProps, createBackgroundVideoEngine };
@@ -0,0 +1,3 @@
1
+ import { createBackgroundVideoEngine } from "./playback/engines/background-video/engine.js";
2
+ import { BackgroundVideoMediaElement, BackgroundVideoMediaMixin, backgroundVideoMediaDefaultProps } from "./playback/engines/background-video/adapter.js";
3
+ export { BackgroundVideoMediaElement, BackgroundVideoMediaMixin, backgroundVideoMediaDefaultProps, createBackgroundVideoEngine };
@@ -1,5 +1,4 @@
1
1
  import { Machine, MachineSnapshot } from "../machine.js";
2
-
3
2
  //#region src/core/actors/actor.d.ts
4
3
  /**
5
4
  * Complete actor snapshot: finite state + non-finite context.
@@ -1 +1 @@
1
- {"version":3,"file":"actor.d.ts","names":[],"sources":["../../../../src/core/actors/actor.ts"],"mappings":";;;;;;;UAeiB,aAAA,uDAAoE,eAAA,CAAgB,KAAA;EACnG,OAAA,EAAS,OAAA;AAAA;;UAIM,WAAA,uDACP,OAAA,CAAQ,aAAA,CAAc,KAAA,EAAO,OAAA;AADvC;;;;;;AAAA,UASiB,aAAA;EAAgC,IAAA;AAAA;EAC/C,IAAA,CAAK,OAAA,EAAS,OAAA;EACd,OAAA;AAAA"}
1
+ {"version":3,"file":"actor.d.ts","names":[],"sources":["../../../../src/core/actors/actor.ts"],"mappings":";;;;;;UAeiB,cAAc,sBAAsB,gCAAgC,gBAAgB;EACnG,SAAS;;;UAIM,YAAY,sBAAsB,gCACzC,QAAQ,cAAc,OAAO;;;;;;;UAQtB,cAAc;EAAkB;;EAC/C,KAAK,SAAS;EACd"}
@@ -1,6 +1,5 @@
1
1
  import { TaskLike } from "../tasks/task.js";
2
2
  import { SignalActor } from "./actor.js";
3
-
4
3
  //#region src/core/actors/create-machine-actor.d.ts
5
4
  /**
6
5
  * Minimal interface for any runner that can be used with createMachineActor.
@@ -17,7 +16,8 @@ interface RunnerLike {
17
16
  * definition includes a runner factory.
18
17
  */
19
18
  type HandlerContext<UserState extends string, Context extends object, RunnerFactory extends (() => RunnerLike) | undefined> = {
20
- transition: (to: UserState) => void; /** Context snapshot captured at dispatch time. Stale after any `setContext` call. */
19
+ transition: (to: UserState) => void;
20
+ /** Context snapshot captured at dispatch time. Stale after any `setContext` call. */
21
21
  context: Context;
22
22
  /**
23
23
  * Live untracked read of the current context. Use in async task closures that
@@ -42,10 +42,11 @@ type ActorStateDefinition<UserState extends string, Context extends object, Mess
42
42
  * re-registering after each `runner.schedule()` call so that
43
43
  * `abortAll()` + reschedule correctly supersedes stale callbacks.
44
44
  */
45
- onSettled?: UserState; /** Message handlers active in this state. Messages with no handler are silently dropped. */
45
+ onSettled?: UserState;
46
+ /** Message handlers active in this state. Messages with no handler are silently dropped. */
46
47
  on?: { [M in Message as M['type']]?: (message: Extract<Message, {
47
48
  type: M['type'];
48
- }>, ctx: HandlerContext<UserState, Context, RunnerFactory>) => void };
49
+ }>, ctx: HandlerContext<UserState, Context, RunnerFactory>) => void; };
49
50
  };
50
51
  /**
51
52
  * Full actor definition passed to `createMachineActor`.
@@ -63,8 +64,10 @@ type ActorDefinition<UserState extends string, Context extends object, Message e
63
64
  * @example
64
65
  * runner: () => new SerialRunner()
65
66
  */
66
- runner?: RunnerFactory; /** Initial state. */
67
- initial: UserState; /** Initial context. */
67
+ runner?: RunnerFactory;
68
+ /** Initial state. */
69
+ initial: UserState;
70
+ /** Initial context. */
68
71
  context: Context;
69
72
  /**
70
73
  * Per-state definitions. States with no definition silently drop all messages.
@@ -1 +1 @@
1
- {"version":3,"file":"create-machine-actor.d.ts","names":[],"sources":["../../../../src/core/actors/create-machine-actor.ts"],"mappings":";;;;;;AAYA;UAAiB,UAAA;EACf,QAAA,8BAAsC,IAAA,EAAM,QAAA,CAAS,KAAA,EAAO,GAAA,IAAO,OAAA,CAAQ,KAAA;EAC3E,QAAA;EACA,OAAA;EACA,WAAA,CAAY,QAAA;AAAA;;;;;;KAYF,cAAA,gFAGmB,UAAA;EAE7B,UAAA,GAAa,EAAA,EAAI,SAAA,WApBoC;EAsBrD,OAAA,EAAS,OAAA;EAtB6B;;;;;;EA6BtC,UAAA,QAAkB,OAAA;EAClB,UAAA,GAAa,IAAA,EAAM,OAAA;AAAA,KAChB,aAAA;EAAwC,MAAA,EAAQ,CAAA;AAAA;;;;KAKzC,oBAAA;EAGQ,IAAA;AAAA,gCACW,UAAA;EAT1B;;;;;;EAiBH,SAAA,GAAY,SAAA,EA9BiB;EAgC7B,EAAA,WACQ,OAAA,IAAW,CAAA,aACf,OAAA,EAAS,OAAA,CAAQ,OAAA;IAAW,IAAA,EAAM,CAAA;EAAA,IAClC,GAAA,EAAK,cAAA,CAAe,SAAA,EAAW,OAAA,EAAS,aAAA;AAAA;;;;;;;KAWlC,eAAA;EAGQ,IAAA;AAAA,gCACW,UAAA;EArCuB;AAKtD;;;;;;EAyCE,MAAA,GAAS,aAAA,EAzBY;EA2BrB,OAAA,EAAS,SAAA,EA3BI;EA6Bb,OAAA,EAAS,OAAA;EA5B0B;;;;EAiCnC,MAAA,EAAQ,OAAA,CAAQ,MAAA,CAAO,SAAA,EAAW,oBAAA,CAAqB,SAAA,EAAW,OAAA,EAAS,OAAA,EAAS,aAAA;AAAA;;UAQrE,YAAA;EAA6E,IAAA;AAAA,WACpF,WAAA,CAAY,KAAA,EAAO,OAAA;EAC3B,IAAA,CAAK,OAAA,EAAS,OAAA;AAAA;;;;;;;;;;;;;;;;;;AAhChB;;;;;;;;;;;;;;;;;;;;;;iBA8EgB,kBAAA;EAGI,IAAA;AAAA,gCACW,UAAA,0BAAA,CAE7B,GAAA,EAAK,eAAA,CAAgB,SAAA,EAAW,OAAA,EAAS,OAAA,EAAS,aAAA,IACjD,YAAA,CAAa,SAAA,gBAAyB,OAAA,EAAS,OAAA"}
1
+ {"version":3,"file":"create-machine-actor.d.ts","names":[],"sources":["../../../../src/core/actors/create-machine-actor.ts"],"mappings":";;;;;;UAYiB;EACf,SAAS,cAAc,eAAe,MAAM,SAAS,OAAO,OAAO,QAAQ;EAC3E;EACA;EACA,YAAY;;;;;;;KAYF,eACV,0BACA,wBACA,6BAA6B;EAE7B,aAAa,IAAI;;EAEjB,SAAS;;;;;;;EAOT,kBAAkB;EAClB,aAAa,MAAM;KAChB,mCAAkC;EAAM,QAAQ;;;;;KAKzC,qBACV,0BACA,wBACA;EAAkB;GAClB,6BAA6B;;;;;;;EAQ7B,YAAY;;EAEZ,QACG,KAAK,WAAW,cACf,SAAS,QAAQ;IAAW,MAAM;MAClC,KAAK,eAAe,WAAW,SAAS;;;;;;;;KAWlC,gBACV,0BACA,wBACA;EAAkB;GAClB,6BAA6B;;;;;;;;EAS7B,SAAS;;EAET,SAAS;;EAET,SAAS;;;;;EAKT,QAAQ,QAAQ,OAAO,WAAW,qBAAqB,WAAW,SAAS,SAAS;;;UAQrE,aAAa,sBAAsB,wBAAwB;EAAkB;WACpF,YAAY,OAAO;EAC3B,KAAK,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA8CA,mBACd,0BACA,wBACA;EAAkB;GAClB,6BAA6B,qCAE7B,KAAK,gBAAgB,WAAW,SAAS,SAAS,iBACjD,aAAa,yBAAyB,SAAS"}
@@ -1 +1 @@
1
- {"version":3,"file":"create-machine-actor.js","names":[],"sources":["../../../../src/core/actors/create-machine-actor.ts"],"sourcesContent":["import { createMachineCore } from '../machine';\nimport { untrack, update } from '../signals/primitives';\nimport type { TaskLike } from '../tasks/task';\nimport type { ActorSnapshot, SignalActor } from './actor';\n\n// =============================================================================\n// Runner interfaces\n// =============================================================================\n\n/**\n * Minimal interface for any runner that can be used with createMachineActor.\n */\nexport interface RunnerLike {\n schedule<Value = void, Err = unknown>(task: TaskLike<Value, Err>): Promise<Value>;\n abortAll(): void;\n destroy(): void;\n whenSettled(callback: () => void): void;\n}\n\n// =============================================================================\n// Definition types\n// =============================================================================\n\n/**\n * Context passed to message handlers.\n * `runner` is present and typed as the exact runner instance only when the\n * definition includes a runner factory.\n */\nexport type HandlerContext<\n UserState extends string,\n Context extends object,\n RunnerFactory extends (() => RunnerLike) | undefined,\n> = {\n transition: (to: UserState) => void;\n /** Context snapshot captured at dispatch time. Stale after any `setContext` call. */\n context: Context;\n /**\n * Live untracked read of the current context. Use in async task closures that\n * execute after the handler returns — e.g. `getCtx: getContext` passed to tasks\n * scheduled on the runner, so each task reads the context committed by the\n * previous task rather than the stale snapshot from dispatch time.\n */\n getContext: () => Context;\n setContext: (next: Context) => void;\n} & (RunnerFactory extends () => infer R ? { runner: R } : object);\n\n/**\n * Definition for a single user-defined state.\n */\nexport type ActorStateDefinition<\n UserState extends string,\n Context extends object,\n Message extends { type: string },\n RunnerFactory extends (() => RunnerLike) | undefined,\n> = {\n /**\n * When the actor's runner settles while in this state, automatically\n * transition to this state. The framework owns the generation-token logic —\n * re-registering after each `runner.schedule()` call so that\n * `abortAll()` + reschedule correctly supersedes stale callbacks.\n */\n onSettled?: UserState;\n /** Message handlers active in this state. Messages with no handler are silently dropped. */\n on?: {\n [M in Message as M['type']]?: (\n message: Extract<Message, { type: M['type'] }>,\n ctx: HandlerContext<UserState, Context, RunnerFactory>\n ) => void;\n };\n};\n\n/**\n * Full actor definition passed to `createMachineActor`.\n *\n * `UserState` is the set of domain-meaningful states. `'destroyed'` is always\n * added by the framework as the implicit terminal state — do not include it here.\n */\nexport type ActorDefinition<\n UserState extends string,\n Context extends object,\n Message extends { type: string },\n RunnerFactory extends (() => RunnerLike) | undefined = undefined,\n> = {\n /**\n * Runner factory — called once at `createMachineActor()` time.\n * The runner lives for the full actor lifetime and is destroyed with it.\n *\n * @example\n * runner: () => new SerialRunner()\n */\n runner?: RunnerFactory;\n /** Initial state. */\n initial: UserState;\n /** Initial context. */\n context: Context;\n /**\n * Per-state definitions. States with no definition silently drop all messages.\n * All user-defined states must appear as keys in the `UserState` union.\n */\n states: Partial<Record<UserState, ActorStateDefinition<UserState, Context, Message, RunnerFactory>>>;\n};\n\n// =============================================================================\n// Live actor interface\n// =============================================================================\n\n/** Live actor instance returned by `createMachineActor`. */\nexport interface MessageActor<State extends string, Context extends object, Message extends { type: string }>\n extends SignalActor<State, Context> {\n send(message: Message): void;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/**\n * Creates a message-driven actor from a declarative definition.\n *\n * The actor owns a reactive snapshot signal (state + context), an optional\n * runner, and dispatches incoming messages to per-state handlers. `'destroyed'`\n * is always the implicit terminal state — `destroy()` transitions there\n * unconditionally and all subsequent `send()` calls are no-ops.\n *\n * When a state declares `onSettled`, the framework calls `runner.whenSettled()`\n * after the handler returns. The runner owns the generation-token logic — if\n * new tasks are scheduled before the current batch settles, the callback is\n * automatically superseded.\n *\n * @example\n * const actor = createMachineActor({\n * runner: () => new SerialRunner(),\n * initial: 'idle',\n * context: {},\n * states: {\n * idle: {\n * on: {\n * load: (msg, { transition, runner }) => {\n * segments.forEach(s => runner.schedule(new Task(...)));\n * transition('loading');\n * }\n * }\n * },\n * loading: {\n * onSettled: 'idle',\n * on: {\n * load: (msg, { runner }) => {\n * runner.abortAll();\n * segments.forEach(s => runner.schedule(new Task(...)));\n * }\n * }\n * }\n * }\n * });\n */\nexport function createMachineActor<\n UserState extends string,\n Context extends object,\n Message extends { type: string },\n RunnerFactory extends (() => RunnerLike) | undefined = undefined,\n>(\n def: ActorDefinition<UserState, Context, Message, RunnerFactory>\n): MessageActor<UserState | 'destroyed', Context, Message> {\n type FullState = UserState | 'destroyed';\n\n const runner = def.runner?.() as RunnerLike | undefined;\n const { snapshotSignal, getState, transition } = createMachineCore<FullState, ActorSnapshot<FullState, Context>>({\n value: def.initial as FullState,\n context: def.context,\n });\n\n const getContext = (): Context => untrack(() => snapshotSignal.get().context);\n\n const setContext = (context: Context): void => {\n update(snapshotSignal, { context });\n };\n\n return {\n get snapshot() {\n return snapshotSignal;\n },\n\n send(message: Message): void {\n const state = getState();\n if (state === 'destroyed') return;\n const stateDef = def.states[state as UserState];\n const handler = stateDef?.on?.[message.type as keyof typeof stateDef.on] as\n | ((msg: Message, ctx: HandlerContext<UserState, Context, RunnerFactory>) => void)\n | undefined;\n if (!handler) return;\n handler(message, {\n context: getContext(),\n getContext,\n transition: (to: UserState) => transition(to as FullState),\n setContext,\n ...(runner ? { runner } : {}),\n } as HandlerContext<UserState, Context, RunnerFactory>);\n // Register onSettled after the handler so we read the post-transition state.\n const newState = getState();\n if (newState !== 'destroyed') {\n const newStateDef = def.states[newState as UserState];\n if (newStateDef?.onSettled && runner) {\n const targetState = newStateDef.onSettled as FullState;\n runner.whenSettled(() => {\n if (getState() !== newState) return;\n transition(targetState);\n });\n }\n }\n },\n\n destroy(): void {\n if (getState() === 'destroyed') return;\n runner?.destroy();\n transition('destroyed');\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2JA,SAAgB,mBAMd,KACyD;CAGzD,MAAM,SAAS,IAAI,UAAU;CAC7B,MAAM,EAAE,gBAAgB,UAAU,eAAe,kBAAgE;EAC/G,OAAO,IAAI;EACX,SAAS,IAAI;EACd,CAAC;CAEF,MAAM,mBAA4B,cAAc,eAAe,KAAK,CAAC,QAAQ;CAE7E,MAAM,cAAc,YAA2B;AAC7C,SAAO,gBAAgB,EAAE,SAAS,CAAC;;AAGrC,QAAO;EACL,IAAI,WAAW;AACb,UAAO;;EAGT,KAAK,SAAwB;GAC3B,MAAM,QAAQ,UAAU;AACxB,OAAI,UAAU,YAAa;GAE3B,MAAM,UADW,IAAI,OAAO,QACF,KAAK,QAAQ;AAGvC,OAAI,CAAC,QAAS;AACd,WAAQ,SAAS;IACf,SAAS,YAAY;IACrB;IACA,aAAa,OAAkB,WAAW,GAAgB;IAC1D;IACA,GAAI,SAAS,EAAE,QAAQ,GAAG,EAAE;IAC7B,CAAsD;GAEvD,MAAM,WAAW,UAAU;AAC3B,OAAI,aAAa,aAAa;IAC5B,MAAM,cAAc,IAAI,OAAO;AAC/B,QAAI,aAAa,aAAa,QAAQ;KACpC,MAAM,cAAc,YAAY;AAChC,YAAO,kBAAkB;AACvB,UAAI,UAAU,KAAK,SAAU;AAC7B,iBAAW,YAAY;OACvB;;;;EAKR,UAAgB;AACd,OAAI,UAAU,KAAK,YAAa;AAChC,WAAQ,SAAS;AACjB,cAAW,YAAY;;EAE1B"}
1
+ {"version":3,"file":"create-machine-actor.js","names":[],"sources":["../../../../src/core/actors/create-machine-actor.ts"],"sourcesContent":["import { createMachineCore } from '../machine';\nimport { untrack, update } from '../signals/primitives';\nimport type { TaskLike } from '../tasks/task';\nimport type { ActorSnapshot, SignalActor } from './actor';\n\n// =============================================================================\n// Runner interfaces\n// =============================================================================\n\n/**\n * Minimal interface for any runner that can be used with createMachineActor.\n */\nexport interface RunnerLike {\n schedule<Value = void, Err = unknown>(task: TaskLike<Value, Err>): Promise<Value>;\n abortAll(): void;\n destroy(): void;\n whenSettled(callback: () => void): void;\n}\n\n// =============================================================================\n// Definition types\n// =============================================================================\n\n/**\n * Context passed to message handlers.\n * `runner` is present and typed as the exact runner instance only when the\n * definition includes a runner factory.\n */\nexport type HandlerContext<\n UserState extends string,\n Context extends object,\n RunnerFactory extends (() => RunnerLike) | undefined,\n> = {\n transition: (to: UserState) => void;\n /** Context snapshot captured at dispatch time. Stale after any `setContext` call. */\n context: Context;\n /**\n * Live untracked read of the current context. Use in async task closures that\n * execute after the handler returns — e.g. `getCtx: getContext` passed to tasks\n * scheduled on the runner, so each task reads the context committed by the\n * previous task rather than the stale snapshot from dispatch time.\n */\n getContext: () => Context;\n setContext: (next: Context) => void;\n} & (RunnerFactory extends () => infer R ? { runner: R } : object);\n\n/**\n * Definition for a single user-defined state.\n */\nexport type ActorStateDefinition<\n UserState extends string,\n Context extends object,\n Message extends { type: string },\n RunnerFactory extends (() => RunnerLike) | undefined,\n> = {\n /**\n * When the actor's runner settles while in this state, automatically\n * transition to this state. The framework owns the generation-token logic —\n * re-registering after each `runner.schedule()` call so that\n * `abortAll()` + reschedule correctly supersedes stale callbacks.\n */\n onSettled?: UserState;\n /** Message handlers active in this state. Messages with no handler are silently dropped. */\n on?: {\n [M in Message as M['type']]?: (\n message: Extract<Message, { type: M['type'] }>,\n ctx: HandlerContext<UserState, Context, RunnerFactory>\n ) => void;\n };\n};\n\n/**\n * Full actor definition passed to `createMachineActor`.\n *\n * `UserState` is the set of domain-meaningful states. `'destroyed'` is always\n * added by the framework as the implicit terminal state — do not include it here.\n */\nexport type ActorDefinition<\n UserState extends string,\n Context extends object,\n Message extends { type: string },\n RunnerFactory extends (() => RunnerLike) | undefined = undefined,\n> = {\n /**\n * Runner factory — called once at `createMachineActor()` time.\n * The runner lives for the full actor lifetime and is destroyed with it.\n *\n * @example\n * runner: () => new SerialRunner()\n */\n runner?: RunnerFactory;\n /** Initial state. */\n initial: UserState;\n /** Initial context. */\n context: Context;\n /**\n * Per-state definitions. States with no definition silently drop all messages.\n * All user-defined states must appear as keys in the `UserState` union.\n */\n states: Partial<Record<UserState, ActorStateDefinition<UserState, Context, Message, RunnerFactory>>>;\n};\n\n// =============================================================================\n// Live actor interface\n// =============================================================================\n\n/** Live actor instance returned by `createMachineActor`. */\nexport interface MessageActor<State extends string, Context extends object, Message extends { type: string }>\n extends SignalActor<State, Context> {\n send(message: Message): void;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/**\n * Creates a message-driven actor from a declarative definition.\n *\n * The actor owns a reactive snapshot signal (state + context), an optional\n * runner, and dispatches incoming messages to per-state handlers. `'destroyed'`\n * is always the implicit terminal state — `destroy()` transitions there\n * unconditionally and all subsequent `send()` calls are no-ops.\n *\n * When a state declares `onSettled`, the framework calls `runner.whenSettled()`\n * after the handler returns. The runner owns the generation-token logic — if\n * new tasks are scheduled before the current batch settles, the callback is\n * automatically superseded.\n *\n * @example\n * const actor = createMachineActor({\n * runner: () => new SerialRunner(),\n * initial: 'idle',\n * context: {},\n * states: {\n * idle: {\n * on: {\n * load: (msg, { transition, runner }) => {\n * segments.forEach(s => runner.schedule(new Task(...)));\n * transition('loading');\n * }\n * }\n * },\n * loading: {\n * onSettled: 'idle',\n * on: {\n * load: (msg, { runner }) => {\n * runner.abortAll();\n * segments.forEach(s => runner.schedule(new Task(...)));\n * }\n * }\n * }\n * }\n * });\n */\nexport function createMachineActor<\n UserState extends string,\n Context extends object,\n Message extends { type: string },\n RunnerFactory extends (() => RunnerLike) | undefined = undefined,\n>(\n def: ActorDefinition<UserState, Context, Message, RunnerFactory>\n): MessageActor<UserState | 'destroyed', Context, Message> {\n type FullState = UserState | 'destroyed';\n\n const runner = def.runner?.() as RunnerLike | undefined;\n const { snapshotSignal, getState, transition } = createMachineCore<FullState, ActorSnapshot<FullState, Context>>({\n value: def.initial as FullState,\n context: def.context,\n });\n\n const getContext = (): Context => untrack(() => snapshotSignal.get().context);\n\n const setContext = (context: Context): void => {\n update(snapshotSignal, { context });\n };\n\n return {\n get snapshot() {\n return snapshotSignal;\n },\n\n send(message: Message): void {\n const state = getState();\n if (state === 'destroyed') return;\n const stateDef = def.states[state as UserState];\n const handler = stateDef?.on?.[message.type as keyof typeof stateDef.on] as\n | ((msg: Message, ctx: HandlerContext<UserState, Context, RunnerFactory>) => void)\n | undefined;\n if (!handler) return;\n handler(message, {\n context: getContext(),\n getContext,\n transition: (to: UserState) => transition(to as FullState),\n setContext,\n ...(runner ? { runner } : {}),\n } as HandlerContext<UserState, Context, RunnerFactory>);\n // Register onSettled after the handler so we read the post-transition state.\n const newState = getState();\n if (newState !== 'destroyed') {\n const newStateDef = def.states[newState as UserState];\n if (newStateDef?.onSettled && runner) {\n const targetState = newStateDef.onSettled as FullState;\n runner.whenSettled(() => {\n if (getState() !== newState) return;\n transition(targetState);\n });\n }\n }\n },\n\n destroy(): void {\n if (getState() === 'destroyed') return;\n runner?.destroy();\n transition('destroyed');\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2JA,SAAgB,mBAMd,KACyD;CAGzD,MAAM,SAAS,IAAI,SAAS;CAC5B,MAAM,EAAE,gBAAgB,UAAU,eAAe,kBAAgE;EAC/G,OAAO,IAAI;EACX,SAAS,IAAI;CACf,CAAC;CAED,MAAM,mBAA4B,cAAc,eAAe,IAAI,CAAC,CAAC,OAAO;CAE5E,MAAM,cAAc,YAA2B;EAC7C,OAAO,gBAAgB,EAAE,QAAQ,CAAC;CACpC;CAEA,OAAO;EACL,IAAI,WAAW;GACb,OAAO;EACT;EAEA,KAAK,SAAwB;GAC3B,MAAM,QAAQ,SAAS;GACvB,IAAI,UAAU,aAAa;GAE3B,MAAM,UADW,IAAI,OAAO,MACJ,EAAE,KAAK,QAAQ;GAGvC,IAAI,CAAC,SAAS;GACd,QAAQ,SAAS;IACf,SAAS,WAAW;IACpB;IACA,aAAa,OAAkB,WAAW,EAAe;IACzD;IACA,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;GAC7B,CAAsD;GAEtD,MAAM,WAAW,SAAS;GAC1B,IAAI,aAAa,aAAa;IAC5B,MAAM,cAAc,IAAI,OAAO;IAC/B,IAAI,aAAa,aAAa,QAAQ;KACpC,MAAM,cAAc,YAAY;KAChC,OAAO,kBAAkB;MACvB,IAAI,SAAS,MAAM,UAAU;MAC7B,WAAW,WAAW;KACxB,CAAC;IACH;GACF;EACF;EAEA,UAAgB;GACd,IAAI,SAAS,MAAM,aAAa;GAChC,QAAQ,QAAQ;GAChB,WAAW,WAAW;EACxB;CACF;AACF"}
@@ -1,6 +1,5 @@
1
1
  import { ActorSnapshot } from "./actor.js";
2
2
  import { Machine } from "../machine.js";
3
-
4
3
  //#region src/core/actors/create-transition-actor.d.ts
5
4
  /**
6
5
  * A reducer-shaped actor: `(context, message) => context`.
@@ -1 +1 @@
1
- {"version":3,"file":"create-transition-actor.d.ts","names":[],"sources":["../../../../src/core/actors/create-transition-actor.ts"],"mappings":";;;;;;AAoBA;;;;;;;;;UAAiB,eAAA;EAA0D,IAAA;AAAA,WACjE,OAAA,CAAQ,aAAA,yBAAsC,OAAA;EACtD,IAAA,CAAK,OAAA,EAAS,OAAA;AAAA;;;;;;;AAuBhB;;;;;;;;;;iBAAgB,qBAAA;EAAgE,IAAA;AAAA,EAAA,CAC9E,cAAA,EAAgB,OAAA,EAChB,OAAA,GAAU,OAAA,EAAS,OAAA,EAAS,OAAA,EAAS,OAAA,KAAY,OAAA,GAChD,eAAA,CAAgB,OAAA,EAAS,OAAA"}
1
+ {"version":3,"file":"create-transition-actor.d.ts","names":[],"sources":["../../../../src/core/actors/create-transition-actor.ts"],"mappings":";;;;;;;;;;;;;;UAoBiB,gBAAgB,wBAAwB;EAAkB;WACjE,QAAQ,sCAAsC;EACtD,KAAK,SAAS;;;;;;;;;;;;;;;;;;iBAuBA,sBAAsB,wBAAwB;EAAkB;GAC9E,gBAAgB,SAChB,UAAU,SAAS,SAAS,SAAS,YAAY,UAChD,gBAAgB,SAAS"}
@@ -1 +1 @@
1
- {"version":3,"file":"create-transition-actor.js","names":[],"sources":["../../../../src/core/actors/create-transition-actor.ts"],"sourcesContent":["import type { Machine } from '../machine';\nimport { createMachineCore } from '../machine';\nimport { untrack, update } from '../signals/primitives';\nimport type { ActorSnapshot } from './actor';\n\n// =============================================================================\n// Definition types\n// =============================================================================\n\n/**\n * A reducer-shaped actor: `(context, message) => context`.\n *\n * No finite states — the snapshot carries `value: 'active' | 'destroyed'`\n * as a universal lifecycle marker rather than domain state. The interesting\n * state is entirely in the context, which is reactive via `snapshot`.\n *\n * Use this when the actor has context that needs to be reactive but no\n * meaningful state machine (e.g., a message-driven model with DOM side\n * effects). For actors that need per-state behavior, use `createMachineActor`.\n */\nexport interface TransitionActor<Context extends object, Message extends { type: string }>\n extends Machine<ActorSnapshot<'active' | 'destroyed', Context>> {\n send(message: Message): void;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/**\n * Creates a reducer-shaped actor from an initial context and a reducer function.\n *\n * The reducer receives the current context and a message and returns the next\n * context. Returning the same reference (by identity) skips the signal update —\n * so early-returning `context` unchanged is both the no-op and the optimization.\n *\n * Side effects (e.g. DOM mutations) may be performed inside the reducer.\n * They run synchronously before the signal is updated.\n *\n * @example\n * const actor = createTransitionActor(\n * { count: 0 },\n * (context, message: { type: 'increment' }) => ({ count: context.count + 1 })\n * );\n */\nexport function createTransitionActor<Context extends object, Message extends { type: string }>(\n initialContext: Context,\n reducer: (context: Context, message: Message) => Context\n): TransitionActor<Context, Message> {\n const { snapshotSignal, getState, transition } = createMachineCore<\n 'active' | 'destroyed',\n ActorSnapshot<'active' | 'destroyed', Context>\n >({ value: 'active', context: initialContext });\n\n const getContext = (): Context => untrack(() => snapshotSignal.get().context);\n const setContext = (context: Context): void => update(snapshotSignal, { context });\n\n return {\n get snapshot() {\n return snapshotSignal;\n },\n\n send(message: Message): void {\n if (getState() === 'destroyed') return;\n const context = getContext();\n const newContext = reducer(context, message);\n if (newContext !== context) setContext(newContext);\n },\n\n destroy(): void {\n if (getState() === 'destroyed') return;\n transition('destroyed');\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA6CA,SAAgB,sBACd,gBACA,SACmC;CACnC,MAAM,EAAE,gBAAgB,UAAU,eAAe,kBAG/C;EAAE,OAAO;EAAU,SAAS;EAAgB,CAAC;CAE/C,MAAM,mBAA4B,cAAc,eAAe,KAAK,CAAC,QAAQ;CAC7E,MAAM,cAAc,YAA2B,OAAO,gBAAgB,EAAE,SAAS,CAAC;AAElF,QAAO;EACL,IAAI,WAAW;AACb,UAAO;;EAGT,KAAK,SAAwB;AAC3B,OAAI,UAAU,KAAK,YAAa;GAChC,MAAM,UAAU,YAAY;GAC5B,MAAM,aAAa,QAAQ,SAAS,QAAQ;AAC5C,OAAI,eAAe,QAAS,YAAW,WAAW;;EAGpD,UAAgB;AACd,OAAI,UAAU,KAAK,YAAa;AAChC,cAAW,YAAY;;EAE1B"}
1
+ {"version":3,"file":"create-transition-actor.js","names":[],"sources":["../../../../src/core/actors/create-transition-actor.ts"],"sourcesContent":["import type { Machine } from '../machine';\nimport { createMachineCore } from '../machine';\nimport { untrack, update } from '../signals/primitives';\nimport type { ActorSnapshot } from './actor';\n\n// =============================================================================\n// Definition types\n// =============================================================================\n\n/**\n * A reducer-shaped actor: `(context, message) => context`.\n *\n * No finite states — the snapshot carries `value: 'active' | 'destroyed'`\n * as a universal lifecycle marker rather than domain state. The interesting\n * state is entirely in the context, which is reactive via `snapshot`.\n *\n * Use this when the actor has context that needs to be reactive but no\n * meaningful state machine (e.g., a message-driven model with DOM side\n * effects). For actors that need per-state behavior, use `createMachineActor`.\n */\nexport interface TransitionActor<Context extends object, Message extends { type: string }>\n extends Machine<ActorSnapshot<'active' | 'destroyed', Context>> {\n send(message: Message): void;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/**\n * Creates a reducer-shaped actor from an initial context and a reducer function.\n *\n * The reducer receives the current context and a message and returns the next\n * context. Returning the same reference (by identity) skips the signal update —\n * so early-returning `context` unchanged is both the no-op and the optimization.\n *\n * Side effects (e.g. DOM mutations) may be performed inside the reducer.\n * They run synchronously before the signal is updated.\n *\n * @example\n * const actor = createTransitionActor(\n * { count: 0 },\n * (context, message: { type: 'increment' }) => ({ count: context.count + 1 })\n * );\n */\nexport function createTransitionActor<Context extends object, Message extends { type: string }>(\n initialContext: Context,\n reducer: (context: Context, message: Message) => Context\n): TransitionActor<Context, Message> {\n const { snapshotSignal, getState, transition } = createMachineCore<\n 'active' | 'destroyed',\n ActorSnapshot<'active' | 'destroyed', Context>\n >({ value: 'active', context: initialContext });\n\n const getContext = (): Context => untrack(() => snapshotSignal.get().context);\n const setContext = (context: Context): void => update(snapshotSignal, { context });\n\n return {\n get snapshot() {\n return snapshotSignal;\n },\n\n send(message: Message): void {\n if (getState() === 'destroyed') return;\n const context = getContext();\n const newContext = reducer(context, message);\n if (newContext !== context) setContext(newContext);\n },\n\n destroy(): void {\n if (getState() === 'destroyed') return;\n transition('destroyed');\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA6CA,SAAgB,sBACd,gBACA,SACmC;CACnC,MAAM,EAAE,gBAAgB,UAAU,eAAe,kBAG/C;EAAE,OAAO;EAAU,SAAS;CAAe,CAAC;CAE9C,MAAM,mBAA4B,cAAc,eAAe,IAAI,CAAC,CAAC,OAAO;CAC5E,MAAM,cAAc,YAA2B,OAAO,gBAAgB,EAAE,QAAQ,CAAC;CAEjF,OAAO;EACL,IAAI,WAAW;GACb,OAAO;EACT;EAEA,KAAK,SAAwB;GAC3B,IAAI,SAAS,MAAM,aAAa;GAChC,MAAM,UAAU,WAAW;GAC3B,MAAM,aAAa,QAAQ,SAAS,OAAO;GAC3C,IAAI,eAAe,SAAS,WAAW,UAAU;EACnD;EAEA,UAAgB;GACd,IAAI,SAAS,MAAM,aAAa;GAChC,WAAW,WAAW;EACxB;CACF;AACF"}