import{assert}from"./assert.js";import{debug}from"./Debug.js";const debugProtocolSend=debug("puppeteer:protocol:SEND \u25ba"),debugProtocolReceive=debug("puppeteer:protocol:RECV \u25c0");import{EventEmitter}from"./EventEmitter.js";export const ConnectionEmittedEvents={Disconnected:Symbol("Connection.Disconnected")};export class Connection extends EventEmitter{constructor(e,s,t=0){super(),this._lastId=0,this._sessions=new Map,this._closed=!1,this._callbacks=new Map,this._url=e,this._delay=t,this._transport=s,this._transport.onmessage=this._onMessage.bind(this),this._transport.onclose=this._onClose.bind(this)}static fromSession(e){return e._connection}session(e){return this._sessions.get(e)||null}url(){return this._url}send(e,...s){const t=s.length?s[0]:void 0,o=this._rawSend({method:e,params:t});return new Promise(((s,t)=>{this._callbacks.set(o,{resolve:s,reject:t,error:new Error,method:e})}))}_rawSend(e){const s=++this._lastId,t=JSON.stringify(Object.assign({},e,{id:s}));return debugProtocolSend(t),this._transport.send(t),s}async _onMessage(e){this._delay&&await new Promise((e=>setTimeout(e,this._delay))),debugProtocolReceive(e);const s=JSON.parse(e);if("Target.attachedToTarget"===s.method){const e=s.params.sessionId,t=new CDPSession(this,s.params.targetInfo.type,e);this._sessions.set(e,t)}else if("Target.detachedFromTarget"===s.method){const e=this._sessions.get(s.params.sessionId);e&&(e._onClosed(),this._sessions.delete(s.params.sessionId))}if(s.sessionId){const e=this._sessions.get(s.sessionId);e&&e._onMessage(s)}else if(s.id){const e=this._callbacks.get(s.id);e&&(this._callbacks.delete(s.id),s.error?e.reject(createProtocolError(e.error,e.method,s)):e.resolve(s.result))}else this.emit(s.method,s.params)}_onClose(){if(!this._closed){this._closed=!0,this._transport.onmessage=null,this._transport.onclose=null;for(const e of this._callbacks.values())e.reject(rewriteError(e.error,`Protocol error (${e.method}): Target closed.`));this._callbacks.clear();for(const e of this._sessions.values())e._onClosed();this._sessions.clear(),this.emit(ConnectionEmittedEvents.Disconnected)}}dispose(){this._onClose(),this._transport.close()}async createSession(e){const{sessionId:s}=await this.send("Target.attachToTarget",{targetId:e.targetId,flatten:!0});return this._sessions.get(s)}}export const CDPSessionEmittedEvents={Disconnected:Symbol("CDPSession.Disconnected")};export class CDPSession extends EventEmitter{constructor(e,s,t){super(),this._callbacks=new Map,this._connection=e,this._targetType=s,this._sessionId=t}send(e,...s){if(!this._connection)return Promise.reject(new Error(`Protocol error (${e}): Session closed. Most likely the ${this._targetType} has been closed.`));const t=s.length?s[0]:void 0,o=this._connection._rawSend({sessionId:this._sessionId,method:e,params:t||{}});return new Promise(((s,t)=>{this._callbacks.set(o,{resolve:s,reject:t,error:new Error,method:e})}))}_onMessage(e){if(e.id&&this._callbacks.has(e.id)){const s=this._callbacks.get(e.id);this._callbacks.delete(e.id),e.error?s.reject(createProtocolError(s.error,s.method,e)):s.resolve(e.result)}else assert(!e.id),this.emit(e.method,e.params)}async detach(){if(!this._connection)throw new Error(`Session already detached. Most likely the ${this._targetType} has been closed.`);await this._connection.send("Target.detachFromTarget",{sessionId:this._sessionId})}_onClosed(){for(const e of this._callbacks.values())e.reject(rewriteError(e.error,`Protocol error (${e.method}): Target closed.`));this._callbacks.clear(),this._connection=null,this.emit(CDPSessionEmittedEvents.Disconnected)}}function createProtocolError(e,s,t){let o=`Protocol error (${s}): ${t.error.message}`;return"data"in t.error&&(o+=` ${t.error.data}`),rewriteError(e,o)}function rewriteError(e,s){return e.message=s,e}