diff --git a/app/src/RoomClient.js b/app/src/RoomClient.js index 1ebdd8acd..9cb440912 100644 --- a/app/src/RoomClient.js +++ b/app/src/RoomClient.js @@ -382,314 +382,328 @@ export default class RoomClient { request.data ); - switch (request.method) { - case 'newConsumer': { - await this._consumingAwaitQueue.push(async () => { - if (!this._consume) { - reject(403, 'I do not want to consume'); - - return; - } - - const { - peerId, - // NOTE: We don't need this since we will use our recv transport - // anyway. - // transportId, - consumerId, - producerId, - kind, - rtpParameters, - type, - producerPaused, - consumerScore, - appData, - } = request.data; + try { + switch (request.method) { + case 'newConsumer': { + await this._consumingAwaitQueue.push(async () => { + if (!this._consume) { + reject(403, 'I do not want to consume'); + + return; + } - try { - const consumer = await this._recvTransport.consume({ - id: consumerId, + const { + peerId, + // NOTE: We don't need this since we will use our recv transport + // anyway. + // transportId, + consumerId, producerId, kind, rtpParameters, - // NOTE: Force streamId to be same in mic and webcam and different - // in screen sharing so libwebrtc will just try to sync mic and - // webcam streams from the same remote peer. - streamId: `${peerId}-${appData.source === 'screensharing' ? 'screensharing' : 'audio-video'}`, - appData: { ...appData, peerId }, - }); + type, + producerPaused, + consumerScore, + appData, + } = request.data; + + try { + const consumer = await this._recvTransport.consume({ + id: consumerId, + producerId, + kind, + rtpParameters, + // NOTE: Force streamId to be same in mic and webcam and different + // in screen sharing so libwebrtc will just try to sync mic and + // webcam streams from the same remote peer. + streamId: `${peerId}-${appData.source === 'screensharing' ? 'screensharing' : 'audio-video'}`, + appData: { ...appData, peerId }, + }); + + if (this._e2eKey && e2e.isSupported()) { + e2e.setupReceiverTransform(consumer.rtpReceiver); + } - if (this._e2eKey && e2e.isSupported()) { - e2e.setupReceiverTransform(consumer.rtpReceiver); - } + // Store in the map. + this._consumers.set(consumer.id, consumer); - // Store in the map. - this._consumers.set(consumer.id, consumer); + consumer.on('transportclose', () => { + this._consumers.delete(consumer.id); + }); - consumer.on('transportclose', () => { - this._consumers.delete(consumer.id); - }); + const { spatialLayers, temporalLayers } = + mediasoupClient.parseScalabilityMode( + consumer.rtpParameters.encodings[0].scalabilityMode + ); - const { spatialLayers, temporalLayers } = - mediasoupClient.parseScalabilityMode( - consumer.rtpParameters.encodings[0].scalabilityMode + store.dispatch( + stateActions.addConsumer( + { + id: consumer.id, + type: type, + locallyPaused: false, + remotelyPaused: producerPaused, + rtpParameters: consumer.rtpParameters, + spatialLayers: spatialLayers, + temporalLayers: temporalLayers, + preferredSpatialLayer: spatialLayers - 1, + preferredTemporalLayer: temporalLayers - 1, + priority: 1, + codec: + consumer.rtpParameters.codecs[0].mimeType.split('/')[1], + track: consumer.track, + }, + peerId + ) ); - store.dispatch( - stateActions.addConsumer( - { - id: consumer.id, - type: type, - locallyPaused: false, - remotelyPaused: producerPaused, - rtpParameters: consumer.rtpParameters, - spatialLayers: spatialLayers, - temporalLayers: temporalLayers, - preferredSpatialLayer: spatialLayers - 1, - preferredTemporalLayer: temporalLayers - 1, - priority: 1, - codec: - consumer.rtpParameters.codecs[0].mimeType.split('/')[1], - track: consumer.track, - }, - peerId - ) - ); - - store.dispatch( - stateActions.setConsumerScore(consumerId, consumerScore) - ); + store.dispatch( + stateActions.setConsumerScore(consumerId, consumerScore) + ); - // We are ready. Answer the protoo request so the server will - // resume this Consumer (which was paused for now if video). - accept(); + // We are ready. Answer the protoo request so the server will + // resume this Consumer (which was paused for now if video). + accept(); - // If audio-only mode is enabled, pause it. - if (consumer.kind === 'video' && store.getState().me.audioOnly) - this._pauseConsumer(consumer); - } catch (error) { - logger.error('"newConsumer" request failed:%o', error); + // If audio-only mode is enabled, pause it. + if (consumer.kind === 'video' && store.getState().me.audioOnly) + this._pauseConsumer(consumer); + } catch (error) { + logger.error('"newConsumer" request failed:%o', error); - store.dispatch( - requestActions.notify({ - type: 'error', - text: `Error creating a Consumer: ${error}`, - }) - ); - - throw error; - } - }); + store.dispatch( + requestActions.notify({ + type: 'error', + text: `Error creating a Consumer: ${error}`, + }) + ); - break; - } + throw error; + } + }); - case 'newDataConsumer': { - await this._consumingAwaitQueue.push(async () => { - if (!this._consume) { - reject(403, 'I do not want to data consume'); + break; + } - return; - } + case 'newDataConsumer': { + await this._consumingAwaitQueue.push(async () => { + if (!this._consume) { + reject(403, 'I do not want to data consume'); - if (!this._useDataChannel) { - reject(403, 'I do not want DataChannels'); + return; + } - return; - } + if (!this._useDataChannel) { + reject(403, 'I do not want DataChannels'); - const { - // NOTE: Undefined if bot. - peerId, - // NOTE: We don't need this since we will use our recv transport - // anyway. - // transportId, - dataConsumerId, - dataProducerId, - sctpStreamParameters, - label, - protocol, - appData, - } = request.data; + return; + } - try { - const dataConsumer = await this._recvTransport.consumeData({ - id: dataConsumerId, + const { + // NOTE: Undefined if bot. + peerId, + // NOTE: We don't need this since we will use our recv transport + // anyway. + // transportId, + dataConsumerId, dataProducerId, sctpStreamParameters, label, protocol, - appData: { ...appData, peerId }, - }); - - switch (appData.channel) { - case 'chat': { - // TODO: For debugging. - window.CHAT_DATA_CONSUMER = dataConsumer; - - break; - } + appData, + } = request.data; - case 'bot': { - // TODO: For debugging. - window.BOT_DATA_CONSUMER = dataConsumer; + try { + const dataConsumer = await this._recvTransport.consumeData({ + id: dataConsumerId, + dataProducerId, + sctpStreamParameters, + label, + protocol, + appData: { ...appData, peerId }, + }); - break; - } - } + switch (appData.channel) { + case 'chat': { + // TODO: For debugging. + window.CHAT_DATA_CONSUMER = dataConsumer; - // Store in the map. - this._dataConsumers.set(dataConsumer.id, dataConsumer); + break; + } - dataConsumer.on('transportclose', () => { - this._dataConsumers.delete(dataConsumer.id); - }); + case 'bot': { + // TODO: For debugging. + window.BOT_DATA_CONSUMER = dataConsumer; - dataConsumer.on('open', () => { - logger.debug('DataConsumer "open" event'); - }); + break; + } + } - dataConsumer.on('close', () => { - logger.warn('DataConsumer "close" event'); + // Store in the map. + this._dataConsumers.set(dataConsumer.id, dataConsumer); + + dataConsumer.on('transportclose', () => { + this._dataConsumers.delete(dataConsumer.id); + }); + + dataConsumer.on('open', () => { + logger.debug('DataConsumer "open" event'); + }); + + dataConsumer.on('close', () => { + logger.warn('DataConsumer "close" event'); + + this._dataConsumers.delete(dataConsumer.id); + }); + + dataConsumer.on('error', error => { + logger.error('DataConsumer "error" event:%o', error); + + store.dispatch( + requestActions.notify({ + type: 'error', + text: `DataConsumer error: ${error}`, + }) + ); + }); + + dataConsumer.on('message', message => { + let messageLength; + + if (typeof message === 'string') { + messageLength = this._textEncoder.encode(message).length; + } else if (message instanceof ArrayBuffer) { + messageLength = message.byteLength; + } else if (ArrayBuffer.isView(message)) { + messageLength = message.byteLength; + } else if (message instanceof Blob) { + messageLength = message.size; + } else { + messageLength = 'N/A'; + } - this._dataConsumers.delete(dataConsumer.id); - }); + logger.debug( + 'DataConsumer "message" event [streamId:%d, length:%s]', + dataConsumer.sctpStreamParameters.streamId, + messageLength + ); - dataConsumer.on('error', error => { - logger.error('DataConsumer "error" event:%o', error); + if (message instanceof ArrayBuffer) { + const view = new DataView(message); + const number = view.getUint32(); - store.dispatch( - requestActions.notify({ - type: 'error', - text: `DataConsumer error: ${error}`, - }) - ); - }); + if (number == Math.pow(2, 32) - 1) { + logger.debug('dataChannelTest finished!'); - dataConsumer.on('message', message => { - let messageLength; - - if (typeof message === 'string') { - messageLength = this._textEncoder.encode(message).length; - } else if (message instanceof ArrayBuffer) { - messageLength = message.byteLength; - } else if (ArrayBuffer.isView(message)) { - messageLength = message.byteLength; - } else if (message instanceof Blob) { - messageLength = message.size; - } else { - messageLength = 'N/A'; - } + this._nextDataChannelTestNumber = 0; - logger.debug( - 'DataConsumer "message" event [streamId:%d, length:%s]', - dataConsumer.sctpStreamParameters.streamId, - messageLength - ); + return; + } - if (message instanceof ArrayBuffer) { - const view = new DataView(message); - const number = view.getUint32(); + if (number > this._nextDataChannelTestNumber) { + logger.warn( + 'dataChannelTest: %s packets missing', + number - this._nextDataChannelTestNumber + ); + } - if (number == Math.pow(2, 32) - 1) { - logger.warn('dataChannelTest finished!'); + logger.debug('dataChannelTest: received packet %o', number); - this._nextDataChannelTestNumber = 0; + this._nextDataChannelTestNumber = number + 1; return; - } - - if (number > this._nextDataChannelTestNumber) { + } else if (typeof message !== 'string') { logger.warn( - 'dataChannelTest: %s packets missing', - number - this._nextDataChannelTestNumber + 'ignoring DataConsumer "message" (not a string)' ); - } - - this._nextDataChannelTestNumber = number + 1; - - return; - } else if (typeof message !== 'string') { - logger.warn('ignoring DataConsumer "message" (not a string)'); - return; - } - - if (messageLength > 200) { - message = `${message.slice(0, 40)} ... ${message.slice(-40)}`; - } + return; + } - switch (dataConsumer.label) { - case 'chat': { - const { peers } = store.getState(); - const peersArray = Object.keys(peers).map( - _peerId => peers[_peerId] - ); - const sendingPeer = peersArray.find(peer => - peer.dataConsumers.includes(dataConsumer.id) - ); + if (messageLength > 200) { + message = `${message.slice(0, 40)} ... ${message.slice(-40)}`; + } - if (!sendingPeer) { - logger.warn('DataConsumer "message" from unknown peer'); + switch (dataConsumer.label) { + case 'chat': { + const { peers } = store.getState(); + const peersArray = Object.keys(peers).map( + _peerId => peers[_peerId] + ); + const sendingPeer = peersArray.find(peer => + peer.dataConsumers.includes(dataConsumer.id) + ); + + if (!sendingPeer) { + logger.warn('DataConsumer "message" from unknown peer'); + + break; + } + + store.dispatch( + requestActions.notify({ + title: `${sendingPeer.displayName} says:`, + text: message, + timeout: 5000, + }) + ); break; } - store.dispatch( - requestActions.notify({ - title: `${sendingPeer.displayName} says:`, - text: message, - timeout: 5000, - }) - ); - - break; - } - - case 'bot': { - store.dispatch( - requestActions.notify({ - title: 'Message from Bot:', - text: message, - timeout: 5000, - }) - ); + case 'bot': { + store.dispatch( + requestActions.notify({ + title: 'Message from Bot:', + text: message, + timeout: 5000, + }) + ); - break; + break; + } } - } - }); + }); - store.dispatch( - stateActions.addDataConsumer( - { - id: dataConsumer.id, - sctpStreamParameters: dataConsumer.sctpStreamParameters, - label: dataConsumer.label, - protocol: dataConsumer.protocol, - }, - peerId - ) - ); + store.dispatch( + stateActions.addDataConsumer( + { + id: dataConsumer.id, + sctpStreamParameters: dataConsumer.sctpStreamParameters, + label: dataConsumer.label, + protocol: dataConsumer.protocol, + }, + peerId + ) + ); - // We are ready. Answer the protoo request. - accept(); - } catch (error) { - logger.error('"newDataConsumer" request failed:%o', error); + // We are ready. Answer the protoo request. + accept(); + } catch (error) { + logger.error('"newDataConsumer" request failed:%o', error); - store.dispatch( - requestActions.notify({ - type: 'error', - text: `Error creating a DataConsumer: ${error}`, - }) - ); + store.dispatch( + requestActions.notify({ + type: 'error', + text: `Error creating a DataConsumer: ${error}`, + }) + ); - throw error; - } - }); + throw error; + } + }); - break; + break; + } } + } catch (error) { + logger.error( + 'failed to process "%s" request:%o', + request.method, + error + ); + + reject(error); } }); @@ -2358,9 +2372,6 @@ export default class RoomClient { const transportInfo = await this._protoo.request( 'createWebRtcTransport', { - sctpCapabilities: this._useDataChannel - ? this._mediasoupDevice.sctpCapabilities - : undefined, forceTcp: this._forceTcp, appData: { direction: 'producer', @@ -2470,9 +2481,6 @@ export default class RoomClient { const transportInfo = await this._protoo.request( 'createWebRtcTransport', { - sctpCapabilities: this._useDataChannel - ? this._mediasoupDevice.sctpCapabilities - : undefined, forceTcp: this._forceTcp, appData: { direction: 'consumer', @@ -2532,10 +2540,6 @@ export default class RoomClient { rtpCapabilities: this._consume ? this._mediasoupDevice.rtpCapabilities : undefined, - sctpCapabilities: - this._useDataChannel && this._consume - ? this._mediasoupDevice.sctpCapabilities - : undefined, }); store.dispatch(stateActions.setRoomState('connected')); diff --git a/app/src/index.jsx b/app/src/index.jsx index 74859efa1..895f54165 100644 --- a/app/src/index.jsx +++ b/app/src/index.jsx @@ -284,6 +284,10 @@ window.__startDataChannelTest = function () { view.setUint32(0, number++); roomClient.sendChatMessage(buffer); } + + if (number === Math.pow(2, 32) - 1) { + clearInterval(dataChannelTestInterval); + } }, 100); }; @@ -299,28 +303,36 @@ window.__stopDataChannelTest = function () { } }; -window.__testSctp = async function ({ timeout = 100, bot = false } = {}) { +window.__testSctp = async function ({ timeout = 250, bot = false } = {}) { let dp; if (!bot) { - await window.CLIENT.enableChatDataProducer(); - dp = window.CLIENT._chatDataProducer; } else { - await window.CLIENT.enableBotDataProducer(); - dp = window.CLIENT._botDataProducer; } logger.debug( - '__testSctp() | DataProducer created [bot:%s, streamId:%d, readyState:%s]', + '__testSctp() | DataProducer selected [bot:%s, streamId:%d, readyState:%s]', bot ? 'true' : 'false', dp.sctpStreamParameters.streamId, dp.readyState ); + let msgIdx = 0; + function send() { - dp.send(`I am streamId ${dp.sctpStreamParameters.streamId}`); + msgIdx++; + + logger.debug('__testSctp() >>> sending message [idx:%o]', msgIdx); + + dp.send( + `message idx ${msgIdx} [streamId:${dp.sctpStreamParameters.streamId}]` + ); + + setTimeout(() => { + send(); + }, timeout); } if (dp.readyState === 'open') { @@ -335,8 +347,6 @@ window.__testSctp = async function ({ timeout = 100, bot = false } = {}) { send(); }); } - - setTimeout(() => window.__testSctp({ timeout, bot }), timeout); }; setInterval(() => { diff --git a/server/config.example.mjs b/server/config.example.mjs index 0fe849f1b..5429a8e4d 100644 --- a/server/config.example.mjs +++ b/server/config.example.mjs @@ -63,7 +63,6 @@ export const config = { 'sctp', ], disableLiburing: false, - useBuiltInSctpStack: false, }, /** * mediasoup Router options. @@ -159,8 +158,11 @@ export const config = { webRtcTransportOptions: { initialAvailableOutgoingBitrate: 1000000, minimumAvailableOutgoingBitrate: 600000, - maxSctpMessageSize: 262144, - sctpSendBufferSize: 262144, + maxSendMessageSize: 5000000, + sctpSendBufferSize: 7000000, + sctpPerStreamSendQueueLimit: 5000000, + maxReceiveMessageSize: 5000000, + sctpMaxReceiverWindowBufferSize: 7000000, }, /** * Additional options that are not part of WebRtcTransportOptions but @@ -186,8 +188,11 @@ export const config = { // sendBufferSize: 2000000, // recvBufferSize: 2000000, }, - maxSctpMessageSize: 262144, - sctpSendBufferSize: 262144, + maxSendMessageSize: 5000000, + sctpSendBufferSize: 7000000, + sctpPerStreamSendQueueLimit: 5000000, + maxReceiveMessageSize: 5000000, + sctpMaxReceiverWindowBufferSize: 7000000, }, }, providers: { diff --git a/server/package-lock.json b/server/package-lock.json index 3d4a5f250..b5ecfee22 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -14,7 +14,7 @@ "body-parser": "^2.2.2", "debug": "^4.4.3", "express": "^5.2.1", - "mediasoup": "^3.19.22", + "mediasoup": "^3.20.0", "picocolors": "^1.1.1", "pidusage": "^4.0.1", "protoo-server": "^4.0.8", @@ -31,15 +31,15 @@ "@types/node": "^24.10.1", "@types/pidusage": "^2.0.5", "@types/protoo-server": "^4.0.6", - "eslint": "^10.3.0", + "eslint": "^10.4.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "globals": "^17.6.0", "nodemon": "^3.1.14", "prettier": "^3.8.3", - "tsx": "^4.21.0", + "tsx": "^4.22.3", "typescript": "^6.0.3", - "typescript-eslint": "^8.59.2" + "typescript-eslint": "^8.60.0" }, "engines": { "node": ">=22" @@ -50,9 +50,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.1.tgz", - "integrity": "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz", + "integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==", "cpu": [ "ppc64" ], @@ -67,9 +67,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.1.tgz", - "integrity": "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz", + "integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==", "cpu": [ "arm" ], @@ -84,9 +84,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.1.tgz", - "integrity": "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz", + "integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==", "cpu": [ "arm64" ], @@ -101,9 +101,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.1.tgz", - "integrity": "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz", + "integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==", "cpu": [ "x64" ], @@ -118,9 +118,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.1.tgz", - "integrity": "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz", + "integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==", "cpu": [ "arm64" ], @@ -135,9 +135,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.1.tgz", - "integrity": "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz", + "integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==", "cpu": [ "x64" ], @@ -152,9 +152,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.1.tgz", - "integrity": "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz", + "integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==", "cpu": [ "arm64" ], @@ -169,9 +169,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.1.tgz", - "integrity": "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz", + "integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==", "cpu": [ "x64" ], @@ -186,9 +186,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.1.tgz", - "integrity": "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz", + "integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==", "cpu": [ "arm" ], @@ -203,9 +203,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.1.tgz", - "integrity": "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz", + "integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==", "cpu": [ "arm64" ], @@ -220,9 +220,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.1.tgz", - "integrity": "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz", + "integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==", "cpu": [ "ia32" ], @@ -237,9 +237,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.1.tgz", - "integrity": "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz", + "integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==", "cpu": [ "loong64" ], @@ -254,9 +254,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.1.tgz", - "integrity": "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz", + "integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==", "cpu": [ "mips64el" ], @@ -271,9 +271,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.1.tgz", - "integrity": "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz", + "integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==", "cpu": [ "ppc64" ], @@ -288,9 +288,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.1.tgz", - "integrity": "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz", + "integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==", "cpu": [ "riscv64" ], @@ -305,9 +305,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.1.tgz", - "integrity": "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz", + "integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==", "cpu": [ "s390x" ], @@ -322,9 +322,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.1.tgz", - "integrity": "sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz", + "integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==", "cpu": [ "x64" ], @@ -339,9 +339,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.1.tgz", - "integrity": "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz", + "integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==", "cpu": [ "arm64" ], @@ -356,9 +356,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.1.tgz", - "integrity": "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz", + "integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==", "cpu": [ "x64" ], @@ -373,9 +373,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.1.tgz", - "integrity": "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz", + "integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==", "cpu": [ "arm64" ], @@ -390,9 +390,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.1.tgz", - "integrity": "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz", + "integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==", "cpu": [ "x64" ], @@ -407,9 +407,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.1.tgz", - "integrity": "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz", + "integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==", "cpu": [ "arm64" ], @@ -424,9 +424,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.1.tgz", - "integrity": "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz", + "integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==", "cpu": [ "x64" ], @@ -441,9 +441,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.1.tgz", - "integrity": "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz", + "integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==", "cpu": [ "arm64" ], @@ -458,9 +458,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.1.tgz", - "integrity": "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz", + "integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==", "cpu": [ "ia32" ], @@ -475,9 +475,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.1.tgz", - "integrity": "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz", + "integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==", "cpu": [ "x64" ], @@ -549,9 +549,9 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.5.tgz", - "integrity": "sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz", + "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -884,17 +884,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.2.tgz", - "integrity": "sha512-j/bwmkBvHUtPNxzuWe5z6BEk3q54YRyGlBXkSsmfoih7zNrBvl5A9A98anlp/7JbyZcWIJ8KXo/3Tq/DjFLtuQ==", + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.60.0.tgz", + "integrity": "sha512-QYb/sa74/s7OKMbACMjrYnGspj9Hs5YI5aaffSL65UfeBUzVzBJfVo3oWSpbzPurvm7yaCCo2Lk7lVj610HqKw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.59.2", - "@typescript-eslint/type-utils": "8.59.2", - "@typescript-eslint/utils": "8.59.2", - "@typescript-eslint/visitor-keys": "8.59.2", + "@typescript-eslint/scope-manager": "8.60.0", + "@typescript-eslint/type-utils": "8.60.0", + "@typescript-eslint/utils": "8.60.0", + "@typescript-eslint/visitor-keys": "8.60.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -907,7 +907,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.59.2", + "@typescript-eslint/parser": "^8.60.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } @@ -923,16 +923,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.2.tgz", - "integrity": "sha512-plR3pp6D+SSUn1HM7xvSkx12/DhoHInI2YF35KAcVFNZvlC0gtrWqx7Qq1oH2Ssgi0vlFRCTbP+DZc7B9+TtsQ==", + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.60.0.tgz", + "integrity": "sha512-fcqpj/MyK4sxDPcbe7STNPbpQL4RLZOPWuaTmwZYuc+hJKzRf58yRxfhqGpc6PIq9ZyfSBpfHgmUHmHs0KwHwg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.59.2", - "@typescript-eslint/types": "8.59.2", - "@typescript-eslint/typescript-estree": "8.59.2", - "@typescript-eslint/visitor-keys": "8.59.2", + "@typescript-eslint/scope-manager": "8.60.0", + "@typescript-eslint/types": "8.60.0", + "@typescript-eslint/typescript-estree": "8.60.0", + "@typescript-eslint/visitor-keys": "8.60.0", "debug": "^4.4.3" }, "engines": { @@ -948,14 +948,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.2.tgz", - "integrity": "sha512-+2hqvEkeyf/0FBor67duF0Ll7Ot8jyKzDQOSrxazF/danillRq2DwR9dLptsXpoZQqxE1UisSmoZewrlPas9Vw==", + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.60.0.tgz", + "integrity": "sha512-aZu74NNKJeUWqCjDddzdiKaS82dgYgV/vmf+Ui3ZdZejmgfXR/q+pRumgobnQ2cCJTgGTWp4ypiwsuofFubavg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.59.2", - "@typescript-eslint/types": "^8.59.2", + "@typescript-eslint/tsconfig-utils": "^8.60.0", + "@typescript-eslint/types": "^8.60.0", "debug": "^4.4.3" }, "engines": { @@ -970,14 +970,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.2.tgz", - "integrity": "sha512-JzfyEpEtOU89CcFSwyNS3mu4MLvLSXqnmX05+aKBDM+TdR5jzcGOEBwxwGNxrEQ7p/z6kK2WyioCGBf2zZBnvg==", + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.60.0.tgz", + "integrity": "sha512-pFzqhllJMs+jghLQWzV00ds39xLzuyqPSev5pd8f4Ir0rtKR3ZLUB4/4dhjOFighWb9larvtfJvqL+4yKDI3Xw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.2", - "@typescript-eslint/visitor-keys": "8.59.2" + "@typescript-eslint/types": "8.60.0", + "@typescript-eslint/visitor-keys": "8.60.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -988,9 +988,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.2.tgz", - "integrity": "sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw==", + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.60.0.tgz", + "integrity": "sha512-BZPR3RGYlAXnly6ymAxfkVn5rCbZzQNou0rxv3GfWZ8cTQp+hhVd73khbGLAd8k1TlAPLISH337M+tAgAnaJDQ==", "dev": true, "license": "MIT", "engines": { @@ -1005,15 +1005,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.2.tgz", - "integrity": "sha512-nhqaj1nmTdVVl/BP5omXNRGO38jn5iosis2vbdmupF2txCf8ylWT8lx+JlvMYYVqzGVKtjojUFoQ3JRWK+mfzQ==", + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.60.0.tgz", + "integrity": "sha512-SX46wEUtitCpq7AN38HkUU/+zvUpdKf7ephtWAFgckH8O7PQIyL5gvrhQgBLuEYgLfuKWOVvWVskMbuFHAz5xg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.2", - "@typescript-eslint/typescript-estree": "8.59.2", - "@typescript-eslint/utils": "8.59.2", + "@typescript-eslint/types": "8.60.0", + "@typescript-eslint/typescript-estree": "8.60.0", + "@typescript-eslint/utils": "8.60.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -1030,9 +1030,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.2.tgz", - "integrity": "sha512-e82GVOE8Ps3E++Egvb6Y3Dw0S10u8NkQ9KXmtRhCWJJ8kDhOJTvtMAWnFL16kB1583goCWXsr0NieKCZMs2/0Q==", + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.60.0.tgz", + "integrity": "sha512-AsE7x2XaAK+CVbeih0Fvbn+r1qHxtpLDJ3XUuFcIinT318T90yHMJC+Zgv+jUuDjQQd06HKwxnDu6sz1IcTilA==", "dev": true, "license": "MIT", "engines": { @@ -1044,16 +1044,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.2.tgz", - "integrity": "sha512-o0XPGNwcWw+FIwStOWn+BwBuEmL6QXP0rsvAFg7ET1dey1Nr6Wb1ac8p5HEsK0ygO/6mUxlk+YWQD9xcb/nnXg==", + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.60.0.tgz", + "integrity": "sha512-3AcZNBGMClm6CXDyo8kYvVGT/sx29sS0oBsIb9oZI2gunA4Vm2M3YHzRLPvsUBBsl+yB5FPtltq7gGH0iTlp9g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.59.2", - "@typescript-eslint/tsconfig-utils": "8.59.2", - "@typescript-eslint/types": "8.59.2", - "@typescript-eslint/visitor-keys": "8.59.2", + "@typescript-eslint/project-service": "8.60.0", + "@typescript-eslint/tsconfig-utils": "8.60.0", + "@typescript-eslint/types": "8.60.0", + "@typescript-eslint/visitor-keys": "8.60.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -1072,16 +1072,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.2.tgz", - "integrity": "sha512-Juw3EinkXqjaffxz6roowvV7GZT/kET5vSKKZT6upl5TXdWkLkYmNPXwDDL2Vkt2DPn0nODIS4egC/0AGxKo/Q==", + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.60.0.tgz", + "integrity": "sha512-HtXuPfrHTyBDkameWpl+vJb1Uevu2tznAyahM1Oc4AENidCLTPiZDWIo4GfcxNdC/RcfGcadzzkqbRG87dUrQA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.59.2", - "@typescript-eslint/types": "8.59.2", - "@typescript-eslint/typescript-estree": "8.59.2" + "@typescript-eslint/scope-manager": "8.60.0", + "@typescript-eslint/types": "8.60.0", + "@typescript-eslint/typescript-estree": "8.60.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1096,13 +1096,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.2.tgz", - "integrity": "sha512-NwjLUnGy8/Zfx23fl50tRC8rYaYnM52xNRYFAXvmiil9yh1+K6aRVQMnzW6gQB/1DLgWt977lYQn7C+wtgXZiA==", + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.60.0.tgz", + "integrity": "sha512-9WI52t8ZGLVGrPMBet25yAftqY/n95+zmoUUtJBBQTKDSKUu7OsPTroT2op7U9JatkoRccL0YkWDNMFfC4Sjxg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/types": "8.60.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -1244,9 +1244,9 @@ } }, "node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", "dev": true, "license": "MIT", "dependencies": { @@ -1576,9 +1576,9 @@ } }, "node_modules/esbuild": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.1.tgz", - "integrity": "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz", + "integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -1589,32 +1589,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.1", - "@esbuild/android-arm": "0.27.1", - "@esbuild/android-arm64": "0.27.1", - "@esbuild/android-x64": "0.27.1", - "@esbuild/darwin-arm64": "0.27.1", - "@esbuild/darwin-x64": "0.27.1", - "@esbuild/freebsd-arm64": "0.27.1", - "@esbuild/freebsd-x64": "0.27.1", - "@esbuild/linux-arm": "0.27.1", - "@esbuild/linux-arm64": "0.27.1", - "@esbuild/linux-ia32": "0.27.1", - "@esbuild/linux-loong64": "0.27.1", - "@esbuild/linux-mips64el": "0.27.1", - "@esbuild/linux-ppc64": "0.27.1", - "@esbuild/linux-riscv64": "0.27.1", - "@esbuild/linux-s390x": "0.27.1", - "@esbuild/linux-x64": "0.27.1", - "@esbuild/netbsd-arm64": "0.27.1", - "@esbuild/netbsd-x64": "0.27.1", - "@esbuild/openbsd-arm64": "0.27.1", - "@esbuild/openbsd-x64": "0.27.1", - "@esbuild/openharmony-arm64": "0.27.1", - "@esbuild/sunos-x64": "0.27.1", - "@esbuild/win32-arm64": "0.27.1", - "@esbuild/win32-ia32": "0.27.1", - "@esbuild/win32-x64": "0.27.1" + "@esbuild/aix-ppc64": "0.28.0", + "@esbuild/android-arm": "0.28.0", + "@esbuild/android-arm64": "0.28.0", + "@esbuild/android-x64": "0.28.0", + "@esbuild/darwin-arm64": "0.28.0", + "@esbuild/darwin-x64": "0.28.0", + "@esbuild/freebsd-arm64": "0.28.0", + "@esbuild/freebsd-x64": "0.28.0", + "@esbuild/linux-arm": "0.28.0", + "@esbuild/linux-arm64": "0.28.0", + "@esbuild/linux-ia32": "0.28.0", + "@esbuild/linux-loong64": "0.28.0", + "@esbuild/linux-mips64el": "0.28.0", + "@esbuild/linux-ppc64": "0.28.0", + "@esbuild/linux-riscv64": "0.28.0", + "@esbuild/linux-s390x": "0.28.0", + "@esbuild/linux-x64": "0.28.0", + "@esbuild/netbsd-arm64": "0.28.0", + "@esbuild/netbsd-x64": "0.28.0", + "@esbuild/openbsd-arm64": "0.28.0", + "@esbuild/openbsd-x64": "0.28.0", + "@esbuild/openharmony-arm64": "0.28.0", + "@esbuild/sunos-x64": "0.28.0", + "@esbuild/win32-arm64": "0.28.0", + "@esbuild/win32-ia32": "0.28.0", + "@esbuild/win32-x64": "0.28.0" } }, "node_modules/escape-html": { @@ -1637,16 +1637,16 @@ } }, "node_modules/eslint": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.3.0.tgz", - "integrity": "sha512-XbEXaRva5cF0ZQB8w6MluHA0kZZfV2DuCMJ3ozyEOHLwDpZX2Lmm/7Pp0xdJmI0GL1W05VH5VwIFHEm1Vcw2gw==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.4.0.tgz", + "integrity": "sha512-loXy6bWOoP3EP6JA7jo6p5jMpBJmHmsNZM5SFRHLdh1MGOPurMnNBj4ZlAbaqUAaQWbCr7jHV4P7gzAyryZWkQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", "@eslint/config-array": "^0.23.5", - "@eslint/config-helpers": "^0.5.5", + "@eslint/config-helpers": "^0.6.0", "@eslint/core": "^1.2.1", "@eslint/plugin-kit": "^0.7.1", "@humanfs/node": "^0.16.6", @@ -2150,19 +2150,6 @@ "node": ">= 0.4" } }, - "node_modules/get-tsconfig": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", - "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -2469,9 +2456,9 @@ } }, "node_modules/mediasoup": { - "version": "3.19.22", - "resolved": "https://registry.npmjs.org/mediasoup/-/mediasoup-3.19.22.tgz", - "integrity": "sha512-BzEl6zSNsANdq8LCGswoF7cr2r84SDgfGHnA393SL4wFjPmW7kij3gHPHvj6o+i15hjnzSvqWV2fUiFNWKq6Jw==", + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/mediasoup/-/mediasoup-3.20.0.tgz", + "integrity": "sha512-IOV/BpuGKJ2gUsA90QCmla+osNNuGHRxyIUxE43abdQ9qqk7iVtoUCUXwNLML+6BXBcVuklBofHJjJDj+jCepQ==", "hasInstallScript": true, "license": "ISC", "dependencies": { @@ -2480,7 +2467,7 @@ "h264-profile-level-id": "^2.3.2", "node-fetch": "^3.3.2", "supports-color": "^10.2.2", - "tar": "^7.5.13" + "tar": "^7.5.15" }, "engines": { "node": ">=22" @@ -2995,16 +2982,6 @@ "node": ">=8.10.0" } }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, "node_modules/router": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", @@ -3249,9 +3226,9 @@ } }, "node_modules/tar": { - "version": "7.5.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.13.tgz", - "integrity": "sha512-tOG/7GyXpFevhXVh8jOPJrmtRpOTsYqUIkVdVooZYJS/z8WhfQUX8RJILmeuJNinGAMSu1veBr4asSHFt5/hng==", + "version": "7.5.15", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.15.tgz", + "integrity": "sha512-dzGK0boVlC4W5QFuQN1EFSl3bIDYsk7Tj40U6eIBnK2k/8ml7TZ5agbI5j5+qnoVcAA+rNtBml8SEiLxZpNqRQ==", "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", @@ -3358,14 +3335,13 @@ } }, "node_modules/tsx": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", - "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "version": "4.22.3", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.3.tgz", + "integrity": "sha512-mdoNxBC/cSQObGGVQ5Bpn5i+yv7j68gk3Nfm3wFjcJg3Z0Mix9jzAFfP12prmm5eVGmDKtp0yyArrs0Q+8gZHg==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "~0.27.0", - "get-tsconfig": "^4.7.5" + "esbuild": "~0.28.0" }, "bin": { "tsx": "dist/cli.mjs" @@ -3434,16 +3410,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.2.tgz", - "integrity": "sha512-pJw051uomb3ZeCzGTpRb8RbEqB5Y4WWet8gl/GcTlU35BSx0PVdZ86/bqkQCyKKuraVQEK7r6kBHQXF+fBhkoQ==", + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.60.0.tgz", + "integrity": "sha512-9f65qWLZdAW9m1JaxBDUHcqRUfL8bkxxXL7XxEfI+F09q56PkBvIfCjLF3yInsDM/BBmwkqmCQdCZe/RYlIWEw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.59.2", - "@typescript-eslint/parser": "8.59.2", - "@typescript-eslint/typescript-estree": "8.59.2", - "@typescript-eslint/utils": "8.59.2" + "@typescript-eslint/eslint-plugin": "8.60.0", + "@typescript-eslint/parser": "8.60.0", + "@typescript-eslint/typescript-estree": "8.60.0", + "@typescript-eslint/utils": "8.60.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/server/package.json b/server/package.json index 32a92992e..518d1cbef 100644 --- a/server/package.json +++ b/server/package.json @@ -46,7 +46,7 @@ "body-parser": "^2.2.2", "debug": "^4.4.3", "express": "^5.2.1", - "mediasoup": "^3.19.22", + "mediasoup": "^3.20.0", "picocolors": "^1.1.1", "pidusage": "^4.0.1", "protoo-server": "^4.0.8", @@ -59,14 +59,14 @@ "@types/node": "^24.10.1", "@types/pidusage": "^2.0.5", "@types/protoo-server": "^4.0.6", - "eslint": "^10.3.0", + "eslint": "^10.4.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "globals": "^17.6.0", "nodemon": "^3.1.14", "prettier": "^3.8.3", - "tsx": "^4.21.0", + "tsx": "^4.22.3", "typescript": "^6.0.3", - "typescript-eslint": "^8.59.2" + "typescript-eslint": "^8.60.0" } } diff --git a/server/src/Bot.ts b/server/src/Bot.ts index 2ea99f514..b8aff9772 100644 --- a/server/src/Bot.ts +++ b/server/src/Bot.ts @@ -38,12 +38,14 @@ export class Bot { logger.debug('create()'); const producerDirectTransport = await producerRouter.createDirectTransport({ - maxMessageSize: 4000000, + maxSendMessageSize: 4000000, + maxReceiveMessageSize: 4000000, }); const consumerDirectTransport = usePipeTransports ? await producerRouter.createDirectTransport({ - maxMessageSize: 4000000, + maxSendMessageSize: 4000000, + maxReceiveMessageSize: 4000000, }) : producerDirectTransport; diff --git a/server/src/Peer.ts b/server/src/Peer.ts index 93fa31cf7..ef4211865 100644 --- a/server/src/Peer.ts +++ b/server/src/Peer.ts @@ -92,7 +92,6 @@ export type PeerEvents = { 'create-webrtc-transport': [ { direction: TransportDirection; - sctpCapabilities?: mediasoupTypes.SctpCapabilities; forceTcp?: boolean; }, resolve: ( @@ -157,7 +156,6 @@ export class Peer extends EnhancedEventEmitter { #displayName?: string; #device?: PeerDevice; #rtpCapabilities?: mediasoupTypes.RtpCapabilities; - #sctpCapabilities?: mediasoupTypes.SctpCapabilities; readonly #transports: Map< string, mediasoupTypes.WebRtcTransport @@ -404,14 +402,6 @@ export class Peer extends EnhancedEventEmitter { dataProducer.appData.channel ); - if (!this.#sctpCapabilities) { - this.#logger.debug( - 'consumeData() | no SCTP capabilities, cannot consume data' - ); - - return; - } - const transport = this.getConsumerWebRtcTransport(); if (!transport) { @@ -806,13 +796,12 @@ export class Peer extends EnhancedEventEmitter { throw new InvalidStateError('Peer already joined'); } - const { displayName, device, rtpCapabilities, sctpCapabilities } = data; + const { displayName, device, rtpCapabilities } = data; this.#joined = true; this.#displayName = displayName; this.#device = device; this.#rtpCapabilities = rtpCapabilities; - this.#sctpCapabilities = sctpCapabilities; clearTimeout(this.#joinTimer); @@ -824,7 +813,7 @@ export class Peer extends EnhancedEventEmitter { } case 'createWebRtcTransport': { - const { sctpCapabilities, forceTcp, appData } = data; + const { forceTcp, appData } = data; const { direction } = appData; const transport = await new Promise< mediasoupTypes.WebRtcTransport @@ -832,7 +821,7 @@ export class Peer extends EnhancedEventEmitter { >((resolve, reject) => { this.emit( 'create-webrtc-transport', - { direction, sctpCapabilities, forceTcp }, + { direction, forceTcp }, resolve, reject ); diff --git a/server/src/Room.ts b/server/src/Room.ts index 59a741e52..8584bd4eb 100644 --- a/server/src/Room.ts +++ b/server/src/Room.ts @@ -536,7 +536,7 @@ export class Room extends EnhancedEventEmitter { peer.on( 'create-webrtc-transport', // eslint-disable-next-line @typescript-eslint/no-misused-promises - async ({ direction, sctpCapabilities, forceTcp }, resolve, reject) => { + async ({ direction, forceTcp }, resolve, reject) => { try { let mediasoupRouter: mediasoupTypes.Router; let mediasoupWebRtcServer: mediasoupTypes.WebRtcServer; @@ -569,15 +569,7 @@ export class Room extends EnhancedEventEmitter { enableTcp: true, webRtcServer: mediasoupWebRtcServer, iceConsentTimeout: 20, - enableSctp: Boolean(sctpCapabilities), - // OS and MIS given to the server transport must be the reversed OS and MIS - // of the SCTP capabilities of the client. - numSctpStreams: sctpCapabilities - ? { - OS: sctpCapabilities.numStreams.MIS, - MIS: sctpCapabilities.numStreams.OS, - } - : undefined, + enableSctp: true, appData: { direction }, } ); diff --git a/server/src/signaling/protooMessages.ts b/server/src/signaling/protooMessages.ts index 38841edd9..22d735edf 100644 --- a/server/src/signaling/protooMessages.ts +++ b/server/src/signaling/protooMessages.ts @@ -124,7 +124,6 @@ type RequestFromPeer = displayName: string; device: PeerDevice; rtpCapabilities?: mediasoupTypes.RtpCapabilities; - sctpCapabilities?: mediasoupTypes.SctpCapabilities; }; responseData: { peers: SerializedPeer[]; @@ -133,7 +132,6 @@ type RequestFromPeer = | { name: 'createWebRtcTransport'; data: { - sctpCapabilities?: mediasoupTypes.SctpCapabilities; forceTcp: boolean; appData: WebRtcTransportAppData; }; diff --git a/server/src/types.ts b/server/src/types.ts index 0706a640b..762dc9e90 100644 --- a/server/src/types.ts +++ b/server/src/types.ts @@ -18,7 +18,6 @@ export type ServerConfig = { logLevel: mediasoupTypes.WorkerLogLevel; logTags: mediasoupTypes.WorkerLogTag[]; disableLiburing?: boolean; - useBuiltInSctpStack?: boolean; }; routerOptions: { mediaCodecs: mediasoupTypes.RouterRtpCodecCapability[]; @@ -29,8 +28,11 @@ export type ServerConfig = { webRtcTransportOptions?: { initialAvailableOutgoingBitrate?: number; minimumAvailableOutgoingBitrate?: number; - maxSctpMessageSize?: number; + maxSendMessageSize?: number; sctpSendBufferSize?: number; + sctpPerStreamSendQueueLimit?: number; + maxReceiveMessageSize?: number; + sctpMaxReceiverWindowBufferSize?: number; }; /** * Additional options that are not part of WebRtcTransportOptions but @@ -41,8 +43,11 @@ export type ServerConfig = { }; plainTransportOptions: { listenInfo: mediasoupTypes.TransportListenInfo; - maxSctpMessageSize?: number; + maxSendMessageSize?: number; sctpSendBufferSize?: number; + sctpPerStreamSendQueueLimit?: number; + maxReceiveMessageSize?: number; + sctpMaxReceiverWindowBufferSize?: number; }; }; providers?: {