@3dsource/angular-unreal-module 0.0.146-dev.1 → 0.0.146-dev.11
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.
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";(()=>{"use strict";if(window.__streamPerf)return;const t=1e3,e=window.__streamPerf={longTasks:[],supported:!1};try{new PerformanceObserver(o=>{for(const r of o.getEntries()){if(e.longTasks.length>=t)break;e.longTasks.push({start:Math.round(r.startTime),dur:Math.round(r.duration)})}}).observe({type:"longtask",buffered:!0}),e.supported=!0}catch{}})();
|
package/js/stream-prefetch.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";(()=>{"use strict";const f="metabox-configurator",m=/metabox-configurator\/(modular|basic)\/([^/?#]+)/i,g="3dsource:region-ping",
|
|
2
|
-
`);let i="maxaveragebitrate=510000;";return i+=this.forceMonoAudio?"stereo=0;":"stereo=1;",i+="useinbandfec=1",
|
|
3
|
-
`),n=[];for(const r of i)if(r.startsWith("a=fmtp:")&&r.toLowerCase().includes(`profile-level-id=${
|
|
4
|
-
`)}send(
|
|
1
|
+
"use strict";(()=>{"use strict";const f="metabox-configurator",m=/metabox-configurator\/(modular|basic)\/([^/?#]+)/i,g="3dsource:region-ping",k="assets/config.json",w="42e01f";function l(){return typeof crypto!="undefined"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,o=>{const t=Math.random()*16|0;return(o==="x"?t:t&3|8).toString(16)})}function I(o){return o.replace("http://","ws://").replace("https://","wss://")}const h="[stream-prefetch]",s=(...o)=>console.log(h,...o),c=(...o)=>console.warn(h,...o),u=(...o)=>console.error(h,...o);class y{constructor(){const t=document.currentScript||(()=>{const n=document.getElementsByTagName("script");return n[n.length-1]})(),e=t&&t.dataset||{};this.configUrl=e.configUrl||k,this.fallbackPollingUrl=e.fallbackUrl||"",this.skipRouteCheck=this.boolOr(e.skipRouteCheck,!1),this.config={ws_timeout:this.numberOr(e.wsTimeout,6e3),poll_timeout:this.numberOr(e.pollTimeout,4e3)};const i=new URLSearchParams(window.location.search);this.forceTURN=i.has("ForceTURN"),this.forceMaxBundle=i.has("ForceMaxBundle"),this.forceMonoAudio=i.has("forceMono"),this.ids={clientId:l(),viewId:l(),correlationId:l()},this.context=null,this.environmentId=void 0,this.orchestrationType="",this.region="",this.requireEnvironmentId=!0,this.pc=null,this.dc=null,this.stream=null,this.ws=null,this.remoteDescriptionSet=!1,this.iceBuffer=[],this.wsTimeoutId=null,this.settled=!1}run(){if(typeof window=="undefined"||!("WebSocket"in window)||!("RTCPeerConnection"in window))return;if(this.skipRouteCheck){this.context={},this.requireEnvironmentId=!1,c("skipRouteCheck=true \u2014 ignoring URL gate, route, context and environmentId; using fallback polling"),this.publishParked("connecting"),this.connect().catch(e=>this.fail("exception",e));return}if(window.location.href.indexOf(f)===-1){s("skip \u2014 URL does not contain gate",`"${f}"`);return}const t=this.parseConfiguratorUrl();t?(this.context={id:t.contextId,data:{configuratorId:t.configuratorId}},this.requireEnvironmentId=!0,s("start",{contextId:t.contextId,configuratorId:t.configuratorId,ids:this.ids})):(this.context={},this.requireEnvironmentId=!1,c("URL is not a configurator route \u2014 prefetching with empty context {} and no required environmentId",window.location.pathname)),this.publishParked("connecting"),this.connect().catch(e=>this.fail("exception",e))}parseConfiguratorUrl(){const t=window.location.pathname.match(m);return t?{contextId:t[1].toLowerCase()==="modular"?"metaboxModularConfigurator":"metaboxBasicConfigurator",configuratorId:decodeURIComponent(t[2])}:null}connect(){return this.mark("prefetch_start"),this.region=this.readRegion()||"",s("region from cache:",this.region||"(none)"),(this.skipRouteCheck?Promise.resolve(null):this.fetchConfig()).then(e=>{let i;if(e)i=this.chooseEndpoint(e);else if(this.fallbackPollingUrl)c("no app config \u2014 falling back to polling",this.fallbackPollingUrl),i={type:"polling",url:this.fallbackPollingUrl,environmentId:""},this.requireEnvironmentId=!1;else{this.fail("no-config");return}if(!i){this.fail("no-endpoint");return}if(s("endpoint chosen",{type:i.type,url:i.url,environmentId:i.environmentId}),this.requireEnvironmentId&&!i.environmentId){this.fail("no-environment-id");return}this.orchestrationType=i.type,this.environmentId=i.environmentId||void 0;const n=this.adaptUrlToRegion(i.url);return(i.type==="polling"?this.pollOrchestration(n):Promise.resolve(n)).then(a=>{if(!a){this.fail("no-ws-url");return}return this.parked.machineId=a,s("opening socket",a),this.openSocket(a)})})}fetchConfig(){const t=new URL(this.configUrl,window.location.origin).href;return this.fetchWithTimeout(t,{credentials:"omit"},this.config.poll_timeout).then(e=>e.ok?e.json().catch(()=>null):null).catch(()=>null)}chooseEndpoint(t){const e=t.newSignalling,i=this.firstVersionUrl(e&&e.versions);if(i&&/^wss?:\/\//i.test(i))return{type:"direct",url:i,environmentId:e&&e.environmentId||""};const n=t.signalling,r=this.firstVersionUrl(n&&n.versions);return r?(this.requireEnvironmentId=!1,{type:"polling",url:r,environmentId:n&&n.environmentId||""}):null}firstVersionUrl(t){if(!t||typeof t!="object")return"";for(const e of Object.keys(t))if(t[e])return t[e];return""}pollOrchestration(t){const e=`${t}${this.ids.clientId}/${this.ids.viewId}`;return this.fetchWithTimeout(e,{credentials:"omit"},this.config.poll_timeout).then(i=>i.ok?i.json().catch(()=>({})):null).then(i=>i&&i.signallingServer?I(`${window.location.protocol}//${i.signallingServer}`):null).catch(()=>null)}adaptUrlToRegion(t){return this.region&&t.indexOf(":region")===-1?t.replace(/signallingserver/gi,`signallingserver/region:${this.region}`):t}openSocket(t){const e=new WebSocket(t);e.binaryType="arraybuffer",this.ws=e,this.parked.ws=e,this.wsTimeoutId=setTimeout(()=>{e.readyState!==WebSocket.OPEN&&this.fail("ws-timeout")},this.config.ws_timeout),e.onopen=()=>{clearTimeout(this.wsTimeoutId),this.mark("ws_open"),s("WS open \u2192 sending requestStream",{environmentId:this.environmentId,region:this.region,context:this.context}),this.sendRequestStream()},e.onmessage=i=>this.onWsMessage(i),e.onerror=i=>{u("WS error",i),this.fail("ws-error")},e.onclose=i=>{!this.settled&&this.parked.state!=="ready"&&(c("WS closed before ready",{code:i&&i.code}),this.fail("ws-closed"))}}sendRequestStream(){this.send({type:"requestStream",correlationId:this.ids.correlationId,payload:{region:this.region,environmentId:this.environmentId,context:this.context}})}onWsMessage(t){let e;try{const i=typeof t.data=="string"?t.data:null;if(i===null)return;e=JSON.parse(i)}catch{return}switch(s("WS \u2190",e.type),e.type){case"ssInfo":this.parked.ssData=e.data;break;case"config":this.onConfig(e);break;case"offer":this.onOffer(e);break;case"answer":this.onAnswer(e);break;case"iceCandidate":this.onIceCandidate(e.candidate);break;case"ping":this.send({type:"pong",source:"front"});break;case"streamerDisconnected":case"playerDisconnected":this.fail("streamer-disconnected");break;case"error":this.fail("orchestration-error");break;default:break}}onConfig(t){if(this.pc)return;const e=Object.assign({},t.peerConnectionOptions);this.forceTURN&&(e.iceTransportPolicy="relay"),e.bundlePolicy=this.forceMaxBundle?"max-bundle":"balanced",e.iceCandidatePoolSize=1,this.parked.config=e;const i=new RTCPeerConnection(e);this.pc=i,this.parked.pc=i,s("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]&&(s("video track received"),this.stream=n.streams[0],this.parked.stream=this.stream,this.maybeReady())}}onOffer(t){return this.pc?(this.remoteDescriptionSet=!1,this.iceBuffer.length=0,this.pc.setRemoteDescription(t).then(()=>(this.remoteDescriptionSet=!0,this.flushIce(),this.pc.createAnswer())).then(e=>(e.sdp=this.mungeSDP(e.sdp||""),this.pc.setLocalDescription(e).then(()=>{s("WS \u2192 answer sent"),this.send(e)}))).catch(e=>this.fail("offer-failed",e))):Promise.resolve()}onAnswer(t){return this.pc?this.pc.setRemoteDescription(t).then(()=>{this.remoteDescriptionSet=!0,this.flushIce()}).catch(e=>this.fail("answer-failed",e)):Promise.resolve()}onIceCandidate(t){t&&(this.forceTURN&&String(t.candidate).indexOf("relay")<0||(this.remoteDescriptionSet&&this.pc?this.pc.addIceCandidate(t).catch(()=>{}):this.iceBuffer.push(t)))}flushIce(){for(const t of this.iceBuffer)this.pc.addIceCandidate(t).catch(()=>{});this.iceBuffer.length=0}wireDataChannel(t){t.binaryType="arraybuffer",this.dc=t,this.parked.dc=t,t.onmessage=e=>{this.parked.dcBuffer.push(e.data)},t.onopen=()=>{s("DataChannel open"),this.mark("dc_open"),this.maybeReady()},t.onclose=()=>{!this.settled&&this.parked.state!=="ready"&&(c("DataChannel closed before ready"),this.fail("dc-closed"))},t.readyState==="open"&&this.maybeReady()}maybeReady(){this.settled||!this.dc||this.dc.readyState!=="open"||this.stream&&(this.settled=!0,this.mark("ready"),this.parked.state="ready",s("READY \u2014 parked on window.__stream",this.parked.timings),this.fireSettled())}publishParked(t){const e=this;this.parked={state:t,pc:null,dc:null,stream:null,ws:null,ssData:void 0,machineId:void 0,config:void 0,ids:this.ids,dcBuffer:[],timings:{},adopt(){return e.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}e.settledCb=i}}},window.__stream=this.parked}fireSettled(){const t=this.settledCb;if(t){this.settledCb=null;try{t(this.parked.state)}catch{}}}clearTimers(){this.wsTimeoutId&&(clearTimeout(this.wsTimeoutId),this.wsTimeoutId=null)}fail(t,e){if(!(this.parked&&(this.parked.state==="adopted"||this.parked.state==="failed"))){u("FAILED:",t,e||""),this.settled=!0,this.clearTimers();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=t,e&&(this.parked.failError=String(e&&e.message||e)),this.fireSettled())}}mungeSDP(t){let e=this.prioritizeH264Profile(t,w);e=e.replace(/(a=fmtp:\d+ .*level-asymmetry-allowed=.*)\r\n/gm,`$1;x-google-start-bitrate=10000;x-google-max-bitrate=100000\r
|
|
2
|
+
`);let i="maxaveragebitrate=510000;";return i+=this.forceMonoAudio?"stereo=0;":"stereo=1;",i+="useinbandfec=1",e=e.replace("useinbandfec=1",i),e}prioritizeH264Profile(t,e){const i=t.split(`\r
|
|
3
|
+
`),n=[];for(const r of i)if(r.startsWith("a=fmtp:")&&r.toLowerCase().includes(`profile-level-id=${e}`)){const a=r.split(":")[1]&&r.split(":")[1].split(" ")[0];a&&n.push(a)}return n.length===0?t:i.map(r=>{if(!r.startsWith("m=video"))return r;const a=r.split(" "),x=a.slice(0,3),p=a.slice(3),b=[...n.filter(d=>p.includes(d)),...p.filter(d=>!n.includes(d))];return[...x,...b].join(" ")}).join(`\r
|
|
4
|
+
`)}send(t){const e=typeof t=="string"?t:JSON.stringify(t);if(this.ws&&this.ws.readyState===WebSocket.OPEN)try{this.ws.send(e)}catch{}}mark(t){this.parked&&(this.parked.timings[t]=performance.now())}readRegion(){try{const t=localStorage.getItem(g);if(!t)return null;const e=JSON.parse(t);return!e||typeof e.region_code!="string"||typeof e.ts!="number"||Date.now()-e.ts>=2592e6?null:e.region_code}catch{return null}}fetchWithTimeout(t,e,i){const n=new AbortController;e.signal=n.signal;const r=setTimeout(()=>n.abort(),i);return fetch(t,e).finally(()=>clearTimeout(r))}numberOr(t,e){const i=Number(t);return t!=null&&t!==""&&!Number.isNaN(i)?i:e}boolOr(t,e){return t==="true"||t===""?!0:t==="false"?!1:e}}new y().run()})();
|
package/package.json
CHANGED
|
@@ -287,10 +287,27 @@ interface ParkedStream {
|
|
|
287
287
|
failError?: string;
|
|
288
288
|
/** Stops the script's watchdog and flips state to `adopted`. */
|
|
289
289
|
adopt: () => ParkedStream;
|
|
290
|
+
/**
|
|
291
|
+
* Register a one-shot callback fired the instant the stream settles
|
|
292
|
+
* (`ready`/`failed`); fires on the next microtask if already settled. This is
|
|
293
|
+
* how {@link StreamAdoptionService} adopts event-driven (no polling) — the
|
|
294
|
+
* head-script must provide it.
|
|
295
|
+
*/
|
|
296
|
+
onSettled: (cb: (state: ParkedStream['state']) => void) => void;
|
|
290
297
|
}
|
|
291
298
|
declare global {
|
|
292
299
|
interface Window {
|
|
293
300
|
__stream?: ParkedStream;
|
|
301
|
+
/** Main-thread long-task log collected by the optional `perf-longtask.js`
|
|
302
|
+
* head-script from page start. Read once at adoption to attribute the
|
|
303
|
+
* boot gap. `start`/`dur` are ms in the page `performance.now()` timebase. */
|
|
304
|
+
__streamPerf?: {
|
|
305
|
+
longTasks: {
|
|
306
|
+
start: number;
|
|
307
|
+
dur: number;
|
|
308
|
+
}[];
|
|
309
|
+
supported: boolean;
|
|
310
|
+
};
|
|
294
311
|
}
|
|
295
312
|
}
|
|
296
313
|
|
|
@@ -923,8 +940,15 @@ declare class RegionsPingService {
|
|
|
923
940
|
* entry is well-formed and still within {@link REGION_PING_CACHE_TTL_MS}.
|
|
924
941
|
* All access is guarded — `localStorage` can throw (private mode, disabled
|
|
925
942
|
* storage) or be absent (non-browser).
|
|
943
|
+
*
|
|
944
|
+
* Public so adopted connections (which skip `getFastest`) can replay the
|
|
945
|
+
* region-resolving telemetry phase from the same cached winner — see
|
|
946
|
+
* `UnrealEffects.regionTelemetryOnAdopt$`.
|
|
926
947
|
*/
|
|
927
|
-
|
|
948
|
+
readCachedWinner(): {
|
|
949
|
+
region_code: string;
|
|
950
|
+
result: number[];
|
|
951
|
+
} | null;
|
|
928
952
|
private writeCachedWinner;
|
|
929
953
|
/**
|
|
930
954
|
* Accurate, OFF-critical-path refresh of the region cache. Mirrors the
|
|
@@ -1090,14 +1114,39 @@ declare class StreamAdoptionService {
|
|
|
1090
1114
|
*
|
|
1091
1115
|
* - nothing parked → emit `false` at once (caller connects normally).
|
|
1092
1116
|
* - already settled (`ready`/`failed`/`adopted`) → adopt-if-ready at once.
|
|
1093
|
-
* - still `connecting` →
|
|
1094
|
-
*
|
|
1117
|
+
* - still `connecting` → wait for the script's `onSettled` callback and
|
|
1118
|
+
* adopt-if-ready the instant it fires (event-driven, zero polling latency).
|
|
1119
|
+
*
|
|
1120
|
+
* Deliberately NO time budget: for DIRECT orchestration the prefetch holds
|
|
1121
|
+
* the socket open while the orchestrator provisions a machine, which can take
|
|
1122
|
+
* minutes (150–200s+). A fixed deadline would make Angular give up and open a
|
|
1123
|
+
* second connection mid-provisioning — exactly the race/orphan this avoids.
|
|
1124
|
+
* The wait is bounded by liveness instead: the script flips the parked state
|
|
1125
|
+
* to `failed` on a dead socket (`ws.onclose`/`onerror`), and the caller wraps
|
|
1126
|
+
* this in `takeUntil(abortEstablishingConnection)`, so a route change / teardown
|
|
1127
|
+
* cancels it. Emits exactly once. Cancelling runs no adoption — which is what
|
|
1128
|
+
* stops a fast route-switch from starting overlapping connections.
|
|
1095
1129
|
*
|
|
1096
|
-
*
|
|
1097
|
-
*
|
|
1098
|
-
*
|
|
1130
|
+
* Wrapped in `defer` so the `tryAdopt()` side effect (which dispatches
|
|
1131
|
+
* `streamAdopted`) runs at SUBSCRIPTION time, not when the observable is
|
|
1132
|
+
* built. The effect prepends `startWith(setEstablishingConnection(true))`,
|
|
1133
|
+
* which starts the telemetry session and subscribes its phase catalog; that
|
|
1134
|
+
* must happen BEFORE `streamAdopted` → `regionTelemetryOnAdopt$` replays
|
|
1135
|
+
* `regionResolved`, or the region phase fires into a session that doesn't
|
|
1136
|
+
* exist yet and is lost. `defer` guarantees that ordering.
|
|
1137
|
+
*/
|
|
1138
|
+
awaitAndAdopt$(): Observable<boolean>;
|
|
1139
|
+
/**
|
|
1140
|
+
* Event-driven wait: the script fires `onSettled` the instant it parks
|
|
1141
|
+
* `ready`/`failed`, so we adopt with zero polling lag. Emits exactly once.
|
|
1142
|
+
*
|
|
1143
|
+
* No timer/budget by design: an indefinitely `connecting` stream is valid
|
|
1144
|
+
* (DIRECT orchestration holds the socket open for minutes while it
|
|
1145
|
+
* provisions), and every terminal state — including a dead socket — flips to
|
|
1146
|
+
* `ready`/`failed` and fires the hook. Teardown (route change → unsubscribe)
|
|
1147
|
+
* sets `done`, so a late callback after cancel can't trigger a stray adoption.
|
|
1099
1148
|
*/
|
|
1100
|
-
|
|
1149
|
+
private awaitSettled$;
|
|
1101
1150
|
/**
|
|
1102
1151
|
* @returns `true` if a ready parked stream was adopted (caller must NOT
|
|
1103
1152
|
* start a fresh connection), `false` otherwise.
|
|
@@ -1194,6 +1243,17 @@ declare class StreamStatusTelemetryService2 {
|
|
|
1194
1243
|
private readonly disconnectReason$;
|
|
1195
1244
|
private readonly errorMessage$;
|
|
1196
1245
|
private readonly viewPortReady$;
|
|
1246
|
+
/**
|
|
1247
|
+
* Synchronous adoption flag. The `streamAdopted` reducer sets it BEFORE any
|
|
1248
|
+
* action subscriber runs, so the connection-phase closers (which fire inside
|
|
1249
|
+
* the same dispatch, e.g. `ssinfo-received` off the `ssData` state change)
|
|
1250
|
+
* can read it and defer to the prefetch-timed attribution emitted by
|
|
1251
|
+
* {@link emitAdoptedConnection} instead of recording the live wall-clock.
|
|
1252
|
+
*/
|
|
1253
|
+
private readonly adopted$;
|
|
1254
|
+
/** Streamer identity for the adopted signaling phase params. */
|
|
1255
|
+
private readonly ssData$;
|
|
1256
|
+
private readonly awsInstance$;
|
|
1197
1257
|
private session;
|
|
1198
1258
|
/**
|
|
1199
1259
|
* Whether a session has already started during the current page lifetime.
|
|
@@ -1257,6 +1317,36 @@ declare class StreamStatusTelemetryService2 {
|
|
|
1257
1317
|
* are derived from neighbor phases.
|
|
1258
1318
|
*/
|
|
1259
1319
|
private emitPhaseEnded;
|
|
1320
|
+
/**
|
|
1321
|
+
* Emit the region / signaling / webrtc phases for an ADOPTED connection.
|
|
1322
|
+
*
|
|
1323
|
+
* The prefetch script established the connection in PARALLEL with page load,
|
|
1324
|
+
* so the phases are positioned at their real (anchor-relative) spans — shifted
|
|
1325
|
+
* LEFT to overlap the HTML-loading / route-idle rows — rather than sequentially
|
|
1326
|
+
* after them. That's the whole point of the prefetch: the handshake already
|
|
1327
|
+
* happened while the page was loading. Each phase ships:
|
|
1328
|
+
* - `duration` — the real sub-step time the prefetch spent.
|
|
1329
|
+
* - `spanStartMs` — anchor-relative start, so the viewer left-shifts the bar.
|
|
1330
|
+
* - `adoptionAtMs` — where Angular took over (the next phase, Unreal
|
|
1331
|
+
* communication, resumes here on the sequential timeline).
|
|
1332
|
+
* - savings params: `prefetchConnectMs` (full handshake), `prefetchSavedMs`
|
|
1333
|
+
* (done before Angular was ready), `adoptionWaitMs` (real connect→adopted
|
|
1334
|
+
* wait), `adoptionOverheadMs` (prefetch-ready → adopted gap).
|
|
1335
|
+
*/
|
|
1336
|
+
private emitAdoptedConnection;
|
|
1337
|
+
/**
|
|
1338
|
+
* Emit one adopted phase positioned at its real `spanStartMs` (left-shifted
|
|
1339
|
+
* to overlap page load) with its real prefetch `duration`. `prefetched`,
|
|
1340
|
+
* `spanStartMs` and `adoptionAtMs` ride as top-level wire fields the viewer
|
|
1341
|
+
* uses to lay the bar out; the rest become display params.
|
|
1342
|
+
*/
|
|
1343
|
+
private emitAdoptedPhase;
|
|
1344
|
+
/** Streamer identity params for the adopted signaling phase — mirrors the
|
|
1345
|
+
* `ssinfo-received` closer's shape (client / instanceName / …ssData). */
|
|
1346
|
+
private adoptedSignalingParams;
|
|
1347
|
+
/** Read the region-ping cache the prefetch reused (localStorage), so the
|
|
1348
|
+
* adopted region phase can still report the winning region + samples. */
|
|
1349
|
+
private readRegionCache;
|
|
1260
1350
|
private handleWindow;
|
|
1261
1351
|
private evaluateAnomalies;
|
|
1262
1352
|
private updateMaxFpsTarget;
|
|
@@ -2569,9 +2659,29 @@ declare const initSignalling: node_modules__ngrx_store_types_ngrx_store.ActionCr
|
|
|
2569
2659
|
declare const streamAdopted: node_modules__ngrx_store_types_ngrx_store.ActionCreator<string, (props: {
|
|
2570
2660
|
machineId: string | null;
|
|
2571
2661
|
ssData: SignalingData | null;
|
|
2662
|
+
/**
|
|
2663
|
+
* `performance.now()` marks the prefetch script recorded while it
|
|
2664
|
+
* established the connection (`prefetch_start` / `ws_open` / `dc_open` /
|
|
2665
|
+
* `ready`), on the page navigation timeline. The telemetry service uses
|
|
2666
|
+
* them to attribute the signaling / webrtc phase durations to the time the
|
|
2667
|
+
* prefetch actually spent — instead of dumping the whole pre-adoption wait
|
|
2668
|
+
* onto whichever phase closer happens to fire first. `null` when adopting a
|
|
2669
|
+
* stream that carried no timings.
|
|
2670
|
+
*/
|
|
2671
|
+
timings: Record<string, number> | null;
|
|
2572
2672
|
}) => {
|
|
2573
2673
|
machineId: string | null;
|
|
2574
2674
|
ssData: SignalingData | null;
|
|
2675
|
+
/**
|
|
2676
|
+
* `performance.now()` marks the prefetch script recorded while it
|
|
2677
|
+
* established the connection (`prefetch_start` / `ws_open` / `dc_open` /
|
|
2678
|
+
* `ready`), on the page navigation timeline. The telemetry service uses
|
|
2679
|
+
* them to attribute the signaling / webrtc phase durations to the time the
|
|
2680
|
+
* prefetch actually spent — instead of dumping the whole pre-adoption wait
|
|
2681
|
+
* onto whichever phase closer happens to fire first. `null` when adopting a
|
|
2682
|
+
* stream that carried no timings.
|
|
2683
|
+
*/
|
|
2684
|
+
timings: Record<string, number> | null;
|
|
2575
2685
|
} & node_modules__ngrx_store_types_ngrx_store.Action<string>>;
|
|
2576
2686
|
declare const startStream: node_modules__ngrx_store_types_ngrx_store.ActionCreator<string, (props: {
|
|
2577
2687
|
config: Partial<StreamConfig>;
|
|
@@ -2806,6 +2916,7 @@ declare const unrealFeature: {
|
|
|
2806
2916
|
name: "unrealFeature";
|
|
2807
2917
|
reducer: node_modules__ngrx_store_types_ngrx_store.ActionReducer<_3dsource_angular_unreal_module.UnrealState, node_modules__ngrx_store_types_ngrx_store.Action<string>>;
|
|
2808
2918
|
selectUnrealFeatureState: node_modules__ngrx_store_types_ngrx_store.MemoizedSelector<Record<string, any>, _3dsource_angular_unreal_module.UnrealState, (featureState: _3dsource_angular_unreal_module.UnrealState) => _3dsource_angular_unreal_module.UnrealState>;
|
|
2919
|
+
selectAdopted: node_modules__ngrx_store_types_ngrx_store.MemoizedSelector<Record<string, any>, boolean, (featureState: _3dsource_angular_unreal_module.UnrealState) => boolean>;
|
|
2809
2920
|
selectFreezeFrame: node_modules__ngrx_store_types_ngrx_store.MemoizedSelector<Record<string, any>, _3dsource_angular_unreal_module.FreezeFrameMessage, (featureState: _3dsource_angular_unreal_module.UnrealState) => _3dsource_angular_unreal_module.FreezeFrameMessage>;
|
|
2810
2921
|
selectIsVideoPlaying: node_modules__ngrx_store_types_ngrx_store.MemoizedSelector<Record<string, any>, boolean, (featureState: _3dsource_angular_unreal_module.UnrealState) => boolean>;
|
|
2811
2922
|
selectStatusMessage: node_modules__ngrx_store_types_ngrx_store.MemoizedSelector<Record<string, any>, string | null, (featureState: _3dsource_angular_unreal_module.UnrealState) => string | null>;
|
|
@@ -2862,6 +2973,13 @@ interface UnrealState {
|
|
|
2862
2973
|
isAfkTimerVisible: boolean;
|
|
2863
2974
|
afkCountdown: number;
|
|
2864
2975
|
cirrusConnected: boolean;
|
|
2976
|
+
/**
|
|
2977
|
+
* True once a prefetched connection has been adopted (`streamAdopted`).
|
|
2978
|
+
* Read synchronously by the telemetry service so the connection-phase
|
|
2979
|
+
* closers know to defer to the prefetch-timed attribution instead of the
|
|
2980
|
+
* live-event one. Reset on every fresh attempt / disconnect.
|
|
2981
|
+
*/
|
|
2982
|
+
adopted: boolean;
|
|
2865
2983
|
viewportReady: boolean;
|
|
2866
2984
|
freezeFrameFromVideo: FreezeFrameMessage;
|
|
2867
2985
|
freezeFrame: FreezeFrameMessage;
|