@3dsource/angular-unreal-module 0.0.146-dev.7 → 0.0.146-dev.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -3589,8 +3589,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
3589
3589
|
args: [{ providedIn: 'root' }]
|
|
3590
3590
|
}], ctorParameters: () => [] });
|
|
3591
3591
|
|
|
3592
|
-
//
|
|
3592
|
+
// Legacy fallback only: poll interval for head-scripts that predate the
|
|
3593
|
+
// `onSettled` hook. The event-driven path (preferred) carries no such latency.
|
|
3593
3594
|
const ADOPT_POLL_MS = 100;
|
|
3595
|
+
// Safety-net re-check for the event path — covers the (unexpected) case where
|
|
3596
|
+
// `onSettled` never fires. Coarse on purpose: it's a backstop, not the hot
|
|
3597
|
+
// path, so it adds no latency to a healthy adoption.
|
|
3598
|
+
const ADOPT_EVENT_SAFETY_MS = 1000;
|
|
3594
3599
|
/**
|
|
3595
3600
|
* Adopts a WebRTC connection prefetched by the `stream-prefetch.js` head-script
|
|
3596
3601
|
* and parked on `window.__stream`, instead of running `connectToSignaling` from
|
|
@@ -3609,7 +3614,9 @@ class StreamAdoptionService {
|
|
|
3609
3614
|
*
|
|
3610
3615
|
* - nothing parked → emit `false` at once (caller connects normally).
|
|
3611
3616
|
* - already settled (`ready`/`failed`/`adopted`) → adopt-if-ready at once.
|
|
3612
|
-
* - still `connecting` →
|
|
3617
|
+
* - still `connecting` → wait for the script's `onSettled` callback (no
|
|
3618
|
+
* polling latency) and adopt-if-ready the instant it fires; on older
|
|
3619
|
+
* scripts without the hook, fall back to a `timer` poll.
|
|
3613
3620
|
*
|
|
3614
3621
|
* Deliberately NO time budget: for DIRECT orchestration the prefetch holds
|
|
3615
3622
|
* the socket open while the orchestrator provisions a machine, which can take
|
|
@@ -3639,12 +3646,48 @@ class StreamAdoptionService {
|
|
|
3639
3646
|
return of(this.tryAdopt());
|
|
3640
3647
|
}
|
|
3641
3648
|
Logger.info('[Adoption] Prefetch in progress — waiting before connecting');
|
|
3649
|
+
// Preferred path: the script fires `onSettled` the moment it parks
|
|
3650
|
+
// `ready`/`failed`, so we adopt with zero polling lag.
|
|
3651
|
+
if (typeof parked.onSettled === 'function') {
|
|
3652
|
+
return this.awaitSettled$(parked);
|
|
3653
|
+
}
|
|
3654
|
+
// Legacy fallback: older head-script without the hook — poll its state.
|
|
3642
3655
|
return timer(0, ADOPT_POLL_MS).pipe(map$1(() => window.__stream?.state ?? 'failed'),
|
|
3643
3656
|
// Keep polling while still connecting; the `true` (inclusive) emits the
|
|
3644
3657
|
// settling value (`ready`/`failed`) that ends the wait.
|
|
3645
3658
|
takeWhile((state) => state === 'connecting', true), last(), map$1(() => this.tryAdopt()));
|
|
3646
3659
|
});
|
|
3647
3660
|
}
|
|
3661
|
+
/**
|
|
3662
|
+
* Event-driven wait: resolve the instant the script's `onSettled` callback
|
|
3663
|
+
* fires (ready/failed), then adopt-if-ready. Emits exactly once.
|
|
3664
|
+
*
|
|
3665
|
+
* A coarse {@link ADOPT_EVENT_SAFETY_MS} interval is a backstop only — if
|
|
3666
|
+
* `onSettled` somehow never fires it still settles, without adding latency to
|
|
3667
|
+
* the normal case. Teardown (route change → unsubscribe) sets `done`, so a
|
|
3668
|
+
* late callback after cancel can't trigger a stray adoption.
|
|
3669
|
+
*/
|
|
3670
|
+
awaitSettled$(parked) {
|
|
3671
|
+
return new Observable((subscriber) => {
|
|
3672
|
+
let done = false;
|
|
3673
|
+
const finish = () => {
|
|
3674
|
+
if (done)
|
|
3675
|
+
return;
|
|
3676
|
+
done = true;
|
|
3677
|
+
subscriber.next(this.tryAdopt());
|
|
3678
|
+
subscriber.complete();
|
|
3679
|
+
};
|
|
3680
|
+
parked.onSettled?.(() => finish());
|
|
3681
|
+
const safety = setInterval(() => {
|
|
3682
|
+
if ((window.__stream?.state ?? 'failed') !== 'connecting')
|
|
3683
|
+
finish();
|
|
3684
|
+
}, ADOPT_EVENT_SAFETY_MS);
|
|
3685
|
+
return () => {
|
|
3686
|
+
done = true;
|
|
3687
|
+
clearInterval(safety);
|
|
3688
|
+
};
|
|
3689
|
+
});
|
|
3690
|
+
}
|
|
3648
3691
|
/**
|
|
3649
3692
|
* @returns `true` if a ready parked stream was adopted (caller must NOT
|
|
3650
3693
|
* start a fresh connection), `false` otherwise.
|
|
@@ -6122,81 +6165,76 @@ class StreamStatusTelemetryService2 {
|
|
|
6122
6165
|
/**
|
|
6123
6166
|
* Emit the region / signaling / webrtc phases for an ADOPTED connection.
|
|
6124
6167
|
*
|
|
6125
|
-
* The prefetch script established the connection in
|
|
6126
|
-
* so
|
|
6127
|
-
*
|
|
6128
|
-
*
|
|
6129
|
-
*
|
|
6130
|
-
*
|
|
6131
|
-
*
|
|
6132
|
-
*
|
|
6133
|
-
*
|
|
6134
|
-
*
|
|
6135
|
-
*
|
|
6136
|
-
*
|
|
6137
|
-
* - `prefetchSavedMs` — how much of it completed before Angular was ready.
|
|
6138
|
-
* - `adoptionWaitMs` — the actual wait Angular experienced (= window).
|
|
6139
|
-
* - `adoptionOverheadMs` — gap between prefetch-ready and adoption.
|
|
6168
|
+
* The prefetch script established the connection in PARALLEL with page load,
|
|
6169
|
+
* so the phases are positioned at their real (anchor-relative) spans — shifted
|
|
6170
|
+
* LEFT to overlap the HTML-loading / route-idle rows — rather than sequentially
|
|
6171
|
+
* after them. That's the whole point of the prefetch: the handshake already
|
|
6172
|
+
* happened while the page was loading. Each phase ships:
|
|
6173
|
+
* - `duration` — the real sub-step time the prefetch spent.
|
|
6174
|
+
* - `spanStartMs` — anchor-relative start, so the viewer left-shifts the bar.
|
|
6175
|
+
* - `adoptionAtMs` — where Angular took over (the next phase, Unreal
|
|
6176
|
+
* communication, resumes here on the sequential timeline).
|
|
6177
|
+
* - savings params: `prefetchConnectMs` (full handshake), `prefetchSavedMs`
|
|
6178
|
+
* (done before Angular was ready), `adoptionWaitMs` (real connect→adopted
|
|
6179
|
+
* wait), `adoptionOverheadMs` (prefetch-ready → adopted gap).
|
|
6140
6180
|
*/
|
|
6141
6181
|
emitAdoptedConnection(timings) {
|
|
6142
6182
|
const s = this.session;
|
|
6143
6183
|
if (!s)
|
|
6144
6184
|
return;
|
|
6145
6185
|
const t = timings ?? {};
|
|
6146
|
-
const start = t['prefetch_start'] ?? s.startedAtPerf;
|
|
6147
|
-
const wsOpen = t['ws_open'] ?? start;
|
|
6148
|
-
const ready = t['ready'] ?? t['dc_open'] ?? wsOpen;
|
|
6149
|
-
// Real prefetch sub-durations — how the handshake actually broke down.
|
|
6150
|
-
const sigRaw = Math.max(0, wsOpen - start);
|
|
6151
|
-
const webRaw = Math.max(0, ready - wsOpen);
|
|
6152
|
-
const connectMs = Math.round(sigRaw + webRaw);
|
|
6153
|
-
// `lastMilestonePerf` is still pinned at session start (the connection
|
|
6154
|
-
// closers are suppressed for adopted sessions), so this is the genuine
|
|
6155
|
-
// connect → adopted wait the user lived through.
|
|
6156
6186
|
const now = performance.now();
|
|
6157
|
-
|
|
6158
|
-
//
|
|
6159
|
-
|
|
6160
|
-
const
|
|
6161
|
-
const
|
|
6187
|
+
// Everything in anchor-relative ms — the same scale the viewer renders on
|
|
6188
|
+
// (and the prefetch marks share the page `performance` clock).
|
|
6189
|
+
const toMs = (perf) => Math.round(perf - s.anchorPerf);
|
|
6190
|
+
const startMs = toMs(t['prefetch_start'] ?? s.startedAtPerf);
|
|
6191
|
+
const wsOpenMs = Math.max(startMs, toMs(t['ws_open'] ?? t['prefetch_start'] ?? s.startedAtPerf));
|
|
6192
|
+
const readyMs = Math.max(wsOpenMs, toMs(t['ready'] ?? t['dc_open'] ?? t['ws_open'] ?? s.startedAtPerf));
|
|
6193
|
+
const adoptionAtMs = toMs(now);
|
|
6194
|
+
const sessionStartMs = toMs(s.startedAtPerf);
|
|
6195
|
+
const sigDur = wsOpenMs - startMs;
|
|
6196
|
+
const webDur = readyMs - wsOpenMs;
|
|
6197
|
+
const connectMs = readyMs - startMs;
|
|
6198
|
+
// Savings, anchored at the moment Angular reached the connect call.
|
|
6162
6199
|
const savings = {
|
|
6163
6200
|
prefetched: true,
|
|
6164
6201
|
prefetchConnectMs: connectMs,
|
|
6165
|
-
|
|
6166
|
-
|
|
6167
|
-
|
|
6202
|
+
// How much of the handshake finished before Angular was ready (the win).
|
|
6203
|
+
prefetchSavedMs: Math.max(0, Math.min(connectMs, sessionStartMs - startMs)),
|
|
6204
|
+
// The real wait Angular experienced (connect call → adopted).
|
|
6205
|
+
adoptionWaitMs: Math.max(0, adoptionAtMs - sessionStartMs),
|
|
6206
|
+
// Gap between the prefetch being ready and Angular adopting it.
|
|
6207
|
+
adoptionOverheadMs: Math.max(0, adoptionAtMs - readyMs),
|
|
6168
6208
|
};
|
|
6169
|
-
// Split the real wait across signaling / webrtc by the prefetch's own
|
|
6170
|
-
// signaling:webrtc proportion.
|
|
6171
|
-
const denom = sigRaw + webRaw;
|
|
6172
|
-
const sigMs = denom > 0 ? Math.round((waitMs * sigRaw) / denom) : 0;
|
|
6173
|
-
const webMs = Math.max(0, waitMs - sigMs);
|
|
6174
6209
|
// Region (phaseNo 1) — served from the ping cache during prefetch (~0),
|
|
6175
|
-
//
|
|
6210
|
+
// positioned at the prefetch start; ship the samples.
|
|
6176
6211
|
const region = this.readRegionCache();
|
|
6177
|
-
this.emitAdoptedPhase(0, 0, {
|
|
6212
|
+
this.emitAdoptedPhase(0, startMs, 0, adoptionAtMs, {
|
|
6178
6213
|
region: region?.region_code ?? '',
|
|
6179
|
-
pingsMs: region?.result.length
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
|
|
6184
|
-
// Signaling (phaseNo 2) — carries the streamer identity + savings summary.
|
|
6185
|
-
this.emitAdoptedPhase(1, sigMs, {
|
|
6214
|
+
pingsMs: region?.result.length ? `${region.result.join(', ')} ms` : null,
|
|
6215
|
+
});
|
|
6216
|
+
// Signaling (phaseNo 2) — [prefetch_start → ws_open]; streamer identity +
|
|
6217
|
+
// savings; ws-connected marker sits at the phase's own end.
|
|
6218
|
+
this.emitAdoptedPhase(1, startMs, sigDur, adoptionAtMs, {
|
|
6186
6219
|
...this.adoptedSignalingParams(),
|
|
6187
6220
|
...savings,
|
|
6188
|
-
prefetchSignalingMs:
|
|
6189
|
-
}, [{ id: 'signaling-ws-connected', time:
|
|
6190
|
-
// WebRTC negotiation (phaseNo 3).
|
|
6191
|
-
this.emitAdoptedPhase(2,
|
|
6221
|
+
prefetchSignalingMs: sigDur,
|
|
6222
|
+
}, [{ id: 'signaling-ws-connected', time: wsOpenMs }]);
|
|
6223
|
+
// WebRTC negotiation (phaseNo 3) — [ws_open → ready].
|
|
6224
|
+
this.emitAdoptedPhase(2, wsOpenMs, webDur, adoptionAtMs, {
|
|
6192
6225
|
iceCandidateCount: 0,
|
|
6193
|
-
|
|
6194
|
-
|
|
6195
|
-
|
|
6226
|
+
prefetchWebrtcMs: webDur,
|
|
6227
|
+
});
|
|
6228
|
+
// Unreal communication chains from the adoption moment (real wall-clock).
|
|
6229
|
+
s.lastMilestonePerf = now;
|
|
6196
6230
|
}
|
|
6197
|
-
/**
|
|
6198
|
-
*
|
|
6199
|
-
|
|
6231
|
+
/**
|
|
6232
|
+
* Emit one adopted phase positioned at its real `spanStartMs` (left-shifted
|
|
6233
|
+
* to overlap page load) with its real prefetch `duration`. `prefetched`,
|
|
6234
|
+
* `spanStartMs` and `adoptionAtMs` ride as top-level wire fields the viewer
|
|
6235
|
+
* uses to lay the bar out; the rest become display params.
|
|
6236
|
+
*/
|
|
6237
|
+
emitAdoptedPhase(bucketIdx, spanStartMs, durationMs, adoptionAtMs, extras, milestones = []) {
|
|
6200
6238
|
const s = this.session;
|
|
6201
6239
|
if (!s)
|
|
6202
6240
|
return;
|
|
@@ -6207,11 +6245,13 @@ class StreamStatusTelemetryService2 {
|
|
|
6207
6245
|
id: bucket.id,
|
|
6208
6246
|
color: bucket.color,
|
|
6209
6247
|
duration: durationMs,
|
|
6248
|
+
prefetched: true,
|
|
6249
|
+
spanStartMs,
|
|
6250
|
+
adoptionAtMs,
|
|
6210
6251
|
milestones,
|
|
6211
|
-
params: toPhaseParams(extras),
|
|
6252
|
+
params: toPhaseParams({ ...extras, prefetched: true }),
|
|
6212
6253
|
overlays: [],
|
|
6213
6254
|
});
|
|
6214
|
-
s.lastMilestonePerf += durationMs;
|
|
6215
6255
|
}
|
|
6216
6256
|
/** Streamer identity params for the adopted signaling phase — mirrors the
|
|
6217
6257
|
* `ssinfo-received` closer's shape (client / instanceName / …ssData). */
|