@arcware-cloud/pixelstreaming-websdk 1.2.18 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.esm.js CHANGED
@@ -22860,17 +22860,20 @@ __webpack_require__.r(__webpack_exports__);
22860
22860
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
22861
22861
  /* harmony export */ "ArcwareApplication": () => (/* binding */ ArcwareApplication)
22862
22862
  /* harmony export */ });
22863
- /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(655);
22864
- /* harmony import */ var _epicgames_ps_lib_pixelstreamingfrontend_ue5_5__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4506);
22865
- /* harmony import */ var _epicgames_ps_lib_pixelstreamingfrontend_ue5_5__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(9689);
22866
- /* harmony import */ var _epicgames_ps_lib_pixelstreamingfrontend_ui_ue5_5__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2092);
22867
- /* harmony import */ var _epicgames_ps_lib_pixelstreamingfrontend_ui_ue5_5__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(2396);
22868
- /* harmony import */ var _epicgames_ps_lib_pixelstreamingfrontend_ui_ue5_5__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(3652);
22869
- /* harmony import */ var _epicgames_ps_lib_pixelstreamingfrontend_ui_ue5_5__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(3579);
22863
+ /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(655);
22864
+ /* harmony import */ var _epicgames_ps_lib_pixelstreamingfrontend_ue5_5__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(4506);
22865
+ /* harmony import */ var _epicgames_ps_lib_pixelstreamingfrontend_ue5_5__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(9689);
22866
+ /* harmony import */ var _epicgames_ps_lib_pixelstreamingfrontend_ui_ue5_5__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(2092);
22867
+ /* harmony import */ var _epicgames_ps_lib_pixelstreamingfrontend_ui_ue5_5__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(2396);
22868
+ /* harmony import */ var _epicgames_ps_lib_pixelstreamingfrontend_ui_ue5_5__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(3652);
22869
+ /* harmony import */ var _epicgames_ps_lib_pixelstreamingfrontend_ui_ue5_5__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(3579);
22870
22870
  /* harmony import */ var _styles_ArcwarePixelStreamingApplicationStyles__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2299);
22871
- /* harmony import */ var _ui_AudioButton__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(7734);
22872
- /* harmony import */ var _ui_MicButton__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(2293);
22873
- /* harmony import */ var _ui_StopButton__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(9684);
22871
+ /* harmony import */ var _ui_AudioButton__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(7734);
22872
+ /* harmony import */ var _ui_MicButton__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(2293);
22873
+ /* harmony import */ var _ui_StopButton__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(9684);
22874
+ /* harmony import */ var _features_ArcwareEventUtil__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(471);
22875
+ /* harmony import */ var _features_ArcwareFileTransferUtil__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2768);
22876
+ /* harmony import */ var _features_common__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(2483);
22874
22877
  var _a;
22875
22878
 
22876
22879
 
@@ -22879,6 +22882,9 @@ var _a;
22879
22882
 
22880
22883
 
22881
22884
 
22885
+
22886
+
22887
+
22882
22888
  const DEBUG = false;
22883
22889
  const TextKeyRegex = /^(\['([a-zA-Z 0-9-_]+)'\] )/;
22884
22890
  function findElementByTextContent(root, selector) {
@@ -22928,7 +22934,7 @@ const removals = {
22928
22934
  // By default the UI settings should be disabled, since we have a custom method for them.
22929
22935
  "['UI'] section.settingsContainer:not([id])": true,
22930
22936
  // Hide option of start audio muted or not
22931
- "#StartVideoMuted": true,
22937
+ "#StartVideoMuted": true
22932
22938
  };
22933
22939
  if (DEBUG)
22934
22940
  Object.keys(removals).forEach((key) => {
@@ -22946,7 +22952,7 @@ function remove(key, element) {
22946
22952
  }
22947
22953
  }
22948
22954
  }
22949
- class ArcwareApplication extends _epicgames_ps_lib_pixelstreamingfrontend_ui_ue5_5__WEBPACK_IMPORTED_MODULE_1__.Application {
22955
+ class ArcwareApplication extends _epicgames_ps_lib_pixelstreamingfrontend_ui_ue5_5__WEBPACK_IMPORTED_MODULE_2__.Application {
22950
22956
  get rootElement() {
22951
22957
  return super.rootElement;
22952
22958
  }
@@ -22954,6 +22960,8 @@ class ArcwareApplication extends _epicgames_ps_lib_pixelstreamingfrontend_ui_ue5
22954
22960
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
22955
22961
  super(options);
22956
22962
  this.responseCallback = null;
22963
+ this.analyticsEventCallback = null;
22964
+ this.fileDownloadCallback = null;
22957
22965
  this.ArcwareSection = this.configUI.buildSectionWithHeading(this.settingsPanel.settingsContentElement, "Arcware Cloud");
22958
22966
  this.stream = options === null || options === void 0 ? void 0 : options.stream;
22959
22967
  this.emitUIInteraction = this.emitUIInteraction.bind(this);
@@ -22966,7 +22974,7 @@ class ArcwareApplication extends _epicgames_ps_lib_pixelstreamingfrontend_ui_ue5
22966
22974
  this.parentElement = (_b = this === null || this === void 0 ? void 0 : this.videoElementParent) === null || _b === void 0 ? void 0 : _b.parentElement;
22967
22975
  this.webRtcController = (_c = this === null || this === void 0 ? void 0 : this.stream) === null || _c === void 0 ? void 0 : _c["_webRtcController"];
22968
22976
  this.addLoveLetterhandler();
22969
- (_d = this === null || this === void 0 ? void 0 : this.stream) === null || _d === void 0 ? void 0 : _d.addResponseEventListener("ue-response", (response) => this === null || this === void 0 ? void 0 : this.applicationResponse(response));
22977
+ (_d = this === null || this === void 0 ? void 0 : this.stream) === null || _d === void 0 ? void 0 : _d.addResponseEventListener("ue-response-internal", (response) => this.applicationResponse(response));
22970
22978
  (_f = (_e = this === null || this === void 0 ? void 0 : this.stream) === null || _e === void 0 ? void 0 : _e.sessionIdHandler) === null || _f === void 0 ? void 0 : _f.add((sessionId) => {
22971
22979
  var _a;
22972
22980
  (_a = this === null || this === void 0 ? void 0 : this.statsPanel) === null || _a === void 0 ? void 0 : _a.addOrUpdateSessionStat("sessionId", "SessionId", sessionId);
@@ -22995,6 +23003,8 @@ class ArcwareApplication extends _epicgames_ps_lib_pixelstreamingfrontend_ui_ue5
22995
23003
  this.uiElementsVisibility(false);
22996
23004
  this.addTextToConnectOverlay();
22997
23005
  this.preventDefaultKeyboardEvents();
23006
+ this.analyticsEventCallback = this.analyticsEvent;
23007
+ this.fileDownloadCallback = this.fileDownload;
22998
23008
  }
22999
23009
  addLoveLetterhandler() {
23000
23010
  var _a, _b;
@@ -23085,9 +23095,9 @@ class ArcwareApplication extends _epicgames_ps_lib_pixelstreamingfrontend_ui_ue5
23085
23095
  (_d = this === null || this === void 0 ? void 0 : this.ArcwareSection) === null || _d === void 0 ? void 0 : _d.appendChild(disclaimer);
23086
23096
  }
23087
23097
  // Add disable SessionId button.
23088
- (_e = this === null || this === void 0 ? void 0 : this.ArcwareSection) === null || _e === void 0 ? void 0 : _e.appendChild(new _epicgames_ps_lib_pixelstreamingfrontend_ui_ue5_5__WEBPACK_IMPORTED_MODULE_2__.SettingUIFlag(new _epicgames_ps_lib_pixelstreamingfrontend_ue5_5__WEBPACK_IMPORTED_MODULE_3__.SettingFlag((_f = ArcwareApplication === null || ArcwareApplication === void 0 ? void 0 : ArcwareApplication.Flags) === null || _f === void 0 ? void 0 : _f.noSession, "Disable SessionId", "Disable sessionId based reconnection.", (_h = (_g = this === null || this === void 0 ? void 0 : this.stream) === null || _g === void 0 ? void 0 : _g.session) === null || _h === void 0 ? void 0 : _h.noSession, true)).rootElement);
23098
+ (_e = this === null || this === void 0 ? void 0 : this.ArcwareSection) === null || _e === void 0 ? void 0 : _e.appendChild(new _epicgames_ps_lib_pixelstreamingfrontend_ui_ue5_5__WEBPACK_IMPORTED_MODULE_3__.SettingUIFlag(new _epicgames_ps_lib_pixelstreamingfrontend_ue5_5__WEBPACK_IMPORTED_MODULE_4__.SettingFlag((_f = ArcwareApplication === null || ArcwareApplication === void 0 ? void 0 : ArcwareApplication.Flags) === null || _f === void 0 ? void 0 : _f.noSession, "Disable SessionId", "Disable sessionId based reconnection.", (_h = (_g = this === null || this === void 0 ? void 0 : this.stream) === null || _g === void 0 ? void 0 : _g.session) === null || _h === void 0 ? void 0 : _h.noSession, true)).rootElement);
23089
23099
  // Add rephrasing of signalling Url.
23090
- (_j = this === null || this === void 0 ? void 0 : this.ArcwareSection) === null || _j === void 0 ? void 0 : _j.appendChild(new _epicgames_ps_lib_pixelstreamingfrontend_ui_ue5_5__WEBPACK_IMPORTED_MODULE_4__.SettingUIText(new _epicgames_ps_lib_pixelstreamingfrontend_ue5_5__WEBPACK_IMPORTED_MODULE_5__.SettingText("signalling-url", "Signalling URL", "Signalling URL", `${(_l = (_k = this === null || this === void 0 ? void 0 : this.stream) === null || _k === void 0 ? void 0 : _k.config) === null || _l === void 0 ? void 0 : _l.getTextSettingValue("ss")}?${(_p = (_o = (_m = this === null || this === void 0 ? void 0 : this.stream) === null || _m === void 0 ? void 0 : _m.config) === null || _o === void 0 ? void 0 : _o.urlFlags) === null || _p === void 0 ? void 0 : _p.slice(1)}`, false)).rootElement);
23100
+ (_j = this === null || this === void 0 ? void 0 : this.ArcwareSection) === null || _j === void 0 ? void 0 : _j.appendChild(new _epicgames_ps_lib_pixelstreamingfrontend_ui_ue5_5__WEBPACK_IMPORTED_MODULE_5__.SettingUIText(new _epicgames_ps_lib_pixelstreamingfrontend_ue5_5__WEBPACK_IMPORTED_MODULE_6__.SettingText("signalling-url", "Signalling URL", "Signalling URL", `${(_l = (_k = this === null || this === void 0 ? void 0 : this.stream) === null || _k === void 0 ? void 0 : _k.config) === null || _l === void 0 ? void 0 : _l.getTextSettingValue("ss")}?${(_p = (_o = (_m = this === null || this === void 0 ? void 0 : this.stream) === null || _m === void 0 ? void 0 : _m.config) === null || _o === void 0 ? void 0 : _o.urlFlags) === null || _p === void 0 ? void 0 : _p.slice(1)}`, false)).rootElement);
23091
23101
  if (DEBUG)
23092
23102
  setTimeout(() => {
23093
23103
  var _a;
@@ -23096,7 +23106,7 @@ class ArcwareApplication extends _epicgames_ps_lib_pixelstreamingfrontend_ui_ue5
23096
23106
  }
23097
23107
  createAudioToggleButton() {
23098
23108
  var _a;
23099
- const audioButton = new _ui_AudioButton__WEBPACK_IMPORTED_MODULE_6__.AudioButton(this === null || this === void 0 ? void 0 : this.stream);
23109
+ const audioButton = new _ui_AudioButton__WEBPACK_IMPORTED_MODULE_7__.AudioButton(this === null || this === void 0 ? void 0 : this.stream);
23100
23110
  const videoElementParent = (_a = this === null || this === void 0 ? void 0 : this.stream) === null || _a === void 0 ? void 0 : _a.videoElementParent;
23101
23111
  const playerUI = videoElementParent.parentElement;
23102
23112
  // Append the button to the videoElementParent
@@ -23112,7 +23122,7 @@ class ArcwareApplication extends _epicgames_ps_lib_pixelstreamingfrontend_ui_ue5
23112
23122
  }
23113
23123
  createMicToggleButton() {
23114
23124
  var _a;
23115
- const micButton = new _ui_MicButton__WEBPACK_IMPORTED_MODULE_7__.MicButton(this === null || this === void 0 ? void 0 : this.stream);
23125
+ const micButton = new _ui_MicButton__WEBPACK_IMPORTED_MODULE_8__.MicButton(this === null || this === void 0 ? void 0 : this.stream);
23116
23126
  const videoElementParent = (_a = this === null || this === void 0 ? void 0 : this.stream) === null || _a === void 0 ? void 0 : _a.videoElementParent;
23117
23127
  const playerUI = videoElementParent.parentElement;
23118
23128
  // Append the button to the videoElementParent
@@ -23129,7 +23139,7 @@ class ArcwareApplication extends _epicgames_ps_lib_pixelstreamingfrontend_ui_ue5
23129
23139
  createStopButton() {
23130
23140
  var _a, _b, _c, _d;
23131
23141
  if (((_c = (_b = (_a = this.stream) === null || _a === void 0 ? void 0 : _a.config) === null || _b === void 0 ? void 0 : _b.settings) === null || _c === void 0 ? void 0 : _c.stopButton) === true) {
23132
- const stopButton = new _ui_StopButton__WEBPACK_IMPORTED_MODULE_8__.StopButton(this === null || this === void 0 ? void 0 : this.stream);
23142
+ const stopButton = new _ui_StopButton__WEBPACK_IMPORTED_MODULE_9__.StopButton(this === null || this === void 0 ? void 0 : this.stream);
23133
23143
  const videoElementParent = (_d = this === null || this === void 0 ? void 0 : this.stream) === null || _d === void 0 ? void 0 : _d.videoElementParent;
23134
23144
  const playerUI = videoElementParent.parentElement;
23135
23145
  // Append the button to the videoElementParent
@@ -23225,12 +23235,30 @@ class ArcwareApplication extends _epicgames_ps_lib_pixelstreamingfrontend_ui_ue5
23225
23235
  });
23226
23236
  }
23227
23237
  applicationResponse(response) {
23228
- if (this === null || this === void 0 ? void 0 : this.responseCallback) {
23229
- this === null || this === void 0 ? void 0 : this.responseCallback(response);
23238
+ var _a, _b;
23239
+ let obj = null;
23240
+ try {
23241
+ obj = (0,_features_common__WEBPACK_IMPORTED_MODULE_10__.parseUnknownToObject)(response, { allowJsonish: true });
23242
+ }
23243
+ catch (_c) {
23244
+ // If not parseable, fall through to generic callback
23245
+ }
23246
+ const t = (0,_features_common__WEBPACK_IMPORTED_MODULE_10__.normalizeType)(obj["type"]);
23247
+ // route to exactly one callback
23248
+ if (t === "event") {
23249
+ (_a = this.analyticsEventCallback) === null || _a === void 0 ? void 0 : _a.call(this, response);
23250
+ return;
23251
+ }
23252
+ if (t === "filetransfer" || t === "createscreenshot") {
23253
+ (_b = this.fileDownloadCallback) === null || _b === void 0 ? void 0 : _b.call(this, response);
23254
+ return;
23255
+ }
23256
+ if (this.responseCallback) {
23257
+ this.responseCallback(response);
23230
23258
  }
23231
23259
  }
23232
23260
  applyArcwareStyles() {
23233
- const PixelStreamingApplicationStyles = new _epicgames_ps_lib_pixelstreamingfrontend_ui_ue5_5__WEBPACK_IMPORTED_MODULE_9__.PixelStreamingApplicationStyle(_styles_ArcwarePixelStreamingApplicationStyles__WEBPACK_IMPORTED_MODULE_0__.ArcwareStyles);
23261
+ const PixelStreamingApplicationStyles = new _epicgames_ps_lib_pixelstreamingfrontend_ui_ue5_5__WEBPACK_IMPORTED_MODULE_11__.PixelStreamingApplicationStyle(_styles_ArcwarePixelStreamingApplicationStyles__WEBPACK_IMPORTED_MODULE_0__.ArcwareStyles);
23234
23262
  PixelStreamingApplicationStyles === null || PixelStreamingApplicationStyles === void 0 ? void 0 : PixelStreamingApplicationStyles.applyStyleSheet();
23235
23263
  }
23236
23264
  /** Emit an event towards the UE Application.
@@ -23265,10 +23293,53 @@ class ArcwareApplication extends _epicgames_ps_lib_pixelstreamingfrontend_ui_ue5
23265
23293
  }
23266
23294
  }
23267
23295
  }
23296
+ analyticsEvent(response) {
23297
+ var _a;
23298
+ const event = (0,_features_ArcwareEventUtil__WEBPACK_IMPORTED_MODULE_12__.toAnalyticsEvent)(response, { maxPayloadBytes: 512, allowJsonish: true });
23299
+ if (event.ok === false) {
23300
+ console.warn("Invalid AnalyticsEvent:", event.error, response);
23301
+ return;
23302
+ }
23303
+ (_a = this.stream) === null || _a === void 0 ? void 0 : _a.send(event.value); // send the sanitized object
23304
+ }
23305
+ fileDownload(response) {
23306
+ var _a;
23307
+ let msg;
23308
+ try {
23309
+ msg = (0,_features_ArcwareFileTransferUtil__WEBPACK_IMPORTED_MODULE_1__.parseControl)(response, _features_ArcwareFileTransferUtil__WEBPACK_IMPORTED_MODULE_1__.ZFileTransfer);
23310
+ }
23311
+ catch (e) {
23312
+ console.warn("Invalid control message:", e);
23313
+ return;
23314
+ }
23315
+ const file = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.file;
23316
+ if (!file || !file.data || !file.mimetype)
23317
+ return;
23318
+ const ext = (0,_features_common__WEBPACK_IMPORTED_MODULE_10__.extFromMime)(file.mimetype);
23319
+ const base = msg.filename && msg.filename.trim() ? (0,_features_common__WEBPACK_IMPORTED_MODULE_10__.sanitizeFilename)(msg.filename) : (0,_features_common__WEBPACK_IMPORTED_MODULE_10__.randomHash)();
23320
+ const name = base + ext;
23321
+ try {
23322
+ // ✅ Make safe Blob parts (clone each chunk to ensure regular ArrayBuffer)
23323
+ const chunks = file.data; // <-- remove the generic
23324
+ const safeParts = chunks.map((u8) => u8.slice()); // fresh ArrayBuffers
23325
+ const blob = new Blob(safeParts, { type: file.mimetype });
23326
+ const url = URL.createObjectURL(blob);
23327
+ const a = document.createElement("a");
23328
+ a.href = url;
23329
+ a.download = name;
23330
+ document.body.appendChild(a);
23331
+ a.click();
23332
+ document.body.removeChild(a);
23333
+ setTimeout(() => URL.revokeObjectURL(url), 0);
23334
+ }
23335
+ catch (err) {
23336
+ console.error("Download failed:", err);
23337
+ }
23338
+ }
23268
23339
  }
23269
23340
  ArcwareApplication.Flags = (_a = class {
23270
23341
  },
23271
- (0,tslib__WEBPACK_IMPORTED_MODULE_10__.__setFunctionName)(_a, "Flags"),
23342
+ (0,tslib__WEBPACK_IMPORTED_MODULE_13__.__setFunctionName)(_a, "Flags"),
23272
23343
  _a.noSession = "noSession",
23273
23344
  _a);
23274
23345
 
@@ -23356,7 +23427,7 @@ class ArcwareConfig extends _epicgames_ps_lib_pixelstreamingfrontend_ue5_5__WEBP
23356
23427
  if (!config.initialSettings.ss)
23357
23428
  config.initialSettings.ss = DefaultUrl;
23358
23429
  super(config);
23359
- this.VERSION = "1.2.18";
23430
+ this.VERSION = "1.3.0";
23360
23431
  this.settings = settings;
23361
23432
  this.session = new _domain_Session__WEBPACK_IMPORTED_MODULE_0__.Session();
23362
23433
  this._initialSettings = config.initialSettings;
@@ -23513,7 +23584,7 @@ __webpack_require__.r(__webpack_exports__);
23513
23584
  /* harmony import */ var _ui_ArcwareLogoLoader__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(6469);
23514
23585
  /* harmony import */ var _ui_MicrophoneOverlay__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(3613);
23515
23586
  /* harmony import */ var _domain_ConnectionIdentifier__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(5999);
23516
- /* harmony import */ var _DiagnosticsCollector__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6478);
23587
+ /* harmony import */ var _features_DiagnosticsCollector__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(8429);
23517
23588
 
23518
23589
 
23519
23590
 
@@ -23573,8 +23644,8 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23573
23644
  this.config = config;
23574
23645
  this.loveLettersList = [];
23575
23646
  this.microphoneOverlay = new _ui_MicrophoneOverlay__WEBPACK_IMPORTED_MODULE_7__.MicrophoneOverlay(this);
23576
- this.diagnosticsCollector = new _DiagnosticsCollector__WEBPACK_IMPORTED_MODULE_3__.DiagnosticsCollector({
23577
- enableBandwidthProbe: false,
23647
+ this.diagnosticsCollector = new _features_DiagnosticsCollector__WEBPACK_IMPORTED_MODULE_3__.DiagnosticsCollector({
23648
+ enableBandwidthProbe: false
23578
23649
  });
23579
23650
  // this.loveLettersContainer = null;
23580
23651
  this.wrapWebSocketOnCloseHandler();
@@ -23602,7 +23673,7 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23602
23673
  this.webRtcController.streamMessageController.registerMessageHandler(0, // MessageDirection.ToStreamer,
23603
23674
  "TextboxEntry", (messageData) => {
23604
23675
  try {
23605
- this.webRtcController.sendMessageController.sendMessageToStreamer('TextboxEntry', messageData);
23676
+ this.webRtcController.sendMessageController.sendMessageToStreamer("TextboxEntry", messageData);
23606
23677
  }
23607
23678
  catch (e) {
23608
23679
  console.error(`Error: ToStreamer.TextboxEntry`, e);
@@ -23704,7 +23775,7 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23704
23775
  _domain_EventHandler__WEBPACK_IMPORTED_MODULE_6__.EventHandler.Emit(this.sessionIdHandler, message.sessionId);
23705
23776
  }
23706
23777
  onVideoInitialized() {
23707
- /** The videoInitialized event is important for the Arcware Cloud BAckend, since without that event the instance will be cleaned up (killed). */
23778
+ /** The videoInitialized event is important for the Arcware Cloud Backend, since without that event the instance will be cleaned up (killed). */
23708
23779
  this.handleMouseLock();
23709
23780
  this.send({ type: "onVideoInitialized" });
23710
23781
  _domain_EventHandler__WEBPACK_IMPORTED_MODULE_6__.EventHandler.Emit(this.videoInitializedHandler, undefined);
@@ -23970,775 +24041,971 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23970
24041
 
23971
24042
  /***/ }),
23972
24043
 
23973
- /***/ 6478:
24044
+ /***/ 5602:
23974
24045
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
23975
24046
 
23976
24047
  __webpack_require__.r(__webpack_exports__);
23977
24048
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
23978
- /* harmony export */ "DiagnosticsCollector": () => (/* binding */ DiagnosticsCollector),
23979
- /* harmony export */ "Orientation": () => (/* binding */ Orientation),
23980
- /* harmony export */ "Type": () => (/* binding */ Type)
24049
+ /* harmony export */ "ArcwareSettingsSchema": () => (/* binding */ ArcwareSettingsSchema)
23981
24050
  /* harmony export */ });
23982
- /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(655);
23983
- /**
23984
- * DiagnosticsCollector.ts
23985
- * Lightweight, privacy-aware client diagnostics for troubleshooting + analytics.
23986
- * WebRTC collection intentionally omitted (to add later).
23987
- *
23988
- * Notes:
23989
- * - Some fields are best-effort due to browser restrictions; they're nullable.
23990
- * - Bandwidth probe requires a small binary served with:
23991
- * Content-Type: application/octet-stream
23992
- * Content-Encoding: identity
23993
- * Cache-Control: no-store
23994
- * Default location: /diag/probe.bin
23995
- */
24051
+ /* harmony import */ var zod__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1604);
24052
+ /* harmony import */ var _arcware_cloud_shared_pixelstreaming_websdk__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7910);
23996
24053
 
23997
- var Orientation;
23998
- (function (Orientation) {
23999
- Orientation["Landscape"] = "landscape";
24000
- Orientation["Portrait"] = "portrait";
24001
- })(Orientation || (Orientation = {}));
24002
- var Type;
24003
- (function (Type) {
24004
- Type["Desktop"] = "desktop";
24005
- Type["Mobile"] = "mobile";
24006
- Type["Tablet"] = "tablet";
24007
- })(Type || (Type = {}));
24008
- /**
24009
- * DiagnosticsCollector
24010
- */
24011
- class DiagnosticsCollector {
24012
- constructor(opts = {}) {
24013
- var _a, _b, _c, _d;
24014
- this.uaInfoCache = null;
24015
- this.opts = {
24016
- enableBandwidthProbe: (_a = opts.enableBandwidthProbe) !== null && _a !== void 0 ? _a : true,
24017
- probeUrl: (_b = opts.probeUrl) !== null && _b !== void 0 ? _b : "/diag/probe.bin",
24018
- respectSaveData: (_c = opts.respectSaveData) !== null && _c !== void 0 ? _c : true,
24019
- sampleRate: Math.max(0, Math.min(1, (_d = opts.sampleRate) !== null && _d !== void 0 ? _d : 1))
24020
- };
24021
- }
24022
- /**
24023
- * Collect the diagnostics payload.
24024
- */
24025
- collect() {
24026
- var _a, _b;
24027
- return (0,tslib__WEBPACK_IMPORTED_MODULE_0__.__awaiter)(this, void 0, void 0, function* () {
24028
- if (!this.shouldSample()) {
24029
- return {
24030
- device: { type: Type.Desktop, touchSupport: false },
24031
- runtime: { browser: { family: "Unknown", version: null }, os: { family: "Unknown", version: null } },
24032
- powerHints: { saveData: null, prefersReducedData: null, prefersReducedMotion: null },
24033
- network: { type: null, effectiveType: null, downlinkMbps: null, rttMs: null, measuredDownlinkMbps: null },
24034
- timestamps: { collectedAt: Date.now() }
24035
- };
24036
- }
24037
- const collectedAt = Date.now();
24038
- const [uaInfo, codecSupport, measuredDownlinkMbps] = yield Promise.all([
24039
- this.getUAInfo(),
24040
- this.getCodecSupport(),
24041
- this.maybeMeasureBandwidth()
24042
- ]);
24043
- return {
24044
- device: {
24045
- type: this.inferDeviceType(),
24046
- vendor: null,
24047
- model: uaInfo.model,
24048
- architecture: uaInfo.architecture,
24049
- bitness: uaInfo.bitness,
24050
- touchSupport: this.hasTouch(),
24051
- hardwareConcurrency: (_a = navigator.hardwareConcurrency) !== null && _a !== void 0 ? _a : null,
24052
- deviceMemory: (_b = navigator.deviceMemory) !== null && _b !== void 0 ? _b : null,
24053
- orientation: this.getOrientation(),
24054
- webgl: this.hasWebGL(),
24055
- codecs: codecSupport
24056
- },
24057
- runtime: {
24058
- browser: { family: uaInfo.browserFamily, version: uaInfo.browserVersion },
24059
- os: { family: uaInfo.osFamily, version: uaInfo.osVersion }
24060
- },
24061
- powerHints: this.getPowerHints(),
24062
- network: this.getNetworkInfo(measuredDownlinkMbps),
24063
- timestamps: { collectedAt }
24064
- };
24065
- });
24066
- }
24067
- /**
24068
- * Collect a safe subset of WebRTC stats once PC is connected.
24054
+
24055
+ /** Arcware Settings. */
24056
+ const ArcwareSettingsSchema = zod__WEBPACK_IMPORTED_MODULE_1__.z.object({
24057
+ /** Overwrites the Session-Tool and uses the provided session instead. */
24058
+ session: zod__WEBPACK_IMPORTED_MODULE_1__.z.string().optional(),
24059
+ /** Can be used to be added to the request in order to verify access to private projects.
24060
+ * For internal use only. => Preview page.
24069
24061
  */
24070
- collectWebRTC(pc) {
24071
- var _a, _b, _c, _d, _e, _f;
24072
- return (0,tslib__WEBPACK_IMPORTED_MODULE_0__.__awaiter)(this, void 0, void 0, function* () {
24073
- try {
24074
- const stats = yield pc.getStats();
24075
- let selected, transport, local, remote;
24076
- stats.forEach((r) => {
24077
- if (r.type === "transport" && r.selectedCandidatePairId)
24078
- transport = r;
24079
- if (r.type === "candidate-pair" && (r.selected || r.nominated))
24080
- selected = r;
24081
- });
24082
- if (selected) {
24083
- local = stats.get(selected.localCandidateId);
24084
- remote = stats.get(selected.remoteCandidateId);
24085
- }
24086
- return {
24087
- candidatePairId: (_a = selected === null || selected === void 0 ? void 0 : selected.id) !== null && _a !== void 0 ? _a : null,
24088
- localCandidateType: (_b = local === null || local === void 0 ? void 0 : local.candidateType) !== null && _b !== void 0 ? _b : null,
24089
- remoteCandidateType: (_c = remote === null || remote === void 0 ? void 0 : remote.candidateType) !== null && _c !== void 0 ? _c : null,
24090
- protocol: (_d = local === null || local === void 0 ? void 0 : local.protocol) !== null && _d !== void 0 ? _d : null,
24091
- networkType: (_e = local === null || local === void 0 ? void 0 : local.networkType) !== null && _e !== void 0 ? _e : null,
24092
- dtlsCipher: (_f = transport === null || transport === void 0 ? void 0 : transport.dtlsCipher) !== null && _f !== void 0 ? _f : null
24093
- };
24094
- }
24095
- catch (_g) {
24096
- return {};
24097
- }
24098
- });
24062
+ token: zod__WEBPACK_IMPORTED_MODULE_1__.z.string().optional(),
24063
+ /** @deprecated in there for legacy use. Can only be used when token is provided. */
24064
+ bypass: zod__WEBPACK_IMPORTED_MODULE_1__.z.boolean().optional(),
24065
+ // /** Configure DirectFlow Token. */
24066
+ // directFlow: z.string().optional(),
24067
+ /** Handler for server side error messages. */
24068
+ errorHandler: zod__WEBPACK_IMPORTED_MODULE_1__.z["function"]().args(_arcware_cloud_shared_pixelstreaming_websdk__WEBPACK_IMPORTED_MODULE_0__.Messages.ZErrorMessage).returns(zod__WEBPACK_IMPORTED_MODULE_1__.z["void"]()).optional(),
24069
+ /** Handler for queue events. */
24070
+ queueHandler: zod__WEBPACK_IMPORTED_MODULE_1__.z["function"]().args(_arcware_cloud_shared_pixelstreaming_websdk__WEBPACK_IMPORTED_MODULE_0__.Messages.ZQueue).returns(zod__WEBPACK_IMPORTED_MODULE_1__.z["void"]()).optional(),
24071
+ /** Handler for sessionId message. */
24072
+ sessionIdHandler: zod__WEBPACK_IMPORTED_MODULE_1__.z["function"]().args(zod__WEBPACK_IMPORTED_MODULE_1__.z.string()).returns(zod__WEBPACK_IMPORTED_MODULE_1__.z["void"]()).optional(),
24073
+ /** Handler for love letters.
24074
+ * "LoveLetters" are send from backend to the SDK to state what phase the connection currently is in. */
24075
+ loveLetterHandler: zod__WEBPACK_IMPORTED_MODULE_1__.z["function"]().args(_arcware_cloud_shared_pixelstreaming_websdk__WEBPACK_IMPORTED_MODULE_0__.Messages.ZLoveLetter).returns(zod__WEBPACK_IMPORTED_MODULE_1__.z["void"]()).optional(),
24076
+ /** Show or hide the fullscreen button. */
24077
+ fullscreenButton: zod__WEBPACK_IMPORTED_MODULE_1__.z.boolean().optional(),
24078
+ /** Show or hide the settings button. */
24079
+ settingsButton: zod__WEBPACK_IMPORTED_MODULE_1__.z.boolean().optional(),
24080
+ /** Show or hide the info button. */
24081
+ infoButton: zod__WEBPACK_IMPORTED_MODULE_1__.z.boolean().optional(),
24082
+ /** Show or hide the audio button. */
24083
+ audioButton: zod__WEBPACK_IMPORTED_MODULE_1__.z.boolean().optional(),
24084
+ /** Show or hide the microphone button. */
24085
+ micButton: zod__WEBPACK_IMPORTED_MODULE_1__.z.boolean().optional(),
24086
+ /** Show or hide the microphone button. */
24087
+ stopButton: zod__WEBPACK_IMPORTED_MODULE_1__.z.boolean().optional(),
24088
+ /** Show or hide the connectionStrengthIcon button. */
24089
+ connectionStrengthIcon: zod__WEBPACK_IMPORTED_MODULE_1__.z.boolean().optional(),
24090
+ /** ShareId, used for sharing your project.
24091
+ * Using ArcwareInit will set this required property for you. */
24092
+ shareId: zod__WEBPACK_IMPORTED_MODULE_1__.z.string().startsWith("share-").optional(),
24093
+ /** Id of your project, only required if your shareId refers to multiple projects.
24094
+ * Using ArcwareInit will set this required property for you. */
24095
+ projectId: zod__WEBPACK_IMPORTED_MODULE_1__.z.string().optional(),
24096
+ /** Enable/Disable LoveLetter logging to the console. */
24097
+ loveLetterLogging: zod__WEBPACK_IMPORTED_MODULE_1__.z.boolean().optional(),
24098
+ /** Enable/Disable Connection Identifier logging to the console. */
24099
+ connectionIdentifierLoggingDisabled: zod__WEBPACK_IMPORTED_MODULE_1__.z.boolean().optional(),
24100
+ /** Width with which instance should be started */
24101
+ startWidth: zod__WEBPACK_IMPORTED_MODULE_1__.z.number().optional(),
24102
+ /** Height with which instance should be started */
24103
+ startHeight: zod__WEBPACK_IMPORTED_MODULE_1__.z.number().optional()
24104
+ });
24105
+
24106
+
24107
+ /***/ }),
24108
+
24109
+ /***/ 5999:
24110
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
24111
+
24112
+ __webpack_require__.r(__webpack_exports__);
24113
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
24114
+ /* harmony export */ "ConnectionIdentifier": () => (/* binding */ ConnectionIdentifier),
24115
+ /* harmony export */ "WebsocketState": () => (/* binding */ WebsocketState)
24116
+ /* harmony export */ });
24117
+ var WebsocketState;
24118
+ (function (WebsocketState) {
24119
+ // Socket has been created. The connection is not yet open.
24120
+ WebsocketState[WebsocketState["CONNECTING"] = 0] = "CONNECTING";
24121
+ // The connection is open and ready to communicate.
24122
+ WebsocketState[WebsocketState["OPEN"] = 1] = "OPEN";
24123
+ // The connection is in the process of closing.
24124
+ WebsocketState[WebsocketState["CLOSING"] = 2] = "CLOSING";
24125
+ // The connection is closed or couldn't be opened.
24126
+ WebsocketState[WebsocketState["CLOSED"] = 3] = "CLOSED";
24127
+ })(WebsocketState || (WebsocketState = {}));
24128
+ class ConnectionIdentifier {
24129
+ static get Instance() {
24130
+ if (!this.instance) {
24131
+ this.instance = new ConnectionIdentifier();
24132
+ }
24133
+ return this.instance;
24099
24134
  }
24100
- /**
24101
- * Helper to embed diagnostics in a hello envelope.
24102
- */
24103
- buildHelloEnvelope(base, pc) {
24104
- return (0,tslib__WEBPACK_IMPORTED_MODULE_0__.__awaiter)(this, void 0, void 0, function* () {
24105
- const diag = yield this.collect();
24106
- const webrtc = pc ? yield this.collectWebRTC(pc) : undefined;
24107
- const ext = webrtc ? { diag: Object.assign(Object.assign({}, diag), { webrtc }) } : { diag };
24108
- return Object.assign(Object.assign({}, base), { ext });
24109
- });
24135
+ get WebsocketStates() {
24136
+ return this.aps.map((ps) => ps.websocketState);
24110
24137
  }
24111
- // ---------- Internals ----------
24112
- shouldSample() {
24113
- if (this.opts.sampleRate >= 1)
24114
- return true;
24115
- return Math.random() < this.opts.sampleRate;
24138
+ constructor() {
24139
+ this.aps = [];
24140
+ this.enabled = true;
24116
24141
  }
24117
- hasTouch() {
24142
+ register(ps) {
24118
24143
  var _a;
24119
- const nav = navigator;
24120
- return "ontouchstart" in window || ((_a = nav.maxTouchPoints) !== null && _a !== void 0 ? _a : 0) > 0;
24121
- }
24122
- getOrientation() {
24123
- var _a;
24124
- try {
24125
- if ((_a = screen.orientation) === null || _a === void 0 ? void 0 : _a.type) {
24126
- return screen.orientation.type.startsWith("portrait") ? Orientation.Portrait : Orientation.Landscape;
24127
- }
24128
- if (window.matchMedia) {
24129
- return window.matchMedia("(orientation: portrait)").matches ? Orientation.Portrait : Orientation.Landscape;
24130
- }
24144
+ if (!this.aps.includes(ps)) {
24145
+ this.aps.push(ps);
24146
+ if ((_a = ps.config.settings) === null || _a === void 0 ? void 0 : _a.connectionIdentifierLoggingDisabled)
24147
+ this.disable();
24131
24148
  }
24132
- catch (_b) { }
24133
- return null;
24149
+ if (!this.interval)
24150
+ this.interval = setInterval(this.connectionWatcher.bind(this), 10 * 1000);
24134
24151
  }
24135
- hasWebGL() {
24136
- try {
24137
- const canvas = document.createElement("canvas");
24138
- return !!(canvas.getContext("webgl") || canvas.getContext("experimental-webgl"));
24139
- }
24140
- catch (_a) {
24141
- return false;
24142
- }
24152
+ GetWebSocketStates() {
24153
+ return this.aps.map((ps) => ps.websocketState);
24154
+ }
24155
+ ActiveInstances() {
24156
+ return this.GetWebSocketStates().filter((state) => state <= WebsocketState.OPEN).length;
24157
+ }
24158
+ disable() {
24159
+ this.enabled = false;
24143
24160
  }
24144
24161
  /**
24145
- * Heuristic device type: best-effort only.
24146
- * Tries UA-CH, then UA fallback, then touch + screen size heuristics.
24162
+ * The purpose of this method is to periodically tell the developer the amount of connected instances.
24163
+ * This way the developer can figure out, when they messed up the implementation and there's shadow instances still running.
24164
+ * Or when they accidentally set up multiple connections!
24165
+ * Things like that can happen, for example if the React.useEffect was not setup perfectly!
24147
24166
  */
24148
- inferDeviceType() {
24149
- const uaData = navigator.userAgentData;
24150
- const ua = navigator.userAgent || "";
24151
- const isMobileCH = (uaData === null || uaData === void 0 ? void 0 : uaData.mobile) === true;
24152
- const minDim = Math.min(screen.width, screen.height);
24153
- // 1. Explicit iPad / tablet detection via UA (iOS often lies in CH)
24154
- if (/\b(iPad|Tablet)\b/i.test(ua)) {
24155
- return Type.Tablet;
24167
+ connectionWatcher() {
24168
+ if (!this.enabled)
24169
+ return;
24170
+ const activeStates = this.GetWebSocketStates().filter((state) => state <= WebsocketState.OPEN);
24171
+ if (activeStates.length === 1) {
24172
+ console.log(`PixelStreaming Instance is connected.`);
24156
24173
  }
24157
- // 2. UA-CH mobile hint (Chromium only, often accurate for Android)
24158
- if (isMobileCH) {
24159
- if (minDim >= 600)
24160
- return Type.Tablet;
24161
- return Type.Mobile;
24174
+ else if (activeStates.length !== 0) {
24175
+ console.warn(`${activeStates.length} PixelStreaming Instances are connected!`);
24162
24176
  }
24163
- // 3. UA sniffing fallback for mobile
24164
- if (/\b(iPhone|Android.*Mobile|Windows Phone)\b/i.test(ua)) {
24165
- return Type.Mobile;
24177
+ }
24178
+ }
24179
+
24180
+
24181
+ /***/ }),
24182
+
24183
+ /***/ 3379:
24184
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
24185
+
24186
+ __webpack_require__.r(__webpack_exports__);
24187
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
24188
+ /* harmony export */ "EventHandler": () => (/* binding */ EventHandler)
24189
+ /* harmony export */ });
24190
+ /** A helper class to spread the event to additional event handlers added from external sources. */
24191
+ class EventHandler {
24192
+ /** Returns the added callback on success or null, if something went wrong. */
24193
+ add(callback) {
24194
+ const cb = this.callbacks.find((cb) => cb === callback);
24195
+ if (cb)
24196
+ return null;
24197
+ this.callbacks.push(callback);
24198
+ return callback;
24199
+ }
24200
+ /** Returns true if the input callback has been found and removed and false if not. */
24201
+ remove(callback) {
24202
+ const cb = this.callbacks.find((cb) => cb === callback);
24203
+ if (cb)
24204
+ this.callbacks.splice(this.callbacks.indexOf(cb), 1);
24205
+ return !!cb;
24206
+ }
24207
+ constructor() {
24208
+ this.callbacks = new Array();
24209
+ }
24210
+ /** Emits the event-data for each of the existing handlers. */
24211
+ static Emit(handler, event) {
24212
+ handler.callbacks.forEach((callback) => callback(event));
24213
+ }
24214
+ }
24215
+
24216
+
24217
+ /***/ }),
24218
+
24219
+ /***/ 2469:
24220
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
24221
+
24222
+ __webpack_require__.r(__webpack_exports__);
24223
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
24224
+ /* harmony export */ "Session": () => (/* binding */ Session)
24225
+ /* harmony export */ });
24226
+ /* harmony import */ var date_fns_addSeconds__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1973);
24227
+ /* harmony import */ var date_fns_isBefore__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(313);
24228
+ /* harmony import */ var zod__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1604);
24229
+
24230
+
24231
+
24232
+ const ZSession = zod__WEBPACK_IMPORTED_MODULE_0__.z.object({
24233
+ id: zod__WEBPACK_IMPORTED_MODULE_0__.z.string().min(1),
24234
+ created: zod__WEBPACK_IMPORTED_MODULE_0__.z.date()
24235
+ .or(zod__WEBPACK_IMPORTED_MODULE_0__.z.string())
24236
+ .transform((arg) => new Date(arg)),
24237
+ expire: zod__WEBPACK_IMPORTED_MODULE_0__.z.boolean().default(false).optional(),
24238
+ })
24239
+ .strict();
24240
+ const ZOptions = zod__WEBPACK_IMPORTED_MODULE_0__.z.object({
24241
+ localStorageKey: zod__WEBPACK_IMPORTED_MODULE_0__.z.string().min(3).default("pxss"),
24242
+ keepSession: zod__WEBPACK_IMPORTED_MODULE_0__.z.number()
24243
+ .int()
24244
+ .min(3)
24245
+ /** Default keep is 10, since this is how long instances will be kept alive by default. */
24246
+ .default(10),
24247
+ });
24248
+ /** The sessionId is stored in the localStorage to allow for reconnection and
24249
+ * to prevent spamming start requests of instances, when smashing F5.
24250
+ */
24251
+ class Session {
24252
+ get current() {
24253
+ return this._current;
24254
+ }
24255
+ get noSession() {
24256
+ return new URLSearchParams(window.location.search).has("noSession");
24257
+ }
24258
+ get id() {
24259
+ // If the query-parameter "noSession" or "nosession" is set, it will be skipped.
24260
+ if (this.noSession)
24261
+ return null;
24262
+ const sessionString = window.localStorage.getItem(this.localStorageKey);
24263
+ // No session yet set.
24264
+ if (sessionString === null) {
24265
+ return null;
24166
24266
  }
24167
- // 4. Touch-capable with "tablet-like" dimensions
24168
- if (("ontouchstart" in window || navigator.maxTouchPoints > 0) && minDim >= 600 && minDim <= 1100) {
24169
- return Type.Tablet;
24267
+ const parsed = ZSession.safeParse(JSON.parse(sessionString));
24268
+ // Clear the session if it's not valid.
24269
+ if (!parsed.success) {
24270
+ this.unset();
24271
+ return null;
24170
24272
  }
24171
- // 5. Default fallback
24172
- return Type.Desktop;
24173
- }
24174
- getPowerHints() {
24175
- const conn = navigator.connection;
24176
- const saveData = typeof (conn === null || conn === void 0 ? void 0 : conn.saveData) === "boolean" ? conn.saveData : null;
24177
- let prefersReducedData = null;
24178
- let prefersReducedMotion = null;
24179
- if (typeof window.matchMedia === "function") {
24180
- try {
24181
- const mqData = window.matchMedia("(prefers-reduced-data: reduce)");
24182
- if (typeof mqData.matches === "boolean") {
24183
- prefersReducedData = mqData.matches;
24184
- }
24185
- }
24186
- catch (_a) { }
24187
- try {
24188
- const mqMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
24189
- if (typeof mqMotion.matches === "boolean") {
24190
- prefersReducedMotion = mqMotion.matches;
24191
- }
24192
- }
24193
- catch (_b) { }
24273
+ const session = parsed.data;
24274
+ const expirationTime = (0,date_fns_addSeconds__WEBPACK_IMPORTED_MODULE_1__["default"])(new Date(session.created), this.options.keepSession);
24275
+ if ((0,date_fns_isBefore__WEBPACK_IMPORTED_MODULE_2__["default"])(expirationTime, new Date())) {
24276
+ this.unset();
24277
+ return null;
24194
24278
  }
24195
- return { saveData, prefersReducedData, prefersReducedMotion };
24279
+ // Return the sessionId.
24280
+ return session.id;
24196
24281
  }
24197
- getNetworkInfo(measuredDownlinkMbps) {
24198
- const conn = navigator.connection;
24199
- return {
24200
- type: typeof (conn === null || conn === void 0 ? void 0 : conn.type) === "string" ? conn.type : null,
24201
- effectiveType: typeof (conn === null || conn === void 0 ? void 0 : conn.effectiveType) === "string" ? conn.effectiveType : null,
24202
- downlinkMbps: typeof (conn === null || conn === void 0 ? void 0 : conn.downlink) === "number" ? conn.downlink : null,
24203
- rttMs: typeof (conn === null || conn === void 0 ? void 0 : conn.rtt) === "number" ? conn.rtt : null,
24204
- measuredDownlinkMbps
24205
- };
24282
+ get localStorageKey() {
24283
+ return this.options.localStorageKey;
24206
24284
  }
24207
- maybeMeasureBandwidth() {
24208
- return (0,tslib__WEBPACK_IMPORTED_MODULE_0__.__awaiter)(this, void 0, void 0, function* () {
24209
- const conn = navigator.connection;
24210
- const saveData = (conn === null || conn === void 0 ? void 0 : conn.saveData) === true;
24211
- if (!this.opts.enableBandwidthProbe)
24212
- return null;
24213
- if (this.opts.respectSaveData && saveData)
24214
- return null;
24215
- try {
24216
- const t0 = performance.now();
24217
- const res = yield fetch(this.withCacheBuster(this.opts.probeUrl), {
24218
- cache: "no-store",
24219
- credentials: "omit"
24220
- });
24221
- const buf = yield res.arrayBuffer();
24222
- const t1 = performance.now();
24223
- const bits = buf.byteLength * 8;
24224
- const seconds = (t1 - t0) / 1000;
24225
- if (seconds <= 0.05)
24226
- return null; // ignore unstable samples
24227
- return +(bits / seconds / 1000000).toFixed(2);
24228
- }
24229
- catch (_a) {
24230
- return null;
24231
- }
24232
- });
24285
+ constructor(options) {
24286
+ this.options = ZOptions.parse(options || {});
24233
24287
  }
24234
- withCacheBuster(url) {
24235
- var _a, _b;
24236
- const u = new URL(url, location.origin);
24237
- const token = (_b = (_a = crypto === null || crypto === void 0 ? void 0 : crypto.randomUUID) === null || _a === void 0 ? void 0 : _a.call(crypto)) !== null && _b !== void 0 ? _b : String(Date.now());
24238
- u.searchParams.set("cb", token);
24239
- return u.toString();
24288
+ /** Set's the session with creation date for the given storage key to the localStorage. */
24289
+ set(sessionId) {
24290
+ ZSession.shape.id.parse(sessionId);
24291
+ this._current = sessionId;
24292
+ const session = {
24293
+ id: sessionId,
24294
+ created: new Date(),
24295
+ expire: false,
24296
+ };
24297
+ window.localStorage.setItem(this.localStorageKey, JSON.stringify(session));
24240
24298
  }
24241
- getCodecSupport() {
24242
- return (0,tslib__WEBPACK_IMPORTED_MODULE_0__.__awaiter)(this, void 0, void 0, function* () {
24243
- const [h264, hevc] = yield Promise.all([
24244
- this.supportsCodec('video/mp4; codecs="avc1.42E01E"'),
24245
- this.supportsCodec('video/mp4; codecs="hvc1.1.6.L93.B0"')
24246
- ]);
24247
- return { h264, hevc };
24248
- });
24249
- }
24250
- supportsCodec(mime) {
24251
- return (0,tslib__WEBPACK_IMPORTED_MODULE_0__.__awaiter)(this, void 0, void 0, function* () {
24252
- try {
24253
- if ("mediaCapabilities" in navigator) {
24254
- // @ts-ignore
24255
- const res = yield navigator.mediaCapabilities.decodingInfo({
24256
- type: "file",
24257
- video: { contentType: mime, width: 1920, height: 1080, bitrate: 5000000, framerate: 30 }
24258
- });
24259
- return !!(res === null || res === void 0 ? void 0 : res.supported);
24260
- }
24261
- }
24262
- catch (_a) { }
24263
- try {
24264
- const v = document.createElement("video");
24265
- return v.canPlayType(mime) !== "";
24266
- }
24267
- catch (_b) {
24268
- return false;
24269
- }
24270
- });
24271
- }
24272
- getUAInfo() {
24273
- return (0,tslib__WEBPACK_IMPORTED_MODULE_0__.__awaiter)(this, void 0, void 0, function* () {
24274
- if (this.uaInfoCache)
24275
- return this.uaInfoCache;
24276
- const nav = navigator;
24277
- const uaData = nav.userAgentData;
24278
- const ua = navigator.userAgent;
24279
- let browserFamily = "Unknown";
24280
- let browserVersion = null;
24281
- let osFamily = "Unknown";
24282
- let osVersion = null;
24283
- let model = null;
24284
- let architecture = null;
24285
- let bitness = null;
24286
- // --- UA-CH (modern browsers) ---
24287
- if (uaData === null || uaData === void 0 ? void 0 : uaData.getHighEntropyValues) {
24288
- try {
24289
- const high = yield uaData.getHighEntropyValues([
24290
- "platform",
24291
- "platformVersion",
24292
- "model",
24293
- "architecture",
24294
- "bitness",
24295
- "fullVersionList"
24296
- ]);
24297
- osFamily = high.platform || "Unknown";
24298
- osVersion = high.platformVersion || null;
24299
- model = high.model || null;
24300
- architecture = high.architecture || null;
24301
- bitness = high.bitness || null;
24302
- const brands = high.fullVersionList || uaData.brands || [];
24303
- const realBrand = brands.find((b) => {
24304
- if (!b.brand)
24305
- return false;
24306
- const normalized = b.brand.replace(/[^a-zA-Z]/g, "").toLowerCase();
24307
- return !normalized.includes("chromium") && !normalized.includes("notabrand");
24308
- });
24309
- if (realBrand) {
24310
- browserFamily = realBrand.brand;
24311
- browserVersion = realBrand.version;
24312
- }
24313
- }
24314
- catch (_a) {
24315
- // silently fail
24316
- }
24317
- }
24318
- // --- Fallback to classic UA parsing if browser unknown ---
24319
- if (!browserFamily || browserFamily === "Unknown") {
24320
- const browserRegexes = [
24321
- [/Edg\/(\S+)/, "Edge"],
24322
- [/OPR\/(\S+)/, "Opera"],
24323
- [/SamsungBrowser\/(\S+)/, "Samsung Internet"],
24324
- [/Firefox\/(\S+)/, "Firefox"],
24325
- [/Chrome\/(\S+)/, "Chrome"],
24326
- [/Version\/(\S+).*Safari/, "Safari"]
24327
- ];
24328
- for (const [regex, name] of browserRegexes) {
24329
- const match = ua.match(regex);
24330
- if (match) {
24331
- browserFamily = name;
24332
- browserVersion = match[1];
24333
- break;
24334
- }
24335
- }
24336
- }
24337
- // --- OS detection (always run, iOS first) ---
24338
- if (/iPhone|iPad|iPod/.test(ua)) {
24339
- osFamily = "iOS";
24340
- const match = ua.match(/OS (\d+[_\d]*)/);
24341
- if (match)
24342
- osVersion = match[1].replace(/_/g, ".");
24343
- }
24344
- else if (uaData === null || uaData === void 0 ? void 0 : uaData.platform) {
24345
- osFamily = uaData.platform;
24346
- }
24347
- else if (/Windows/.test(ua)) {
24348
- osFamily = "Windows";
24349
- const match = ua.match(/Windows NT (\d+\.\d+)/);
24350
- if (match)
24351
- osVersion = match[1];
24352
- }
24353
- else if (/Mac OS X/.test(ua)) {
24354
- osFamily = "macOS";
24355
- const match = ua.match(/Mac OS X (\d+[_\d]*)/);
24356
- if (match)
24357
- osVersion = match[1].replace(/_/g, ".");
24358
- }
24359
- else if (/Android/.test(ua)) {
24360
- osFamily = "Android";
24361
- const match = ua.match(/Android (\d+(\.\d+)?)/);
24362
- if (match)
24363
- osVersion = match[1];
24364
- }
24365
- else if (/Linux/.test(ua)) {
24366
- osFamily = "Linux";
24367
- }
24368
- const result = { browserFamily, browserVersion, osFamily, osVersion, model, architecture, bitness };
24369
- this.uaInfoCache = result;
24370
- return result;
24371
- });
24299
+ /** Removes a session from the localStorage. */
24300
+ unset() {
24301
+ window.localStorage.removeItem(this.localStorageKey);
24372
24302
  }
24373
24303
  }
24374
24304
 
24375
24305
 
24376
24306
  /***/ }),
24377
24307
 
24378
- /***/ 5602:
24308
+ /***/ 9764:
24379
24309
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
24380
24310
 
24381
24311
  __webpack_require__.r(__webpack_exports__);
24382
24312
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
24383
- /* harmony export */ "ArcwareSettingsSchema": () => (/* binding */ ArcwareSettingsSchema)
24313
+ /* harmony export */ "Stats": () => (/* binding */ Stats)
24384
24314
  /* harmony export */ });
24385
- /* harmony import */ var zod__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1604);
24386
24315
  /* harmony import */ var _arcware_cloud_shared_pixelstreaming_websdk__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7910);
24387
24316
 
24388
-
24389
- /** Arcware Settings. */
24390
- const ArcwareSettingsSchema = zod__WEBPACK_IMPORTED_MODULE_1__.z.object({
24391
- /** Overwrites the Session-Tool and uses the provided session instead. */
24392
- session: zod__WEBPACK_IMPORTED_MODULE_1__.z.string().optional(),
24393
- /** Can be used to be added to the request in order to verify access to private projects.
24394
- * For internal use only. => Preview page.
24395
- */
24396
- token: zod__WEBPACK_IMPORTED_MODULE_1__.z.string().optional(),
24397
- /** @deprecated in there for legacy use. Can only be used when token is provided. */
24398
- bypass: zod__WEBPACK_IMPORTED_MODULE_1__.z.boolean().optional(),
24399
- // /** Configure DirectFlow Token. */
24400
- // directFlow: z.string().optional(),
24401
- /** Handler for server side error messages. */
24402
- errorHandler: zod__WEBPACK_IMPORTED_MODULE_1__.z["function"]().args(_arcware_cloud_shared_pixelstreaming_websdk__WEBPACK_IMPORTED_MODULE_0__.Messages.ZErrorMessage).returns(zod__WEBPACK_IMPORTED_MODULE_1__.z["void"]()).optional(),
24403
- /** Handler for queue events. */
24404
- queueHandler: zod__WEBPACK_IMPORTED_MODULE_1__.z["function"]().args(_arcware_cloud_shared_pixelstreaming_websdk__WEBPACK_IMPORTED_MODULE_0__.Messages.ZQueue).returns(zod__WEBPACK_IMPORTED_MODULE_1__.z["void"]()).optional(),
24405
- /** Handler for sessionId message. */
24406
- sessionIdHandler: zod__WEBPACK_IMPORTED_MODULE_1__.z["function"]().args(zod__WEBPACK_IMPORTED_MODULE_1__.z.string()).returns(zod__WEBPACK_IMPORTED_MODULE_1__.z["void"]()).optional(),
24407
- /** Handler for love letters.
24408
- * "LoveLetters" are send from backend to the SDK to state what phase the connection currently is in. */
24409
- loveLetterHandler: zod__WEBPACK_IMPORTED_MODULE_1__.z["function"]().args(_arcware_cloud_shared_pixelstreaming_websdk__WEBPACK_IMPORTED_MODULE_0__.Messages.ZLoveLetter).returns(zod__WEBPACK_IMPORTED_MODULE_1__.z["void"]()).optional(),
24410
- /** Show or hide the fullscreen button. */
24411
- fullscreenButton: zod__WEBPACK_IMPORTED_MODULE_1__.z.boolean().optional(),
24412
- /** Show or hide the settings button. */
24413
- settingsButton: zod__WEBPACK_IMPORTED_MODULE_1__.z.boolean().optional(),
24414
- /** Show or hide the info button. */
24415
- infoButton: zod__WEBPACK_IMPORTED_MODULE_1__.z.boolean().optional(),
24416
- /** Show or hide the audio button. */
24417
- audioButton: zod__WEBPACK_IMPORTED_MODULE_1__.z.boolean().optional(),
24418
- /** Show or hide the microphone button. */
24419
- micButton: zod__WEBPACK_IMPORTED_MODULE_1__.z.boolean().optional(),
24420
- /** Show or hide the microphone button. */
24421
- stopButton: zod__WEBPACK_IMPORTED_MODULE_1__.z.boolean().optional(),
24422
- /** Show or hide the connectionStrengthIcon button. */
24423
- connectionStrengthIcon: zod__WEBPACK_IMPORTED_MODULE_1__.z.boolean().optional(),
24424
- /** ShareId, used for sharing your project.
24425
- * Using ArcwareInit will set this required property for you. */
24426
- shareId: zod__WEBPACK_IMPORTED_MODULE_1__.z.string().startsWith("share-").optional(),
24427
- /** Id of your project, only required if your shareId refers to multiple projects.
24428
- * Using ArcwareInit will set this required property for you. */
24429
- projectId: zod__WEBPACK_IMPORTED_MODULE_1__.z.string().optional(),
24430
- /** Enable/Disable LoveLetter logging to the console. */
24431
- loveLetterLogging: zod__WEBPACK_IMPORTED_MODULE_1__.z.boolean().optional(),
24432
- /** Enable/Disable Connection Identifier logging to the console. */
24433
- connectionIdentifierLoggingDisabled: zod__WEBPACK_IMPORTED_MODULE_1__.z.boolean().optional(),
24434
- /** Width with which instance should be started */
24435
- startWidth: zod__WEBPACK_IMPORTED_MODULE_1__.z.number().optional(),
24436
- /** Height with which instance should be started */
24437
- startHeight: zod__WEBPACK_IMPORTED_MODULE_1__.z.number().optional()
24438
- });
24317
+ function Stats(stats) {
24318
+ const result = _arcware_cloud_shared_pixelstreaming_websdk__WEBPACK_IMPORTED_MODULE_0__.Messages.ZStats.shape.stats.parse({
24319
+ /**
24320
+ * Other known properties.
24321
+ */
24322
+ codecs: stats.codecs,
24323
+ candidatePair: stats.candidatePairs,
24324
+ localCandidates: stats.localCandidates,
24325
+ remoteCandidates: stats.remoteCandidates,
24326
+ DataChannelStats: stats.datachannelStats,
24327
+ // Received (last) for ticket
24328
+ bytesReceived: forceNumber(stats.inboundVideoStats.bytesReceived),
24329
+ // Packets Lost (last) for ticket
24330
+ packetsLost: forceNumber(stats.inboundVideoStats.packetsLost),
24331
+ // Video resolution: highest lowest average (portrait) and highest lowest average (landscape) for ticket
24332
+ frameWidth: forceNumber(stats.inboundVideoStats.frameWidth),
24333
+ // Video resolution: highest lowest average (portrait) and highest lowest average (landscape) for ticket
24334
+ frameHeight: forceNumber(stats.inboundVideoStats.frameHeight),
24335
+ // Frames decoded (Last) for ticket
24336
+ framesDecoded: forceNumber(stats.inboundVideoStats.framesDecoded),
24337
+ // Framerate (low / high, avg) for ticket
24338
+ framesPerSecond: forceNumber(stats.inboundVideoStats.framesPerSecond),
24339
+ // frames dropped (last) for ticket
24340
+ framesDropped: forceNumber(stats.inboundVideoStats.framesDropped),
24341
+ // Videocodec (once) for ticket
24342
+ videoCodec: stats.inboundVideoStats.codecId,
24343
+ // audiocodec (once) for ticket
24344
+ audioCodec: stats.inboundAudioStats.codecId,
24345
+ // browser type and version for ticket (arcware / addition)
24346
+ browserInfo: {
24347
+ // to be received!
24348
+ userAgent: navigator.userAgent,
24349
+ platform: navigator.oscpu || navigator.platform || null,
24350
+ language: navigator.language,
24351
+ },
24352
+ // Net RTT (low, high, avg)
24353
+ // BEGIN TW CHANGE
24354
+ currentRTT: calcRTT(stats.candidatePairs),
24355
+ // END TW CHANGE
24356
+ // Duration (last)
24357
+ sessionRunTime: stats.sessionStats.runTime,
24358
+ // Controls stream input (??)
24359
+ controlsStreamInput: stats.sessionStats.controlsStreamInput,
24360
+ // video quantization parameter (low / high / avg if applicable)
24361
+ videoEncoderAvgQP: forceNumber(stats.sessionStats.videoEncoderAvgQP),
24362
+ // Video bitrate (min / max / avg)
24363
+ videoBitrate: forceNumber(stats.inboundVideoStats.bitrate),
24364
+ // Audio bitrate (min / max / avg)
24365
+ audioBitrate: forceNumber(stats.inboundAudioStats.bitrate),
24366
+ });
24367
+ return result;
24368
+ }
24369
+ /**
24370
+ * Given an array of candidate pairs this function will find the highest RTT for the selected pair
24371
+ * @param pairs - An array of candidate pairs
24372
+ * @returns The highest round trip time of a selected candidate pair
24373
+ */
24374
+ function calcRTT(pairs) {
24375
+ let rtt = 0;
24376
+ pairs.forEach((pair) => {
24377
+ if (pair.selected && pair.currentRoundTripTime > rtt) {
24378
+ rtt = pair.currentRoundTripTime;
24379
+ }
24380
+ });
24381
+ return rtt;
24382
+ }
24383
+ /**
24384
+ * Takes a number and forces it to return a number. If the value passed in is NaN, 0 will be returned.
24385
+ * @param value - A number
24386
+ * @returns The number passed in as value or 0 if vlaue was NaN
24387
+ */
24388
+ function forceNumber(value) {
24389
+ return isNaN(value) ? 0 : value;
24390
+ }
24439
24391
 
24440
24392
 
24441
24393
  /***/ }),
24442
24394
 
24443
- /***/ 5999:
24395
+ /***/ 9580:
24444
24396
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
24445
24397
 
24446
24398
  __webpack_require__.r(__webpack_exports__);
24447
24399
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
24448
- /* harmony export */ "ConnectionIdentifier": () => (/* binding */ ConnectionIdentifier),
24449
- /* harmony export */ "WebsocketState": () => (/* binding */ WebsocketState)
24400
+ /* harmony export */ "default": () => (/* binding */ debounce)
24450
24401
  /* harmony export */ });
24451
- var WebsocketState;
24452
- (function (WebsocketState) {
24453
- // Socket has been created. The connection is not yet open.
24454
- WebsocketState[WebsocketState["CONNECTING"] = 0] = "CONNECTING";
24455
- // The connection is open and ready to communicate.
24456
- WebsocketState[WebsocketState["OPEN"] = 1] = "OPEN";
24457
- // The connection is in the process of closing.
24458
- WebsocketState[WebsocketState["CLOSING"] = 2] = "CLOSING";
24459
- // The connection is closed or couldn't be opened.
24460
- WebsocketState[WebsocketState["CLOSED"] = 3] = "CLOSED";
24461
- })(WebsocketState || (WebsocketState = {}));
24462
- class ConnectionIdentifier {
24463
- static get Instance() {
24464
- if (!this.instance) {
24465
- this.instance = new ConnectionIdentifier();
24466
- }
24467
- return this.instance;
24468
- }
24469
- get WebsocketStates() {
24470
- return this.aps.map((ps) => ps.websocketState);
24471
- }
24472
- constructor() {
24473
- this.aps = [];
24474
- this.enabled = true;
24475
- }
24476
- register(ps) {
24477
- var _a;
24478
- if (!this.aps.includes(ps)) {
24479
- this.aps.push(ps);
24480
- if ((_a = ps.config.settings) === null || _a === void 0 ? void 0 : _a.connectionIdentifierLoggingDisabled)
24481
- this.disable();
24482
- }
24483
- if (!this.interval)
24484
- this.interval = setInterval(this.connectionWatcher.bind(this), 10 * 1000);
24485
- }
24486
- GetWebSocketStates() {
24487
- return this.aps.map((ps) => ps.websocketState);
24488
- }
24489
- ActiveInstances() {
24490
- return this.GetWebSocketStates().filter((state) => state <= WebsocketState.OPEN).length;
24491
- }
24492
- disable() {
24493
- this.enabled = false;
24494
- }
24495
- /**
24496
- * The purpose of this method is to periodically tell the developer the amount of connected instances.
24497
- * This way the developer can figure out, when they messed up the implementation and there's shadow instances still running.
24498
- * Or when they accidentally set up multiple connections!
24499
- * Things like that can happen, for example if the React.useEffect was not setup perfectly!
24500
- */
24501
- connectionWatcher() {
24502
- if (!this.enabled)
24503
- return;
24504
- const activeStates = this.GetWebSocketStates().filter((state) => state <= WebsocketState.OPEN);
24505
- if (activeStates.length === 1) {
24506
- console.log(`PixelStreaming Instance is connected.`);
24507
- }
24508
- else if (activeStates.length !== 0) {
24509
- console.warn(`${activeStates.length} PixelStreaming Instances are connected!`);
24510
- }
24511
- }
24402
+ function debounce(func, wait) {
24403
+ let timeout;
24404
+ return function (...args) {
24405
+ clearTimeout(timeout);
24406
+ timeout = window.setTimeout(() => func(...args), wait);
24407
+ };
24512
24408
  }
24513
24409
 
24514
24410
 
24515
24411
  /***/ }),
24516
24412
 
24517
- /***/ 3379:
24413
+ /***/ 471:
24518
24414
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
24519
24415
 
24520
24416
  __webpack_require__.r(__webpack_exports__);
24521
24417
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
24522
- /* harmony export */ "EventHandler": () => (/* binding */ EventHandler)
24418
+ /* harmony export */ "isAnalyticsEvent": () => (/* binding */ isAnalyticsEvent),
24419
+ /* harmony export */ "toAnalyticsEvent": () => (/* binding */ toAnalyticsEvent)
24523
24420
  /* harmony export */ });
24524
- /** A helper class to spread the event to additional event handlers added from external sources. */
24525
- class EventHandler {
24526
- /** Returns the added callback on success or null, if something went wrong. */
24527
- add(callback) {
24528
- const cb = this.callbacks.find((cb) => cb === callback);
24529
- if (cb)
24530
- return null;
24531
- this.callbacks.push(callback);
24532
- return callback;
24533
- }
24534
- /** Returns true if the input callback has been found and removed and false if not. */
24535
- remove(callback) {
24536
- const cb = this.callbacks.find((cb) => cb === callback);
24537
- if (cb)
24538
- this.callbacks.splice(this.callbacks.indexOf(cb), 1);
24539
- return !!cb;
24540
- }
24541
- constructor() {
24542
- this.callbacks = new Array();
24543
- }
24544
- /** Emits the event-data for each of the existing handlers. */
24545
- static Emit(handler, event) {
24546
- handler.callbacks.forEach((callback) => callback(event));
24547
- }
24421
+ /* harmony import */ var _common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2483);
24422
+ // analytics.ts
24423
+
24424
+ /**
24425
+ * Build an AnalyticsEvent from unknown input.
24426
+ * - accepts object or JSON/JSON-ish string
24427
+ * - drops extra fields
24428
+ * - requires non-empty string `type` and `customName`
24429
+ * - accepts `payload` only if it's a string; truncates by **bytes**
24430
+ */
24431
+ function toAnalyticsEvent(input, opts = {}) {
24432
+ var _a, _b, _c;
24433
+ const maxBytes = (_a = opts.maxPayloadBytes) !== null && _a !== void 0 ? _a : 512;
24434
+ let obj;
24435
+ try {
24436
+ obj = (0,_common__WEBPACK_IMPORTED_MODULE_0__.parseUnknownToObject)(input, { allowJsonish: (_b = opts.allowJsonish) !== null && _b !== void 0 ? _b : true });
24437
+ }
24438
+ catch (e) {
24439
+ return { ok: false, error: (_c = e === null || e === void 0 ? void 0 : e.message) !== null && _c !== void 0 ? _c : "Invalid input" };
24440
+ }
24441
+ const typeVal = safeString(obj["type"]);
24442
+ const customNameVal = safeString(obj["customName"]);
24443
+ if (!typeVal)
24444
+ return { ok: false, error: "`type` must be a non-empty string." };
24445
+ if (!customNameVal)
24446
+ return { ok: false, error: "`customName` must be a non-empty string." };
24447
+ let payloadStr;
24448
+ if ("payload" in obj && typeof obj["payload"] === "string") {
24449
+ payloadStr = (0,_common__WEBPACK_IMPORTED_MODULE_0__.truncateByBytes)(obj["payload"], maxBytes);
24450
+ }
24451
+ const value = payloadStr !== undefined
24452
+ ? { type: typeVal, customName: customNameVal, payload: payloadStr }
24453
+ : { type: typeVal, customName: customNameVal };
24454
+ return { ok: true, value };
24455
+ }
24456
+ /** Type guard */
24457
+ function isAnalyticsEvent(x) {
24458
+ return (!!x &&
24459
+ typeof x === "object" &&
24460
+ typeof x.type === "string" &&
24461
+ typeof x.customName === "string" &&
24462
+ (typeof x.payload === "string" || x.payload === undefined));
24463
+ }
24464
+ function safeString(x) {
24465
+ if (typeof x !== "string")
24466
+ return undefined;
24467
+ const trimmed = x.trim();
24468
+ return trimmed.length ? trimmed : undefined;
24548
24469
  }
24549
24470
 
24550
24471
 
24551
24472
  /***/ }),
24552
24473
 
24553
- /***/ 2469:
24474
+ /***/ 2768:
24554
24475
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
24555
24476
 
24556
24477
  __webpack_require__.r(__webpack_exports__);
24557
24478
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
24558
- /* harmony export */ "Session": () => (/* binding */ Session)
24479
+ /* harmony export */ "ZFileTransfer": () => (/* binding */ ZFileTransfer),
24480
+ /* harmony export */ "parseControl": () => (/* binding */ parseControl)
24559
24481
  /* harmony export */ });
24560
- /* harmony import */ var date_fns_addSeconds__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1973);
24561
- /* harmony import */ var date_fns_isBefore__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(313);
24562
24482
  /* harmony import */ var zod__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1604);
24483
+ /* harmony import */ var _common__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2483);
24484
+ // fileTransfer.ts
24563
24485
 
24564
24486
 
24565
-
24566
- const ZSession = zod__WEBPACK_IMPORTED_MODULE_0__.z.object({
24567
- id: zod__WEBPACK_IMPORTED_MODULE_0__.z.string().min(1),
24568
- created: zod__WEBPACK_IMPORTED_MODULE_0__.z.date()
24569
- .or(zod__WEBPACK_IMPORTED_MODULE_0__.z.string())
24570
- .transform((arg) => new Date(arg)),
24571
- expire: zod__WEBPACK_IMPORTED_MODULE_0__.z.boolean().default(false).optional(),
24572
- })
24573
- .strict();
24574
- const ZOptions = zod__WEBPACK_IMPORTED_MODULE_0__.z.object({
24575
- localStorageKey: zod__WEBPACK_IMPORTED_MODULE_0__.z.string().min(3).default("pxss"),
24576
- keepSession: zod__WEBPACK_IMPORTED_MODULE_0__.z.number()
24577
- .int()
24578
- .min(3)
24579
- /** Default keep is 10, since this is how long instances will be kept alive by default. */
24580
- .default(10),
24487
+ // Schema
24488
+ const ZFileTransfer = zod__WEBPACK_IMPORTED_MODULE_0__.z.object({
24489
+ type: zod__WEBPACK_IMPORTED_MODULE_0__.z.literal("fileTransfer"),
24490
+ filename: zod__WEBPACK_IMPORTED_MODULE_0__.z.string().trim().optional()
24581
24491
  });
24582
- /** The sessionId is stored in the localStorage to allow for reconnection and
24583
- * to prevent spamming start requests of instances, when smashing F5.
24492
+ // Normalized parse + validate
24493
+ function parseControl(input, schema) {
24494
+ const obj = (0,_common__WEBPACK_IMPORTED_MODULE_1__.parseUnknownToObject)(input, { allowJsonish: true });
24495
+ return schema.parse(obj);
24496
+ }
24497
+
24498
+
24499
+ /***/ }),
24500
+
24501
+ /***/ 8429:
24502
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
24503
+
24504
+ __webpack_require__.r(__webpack_exports__);
24505
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
24506
+ /* harmony export */ "DiagnosticsCollector": () => (/* binding */ DiagnosticsCollector),
24507
+ /* harmony export */ "Orientation": () => (/* binding */ Orientation),
24508
+ /* harmony export */ "Type": () => (/* binding */ Type)
24509
+ /* harmony export */ });
24510
+ /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(655);
24511
+ /**
24512
+ * DiagnosticsCollector.ts
24513
+ * Lightweight, privacy-aware client diagnostics for troubleshooting + analytics.
24514
+ * WebRTC collection intentionally omitted (to add later).
24515
+ *
24516
+ * Notes:
24517
+ * - Some fields are best-effort due to browser restrictions; they're nullable.
24518
+ * - Bandwidth probe requires a small binary served with:
24519
+ * Content-Type: application/octet-stream
24520
+ * Content-Encoding: identity
24521
+ * Cache-Control: no-store
24522
+ * Default location: /diag/probe.bin
24584
24523
  */
24585
- class Session {
24586
- get current() {
24587
- return this._current;
24524
+
24525
+ var Orientation;
24526
+ (function (Orientation) {
24527
+ Orientation["Landscape"] = "landscape";
24528
+ Orientation["Portrait"] = "portrait";
24529
+ })(Orientation || (Orientation = {}));
24530
+ var Type;
24531
+ (function (Type) {
24532
+ Type["Desktop"] = "desktop";
24533
+ Type["Mobile"] = "mobile";
24534
+ Type["Tablet"] = "tablet";
24535
+ })(Type || (Type = {}));
24536
+ /**
24537
+ * DiagnosticsCollector
24538
+ */
24539
+ class DiagnosticsCollector {
24540
+ constructor(opts = {}) {
24541
+ var _a, _b, _c, _d;
24542
+ this.uaInfoCache = null;
24543
+ this.opts = {
24544
+ enableBandwidthProbe: (_a = opts.enableBandwidthProbe) !== null && _a !== void 0 ? _a : true,
24545
+ probeUrl: (_b = opts.probeUrl) !== null && _b !== void 0 ? _b : "/diag/probe.bin",
24546
+ respectSaveData: (_c = opts.respectSaveData) !== null && _c !== void 0 ? _c : true,
24547
+ sampleRate: Math.max(0, Math.min(1, (_d = opts.sampleRate) !== null && _d !== void 0 ? _d : 1))
24548
+ };
24588
24549
  }
24589
- get noSession() {
24590
- return new URLSearchParams(window.location.search).has("noSession");
24550
+ /**
24551
+ * Collect the diagnostics payload.
24552
+ */
24553
+ collect() {
24554
+ var _a, _b;
24555
+ return (0,tslib__WEBPACK_IMPORTED_MODULE_0__.__awaiter)(this, void 0, void 0, function* () {
24556
+ if (!this.shouldSample()) {
24557
+ return {
24558
+ device: { type: Type.Desktop, touchSupport: false },
24559
+ runtime: { browser: { family: "Unknown", version: null }, os: { family: "Unknown", version: null } },
24560
+ powerHints: { saveData: null, prefersReducedData: null, prefersReducedMotion: null },
24561
+ network: { type: null, effectiveType: null, downlinkMbps: null, rttMs: null, measuredDownlinkMbps: null },
24562
+ timestamps: { collectedAt: Date.now() }
24563
+ };
24564
+ }
24565
+ const collectedAt = Date.now();
24566
+ const [uaInfo, codecSupport, measuredDownlinkMbps] = yield Promise.all([
24567
+ this.getUAInfo(),
24568
+ this.getCodecSupport(),
24569
+ this.maybeMeasureBandwidth()
24570
+ ]);
24571
+ return {
24572
+ device: {
24573
+ type: this.inferDeviceType(),
24574
+ vendor: null,
24575
+ model: uaInfo.model,
24576
+ architecture: uaInfo.architecture,
24577
+ bitness: uaInfo.bitness,
24578
+ touchSupport: this.hasTouch(),
24579
+ hardwareConcurrency: (_a = navigator.hardwareConcurrency) !== null && _a !== void 0 ? _a : null,
24580
+ deviceMemory: (_b = navigator.deviceMemory) !== null && _b !== void 0 ? _b : null,
24581
+ orientation: this.getOrientation(),
24582
+ webgl: this.hasWebGL(),
24583
+ codecs: codecSupport
24584
+ },
24585
+ runtime: {
24586
+ browser: { family: uaInfo.browserFamily, version: uaInfo.browserVersion },
24587
+ os: { family: uaInfo.osFamily, version: uaInfo.osVersion }
24588
+ },
24589
+ powerHints: this.getPowerHints(),
24590
+ network: this.getNetworkInfo(measuredDownlinkMbps),
24591
+ timestamps: { collectedAt }
24592
+ };
24593
+ });
24591
24594
  }
24592
- get id() {
24593
- // If the query-parameter "noSession" or "nosession" is set, it will be skipped.
24594
- if (this.noSession)
24595
- return null;
24596
- const sessionString = window.localStorage.getItem(this.localStorageKey);
24597
- // No session yet set.
24598
- if (sessionString === null) {
24599
- return null;
24595
+ /**
24596
+ * Collect a safe subset of WebRTC stats once PC is connected.
24597
+ */
24598
+ collectWebRTC(pc) {
24599
+ var _a, _b, _c, _d, _e, _f;
24600
+ return (0,tslib__WEBPACK_IMPORTED_MODULE_0__.__awaiter)(this, void 0, void 0, function* () {
24601
+ try {
24602
+ const stats = yield pc.getStats();
24603
+ let selected, transport, local, remote;
24604
+ stats.forEach((r) => {
24605
+ if (r.type === "transport" && r.selectedCandidatePairId)
24606
+ transport = r;
24607
+ if (r.type === "candidate-pair" && (r.selected || r.nominated))
24608
+ selected = r;
24609
+ });
24610
+ if (selected) {
24611
+ local = stats.get(selected.localCandidateId);
24612
+ remote = stats.get(selected.remoteCandidateId);
24613
+ }
24614
+ return {
24615
+ candidatePairId: (_a = selected === null || selected === void 0 ? void 0 : selected.id) !== null && _a !== void 0 ? _a : null,
24616
+ localCandidateType: (_b = local === null || local === void 0 ? void 0 : local.candidateType) !== null && _b !== void 0 ? _b : null,
24617
+ remoteCandidateType: (_c = remote === null || remote === void 0 ? void 0 : remote.candidateType) !== null && _c !== void 0 ? _c : null,
24618
+ protocol: (_d = local === null || local === void 0 ? void 0 : local.protocol) !== null && _d !== void 0 ? _d : null,
24619
+ networkType: (_e = local === null || local === void 0 ? void 0 : local.networkType) !== null && _e !== void 0 ? _e : null,
24620
+ dtlsCipher: (_f = transport === null || transport === void 0 ? void 0 : transport.dtlsCipher) !== null && _f !== void 0 ? _f : null
24621
+ };
24622
+ }
24623
+ catch (_g) {
24624
+ return {};
24625
+ }
24626
+ });
24627
+ }
24628
+ /**
24629
+ * Helper to embed diagnostics in a hello envelope.
24630
+ */
24631
+ buildHelloEnvelope(base, pc) {
24632
+ return (0,tslib__WEBPACK_IMPORTED_MODULE_0__.__awaiter)(this, void 0, void 0, function* () {
24633
+ const diag = yield this.collect();
24634
+ const webrtc = pc ? yield this.collectWebRTC(pc) : undefined;
24635
+ const ext = webrtc ? { diag: Object.assign(Object.assign({}, diag), { webrtc }) } : { diag };
24636
+ return Object.assign(Object.assign({}, base), { ext });
24637
+ });
24638
+ }
24639
+ // ---------- Internals ----------
24640
+ shouldSample() {
24641
+ if (this.opts.sampleRate >= 1)
24642
+ return true;
24643
+ return Math.random() < this.opts.sampleRate;
24644
+ }
24645
+ hasTouch() {
24646
+ var _a;
24647
+ const nav = navigator;
24648
+ return "ontouchstart" in window || ((_a = nav.maxTouchPoints) !== null && _a !== void 0 ? _a : 0) > 0;
24649
+ }
24650
+ getOrientation() {
24651
+ var _a;
24652
+ try {
24653
+ if ((_a = screen.orientation) === null || _a === void 0 ? void 0 : _a.type) {
24654
+ return screen.orientation.type.startsWith("portrait") ? Orientation.Portrait : Orientation.Landscape;
24655
+ }
24656
+ if (window.matchMedia) {
24657
+ return window.matchMedia("(orientation: portrait)").matches ? Orientation.Portrait : Orientation.Landscape;
24658
+ }
24600
24659
  }
24601
- const parsed = ZSession.safeParse(JSON.parse(sessionString));
24602
- // Clear the session if it's not valid.
24603
- if (!parsed.success) {
24604
- this.unset();
24605
- return null;
24660
+ catch (_b) { }
24661
+ return null;
24662
+ }
24663
+ hasWebGL() {
24664
+ try {
24665
+ const canvas = document.createElement("canvas");
24666
+ return !!(canvas.getContext("webgl") || canvas.getContext("experimental-webgl"));
24606
24667
  }
24607
- const session = parsed.data;
24608
- const expirationTime = (0,date_fns_addSeconds__WEBPACK_IMPORTED_MODULE_1__["default"])(new Date(session.created), this.options.keepSession);
24609
- if ((0,date_fns_isBefore__WEBPACK_IMPORTED_MODULE_2__["default"])(expirationTime, new Date())) {
24610
- this.unset();
24611
- return null;
24668
+ catch (_a) {
24669
+ return false;
24612
24670
  }
24613
- // Return the sessionId.
24614
- return session.id;
24615
24671
  }
24616
- get localStorageKey() {
24617
- return this.options.localStorageKey;
24672
+ /**
24673
+ * Heuristic device type: best-effort only.
24674
+ * Tries UA-CH, then UA fallback, then touch + screen size heuristics.
24675
+ */
24676
+ inferDeviceType() {
24677
+ const uaData = navigator.userAgentData;
24678
+ const ua = navigator.userAgent || "";
24679
+ const isMobileCH = (uaData === null || uaData === void 0 ? void 0 : uaData.mobile) === true;
24680
+ const minDim = Math.min(screen.width, screen.height);
24681
+ // 1. Explicit iPad / tablet detection via UA (iOS often lies in CH)
24682
+ if (/\b(iPad|Tablet)\b/i.test(ua)) {
24683
+ return Type.Tablet;
24684
+ }
24685
+ // 2. UA-CH mobile hint (Chromium only, often accurate for Android)
24686
+ if (isMobileCH) {
24687
+ if (minDim >= 600)
24688
+ return Type.Tablet;
24689
+ return Type.Mobile;
24690
+ }
24691
+ // 3. UA sniffing fallback for mobile
24692
+ if (/\b(iPhone|Android.*Mobile|Windows Phone)\b/i.test(ua)) {
24693
+ return Type.Mobile;
24694
+ }
24695
+ // 4. Touch-capable with "tablet-like" dimensions
24696
+ if (("ontouchstart" in window || navigator.maxTouchPoints > 0) && minDim >= 600 && minDim <= 1100) {
24697
+ return Type.Tablet;
24698
+ }
24699
+ // 5. Default fallback
24700
+ return Type.Desktop;
24701
+ }
24702
+ getPowerHints() {
24703
+ const conn = navigator.connection;
24704
+ const saveData = typeof (conn === null || conn === void 0 ? void 0 : conn.saveData) === "boolean" ? conn.saveData : null;
24705
+ let prefersReducedData = null;
24706
+ let prefersReducedMotion = null;
24707
+ if (typeof window.matchMedia === "function") {
24708
+ try {
24709
+ const mqData = window.matchMedia("(prefers-reduced-data: reduce)");
24710
+ if (typeof mqData.matches === "boolean") {
24711
+ prefersReducedData = mqData.matches;
24712
+ }
24713
+ }
24714
+ catch (_a) { }
24715
+ try {
24716
+ const mqMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
24717
+ if (typeof mqMotion.matches === "boolean") {
24718
+ prefersReducedMotion = mqMotion.matches;
24719
+ }
24720
+ }
24721
+ catch (_b) { }
24722
+ }
24723
+ return { saveData, prefersReducedData, prefersReducedMotion };
24724
+ }
24725
+ getNetworkInfo(measuredDownlinkMbps) {
24726
+ const conn = navigator.connection;
24727
+ return {
24728
+ type: typeof (conn === null || conn === void 0 ? void 0 : conn.type) === "string" ? conn.type : null,
24729
+ effectiveType: typeof (conn === null || conn === void 0 ? void 0 : conn.effectiveType) === "string" ? conn.effectiveType : null,
24730
+ downlinkMbps: typeof (conn === null || conn === void 0 ? void 0 : conn.downlink) === "number" ? conn.downlink : null,
24731
+ rttMs: typeof (conn === null || conn === void 0 ? void 0 : conn.rtt) === "number" ? conn.rtt : null,
24732
+ measuredDownlinkMbps
24733
+ };
24734
+ }
24735
+ maybeMeasureBandwidth() {
24736
+ return (0,tslib__WEBPACK_IMPORTED_MODULE_0__.__awaiter)(this, void 0, void 0, function* () {
24737
+ const conn = navigator.connection;
24738
+ const saveData = (conn === null || conn === void 0 ? void 0 : conn.saveData) === true;
24739
+ if (!this.opts.enableBandwidthProbe)
24740
+ return null;
24741
+ if (this.opts.respectSaveData && saveData)
24742
+ return null;
24743
+ try {
24744
+ const t0 = performance.now();
24745
+ const res = yield fetch(this.withCacheBuster(this.opts.probeUrl), {
24746
+ cache: "no-store",
24747
+ credentials: "omit"
24748
+ });
24749
+ const buf = yield res.arrayBuffer();
24750
+ const t1 = performance.now();
24751
+ const bits = buf.byteLength * 8;
24752
+ const seconds = (t1 - t0) / 1000;
24753
+ if (seconds <= 0.05)
24754
+ return null; // ignore unstable samples
24755
+ return +(bits / seconds / 1000000).toFixed(2);
24756
+ }
24757
+ catch (_a) {
24758
+ return null;
24759
+ }
24760
+ });
24761
+ }
24762
+ withCacheBuster(url) {
24763
+ var _a, _b;
24764
+ const u = new URL(url, location.origin);
24765
+ const token = (_b = (_a = crypto === null || crypto === void 0 ? void 0 : crypto.randomUUID) === null || _a === void 0 ? void 0 : _a.call(crypto)) !== null && _b !== void 0 ? _b : String(Date.now());
24766
+ u.searchParams.set("cb", token);
24767
+ return u.toString();
24618
24768
  }
24619
- constructor(options) {
24620
- this.options = ZOptions.parse(options || {});
24769
+ getCodecSupport() {
24770
+ return (0,tslib__WEBPACK_IMPORTED_MODULE_0__.__awaiter)(this, void 0, void 0, function* () {
24771
+ const [h264, hevc] = yield Promise.all([
24772
+ this.supportsCodec('video/mp4; codecs="avc1.42E01E"'),
24773
+ this.supportsCodec('video/mp4; codecs="hvc1.1.6.L93.B0"')
24774
+ ]);
24775
+ return { h264, hevc };
24776
+ });
24621
24777
  }
24622
- /** Set's the session with creation date for the given storage key to the localStorage. */
24623
- set(sessionId) {
24624
- ZSession.shape.id.parse(sessionId);
24625
- this._current = sessionId;
24626
- const session = {
24627
- id: sessionId,
24628
- created: new Date(),
24629
- expire: false,
24630
- };
24631
- window.localStorage.setItem(this.localStorageKey, JSON.stringify(session));
24778
+ supportsCodec(mime) {
24779
+ return (0,tslib__WEBPACK_IMPORTED_MODULE_0__.__awaiter)(this, void 0, void 0, function* () {
24780
+ try {
24781
+ if ("mediaCapabilities" in navigator) {
24782
+ // @ts-ignore
24783
+ const res = yield navigator.mediaCapabilities.decodingInfo({
24784
+ type: "file",
24785
+ video: { contentType: mime, width: 1920, height: 1080, bitrate: 5000000, framerate: 30 }
24786
+ });
24787
+ return !!(res === null || res === void 0 ? void 0 : res.supported);
24788
+ }
24789
+ }
24790
+ catch (_a) { }
24791
+ try {
24792
+ const v = document.createElement("video");
24793
+ return v.canPlayType(mime) !== "";
24794
+ }
24795
+ catch (_b) {
24796
+ return false;
24797
+ }
24798
+ });
24632
24799
  }
24633
- /** Removes a session from the localStorage. */
24634
- unset() {
24635
- window.localStorage.removeItem(this.localStorageKey);
24800
+ getUAInfo() {
24801
+ return (0,tslib__WEBPACK_IMPORTED_MODULE_0__.__awaiter)(this, void 0, void 0, function* () {
24802
+ if (this.uaInfoCache)
24803
+ return this.uaInfoCache;
24804
+ const nav = navigator;
24805
+ const uaData = nav.userAgentData;
24806
+ const ua = navigator.userAgent;
24807
+ let browserFamily = "Unknown";
24808
+ let browserVersion = null;
24809
+ let osFamily = "Unknown";
24810
+ let osVersion = null;
24811
+ let model = null;
24812
+ let architecture = null;
24813
+ let bitness = null;
24814
+ // --- UA-CH (modern browsers) ---
24815
+ if (uaData === null || uaData === void 0 ? void 0 : uaData.getHighEntropyValues) {
24816
+ try {
24817
+ const high = yield uaData.getHighEntropyValues([
24818
+ "platform",
24819
+ "platformVersion",
24820
+ "model",
24821
+ "architecture",
24822
+ "bitness",
24823
+ "fullVersionList"
24824
+ ]);
24825
+ osFamily = high.platform || "Unknown";
24826
+ osVersion = high.platformVersion || null;
24827
+ model = high.model || null;
24828
+ architecture = high.architecture || null;
24829
+ bitness = high.bitness || null;
24830
+ const brands = high.fullVersionList || uaData.brands || [];
24831
+ const realBrand = brands.find((b) => {
24832
+ if (!b.brand)
24833
+ return false;
24834
+ const normalized = b.brand.replace(/[^a-zA-Z]/g, "").toLowerCase();
24835
+ return !normalized.includes("chromium") && !normalized.includes("notabrand");
24836
+ });
24837
+ if (realBrand) {
24838
+ browserFamily = realBrand.brand;
24839
+ browserVersion = realBrand.version;
24840
+ }
24841
+ }
24842
+ catch (_a) {
24843
+ // silently fail
24844
+ }
24845
+ }
24846
+ // --- Fallback to classic UA parsing if browser unknown ---
24847
+ if (!browserFamily || browserFamily === "Unknown") {
24848
+ const browserRegexes = [
24849
+ [/Edg\/(\S+)/, "Edge"],
24850
+ [/OPR\/(\S+)/, "Opera"],
24851
+ [/SamsungBrowser\/(\S+)/, "Samsung Internet"],
24852
+ [/Firefox\/(\S+)/, "Firefox"],
24853
+ [/Chrome\/(\S+)/, "Chrome"],
24854
+ [/Version\/(\S+).*Safari/, "Safari"]
24855
+ ];
24856
+ for (const [regex, name] of browserRegexes) {
24857
+ const match = ua.match(regex);
24858
+ if (match) {
24859
+ browserFamily = name;
24860
+ browserVersion = match[1];
24861
+ break;
24862
+ }
24863
+ }
24864
+ }
24865
+ // --- OS detection (always run, iOS first) ---
24866
+ if (/iPhone|iPad|iPod/.test(ua)) {
24867
+ osFamily = "iOS";
24868
+ const match = ua.match(/OS (\d+[_\d]*)/);
24869
+ if (match)
24870
+ osVersion = match[1].replace(/_/g, ".");
24871
+ }
24872
+ else if (uaData === null || uaData === void 0 ? void 0 : uaData.platform) {
24873
+ osFamily = uaData.platform;
24874
+ }
24875
+ else if (/Windows/.test(ua)) {
24876
+ osFamily = "Windows";
24877
+ const match = ua.match(/Windows NT (\d+\.\d+)/);
24878
+ if (match)
24879
+ osVersion = match[1];
24880
+ }
24881
+ else if (/Mac OS X/.test(ua)) {
24882
+ osFamily = "macOS";
24883
+ const match = ua.match(/Mac OS X (\d+[_\d]*)/);
24884
+ if (match)
24885
+ osVersion = match[1].replace(/_/g, ".");
24886
+ }
24887
+ else if (/Android/.test(ua)) {
24888
+ osFamily = "Android";
24889
+ const match = ua.match(/Android (\d+(\.\d+)?)/);
24890
+ if (match)
24891
+ osVersion = match[1];
24892
+ }
24893
+ else if (/Linux/.test(ua)) {
24894
+ osFamily = "Linux";
24895
+ }
24896
+ const result = { browserFamily, browserVersion, osFamily, osVersion, model, architecture, bitness };
24897
+ this.uaInfoCache = result;
24898
+ return result;
24899
+ });
24636
24900
  }
24637
24901
  }
24638
24902
 
24639
24903
 
24640
24904
  /***/ }),
24641
24905
 
24642
- /***/ 9764:
24906
+ /***/ 2483:
24643
24907
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
24644
24908
 
24645
24909
  __webpack_require__.r(__webpack_exports__);
24646
24910
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
24647
- /* harmony export */ "Stats": () => (/* binding */ Stats)
24911
+ /* harmony export */ "extFromMime": () => (/* binding */ extFromMime),
24912
+ /* harmony export */ "normalizeType": () => (/* binding */ normalizeType),
24913
+ /* harmony export */ "parseUnknownToObject": () => (/* binding */ parseUnknownToObject),
24914
+ /* harmony export */ "randomHash": () => (/* binding */ randomHash),
24915
+ /* harmony export */ "sanitizeFilename": () => (/* binding */ sanitizeFilename),
24916
+ /* harmony export */ "truncateByBytes": () => (/* binding */ truncateByBytes)
24648
24917
  /* harmony export */ });
24649
- /* harmony import */ var _arcware_cloud_shared_pixelstreaming_websdk__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7910);
24650
-
24651
- function Stats(stats) {
24652
- const result = _arcware_cloud_shared_pixelstreaming_websdk__WEBPACK_IMPORTED_MODULE_0__.Messages.ZStats.shape.stats.parse({
24653
- /**
24654
- * Other known properties.
24655
- */
24656
- codecs: stats.codecs,
24657
- candidatePair: stats.candidatePairs,
24658
- localCandidates: stats.localCandidates,
24659
- remoteCandidates: stats.remoteCandidates,
24660
- DataChannelStats: stats.datachannelStats,
24661
- // Received (last) for ticket
24662
- bytesReceived: forceNumber(stats.inboundVideoStats.bytesReceived),
24663
- // Packets Lost (last) for ticket
24664
- packetsLost: forceNumber(stats.inboundVideoStats.packetsLost),
24665
- // Video resolution: highest lowest average (portrait) and highest lowest average (landscape) for ticket
24666
- frameWidth: forceNumber(stats.inboundVideoStats.frameWidth),
24667
- // Video resolution: highest lowest average (portrait) and highest lowest average (landscape) for ticket
24668
- frameHeight: forceNumber(stats.inboundVideoStats.frameHeight),
24669
- // Frames decoded (Last) for ticket
24670
- framesDecoded: forceNumber(stats.inboundVideoStats.framesDecoded),
24671
- // Framerate (low / high, avg) for ticket
24672
- framesPerSecond: forceNumber(stats.inboundVideoStats.framesPerSecond),
24673
- // frames dropped (last) for ticket
24674
- framesDropped: forceNumber(stats.inboundVideoStats.framesDropped),
24675
- // Videocodec (once) for ticket
24676
- videoCodec: stats.inboundVideoStats.codecId,
24677
- // audiocodec (once) for ticket
24678
- audioCodec: stats.inboundAudioStats.codecId,
24679
- // browser type and version for ticket (arcware / addition)
24680
- browserInfo: {
24681
- // to be received!
24682
- userAgent: navigator.userAgent,
24683
- platform: navigator.oscpu || navigator.platform || null,
24684
- language: navigator.language,
24685
- },
24686
- // Net RTT (low, high, avg)
24687
- // BEGIN TW CHANGE
24688
- currentRTT: calcRTT(stats.candidatePairs),
24689
- // END TW CHANGE
24690
- // Duration (last)
24691
- sessionRunTime: stats.sessionStats.runTime,
24692
- // Controls stream input (??)
24693
- controlsStreamInput: stats.sessionStats.controlsStreamInput,
24694
- // video quantization parameter (low / high / avg if applicable)
24695
- videoEncoderAvgQP: forceNumber(stats.sessionStats.videoEncoderAvgQP),
24696
- // Video bitrate (min / max / avg)
24697
- videoBitrate: forceNumber(stats.inboundVideoStats.bitrate),
24698
- // Audio bitrate (min / max / avg)
24699
- audioBitrate: forceNumber(stats.inboundAudioStats.bitrate),
24700
- });
24701
- return result;
24702
- }
24703
- /**
24704
- * Given an array of candidate pairs this function will find the highest RTT for the selected pair
24705
- * @param pairs - An array of candidate pairs
24706
- * @returns The highest round trip time of a selected candidate pair
24707
- */
24708
- function calcRTT(pairs) {
24709
- let rtt = 0;
24710
- pairs.forEach((pair) => {
24711
- if (pair.selected && pair.currentRoundTripTime > rtt) {
24712
- rtt = pair.currentRoundTripTime;
24918
+ /** Conservative normalizer: accepts object, strict JSON string, or JSON-ish like {foo: "bar"} */
24919
+ function parseUnknownToObject(input, opts = { allowJsonish: true }) {
24920
+ if (input && typeof input === "object")
24921
+ return input;
24922
+ if (typeof input === "string") {
24923
+ try {
24924
+ const parsed = JSON.parse(input);
24925
+ if (parsed && typeof parsed === "object")
24926
+ return parsed;
24927
+ }
24928
+ catch (_a) { }
24929
+ if (opts.allowJsonish) {
24930
+ const repaired = input
24931
+ .trim()
24932
+ .replace(/'/g, '"')
24933
+ .replace(/([{,]\s*)([A-Za-z_][$\w-]*)(\s*:)/g, '$1"$2"$3');
24934
+ try {
24935
+ const parsed = JSON.parse(repaired);
24936
+ if (parsed && typeof parsed === "object")
24937
+ return parsed;
24938
+ }
24939
+ catch (_b) { }
24713
24940
  }
24714
- });
24715
- return rtt;
24716
- }
24717
- /**
24718
- * Takes a number and forces it to return a number. If the value passed in is NaN, 0 will be returned.
24719
- * @param value - A number
24720
- * @returns The number passed in as value or 0 if vlaue was NaN
24721
- */
24722
- function forceNumber(value) {
24723
- return isNaN(value) ? 0 : value;
24941
+ }
24942
+ throw new Error("Input must be an object or a JSON string of an object.");
24724
24943
  }
24725
-
24726
-
24727
- /***/ }),
24728
-
24729
- /***/ 9580:
24730
- /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
24731
-
24732
- __webpack_require__.r(__webpack_exports__);
24733
- /* harmony export */ __webpack_require__.d(__webpack_exports__, {
24734
- /* harmony export */ "default": () => (/* binding */ debounce)
24735
- /* harmony export */ });
24736
- function debounce(func, wait) {
24737
- let timeout;
24738
- return function (...args) {
24739
- clearTimeout(timeout);
24740
- timeout = window.setTimeout(() => func(...args), wait);
24944
+ /** Byte-safe string truncation (no broken unicode) */
24945
+ function truncateByBytes(input, maxBytes) {
24946
+ const enc = new TextEncoder();
24947
+ const dec = new TextDecoder();
24948
+ const bytes = enc.encode(input);
24949
+ if (bytes.byteLength <= maxBytes)
24950
+ return input;
24951
+ // Back off until decode works
24952
+ for (let cut = maxBytes; cut > 0; cut--) {
24953
+ try {
24954
+ return dec.decode(bytes.subarray(0, cut), { stream: false });
24955
+ }
24956
+ catch (_a) { }
24957
+ }
24958
+ return "";
24959
+ }
24960
+ /** Cross-platform safe filename */
24961
+ function sanitizeFilename(input) {
24962
+ let name = input.replace(/\s+/g, " ").trim();
24963
+ name = name.replace(/[\\\/:\*\?"<>\|\u0000-\u001F]+/g, "");
24964
+ name = name.replace(/^\.+/, ""); // avoid hidden files
24965
+ const reserved = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\..*)?$/i;
24966
+ if (reserved.test(name) || !name)
24967
+ name = randomHash();
24968
+ if (name.length > 100)
24969
+ name = name.slice(0, 100);
24970
+ return name;
24971
+ }
24972
+ /** MIME -> extension */
24973
+ function extFromMime(mime) {
24974
+ const map = {
24975
+ "image/png": ".png",
24976
+ "image/jpeg": ".jpg",
24977
+ "image/jpg": ".jpg",
24978
+ "image/webp": ".webp",
24979
+ "image/gif": ".gif",
24980
+ "image/bmp": ".bmp",
24981
+ "image/tiff": ".tiff",
24982
+ "video/mp4": ".mp4",
24983
+ "video/webm": ".webm",
24984
+ "video/ogg": ".ogv",
24985
+ "audio/mpeg": ".mp3",
24986
+ "audio/ogg": ".ogg",
24987
+ "audio/wav": ".wav",
24988
+ "audio/webm": ".weba",
24989
+ "application/pdf": ".pdf",
24990
+ "application/zip": ".zip",
24991
+ "application/x-zip-compressed": ".zip",
24992
+ "application/json": ".json",
24993
+ "text/plain": ".txt",
24994
+ "text/csv": ".csv"
24741
24995
  };
24996
+ if (map[mime])
24997
+ return map[mime];
24998
+ if (mime.startsWith("image/") || mime.startsWith("video/") || mime.startsWith("audio/")) {
24999
+ return "." + mime.split("/")[1];
25000
+ }
25001
+ return "";
25002
+ }
25003
+ /** Short random fallback */
25004
+ function randomHash() {
25005
+ return Math.random().toString(36).slice(2, 8);
25006
+ }
25007
+ function normalizeType(v) {
25008
+ return typeof v === "string" ? v.trim().toLowerCase() : "";
24742
25009
  }
24743
25010
 
24744
25011