{"version":3,"sources":["jsdelivr-header.js","/npm/@freeasin/rtc-connect@0.2.1/dist/peer.js"],"names":[],"mappings":"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","file":"/npm/@freeasin/rtc-connect@0.2.1/dist/peer.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.39.0.\n * Original file: /npm/@freeasin/rtc-connect@0.2.1/dist/peer.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","// External modules\nimport { v4 as uuid } from \"@freeasin/uuid\";\nimport { defaultServers as stunServers } from \"./defaultSTUN\";\nimport { DataChannel } from \"./dataChannel\";\nimport { ArrayUpdateProxy } from \"./arrayUpdates\";\n/** Manages a WebRTC peer connection */\nclass Peer {\n    /**\n     * Initialize the connection object\n     * @param options - Initialzation options\n     * @throws If RTC support is not found\n     */\n    constructor({ logToConsole, defaultDataChannel } = {}) {\n        /** Hold state change handlers for the peer connection */\n        this.peerConnectionStateChangeHandlers = [];\n        //#endregion Public accessors\n        //#region Public properties\n        /** Unique ID for this connection */\n        this.connectionId = uuid();\n        /** Internal list of generated ICE candidates to signal */\n        this.generatedICECandidates = [];\n        /**\n         * STUN/TURN servers to use for ICE candidate negotiation\n         *   - Initially set to a copy of the default server list, but can be overriden\n         */\n        this.iceServers = ArrayUpdateProxy(this.CreatePeerConnection, stunServers.filter(() => true));\n        this.logToConsole = logToConsole !== null && logToConsole !== void 0 ? logToConsole : true;\n        this.defaultDataChannelName = defaultDataChannel || `default`;\n        // Check for RTC Support at instantiation\n        if (this.RTCSupported()) {\n            this.CreatePeerConnection();\n            this.WriteLog(`Peer connection created`, this.connectionId);\n        }\n        else\n            // If RTC is not supported, throw an error\n            throw new Error(`WebRTC is not supported`);\n    }\n    //#endregion Private properties\n    //#region Public accessors\n    /** Communication with the remote via a data channel */\n    get dataChannel() { return this._dataChannel; }\n    /** Unique ID for the remote host connection */\n    get remoteId() { return this._remoteConnectionId; }\n    //#endregion Public properties\n    //#region Private methods\n    /** Initialize the RTCPeerConnection object, and assign existing data channels */\n    CreatePeerConnection() {\n        // Set the configuration to the assigned ICE servers\n        const configuration = !!this.iceServers ? { iceServers: this.iceServers } : null;\n        // Create a new peer connection from the configuration\n        this.peerConnection = new RTCPeerConnection(configuration);\n        // Assign a handler for the ICE candidates\n        this.peerConnection.onicecandidate = (iceEvent) => {\n            this.WriteLog(`ICE Candidate received`, iceEvent);\n            // Always generate a handshake, leaving the handler to handle end-of-candidates NULL\n            this.GenerateRemoteHandshake({ iceCandidate: !!iceEvent ? iceEvent.candidate : null });\n        };\n        // Assign a handler for the connection state changes\n        this.peerConnection.onconnectionstatechange = (connectionStateChangeEvent) => {\n            this.WriteLog(`Connection state changed`, connectionStateChangeEvent);\n            this.peerConnection_onStateChanged(connectionStateChangeEvent);\n        };\n        this._dataChannel = new DataChannel(this, this.defaultDataChannelName);\n    }\n    /**\n     * Generate an answer to an RTC offer\n     */\n    async GenerateAnswer() {\n        try {\n            const answer = await this.peerConnection.createAnswer();\n            this.WriteLog(`Answer to offer created`, answer);\n            await this.SetDescription(answer);\n        }\n        catch (err) {\n            this.WriteError(err, `Creating Answer`);\n        }\n    }\n    /**\n     * Generate an offer for RTC connections\n     *   - Must be responded to with an **Answer** created by the other endpoint, and in response to this offer\n     */\n    async GenerateOffer() {\n        try {\n            const offer = await this.peerConnection.createOffer();\n            this.WriteLog(`Offer created`, offer);\n            await this.SetDescription(offer);\n        }\n        catch (err) {\n            this.WriteError(err, `Creating Offer`);\n        }\n    }\n    /** Create a new handshake object from either an RTCSessionDescriptionInit or RTCIceCandidate */\n    GenerateRemoteHandshake({ description, iceCandidate }) {\n        // Log the parameters\n        this.WriteLog(`Handshake generation`, { connection: this.connectionId, description, iceCandidate });\n        const handshake = JSON.stringify({ fromId: this.connectionId, description, iceCandidate });\n        // Immediately process a description\n        if (!!description)\n            this.onGeneratedHandshake(handshake);\n        else\n            // ICE candidates are added to an array for later use\n            this.generatedICECandidates.push(handshake);\n    }\n    /**\n     * Check for a handshake message source that is not the expected host\n     * @param handshake - Current handshake message to check\n     */\n    InvalidSourceCheck(handshake) {\n        if (this.remoteId !== handshake.fromId) {\n            // Error on invalid message source\n            const err = new Error(`From ID \"${handshake.fromId}\" does not match connection remote ID (${this.remoteId})`);\n            err.name = `INVALID HANDSHAKE SOURCE`;\n            throw err;\n        }\n    }\n    /**\n     * Set the current local description, and pass that to handshake\n     * @param description - The description generated from an offer, an answer, or an ice candidate\n     */\n    async SetDescription(description) {\n        try {\n            await this.peerConnection.setLocalDescription(description);\n            this.WriteLog(`setLocalDescription`, this.peerConnection.localDescription);\n            this.GenerateRemoteHandshake({ description });\n        }\n        catch (err) {\n            this.WriteError(err, `setting local description`);\n        }\n    }\n    /** Does the browser support RTC? */\n    RTCSupported() {\n        if (!!window.RTCPeerConnection) {\n            this.WriteLog(`WebRTC SUPPORTED`);\n            return true;\n        }\n        return false;\n    }\n    /** Handle the peer connection's onstatechanged event */\n    peerConnection_onStateChanged(evt) {\n        // Warn if no event handler is defined\n        if (this.peerConnectionStateChangeHandlers.length == 0)\n            this.WriteLog(`No event handler defined for onStateChanged for the peer connection`);\n        this.peerConnectionStateChangeHandlers.forEach(handler => handler(evt));\n    }\n    //#endregion Private methods\n    //#region Public methods\n    async ConsumeHandshake(rawHandshake) {\n        const handshake = JSON.parse(rawHandshake);\n        // Send the next ICE candidate to the consumer\n        let processICECandidate = true;\n        // Throw exception for attempt to use handshake generated by this Peer\n        if (handshake.fromId == this.connectionId) {\n            const err = new Error(`Peer connection can't process a handshake signal from itself`);\n            err.name = `SAME ORIGIN HANDSHAKE`;\n            throw err;\n        }\n        // Descriptions and ICE candidates are handled differently\n        if (!!handshake.description) {\n            // Confirm the ID source of the message\n            if (!this.remoteId)\n                this._remoteConnectionId = handshake.fromId;\n            else\n                this.InvalidSourceCheck(handshake);\n            // Set the remote description for the connection\n            await this.peerConnection.setRemoteDescription(new RTCSessionDescription(handshake.description));\n            // Create an answer to respond to an offer\n            if (handshake.description.type == `offer`) {\n                // Don't send an ICE candidate as GenerateAnswer() sends the generated description\n                processICECandidate = false;\n                await this.GenerateAnswer();\n            }\n        }\n        else if (!!handshake.iceCandidate) {\n            this.InvalidSourceCheck(handshake);\n            // Add the ICE Candidate to the connection\n            await this.peerConnection.addIceCandidate(new RTCIceCandidate(handshake.iceCandidate));\n        }\n        // If the ICE candidate list has more entries, send the next entry\n        if (processICECandidate && (this.generatedICECandidates.length > 0))\n            this.onGeneratedHandshake(this.generatedICECandidates.shift());\n    }\n    /**\n     * Start a new connection process\n     *   - Called by only the initiating browser, not both sides\n     */\n    async InitiateConnection() {\n        await this.GenerateOffer();\n    }\n    /** Expose the peer connection's onstatechanged event */\n    onStateChanged(stateChangeHandler) {\n        this.peerConnectionStateChangeHandlers.push(stateChangeHandler);\n    }\n    /** Log any parameters to the console, if logToConsole == true */\n    // eslint-disable-next-line @typescript-eslint/no-explicit-any\n    WriteLog(...args) {\n        if (this.logToConsole)\n            // eslint-disable-next-line no-console\n            console.log(args);\n    }\n    /** Log any parameters as warnings */\n    // eslint-disable-next-line @typescript-eslint/no-explicit-any\n    WriteWarning(...args) {\n        // eslint-disable-next-line no-console\n        console.log(args);\n    }\n    /**\n     * Write exceptions to the console\n     * @param err - The exception\n     * @param note - Any details provided will be written with the exception\n     */\n    // eslint-disable-next-line @typescript-eslint/no-explicit-any\n    WriteError(err, note, ...args) {\n        let tag = `EXCEPTION`;\n        if (!!note)\n            tag += ` - ${note}`;\n        // eslint-disable-next-line no-console\n        console.error(tag, err, args);\n    }\n}\nexport { Peer, };\n"]}