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