@3dsource/angular-unreal-module 0.0.199 → 0.0.200-dev.1

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.
@@ -1,4 +1,4 @@
1
- "use strict";(()=>{"use strict";const p=/metabox-configurator\/modular\/([^/?#]+)/i,m="features",g="3dsource:region-ping:v2",I="__3dsourceRegionPing",w="assets/config.json",y="assets/features.json",T="metaboxModularConfigurator",k=new Set(["insecure-endpoint","no-endpoint","no-config"]),R="42e01f";function S(a){return a.replace("http://","ws://").replace("https://","wss://")}const l="[stream-prefetch]",r=(...a)=>console.log(l,...a),c=(...a)=>console.warn(l,...a),d=(...a)=>console.error(l,...a);class O{constructor(){this.readOptions(),this.ids={clientId:"",viewId:"",correlationId:"",streamRequestId:""},this.configuratorId="",this.region="",this.newOrchestration=!1,this.environmentId="",this.controlWs=null,this.handedOverControlWs=null,this.orchestrationRetryId=null,this.instanceReadyTimeoutId=null,this.streamReadyTimeoutId=null,this.pc=null,this.dc=null,this.stream=null,this.ws=null,this.remoteDescriptionSet=!1,this.iceBuffer=[],this.wsTimeoutId=null,this.settled=!1,this.settledCbs=[]}readOptions(){const e=document.currentScript||(()=>{const n=document.getElementsByTagName("script");return n[n.length-1]})(),t=e&&e.dataset||{};this.configUrl=t.configUrl||w,this.featuresUrl=t.featuresUrl||y,this.config={ws_timeout:this.numberOr(t.wsTimeout,6e3),poll_timeout:this.numberOr(t.pollTimeout,4e3)};const i=new URLSearchParams(window.location.search);this.forceTURN=i.has("ForceTURN"),this.forceMaxBundle=i.has("ForceMaxBundle"),this.forceMonoAudio=i.has("forceMono")}run(){if(!(typeof window=="undefined"||!("WebSocket"in window)||!("RTCPeerConnection"in window))){if(window.__stream){r("skip \u2014 this page already has a stream owner",{owner:window.__stream.owner||"prefetch"});return}if(this.configuratorId=this.resolveConfiguratorId(),!this.configuratorId){r("skip \u2014 not a metabox-configurator/modular route");return}this.ids.correlationId=this.configuratorId,r("start",{configuratorId:this.configuratorId}),this.publishParked("connecting"),this.connect().catch(e=>this.fail("exception",e))}}connect(){return this.mark("prefetch_start"),Promise.all([this.fetchConfig(),this.fetchFeatures()]).then(([e,t])=>this.withConfig(e,t))}withConfig(e,t){if(this.newOrchestration=this.isNewOrchestrationEnabled(t),r("orchestration mode",{newOrchestration:this.newOrchestration}),!e){this.fail("no-config");return}return this.fetchConfigurator(e).then(i=>this.withConfigurator(e,i))}withConfigurator(e,t){if(!t){this.fail("no-configurator");return}if(this.deriveIdentity(t),!this.ids.clientId||!this.ids.viewId){this.fail("no-identity");return}r("identity from configurator cache",{clientId:this.ids.clientId,viewId:this.ids.viewId});const i=this.chooseEndpoint(e,t);if(!i||!i.url){this.fail("no-endpoint");return}if(!this.isAllowedEndpoint(i.url)){r("skip \u2014 endpoint is not encrypted",i.url),this.fail("insecure-endpoint");return}return r("endpoint chosen",i),i.type==="orchestration"?this.runOrchestration(i.url):this.runLegacy(i.url)}resolveConfiguratorId(){const e=p.exec(window.location.href);if(!e)return"";try{return decodeURIComponent(e[1])}catch{return e[1]}}fetchConfig(){return this.fetchJson(this.configUrl)}fetchFeatures(){return this.fetchJson(this.featuresUrl)}fetchJson(e){const t=new URL(e,window.location.origin).href;return this.fetchWithTimeout(t,{credentials:"omit"},this.config.poll_timeout).then(i=>i.ok?i.json().catch(()=>null):null).catch(()=>null)}isNewOrchestrationEnabled(e){const t=e&&typeof e=="object"?e:{},i=this.readFeatureOverrides();return("newOrchestration"in i?i.newOrchestration:t.newOrchestration)===!0}readFeatureOverrides(){try{const e=localStorage.getItem(m);if(!e)return{};const t=JSON.parse(e);return t&&typeof t=="object"?t:{}}catch{return{}}}fetchConfigurator(e){const t=e&&e.cdn;if(!t)return c("app config has no `cdn` \u2014 cannot read the configurator identity"),Promise.resolve(null);const i=`${String(t).replace(/\/+$/,"")}/cache/configurator/${encodeURIComponent(this.configuratorId)}.json`;return this.fetchWithTimeout(i,{credentials:"omit"},this.config.poll_timeout).then(n=>n.ok?n.json().catch(()=>null):null).then(n=>{const s=n&&n.data&&n.data.modularConfigurator;return s||(c("configurator cache response has no modularConfigurator",i),null)}).catch(()=>null)}deriveIdentity(e){const t=e.client&&e.client.id;this.ids.clientId=t?`${t}_configurator`:"",this.ids.viewId=e.id||""}chooseEndpoint(e,t){const i=this.newOrchestration?e.newSignalling:e.signalling;if(!i)return null;this.environmentId=i.environmentId||"";const n=this.versionKey(t),s=i.versions,o=n&&s&&s[n]||"";return o||c("no endpoint for Unreal version key",n||"(none)"),{type:this.newOrchestration?"orchestration":"polling",url:o}}versionKey(e){const i=(Array.isArray(e.components)?e.components.slice().sort((o,h)=>(o&&o.position)-(h&&h.position)):[])[0],n=i&&i.product&&i.product.model?i.product.model.unrealVersion:"";if(!n)return"";const s=String(n).split(".");return s.length<2?"":[s[0],s[1],"*"].join(".")}isAllowedEndpoint(e){if(typeof e!="string")return!1;let t;try{t=new URL(e,window.location.href)}catch{return!1}return t.protocol==="https:"||t.protocol==="wss:"?!0:t.protocol!=="http:"&&t.protocol!=="ws:"?!1:t.hostname==="localhost"||t.hostname==="127.0.0.1"||t.hostname==="[::1]"}resolveRegion(){const e=this.readRegion()||this.pickFromRegionPing()||{region:"",pingsMs:[],source:"none"};this.region=e.region,this.parked.region=e,r("region:",e.region||"(none, geo-IP)",e.source)}pickFromRegionPing(){try{const e=window[I];return e&&e.version===1?e.pick():null}catch{return null}}readRegion(){try{const e=localStorage.getItem(g);if(!e)return null;const t=JSON.parse(e);return!t||typeof t.region_code!="string"||typeof t.ts!="number"||Date.now()-t.ts>=2592e6?null:{region:t.region_code,pingsMs:Array.isArray(t.result)?t.result.filter(i=>typeof i=="number"):[],source:"cache",probes:t.probes||void 0,cacheAgeMs:Date.now()-t.ts}}catch{return null}}adaptUrlToRegion(e){return this.region&&e.indexOf(":region")===-1?e.replace(/signallingserver/gi,`signallingserver/region:${this.region}`):e}runLegacy(e){this.resolveRegion();const t=this.adaptUrlToRegion(e);return this.pollOrchestration(t).then(i=>{if(!i){this.fail("no-ws-url");return}return this.parked.machineId=i,this.parked.pollingUrl=t,this.parked.kind="polling",r("opening socket",i),this.openSocket(i)})}pollOrchestration(e){const t=`${e}${this.ids.clientId}/${this.ids.viewId}`;return this.fetchWithTimeout(t,{credentials:"omit"},this.config.poll_timeout).then(i=>i.ok?i.json().catch(()=>({})):null).then(i=>!i||!i.signallingServer?null:(this.ids.streamRequestId=i.streamRequestId||"",this.parked.instanceName=i.signallingServer,S(`${window.location.protocol}//${i.signallingServer}`))).catch(()=>null)}runOrchestration(e){if(!this.environmentId)return this.fail("no-environment-id"),Promise.resolve();this.resolveRegion();const t=this.adaptUrlToRegion(e);this.parked.pollingUrl=t;const i=new WebSocket(t);return this.controlWs=i,this.wsTimeoutId=setTimeout(()=>{i.readyState!==WebSocket.OPEN&&this.fail("control-ws-timeout")},this.config.ws_timeout),i.onopen=()=>{clearTimeout(this.wsTimeoutId),this.wsTimeoutId=null,this.mark("control_ws_open"),r("control socket open \u2014 requesting stream"),this.instanceReadyTimeoutId=setTimeout(()=>this.fail("instance-ready-timeout"),18e4),this.sendRequestStream()},i.onmessage=n=>this.guard("control-message-failed",()=>this.onControlMessage(n)),i.onerror=n=>{d("control WS error",n),this.fail("control-ws-error")},i.onclose=()=>{!this.settled&&!this.ws&&(c("control WS closed before instanceReady"),this.fail("control-ws-closed"))},Promise.resolve()}sendRequestStream(){this.sendControl({type:"requestStream",correlationId:this.ids.correlationId,payload:{...this.region&&{region:this.region},environmentId:this.environmentId,context:{id:T,data:{configuratorId:this.configuratorId}}}})}sendControl(e){const t=this.controlWs;if(t&&t.readyState===WebSocket.OPEN)try{t.send(JSON.stringify(e))}catch{}}onControlMessage(e){var i;let t;try{const n=typeof e.data=="string"?e.data:null;if(n===null)return;t=JSON.parse(n)}catch{return}switch(r("control \u2190",t.type),t.type){case"instanceReserved":this.parked.eta=(i=t.payload&&t.payload.eta)!=null?i:null;break;case"instanceReady":this.onInstanceReady(t.payload||{});break;case"error":this.onOrchestrationError(t.payload||{});break;default:break}}onInstanceReady(e){if(this.ws)return;const t=e.originInstanceUri;if(!t){this.fail("no-instance-uri");return}clearTimeout(this.orchestrationRetryId),this.orchestrationRetryId=null,clearTimeout(this.instanceReadyTimeoutId),this.instanceReadyTimeoutId=null,this.ids.streamRequestId=e.streamRequestId||"",this.parked.machineId=t,this.parked.kind="socket",this.parked.instanceName=e.originInstanceId||null,this.mark("instance_ready"),r("instance ready \u2014 opening Cirrus socket",t),this.handOverControlWs(),this.openSocket(t)}onOrchestrationError(e){const t=e.code;if(t===4001||t===4002){c("orchestration refused the request, retrying",t),clearTimeout(this.orchestrationRetryId),this.orchestrationRetryId=setTimeout(()=>this.sendRequestStream(),this.config.poll_timeout);return}t!==4003&&this.fail("orchestration-error",e.message||String(t))}handOverControlWs(){const e=this.controlWs;e&&(this.controlWs=null,this.handedOverControlWs=e,this.parked.controlWs=e,e.onmessage=null,e.onerror=null,e.onclose=null,r("control socket handed over \u2014 left open for orchestration"))}closeControlWs(){const e=[this.controlWs,this.handedOverControlWs];this.controlWs=null,this.handedOverControlWs=null;for(const t of e)if(t){t.onopen=null,t.onmessage=null,t.onerror=null,t.onclose=null;try{t.close()}catch{}}}openSocket(e){const t=this.ids.streamRequestId,i=t?e+(e.indexOf("?")===-1?"?":"&")+"streamRequestId="+encodeURIComponent(t):e,n=new WebSocket(i);n.binaryType="arraybuffer",this.ws=n,this.parked.ws=n,this.wsTimeoutId=setTimeout(()=>{n.readyState!==WebSocket.OPEN&&this.fail("ws-timeout")},this.config.ws_timeout),n.onopen=()=>{clearTimeout(this.wsTimeoutId),this.wsTimeoutId=null,this.streamReadyTimeoutId=setTimeout(()=>this.fail("stream-ready-timeout"),6e4),this.mark("ws_open"),r("WS open \u2014 waiting for config",{region:this.region,streamRequestId:t||"(none)"})},n.onmessage=s=>this.guard("message-failed",()=>this.onWsMessage(s)),n.onerror=s=>{d("WS error",s),this.fail("ws-error")},n.onclose=s=>{!this.settled&&this.parked.state!=="ready"&&(c("WS closed before ready",{code:s&&s.code}),this.fail("ws-closed"))}}send(e){const t=typeof e=="string"?e:JSON.stringify(e);if(this.ws&&this.ws.readyState===WebSocket.OPEN)try{this.ws.send(t)}catch{}}onWsMessage(e){let t;try{const i=typeof e.data=="string"?e.data:null;if(i===null)return;t=JSON.parse(i)}catch{return}switch(r("WS \u2190",t.type),t.type){case"ssInfo":this.parked.ssData=t.data;break;case"config":this.onConfig(t);break;case"offer":this.onOffer(t);break;case"answer":this.onAnswer(t);break;case"iceCandidate":this.onIceCandidate(t.candidate);break;default:break}}onConfig(e){if(this.pc)return;const t=Object.assign({},e.peerConnectionOptions);this.forceTURN&&(t.iceTransportPolicy="relay"),t.bundlePolicy=this.forceMaxBundle?"max-bundle":"balanced",t.iceCandidatePoolSize=1,this.parked.config=t;const i=new RTCPeerConnection(t);this.pc=i,this.parked.pc=i,r("PeerConnection created from config"),i.onicecandidate=n=>{n.candidate&&this.send({type:"iceCandidate",candidate:n.candidate})},i.ondatachannel=n=>this.wireDataChannel(n.channel),i.ontrack=n=>{n.track&&n.track.kind==="video"&&n.streams[0]&&(r("video track received"),this.stream=n.streams[0],this.parked.stream=this.stream,this.maybeReady())}}onOffer(e){return this.pc?(this.remoteDescriptionSet=!1,this.iceBuffer.length=0,this.pc.setRemoteDescription(e).then(()=>(this.remoteDescriptionSet=!0,this.flushIce(),this.pc.createAnswer())).then(t=>(t.sdp=this.mungeSDP(t.sdp||""),this.pc.setLocalDescription(t).then(()=>{r("WS \u2192 answer sent"),this.send(t)}))).catch(t=>this.fail("offer-failed",t))):Promise.resolve()}onAnswer(e){return this.pc?this.pc.setRemoteDescription(e).then(()=>{this.remoteDescriptionSet=!0,this.flushIce()}).catch(t=>this.fail("answer-failed",t)):Promise.resolve()}onIceCandidate(e){e&&(this.forceTURN&&String(e.candidate).indexOf("relay")<0||(this.remoteDescriptionSet&&this.pc?this.pc.addIceCandidate(e).catch(()=>{}):this.iceBuffer.push(e)))}flushIce(){for(const e of this.iceBuffer)this.pc.addIceCandidate(e).catch(()=>{});this.iceBuffer.length=0}wireDataChannel(e){e.binaryType="arraybuffer",this.dc=e,this.parked.dc=e,e.onmessage=t=>{this.parked.dcBuffer.push(t.data)},e.onopen=()=>{r("DataChannel open"),this.mark("dc_open"),this.maybeReady()},e.onclose=()=>{!this.settled&&this.parked.state!=="ready"&&(c("DataChannel closed before ready"),this.fail("dc-closed"))},e.readyState==="open"&&this.maybeReady()}maybeReady(){this.settled||!this.dc||this.dc.readyState!=="open"||this.stream&&(this.settled=!0,clearTimeout(this.streamReadyTimeoutId),this.streamReadyTimeoutId=null,this.mark("ready"),this.parked.state="ready",r("READY \u2014 parked on window.__stream",this.parked.timings),this.fireSettled())}mungeSDP(e){let t=this.prioritizeH264Profile(e,R);t=t.replace(/(a=fmtp:\d+ .*level-asymmetry-allowed=.*)\r\n/gm,`$1;x-google-start-bitrate=10000;x-google-max-bitrate=100000\r
1
+ "use strict";(()=>{"use strict";const g=/metabox-configurator\/modular\/([^/?#]+)/i,I="features",T="3dsource:region-ping:v2",w="__3dsourceRegionPing",y="assets/config.json",k="assets/features.json",R="metaboxModularConfigurator",S=new Set(["insecure-endpoint","no-endpoint","no-config"]),_="42e01f";function O(s){return s.replace("http://","ws://").replace("https://","wss://")}function f(s){try{const e=new URL(s,window.location.href);return`${e.host}${e.pathname}`}catch{return""}}function E(s){const e=[s.signallingServer?"ready":"waiting"],t=s.info&&s.info.percent;typeof t=="number"&&e.push(`${t}%`);const i=s.mm_message||s.error;return i&&e.push(String(i)),e.join(" ")}function p(s){const e=s&&s.code,t=s&&s.reason;return[e,t].filter(i=>i!==void 0&&i!=="").join(" ")}function C(s){return s&&s.message||String(s)}const l="[stream-prefetch]",o=(...s)=>console.log(l,...s),c=(...s)=>console.warn(l,...s),d=(...s)=>console.error(l,...s);class v{constructor(){this.readOptions(),this.ids={clientId:"",viewId:"",correlationId:"",streamRequestId:""},this.configuratorId="",this.region="",this.newOrchestration=!1,this.environmentId="",this.controlWs=null,this.handedOverControlWs=null,this.orchestrationRetryId=null,this.instanceReadyTimeoutId=null,this.streamReadyTimeoutId=null,this.pc=null,this.dc=null,this.stream=null,this.ws=null,this.remoteDescriptionSet=!1,this.iceBuffer=[],this.wsTimeoutId=null,this.linkTimeoutId=null,this.dcTimeoutId=null,this.iceCandidatesIn=0,this.settled=!1,this.settledCbs=[]}readOptions(){const e=document.currentScript||(()=>{const n=document.getElementsByTagName("script");return n[n.length-1]})(),t=e&&e.dataset||{};this.configUrl=t.configUrl||y,this.featuresUrl=t.featuresUrl||k,this.config={ws_timeout:this.numberOr(t.wsTimeout,6e3),poll_timeout:this.numberOr(t.pollTimeout,4e3),dc_timeout:this.numberOr(t.dcTimeout,8e3)};const i=new URLSearchParams(window.location.search);this.forceTURN=i.has("ForceTURN"),this.forceMaxBundle=i.has("ForceMaxBundle"),this.forceMonoAudio=i.has("forceMono")}run(){if(!(typeof window=="undefined"||!("WebSocket"in window)||!("RTCPeerConnection"in window))){if(window.__stream){o("skip \u2014 this page already has a stream owner",{owner:window.__stream.owner||"prefetch"});return}if(this.configuratorId=this.resolveConfiguratorId(),!this.configuratorId){o("skip \u2014 not a metabox-configurator/modular route");return}this.ids.correlationId=this.configuratorId,o("start",{configuratorId:this.configuratorId}),this.publishParked("connecting"),this.connect().catch(e=>this.fail("exception",e))}}connect(){return this.mark("prefetch_start"),Promise.all([this.fetchConfig(),this.fetchFeatures()]).then(([e,t])=>this.withConfig(e,t))}withConfig(e,t){if(this.newOrchestration=this.isNewOrchestrationEnabled(t),o("orchestration mode",{newOrchestration:this.newOrchestration}),!e){this.fail("no-config");return}return this.fetchConfigurator(e).then(i=>this.withConfigurator(e,i))}withConfigurator(e,t){if(!t){this.fail("no-configurator");return}if(this.deriveIdentity(t),!this.ids.clientId||!this.ids.viewId){this.fail("no-identity");return}o("identity from configurator cache",{clientId:this.ids.clientId,viewId:this.ids.viewId});const i=this.chooseEndpoint(e,t);if(!i||!i.url){this.fail("no-endpoint");return}if(!this.isAllowedEndpoint(i.url)){o("skip \u2014 endpoint is not encrypted",i.url),this.fail("insecure-endpoint");return}return o("endpoint chosen",i),this.note("endpoint",`${i.type} ${f(i.url)}`),i.type==="orchestration"?this.runOrchestration(i.url):this.runLegacy(i.url)}resolveConfiguratorId(){const e=g.exec(window.location.href);if(!e)return"";try{return decodeURIComponent(e[1])}catch{return e[1]}}fetchConfig(){return this.fetchJson(this.configUrl,"config")}fetchFeatures(){return this.fetchJson(this.featuresUrl,"features")}fetchJson(e,t){const i=new URL(e,window.location.origin).href,n=performance.now();return this.fetchWithTimeout(i,{credentials:"omit"},this.config.poll_timeout).then(r=>(this.noteHttp(t,r,n),r.ok?r.json().catch(()=>null):null)).catch(r=>(this.noteHttpError(t,r,n),null))}isNewOrchestrationEnabled(e){const t=e&&typeof e=="object"?e:{},i=this.readFeatureOverrides();return("newOrchestration"in i?i.newOrchestration:t.newOrchestration)===!0}readFeatureOverrides(){try{const e=localStorage.getItem(I);if(!e)return{};const t=JSON.parse(e);return t&&typeof t=="object"?t:{}}catch{return{}}}fetchConfigurator(e){const t=e&&e.cdn;if(!t)return c("app config has no `cdn` \u2014 cannot read the configurator identity"),Promise.resolve(null);const i=`${String(t).replace(/\/+$/,"")}/cache/configurator/${encodeURIComponent(this.configuratorId)}.json`,n=performance.now();return this.fetchWithTimeout(i,{credentials:"omit"},this.config.poll_timeout).then(r=>(this.noteHttp("configurator",r,n),r.ok?r.json().catch(()=>null):null)).then(r=>{const a=r&&r.data&&r.data.modularConfigurator;return a||(c("configurator cache response has no modularConfigurator",i),null)}).catch(r=>(this.noteHttpError("configurator",r,n),null))}deriveIdentity(e){const t=e.client&&e.client.id;this.ids.clientId=t?`${t}_configurator`:"",this.ids.viewId=e.id||""}chooseEndpoint(e,t){const i=this.newOrchestration?e.newSignalling:e.signalling;if(!i)return null;this.environmentId=i.environmentId||"";const n=this.versionKey(t),r=i.versions,a=n&&r&&r[n]||"";return a||c("no endpoint for Unreal version key",n||"(none)"),{type:this.newOrchestration?"orchestration":"polling",url:a}}versionKey(e){const i=(Array.isArray(e.components)?e.components.slice().sort((a,h)=>(a&&a.position)-(h&&h.position)):[])[0],n=i&&i.product&&i.product.model?i.product.model.unrealVersion:"";if(!n)return"";const r=String(n).split(".");return r.length<2?"":[r[0],r[1],"*"].join(".")}isAllowedEndpoint(e){if(typeof e!="string")return!1;let t;try{t=new URL(e,window.location.href)}catch{return!1}return t.protocol==="https:"||t.protocol==="wss:"?!0:t.protocol!=="http:"&&t.protocol!=="ws:"?!1:t.hostname==="localhost"||t.hostname==="127.0.0.1"||t.hostname==="[::1]"}resolveRegion(){const e=this.readRegion()||this.pickFromRegionPing()||{region:"",pingsMs:[],source:"none"};this.region=e.region,this.parked.region=e,o("region:",e.region||"(none, geo-IP)",e.source),this.note("region",`${e.region||"none"} ${e.source}`)}pickFromRegionPing(){try{const e=window[w];return e&&e.version===1?e.pick():null}catch{return null}}readRegion(){try{const e=localStorage.getItem(T);if(!e)return null;const t=JSON.parse(e);return!t||typeof t.region_code!="string"||typeof t.ts!="number"||Date.now()-t.ts>=2592e6?null:{region:t.region_code,pingsMs:Array.isArray(t.result)?t.result.filter(i=>typeof i=="number"):[],source:"cache",probes:t.probes||void 0,cacheAgeMs:Date.now()-t.ts}}catch{return null}}adaptUrlToRegion(e){return this.region&&e.indexOf(":region")===-1?e.replace(/signallingserver/gi,`signallingserver/region:${this.region}`):e}runLegacy(e){this.resolveRegion();const t=this.adaptUrlToRegion(e);return this.pollOrchestration(t).then(i=>{if(!i){this.fail("no-ws-url");return}return this.parked.machineId=i,this.parked.pollingUrl=t,this.parked.kind="polling",o("opening socket",i),this.openSocket(i)})}pollOrchestration(e){const t=`${e}${this.ids.clientId}/${this.ids.viewId}`,i=performance.now();return this.fetchWithTimeout(t,{credentials:"omit"},this.config.poll_timeout).then(n=>(this.noteHttp("poll",n,i),n.ok?n.json().catch(()=>({})):null)).then(n=>(n&&this.note("poll-answer",E(n)),!n||!n.signallingServer?null:(this.ids.streamRequestId=n.streamRequestId||"",this.parked.instanceName=n.signallingServer,this.note("instance",n.signallingServer),O(`${window.location.protocol}//${n.signallingServer}`)))).catch(n=>(this.noteHttpError("poll",n,i),null))}runOrchestration(e){if(!this.environmentId)return this.fail("no-environment-id"),Promise.resolve();this.resolveRegion();const t=this.adaptUrlToRegion(e);this.parked.pollingUrl=t;const i=new WebSocket(t);return this.controlWs=i,this.wsTimeoutId=setTimeout(()=>{i.readyState!==WebSocket.OPEN&&this.fail("control-ws-timeout")},this.config.ws_timeout),i.onopen=()=>{clearTimeout(this.wsTimeoutId),this.wsTimeoutId=null,this.mark("control_ws_open"),o("control socket open \u2014 requesting stream"),this.instanceReadyTimeoutId=setTimeout(()=>this.fail("instance-ready-timeout"),18e4),this.sendRequestStream()},i.onmessage=n=>this.guard("control-message-failed",()=>this.onControlMessage(n)),i.onerror=n=>{d("control WS error",n),this.note("control-ws-error"),this.fail("control-ws-error")},i.onclose=n=>{this.note("control-ws-close",p(n)),!this.settled&&!this.ws&&(c("control WS closed before instanceReady"),this.fail("control-ws-closed"))},Promise.resolve()}sendRequestStream(){this.sendControl({type:"requestStream",correlationId:this.ids.correlationId,payload:{...this.region&&{region:this.region},environmentId:this.environmentId,context:{id:R,data:{configuratorId:this.configuratorId}}}})}sendControl(e){const t=this.controlWs;if(t&&t.readyState===WebSocket.OPEN)try{t.send(JSON.stringify(e))}catch{}}onControlMessage(e){var i;let t;try{const n=typeof e.data=="string"?e.data:null;if(n===null)return;t=JSON.parse(n)}catch{return}switch(o("control \u2190",t.type),t.type!=="instanceReady"&&t.type!=="error"&&this.note(`control:${t.type}`),t.type){case"instanceReserved":this.parked.eta=(i=t.payload&&t.payload.eta)!=null?i:null,this.note("instance-reserved",`eta=${this.parked.eta}`);break;case"instanceReady":this.onInstanceReady(t.payload||{});break;case"error":this.onOrchestrationError(t.payload||{});break;default:break}}onInstanceReady(e){if(this.ws)return;const t=e.originInstanceUri;if(!t){this.fail("no-instance-uri");return}clearTimeout(this.orchestrationRetryId),this.orchestrationRetryId=null,clearTimeout(this.instanceReadyTimeoutId),this.instanceReadyTimeoutId=null,this.ids.streamRequestId=e.streamRequestId||"",this.parked.machineId=t,this.parked.kind="socket",this.parked.instanceName=e.originInstanceId||null,this.note("instance",`${this.parked.instanceName||""} ${f(t)}`),this.mark("instance_ready"),o("instance ready \u2014 opening Cirrus socket",t),this.handOverControlWs(),this.openSocket(t)}onOrchestrationError(e){const t=e.code;if(this.note("orchestration-error",`${t} ${e.message||""}`),t===4001||t===4002){c("orchestration refused the request, retrying",t),clearTimeout(this.orchestrationRetryId),this.orchestrationRetryId=setTimeout(()=>this.sendRequestStream(),this.config.poll_timeout);return}t!==4003&&this.fail("orchestration-error",e.message||String(t))}handOverControlWs(){const e=this.controlWs;e&&(this.controlWs=null,this.handedOverControlWs=e,this.parked.controlWs=e,e.onmessage=null,e.onerror=null,e.onclose=null,o("control socket handed over \u2014 left open for orchestration"))}closeControlWs(){const e=[this.controlWs,this.handedOverControlWs];this.controlWs=null,this.handedOverControlWs=null;for(const t of e)if(t){t.onopen=null,t.onmessage=null,t.onerror=null,t.onclose=null;try{t.close()}catch{}}}openSocket(e){const t=this.ids.streamRequestId,i=t?e+(e.indexOf("?")===-1?"?":"&")+"streamRequestId="+encodeURIComponent(t):e,n=new WebSocket(i);n.binaryType="arraybuffer",this.ws=n,this.parked.ws=n,this.wsTimeoutId=setTimeout(()=>{n.readyState!==WebSocket.OPEN&&this.fail("ws-timeout")},this.config.ws_timeout),n.onopen=()=>{clearTimeout(this.wsTimeoutId),this.wsTimeoutId=null,this.streamReadyTimeoutId=setTimeout(()=>this.fail("stream-ready-timeout"),6e4),this.linkTimeoutId=setTimeout(()=>this.fail("no-streamer"),2e4),this.mark("ws_open"),o("WS open \u2014 waiting for config",{region:this.region,streamRequestId:t||"(none)"})},n.onmessage=r=>this.guard("message-failed",()=>this.onWsMessage(r)),n.onerror=r=>{d("WS error",r),this.note("cirrus-ws-error"),this.fail("ws-error")},n.onclose=r=>{if(this.note("cirrus-close",p(r)),!this.settled&&this.parked.state!=="ready"){c("WS closed before ready",{code:r&&r.code}),this.fail("ws-closed");return}this.markDead("ws-closed")}}send(e){const t=typeof e=="string"?e:JSON.stringify(e);if(this.ws&&this.ws.readyState===WebSocket.OPEN)try{this.ws.send(t)}catch{}}onWsMessage(e){let t;try{const i=typeof e.data=="string"?e.data:null;if(i===null)return;t=JSON.parse(i)}catch{return}switch(o("WS \u2190",t.type),this.noteCirrusMessage(t),t.type){case"ssInfo":this.parked.ssData=t.data,this.onStreamerLinked();break;case"config":this.onConfig(t);break;case"offer":this.clearLinkTimeout(),this.onOffer(t);break;case"answer":this.onAnswer(t);break;case"iceCandidate":this.onIceCandidate(t.candidate);break;default:break}}onConfig(e){if(this.pc)return;const t=Object.assign({},e.peerConnectionOptions);this.forceTURN&&(t.iceTransportPolicy="relay"),t.bundlePolicy=this.forceMaxBundle?"max-bundle":"balanced",t.iceCandidatePoolSize=1,this.parked.config=t;const i=new RTCPeerConnection(t);this.pc=i,this.parked.pc=i,o("PeerConnection created from config"),i.onicecandidate=n=>{n.candidate&&this.send({type:"iceCandidate",candidate:n.candidate})},i.onconnectionstatechange=()=>this.onPcState(i),i.oniceconnectionstatechange=()=>this.note("ice-state",i.iceConnectionState),i.ondatachannel=n=>this.wireDataChannel(n.channel),i.ontrack=n=>{n.track&&n.track.kind==="video"&&n.streams[0]&&(o("video track received"),this.stream=n.streams[0],this.parked.stream=this.stream,this.maybeReady())}}onOffer(e){return this.pc?(this.remoteDescriptionSet=!1,this.iceBuffer.length=0,this.pc.setRemoteDescription(e).then(()=>(this.remoteDescriptionSet=!0,this.flushIce(),this.pc.createAnswer())).then(t=>(t.sdp=this.mungeSDP(t.sdp||""),this.pc.setLocalDescription(t).then(()=>{o("WS \u2192 answer sent"),this.send(t)}))).catch(t=>this.fail("offer-failed",t))):Promise.resolve()}onAnswer(e){return this.pc?this.pc.setRemoteDescription(e).then(()=>{this.remoteDescriptionSet=!0,this.flushIce()}).catch(t=>this.fail("answer-failed",t)):Promise.resolve()}onIceCandidate(e){e&&(this.forceTURN&&String(e.candidate).indexOf("relay")<0||(this.remoteDescriptionSet&&this.pc?this.pc.addIceCandidate(e).catch(()=>{}):this.iceBuffer.push(e)))}flushIce(){for(const e of this.iceBuffer)this.pc.addIceCandidate(e).catch(()=>{});this.iceBuffer.length=0}wireDataChannel(e){e.binaryType="arraybuffer",this.dc=e,this.parked.dc=e,e.onmessage=t=>{this.parked.dcBuffer.push(t.data)},e.onopen=()=>{o("DataChannel open"),clearTimeout(this.dcTimeoutId),this.dcTimeoutId=null,this.mark("dc_open"),this.maybeReady()},e.onclose=()=>{if(this.note("dc-close"),!this.settled&&this.parked.state!=="ready"){c("DataChannel closed before ready"),this.fail("dc-closed");return}this.markDead("dc-closed")},e.readyState==="open"&&this.maybeReady()}maybeReady(){this.settled||!this.dc||this.dc.readyState!=="open"||this.stream&&(this.settled=!0,clearTimeout(this.streamReadyTimeoutId),this.streamReadyTimeoutId=null,this.clearLinkTimeout(),clearTimeout(this.dcTimeoutId),this.dcTimeoutId=null,this.mark("ready"),this.parked.state="ready",o("READY \u2014 parked on window.__stream",this.parked.timings),this.fireSettled())}mungeSDP(e){let t=this.prioritizeH264Profile(e,_);t=t.replace(/(a=fmtp:\d+ .*level-asymmetry-allowed=.*)\r\n/gm,`$1;x-google-start-bitrate=10000;x-google-max-bitrate=100000\r
2
2
  `);let i="maxaveragebitrate=510000;";return i+=this.forceMonoAudio?"stereo=0;":"stereo=1;",i+="useinbandfec=1",t=t.replace("useinbandfec=1",i),t}prioritizeH264Profile(e,t){const i=e.split(`\r
3
- `),n=[];for(const s of i)if(s.startsWith("a=fmtp:")&&s.toLowerCase().includes(`profile-level-id=${t}`)){const o=s.split(":")[1]&&s.split(":")[1].split(" ")[0];o&&n.push(o)}return n.length===0?e:i.map(s=>{if(!s.startsWith("m=video"))return s;const o=s.split(" "),h=o.slice(0,3),f=o.slice(3),_=[...n.filter(u=>f.includes(u)),...f.filter(u=>!n.includes(u))];return[...h,..._].join(" ")}).join(`\r
4
- `)}publishParked(e){const t=this;this.parked={state:e,pc:null,dc:null,stream:null,ws:null,ssData:void 0,machineId:void 0,pollingUrl:void 0,kind:void 0,instanceName:void 0,eta:void 0,region:void 0,controlWs:void 0,config:void 0,ids:this.ids,dcBuffer:[],timings:{},adopt(){return t.clearTimers(),this.state="adopted",this},onSettled(i){if(typeof i=="function"){if(this.state!=="connecting"){const n=this.state;Promise.resolve().then(()=>i(n));return}t.settledCbs.push(i)}}},window.__stream=this.parked}fireSettled(){const e=this.settledCbs;this.settledCbs=[];for(const t of e)try{t(this.parked.state)}catch{}}mark(e){this.parked&&(this.parked.timings[e]=performance.now())}clearTimers(){this.wsTimeoutId&&(clearTimeout(this.wsTimeoutId),this.wsTimeoutId=null),clearTimeout(this.orchestrationRetryId),this.orchestrationRetryId=null,clearTimeout(this.instanceReadyTimeoutId),this.instanceReadyTimeoutId=null,clearTimeout(this.streamReadyTimeoutId),this.streamReadyTimeoutId=null}fail(e,t){if(!(this.parked&&(this.parked.state==="adopted"||this.parked.state==="failed"))){(k.has(e)?r:d)("FAILED:",e,t||""),this.settled=!0,this.clearTimers(),this.closeControlWs();try{this.dc&&this.dc.close()}catch{}try{this.pc&&this.pc.close()}catch{}try{this.ws&&this.ws.close()}catch{}this.parked&&(this.parked.state="failed",this.parked.failReason=e,t&&(this.parked.failError=String(t&&t.message||t)),this.fireSettled())}}guard(e,t){try{t()}catch(i){this.fail(e,i)}}fetchWithTimeout(e,t,i){const n=new AbortController;t.signal=n.signal;const s=setTimeout(()=>n.abort(),i);return fetch(e,t).finally(()=>clearTimeout(s))}numberOr(e,t){const i=Number(e);return e!=null&&e!==""&&!Number.isNaN(i)?i:t}}new O().run()})();
3
+ `),n=[];for(const r of i)if(r.startsWith("a=fmtp:")&&r.toLowerCase().includes(`profile-level-id=${t}`)){const a=r.split(":")[1]&&r.split(":")[1].split(" ")[0];a&&n.push(a)}return n.length===0?e:i.map(r=>{if(!r.startsWith("m=video"))return r;const a=r.split(" "),h=a.slice(0,3),m=a.slice(3),A=[...n.filter(u=>m.includes(u)),...m.filter(u=>!n.includes(u))];return[...h,...A].join(" ")}).join(`\r
4
+ `)}publishParked(e){const t=this;this.parked={state:e,pc:null,dc:null,stream:null,ws:null,ssData:void 0,machineId:void 0,pollingUrl:void 0,kind:void 0,instanceName:void 0,eta:void 0,region:void 0,controlWs:void 0,config:void 0,ids:this.ids,dcBuffer:[],timings:{},trail:[],deadReason:void 0,adopt(){return t.clearTimers(),this.state="adopted",this},onSettled(i){if(typeof i=="function"){if(this.state!=="connecting"){const n=this.state;Promise.resolve().then(()=>i(n));return}t.settledCbs.push(i)}}},window.__stream=this.parked}fireSettled(){const e=this.settledCbs;this.settledCbs=[];for(const t of e)try{t(this.parked.state)}catch{}}mark(e){this.parked&&(this.parked.timings[e]=performance.now(),this.note(e))}note(e,t){const i=this.parked&&this.parked.trail;if(!i)return;const n={t:Math.round(performance.now()),step:e};t!=null&&t!==""&&(n.info=String(t).slice(0,160)),i.length>=60&&i.splice(20,1),i.push(n)}noteHttp(e,t,i){var r;const n=(r=t.status)!=null?r:t.ok?200:"error";this.note(e,`${n} ${Math.round(performance.now()-i)}ms`)}noteHttpError(e,t,i){const n=t&&t.name==="AbortError"?"timeout":"network";this.note(e,`${n} ${Math.round(performance.now()-i)}ms`)}noteCirrusMessage(e){if(e.type==="iceCandidate"&&++this.iceCandidatesIn>1)return;const t=e.type==="streamerList"?e.ids:null;this.note(`cirrus:${e.type}`,Array.isArray(t)?`streamers=${t.length}`:void 0)}onPcState(e){const t=e.connectionState;if(this.note("pc-state",t),t==="failed"){if(!this.settled){this.fail("webrtc-failed");return}this.markDead("webrtc-failed")}}markDead(e){const t=this.parked;!t||t.state!=="ready"||t.deadReason||(t.deadReason=e,this.note("dead-while-parked",e))}clearTimers(){this.wsTimeoutId&&(clearTimeout(this.wsTimeoutId),this.wsTimeoutId=null),clearTimeout(this.orchestrationRetryId),this.orchestrationRetryId=null,clearTimeout(this.instanceReadyTimeoutId),this.instanceReadyTimeoutId=null,clearTimeout(this.streamReadyTimeoutId),this.streamReadyTimeoutId=null,this.clearLinkTimeout(),clearTimeout(this.dcTimeoutId),this.dcTimeoutId=null}clearLinkTimeout(){clearTimeout(this.linkTimeoutId),this.linkTimeoutId=null}onStreamerLinked(){this.clearLinkTimeout(),!(this.dcTimeoutId||this.dc&&this.dc.readyState==="open")&&(this.dcTimeoutId=setTimeout(()=>this.fail("dc-timeout"),this.config.dc_timeout))}fail(e,t){if(!(this.parked&&(this.parked.state==="adopted"||this.parked.state==="failed"))){(S.has(e)?o:d)("FAILED:",e,t||""),this.note("fail",t?`${e}: ${C(t)}`:e),this.settled=!0,this.clearTimers(),this.closeControlWs();try{this.dc&&this.dc.close()}catch{}try{this.pc&&this.pc.close()}catch{}try{this.ws&&this.ws.close()}catch{}this.parked&&(this.parked.state="failed",this.parked.failReason=e,t&&(this.parked.failError=String(t&&t.message||t)),this.fireSettled())}}guard(e,t){try{t()}catch(i){this.fail(e,i)}}fetchWithTimeout(e,t,i){const n=new AbortController;t.signal=n.signal;const r=setTimeout(()=>n.abort(),i);return fetch(e,t).finally(()=>clearTimeout(r))}numberOr(e,t){const i=Number(e);return e!=null&&e!==""&&!Number.isNaN(i)?i:t}}new v().run()})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@3dsource/angular-unreal-module",
3
- "version": "0.0.199",
3
+ "version": "0.0.200-dev.1",
4
4
  "description": "Angular integration for Unreal Engine Pixel Streaming",
5
5
  "keywords": [
6
6
  "3dsource",
@@ -627,7 +627,7 @@ type Quality = 'lime' | 'red' | 'orange';
627
627
  * every id added here needs a paired vocabulary row in the viewer
628
628
  * (`telemetry-vocabulary.ts`).
629
629
  */
630
- type EventId = 'region-resolving' | 'signaling-connection' | 'webrtc-negotiation' | 'unreal-communication' | 'region-ping-fetch-start' | 'region-ping-fetch-end' | 'region-resolved' | 'polling-started' | 'polling-ended' | 'aws-instance-resolved' | 'signaling-ws-connected' | 'instance-reserved' | 'instance-ready' | 'config-received' | 'ssinfo-received' | 'offer-received' | 'answer-sent' | 'answer-received' | 'first-ice-candidate' | 'last-ice-candidate' | 'ice-checking' | 'ice-gathering-complete' | 'ice-connected' | 'ice-failed' | 'datachannel-connected' | 'first-command-started' | 'loopback-sent' | 'loopback-received' | 'viewport-ready' | 'polling-to-instance' | 'signaling-handshake' | 'ice-connectivity-checks' | 'stabilization-warmup';
630
+ type EventId = 'region-resolving' | 'signaling-connection' | 'webrtc-negotiation' | 'unreal-communication' | 'region-ping-fetch-start' | 'region-ping-fetch-end' | 'prefetch-wait-start' | 'region-resolved' | 'polling-started' | 'polling-ended' | 'request-stream-sent' | 'aws-instance-resolved' | 'signaling-ws-connected' | 'instance-reserved' | 'instance-ready' | 'config-received' | 'ssinfo-received' | 'offer-received' | 'answer-sent' | 'answer-received' | 'first-ice-candidate' | 'last-ice-candidate' | 'ice-checking' | 'ice-gathering-complete' | 'ice-connected' | 'ice-failed' | 'datachannel-connected' | 'first-command-started' | 'loopback-sent' | 'loopback-received' | 'viewport-ready' | 'polling-to-instance' | 'signaling-handshake' | 'ice-connectivity-checks' | 'stabilization-warmup';
631
631
  /**
632
632
  * High-level cause of `stream.session_ended`.
633
633
  * - `disconnected` — Cirrus/WebRTC dropped us mid-stream (granular cause
@@ -871,6 +871,8 @@ interface SessionEndedData extends TelemetryLifecycleEnvelope {
871
871
  /** Free-text message paired with {@link disconnectReason}, only when the
872
872
  * originating action carried one (e.g. `"Manual Console Drop"`). */
873
873
  readonly disconnectMessage?: string;
874
+ readonly closeCode?: number;
875
+ readonly closeReason?: string;
874
876
  readonly timestamp: string;
875
877
  }
876
878
  /** `stream.sample` — every 5 s, window-aggregated. `seq` is monotonic per
@@ -946,6 +948,23 @@ interface SignalingRetryData extends TelemetryEventEnvelope {
946
948
  readonly reason: string;
947
949
  readonly kind: SignalingRetryKind;
948
950
  }
951
+ /** `stream.prefetch` — once per session that met a prefetched stream: how it
952
+ * ended for the module, and the script's step log. */
953
+ interface PrefetchData extends TelemetryEventEnvelope {
954
+ readonly outcome: PrefetchOutcome;
955
+ /** Script `failReason`, or why a ready stream was dead (`ws-closed`, …). */
956
+ readonly failReason?: string;
957
+ readonly failError?: string;
958
+ /** How long the module waited on the in-flight prefetch, ms. */
959
+ readonly waitMs?: number;
960
+ readonly kind?: CirrusRequestKind;
961
+ readonly instanceName?: string;
962
+ readonly streamRequestId?: string;
963
+ /** Steps, oldest first; `t` is ms since the session anchor (may be < 0:
964
+ * the script starts before the session does). */
965
+ readonly trail: readonly PrefetchTrailEntry[];
966
+ readonly timestamp: string;
967
+ }
949
968
  /** A command with ≥ 1 pak, send → `commandCallback` (by `correlationId`).
950
969
  * Browser and Unreal clocks are never mixed. */
951
970
  interface PakTimingItem {
@@ -980,6 +999,7 @@ interface StreamTelemetryEventMap {
980
999
  'stream.fps_low': FpsLowData;
981
1000
  'stream.signaling_retry': SignalingRetryData;
982
1001
  'stream.pak_timing': PakTimingData;
1002
+ 'stream.prefetch': PrefetchData;
983
1003
  }
984
1004
  type StreamTelemetryEventName = keyof StreamTelemetryEventMap;
985
1005
  /** Names emitted via `emitLifecycle` — the three `duration`-carrying events. */
@@ -1106,6 +1126,13 @@ export interface ParkedStream {
1106
1126
  timings: Record<string, number>;
1107
1127
  failReason?: string;
1108
1128
  failError?: string;
1129
+ /** Step log for telemetry, oldest first. Absent from scripts older than it. */
1130
+ trail?: PrefetchTrailEntry[];
1131
+ /**
1132
+ * Set when the transport of a `ready` stream died before adoption
1133
+ * (`ws-closed` / `dc-closed` / `webrtc-failed`). Such a stream is not adopted.
1134
+ */
1135
+ deadReason?: string;
1109
1136
  /** Stops the script's watchdog and flips state to `adopted`. */
1110
1137
  adopt: () => ParkedStream;
1111
1138
  /**
@@ -1114,6 +1141,32 @@ export interface ParkedStream {
1114
1141
  */
1115
1142
  onSettled: (cb: (state: ParkedStream['state']) => void) => void;
1116
1143
  }
1144
+ /** One step of the prefetch: `t` is `performance.now()` at that moment. */
1145
+ export interface PrefetchTrailEntry {
1146
+ t: number;
1147
+ step: string;
1148
+ info?: string;
1149
+ }
1150
+ /**
1151
+ * How a prefetch ended for the module: `adopted`; `failed` (the script gave
1152
+ * up); `dead` (ready, but its transport died before adoption); `waiting` (the
1153
+ * session ended while the module still waited); `abandoned` (the wait was
1154
+ * cancelled).
1155
+ */
1156
+ export type PrefetchOutcome = 'adopted' | 'failed' | 'dead' | 'waiting' | 'abandoned';
1157
+ /** What {@link StreamAdoptionService} knew about one prefetch, for telemetry. */
1158
+ export interface PrefetchReport {
1159
+ outcome: PrefetchOutcome;
1160
+ /** `failReason` of the script, or the `deadReason` of a dead stream. */
1161
+ failReason: string | null;
1162
+ failError: string | null;
1163
+ /** How long the module waited on the in-flight prefetch; `null` = no wait. */
1164
+ waitMs: number | null;
1165
+ kind: CirrusRequestKind | null;
1166
+ instanceName: string | null;
1167
+ streamRequestId: string | null;
1168
+ trail: readonly PrefetchTrailEntry[];
1169
+ }
1117
1170
  /** Marker the service writes when it finds the slot free and connects itself. */
1118
1171
  export interface AppOwnedStream {
1119
1172
  owner: 'app';
@@ -1373,6 +1426,8 @@ export declare const RECONNECT_CONFIG: {
1373
1426
  readonly maxAttempts: 3;
1374
1427
  readonly delayMs: 1000;
1375
1428
  };
1429
+ export declare const DC_WATCH_INTERVAL_MS = 1000;
1430
+ export declare const DC_CLOSING_GRACE_MS = 2000;
1376
1431
  export declare const DATA_CHANNEL_TIMEOUT_RESTARTS: {
1377
1432
  readonly hidden: 3;
1378
1433
  readonly visible: 10;
@@ -1693,6 +1748,16 @@ export declare class SignallingService extends SubService {
1693
1748
  streamRequestId: string;
1694
1749
  originInstanceUri: string;
1695
1750
  }>;
1751
+ onRequestStreamSent$: Subject<string>;
1752
+ onOrchestrationError$: Subject<{
1753
+ code: number;
1754
+ message: string;
1755
+ }>;
1756
+ onCirrusClose$: Subject<{
1757
+ code: number;
1758
+ reason: string;
1759
+ }>;
1760
+ onPollResponse$: Subject<PollResponse>;
1696
1761
  abort$: Observable<import("@ngrx/store").Action<string>>;
1697
1762
  private ws;
1698
1763
  private controlWs;
@@ -1861,6 +1926,16 @@ export declare class SignallingService extends SubService {
1861
1926
  static ɵfac: i0.ɵɵFactoryDeclaration<SignallingService, never>;
1862
1927
  static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1863
1928
  }
1929
+ /** One orchestration poll, as telemetry sees it. */
1930
+ export interface PollResponse {
1931
+ status: number | 'timeout' | 'network';
1932
+ ms: number;
1933
+ /** The answer named a machine. */
1934
+ ready: boolean;
1935
+ percent: number | null;
1936
+ /** `mm_message`, else `error` (the backend's progress text). */
1937
+ message: string | null;
1938
+ }
1864
1939
  /**
1865
1940
  * Adopts a WebRTC connection prefetched by the `stream-prefetch.js` head-script
1866
1941
  * and parked on `window.__stream`, instead of running `connectToSignaling` from
@@ -1872,6 +1947,13 @@ export declare class StreamAdoptionService {
1872
1947
  private readonly store;
1873
1948
  private readonly signalling;
1874
1949
  private readonly webRtc;
1950
+ /** One report per prefetch the module settled on — read by telemetry. */
1951
+ readonly report$: Subject<PrefetchReport>;
1952
+ /** The module started waiting for an in-flight prefetch. */
1953
+ readonly waitStarted$: Subject<void>;
1954
+ private waiting;
1955
+ /** Snapshot of the prefetch still being waited on, or `null`. */
1956
+ waitingReport(): PrefetchReport | null;
1875
1957
  /**
1876
1958
  * Decide adoption, WAITING for an in-flight prefetch instead of racing it:
1877
1959
  * nothing parked → `false`; settled → adopt-if-ready; still `connecting` →
@@ -1893,6 +1975,11 @@ export declare class StreamAdoptionService {
1893
1975
  * cancel can't trigger a stray adoption.
1894
1976
  */
1895
1977
  private awaitSettled$;
1978
+ /**
1979
+ * Adopt-or-connect for a settled prefetch, and report how it ended. A prefetch
1980
+ * already adopted on an earlier pass is not reported again.
1981
+ */
1982
+ private settle;
1896
1983
  /** Every `false` here means we are about to connect ourselves — take the slot. */
1897
1984
  private tryAdoptOrTakeSlot;
1898
1985
  /** Mark the slot as the app's, replacing a spent prefetch object. */
@@ -1928,6 +2015,7 @@ export declare class StreamStatusTelemetryService2 {
1928
2015
  private readonly videoService;
1929
2016
  private readonly signallingService;
1930
2017
  private readonly webRtcPlayer;
2018
+ private readonly adoption;
1931
2019
  private readonly unrealConfig;
1932
2020
  private readonly destroyRef;
1933
2021
  private readonly destroy$;
@@ -2066,7 +2154,7 @@ export declare class StreamStatusTelemetryService2 {
2066
2154
  * `ssinfo-received` closer's shape (client / instanceName / …ssData /
2067
2155
  * connectionId). For an adopted stream `ssData` is the one the prefetch
2068
2156
  * captured and `streamAdopted` hydrated into the store. */
2069
- private adoptedSignalingParams;
2157
+ private signalingParams;
2070
2158
  private handleWindow;
2071
2159
  private evaluateAnomalies;
2072
2160
  /** Ship the pak commands finished this window as one `stream.pak_timing`;
@@ -2120,6 +2208,11 @@ export declare class StreamStatusTelemetryService2 {
2120
2208
  * stamped here so every event of a session shares a single id.
2121
2209
  */
2122
2210
  private track;
2211
+ /**
2212
+ * `stream.prefetch`, once per session: how the prefetch ended and its step
2213
+ * log, with `t` moved onto the session anchor like every other milestone.
2214
+ */
2215
+ private trackPrefetch;
2123
2216
  private emit;
2124
2217
  static ɵfac: i0.ɵɵFactoryDeclaration<StreamStatusTelemetryService2, never>;
2125
2218
  static ɵprov: i0.ɵɵInjectableDeclaration<any>;
@@ -2383,6 +2476,14 @@ export declare class WebRtcPlayerService extends SubService {
2383
2476
  * listeners are wired and before the announcement.
2384
2477
  */
2385
2478
  private ownDataChannel;
2479
+ /**
2480
+ * A close the remote side never acknowledges leaves the channel in
2481
+ * `closing` with no `close` event, and only the next send would notice.
2482
+ * Once the channel has been open, a `closing` / `closed` state that
2483
+ * outlives the grace period reconnects the peer through the same path a
2484
+ * failed send takes.
2485
+ */
2486
+ private watchForStuckClose;
2386
2487
  private onDataChannelConnected;
2387
2488
  /**
2388
2489
  * Creates an SDP offer, applies SDP munging, sets it as a local description,
@@ -3170,6 +3271,7 @@ export declare const streamAdopted: import("@ngrx/store").ActionCreator<string,
3170
3271
  * same `kind` the service's own flow would have set.
3171
3272
  */
3172
3273
  kind: CirrusRequestKind | null;
3274
+ correlationId?: string | null;
3173
3275
  /**
3174
3276
  * The machine's own id (`originInstanceId` / `signallingServer`). `null`
3175
3277
  * from a script that predates this field — the reducer then falls back to a
@@ -3206,6 +3308,7 @@ export declare const streamAdopted: import("@ngrx/store").ActionCreator<string,
3206
3308
  * same `kind` the service's own flow would have set.
3207
3309
  */
3208
3310
  kind: CirrusRequestKind | null;
3311
+ correlationId?: string | null;
3209
3312
  /**
3210
3313
  * The machine's own id (`originInstanceId` / `signallingServer`). `null`
3211
3314
  * from a script that predates this field — the reducer then falls back to a
@@ -3761,4 +3864,4 @@ export declare class Semaphore {
3761
3864
  release(): void;
3762
3865
  }
3763
3866
  export declare const tapLog: <T>(text: string, ...args: unknown[]) => ((source: Observable<T>) => Observable<T>);
3764
- export { ALL_KEYBOARD_KEYS, type AggregatedStat, type Area, type AwsInstance, type BootstrapInfo, type CirrusRequestKind, type CloseReason, type Coords, type EmittedMilestone, type EventId, type FpsLowData, type FreezeFrameMessage, type IKeyCode, type IKeyCodeName, type IKeyName, IME_PROCESS_KEY, type IceCandidateInfo, type IceRoute, type ImageOutputValues, type InboundVideoStats, type InputProps, MODIFIER_KEY_CODES, type PakTimingData, type PakTimingItem, type PhaseEndedData, type PhaseOverlay, type PhaseParam, type Quality, type QualityAnomalyData, type Rect, type RegionPick, type RegionProbes, type RegionSource, type ResourceBucket, type ResourceTotals, type RouteSegment, type SampleData, type SelectedIceCandidatePair, type SessionEndReason, type SessionEndedData, type SessionStartedData, type SignalingData, type SignalingRetryData, type SignalingRetryKind, type Size, type SlowResource, type StreamConfig, type StreamResolutionProps, type StreamTelemetryEvent, type StreamTelemetryEventMap, type StreamTelemetryEventName, type StreamTelemetryLifecycleData, type StreamTelemetryLifecycleEventName, type StreamTelemetryTrackData, type TelemetryEventEnvelope, type TelemetryLifecycleEnvelope, UNREAL_CONFIG, type UnrealAfkSlotContext, type UnrealInitialConfig, UnrealKeyCode, type UnrealMode, type UnrealResumeSlotContext, type UnrealSlotTemplateContext, type UnrealStatusSlotContext, type UnrealUiClasses, type UnrealUiPart, type WindowAggregate };
3867
+ export { ALL_KEYBOARD_KEYS, type AggregatedStat, type Area, type AwsInstance, type BootstrapInfo, type CirrusRequestKind, type CloseReason, type Coords, type EmittedMilestone, type EventId, type FpsLowData, type FreezeFrameMessage, type IKeyCode, type IKeyCodeName, type IKeyName, IME_PROCESS_KEY, type IceCandidateInfo, type IceRoute, type ImageOutputValues, type InboundVideoStats, type InputProps, MODIFIER_KEY_CODES, type PakTimingData, type PakTimingItem, type PhaseEndedData, type PhaseOverlay, type PhaseParam, type PrefetchData, type Quality, type QualityAnomalyData, type Rect, type RegionPick, type RegionProbes, type RegionSource, type ResourceBucket, type ResourceTotals, type RouteSegment, type SampleData, type SelectedIceCandidatePair, type SessionEndReason, type SessionEndedData, type SessionStartedData, type SignalingData, type SignalingRetryData, type SignalingRetryKind, type Size, type SlowResource, type StreamConfig, type StreamResolutionProps, type StreamTelemetryEvent, type StreamTelemetryEventMap, type StreamTelemetryEventName, type StreamTelemetryLifecycleData, type StreamTelemetryLifecycleEventName, type StreamTelemetryTrackData, type TelemetryEventEnvelope, type TelemetryLifecycleEnvelope, UNREAL_CONFIG, type UnrealAfkSlotContext, type UnrealInitialConfig, UnrealKeyCode, type UnrealMode, type UnrealResumeSlotContext, type UnrealSlotTemplateContext, type UnrealStatusSlotContext, type UnrealUiClasses, type UnrealUiPart, type WindowAggregate };