diff --git a/electron.vite.config.ts b/electron.vite.config.ts index e6b320a..3e68669 100644 --- a/electron.vite.config.ts +++ b/electron.vite.config.ts @@ -39,6 +39,7 @@ const config: ElectronViteConfig = { ...external.map(genCpModule), { src: './manifest.json', dest: 'dist' }, { src: './icon.webp', dest: 'dist' }, + { src: './src/ntqqapi/native/napcat-protocol-packet/Moehoo/*', dest: 'dist/main/Moehoo' }, ], }), ], diff --git a/manifest.json b/manifest.json index 28e50ec..db4ebe7 100644 --- a/manifest.json +++ b/manifest.json @@ -4,12 +4,16 @@ "name": "LLOneBot", "slug": "LLOneBot", "description": "实现 OneBot 11 和 Satori 协议,用于 QQ 机器人开发", - "version": "4.1.4", + "version": "4.2.0", "icon": "./icon.webp", "authors": [ { "name": "linyuchen", "link": "https://github.com/linyuchen" + }, + { + "name": "idranme", + "link": "https://github.com/idranme" } ], "repository": { diff --git a/package.json b/package.json index 2be116a..9b1837b 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "cosmokit": "^1.6.3", "express": "^5.0.1", "fluent-ffmpeg": "^2.1.3", - "minato": "^3.6.0", + "minato": "^3.6.1", "protobufjs": "^7.4.0", "silk-wasm": "^3.6.3", "ts-case-convert": "^2.1.0", @@ -35,14 +35,14 @@ "devDependencies": { "@types/cors": "^2.8.17", "@types/express": "^5.0.0", - "@types/fluent-ffmpeg": "^2.1.26", + "@types/fluent-ffmpeg": "^2.1.27", "@types/node": "^20.14.15", - "@types/ws": "^8.5.12", + "@types/ws": "^8.5.13", "electron": "^31.4.0", "electron-vite": "^2.3.0", "protobufjs-cli": "^1.1.3", "typescript": "^5.6.3", - "vite": "^5.4.10 ", + "vite": "^5.4.10", "vite-plugin-cp": "^4.0.8" }, "packageManager": "yarn@4.5.1" diff --git a/scripts/gen-manifest.ts b/scripts/gen-manifest.ts index f90eb74..01eff1f 100644 --- a/scripts/gen-manifest.ts +++ b/scripts/gen-manifest.ts @@ -13,6 +13,10 @@ const manifest = { { name: 'linyuchen', link: 'https://github.com/linyuchen' + }, + { + "name": "idranme", + "link": "https://github.com/idranme" } ], repository: { diff --git a/src/common/utils/file.ts b/src/common/utils/file.ts index 5256ab6..bd77aa2 100644 --- a/src/common/utils/file.ts +++ b/src/common/utils/file.ts @@ -27,9 +27,10 @@ export function checkFileReceived(path: string, timeout: number = 3000): Promise export function calculateFileMD5(filePath: string): Promise { return new Promise((resolve, reject) => { + const hash = createHash('md5') + // 创建一个流式读取器 const stream = fs.createReadStream(filePath) - const hash = createHash('md5') stream.on('data', (data: Buffer) => { // 当读取到数据时,更新哈希对象的状态 diff --git a/src/main/main.ts b/src/main/main.ts index 120f7ba..2c2339d 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -35,7 +35,9 @@ import { NTQQWindowApi } from '../ntqqapi/api' import { existsSync, mkdirSync } from 'node:fs' -import { checkChanelId } from '@/ntqqapi/ntcall' + +import { initWrapperSession} from '@/ntqqapi/native/napcat-protocol-packet' +initWrapperSession().then() declare module 'cordis' { interface Events { @@ -185,7 +187,6 @@ function onLoad() { if (config.enableLLOB && (config.satori.enable || config.ob11.enable)) { startHook() - await checkChanelId() await ctx.sleep(600) } else { llonebotError.otherError = 'LLOneBot 未启动' diff --git a/src/ntqqapi/core.ts b/src/ntqqapi/core.ts index 54b7e15..ce866c8 100644 --- a/src/ntqqapi/core.ts +++ b/src/ntqqapi/core.ts @@ -19,7 +19,8 @@ import { import { selfInfo } from '../common/globalVars' import { version } from '../version' import { invoke } from './ntcall' -import { Native } from './native/index' +import { Native } from './native/crychic' +import { initWrapperSession, NTQQPacketApi } from './native/napcat-protocol-packet' declare module 'cordis' { interface Context { @@ -40,10 +41,12 @@ class Core extends Service { static inject = ['ntMsgApi', 'ntFriendApi', 'ntGroupApi', 'store'] public startTime = 0 public native + public ntqqPacketApi: NTQQPacketApi constructor(protected ctx: Context, public config: Core.Config) { super(ctx, 'app', true) this.native = new Native(ctx) + initWrapperSession().then(session=>{this.ntqqPacketApi = new NTQQPacketApi(session)}) } public start() { diff --git a/src/ntqqapi/hook.ts b/src/ntqqapi/hook.ts index 59e9c2c..66fe858 100644 --- a/src/ntqqapi/hook.ts +++ b/src/ntqqapi/hook.ts @@ -1,4 +1,4 @@ -import { invoke, NTChannel, NTMethod } from './ntcall' +import { NTMethod } from './ntcall' import { log } from '@/common/utils' import { randomUUID } from 'node:crypto' import { ipcMain } from 'electron' @@ -40,7 +40,7 @@ const callHooks: Array<{ }> = [] export function startHook() { - log("start hook") + log('start hook') const senderExclude = Symbol() diff --git a/src/ntqqapi/native/external/crychic-win32-x64.node b/src/ntqqapi/native/crychic/external/crychic-win32-x64.node similarity index 100% rename from src/ntqqapi/native/external/crychic-win32-x64.node rename to src/ntqqapi/native/crychic/external/crychic-win32-x64.node diff --git a/src/ntqqapi/native/index.ts b/src/ntqqapi/native/crychic/index.ts similarity index 94% rename from src/ntqqapi/native/index.ts rename to src/ntqqapi/native/crychic/index.ts index 843e1ef..aa8670d 100644 --- a/src/ntqqapi/native/index.ts +++ b/src/ntqqapi/native/crychic/index.ts @@ -1,9 +1,9 @@ import { Context } from 'cordis' import { Dict } from 'cosmokit' -import { getBuildVersion } from '@/common/utils/misc' -import { TEMP_DIR } from '@/common/globalVars' +import { getBuildVersion } from '../../../common/utils/misc' +import { TEMP_DIR } from '../../../common/globalVars' import { copyFile } from 'fs/promises' -import { ChatType, Peer } from '../types' +import { ChatType, Peer } from '../../types' import path from 'node:path' import addon from './external/crychic-win32-x64.node?asset' diff --git a/src/ntqqapi/native/napcat-protocol-packet/Moehoo/MoeHoo.darwin.arm64.node b/src/ntqqapi/native/napcat-protocol-packet/Moehoo/MoeHoo.darwin.arm64.node new file mode 100644 index 0000000..829749c Binary files /dev/null and b/src/ntqqapi/native/napcat-protocol-packet/Moehoo/MoeHoo.darwin.arm64.node differ diff --git a/src/ntqqapi/native/napcat-protocol-packet/Moehoo/MoeHoo.linux.arm64.node b/src/ntqqapi/native/napcat-protocol-packet/Moehoo/MoeHoo.linux.arm64.node new file mode 100644 index 0000000..73d9a01 Binary files /dev/null and b/src/ntqqapi/native/napcat-protocol-packet/Moehoo/MoeHoo.linux.arm64.node differ diff --git a/src/ntqqapi/native/napcat-protocol-packet/Moehoo/MoeHoo.linux.x64.node b/src/ntqqapi/native/napcat-protocol-packet/Moehoo/MoeHoo.linux.x64.node new file mode 100644 index 0000000..70ca668 Binary files /dev/null and b/src/ntqqapi/native/napcat-protocol-packet/Moehoo/MoeHoo.linux.x64.node differ diff --git a/src/ntqqapi/native/napcat-protocol-packet/Moehoo/MoeHoo.win32.x64.node b/src/ntqqapi/native/napcat-protocol-packet/Moehoo/MoeHoo.win32.x64.node new file mode 100644 index 0000000..ab1701c Binary files /dev/null and b/src/ntqqapi/native/napcat-protocol-packet/Moehoo/MoeHoo.win32.x64.node differ diff --git a/src/ntqqapi/native/napcat-protocol-packet/index.d.ts b/src/ntqqapi/native/napcat-protocol-packet/index.d.ts new file mode 100644 index 0000000..e53be46 --- /dev/null +++ b/src/ntqqapi/native/napcat-protocol-packet/index.d.ts @@ -0,0 +1,17 @@ +import { WrapperSession } from './wrapper-session/types'; +export { initWrapperSession } from './wrapper-session'; +export declare class NTQQPacketApi { + private qqVersion; + private packetSession; + private logger; + private readonly wrapperSession; + constructor(wrapperSession: WrapperSession); + get available(): boolean; + private checkQQVersion; + private InitSendPacket; + private sendPacket; + private sendOidbPacket; + sendPokePacket(peer: number, group?: number): Promise; + sendGroupSignPacket(selfUin: string, groupCode: string): Promise; + sendSetSpecialTittlePacket(groupCode: string, uid: string, tittle: string): Promise; +} diff --git a/src/ntqqapi/native/napcat-protocol-packet/index.mjs b/src/ntqqapi/native/napcat-protocol-packet/index.mjs new file mode 100644 index 0000000..2d589a5 --- /dev/null +++ b/src/ntqqapi/native/napcat-protocol-packet/index.mjs @@ -0,0 +1,7187 @@ +import _0x2378fb from 'os'; +import require$$0$2 from 'stream'; +import require$$0 from 'zlib'; +import require$$0$1 from 'buffer'; +import require$$1$1, { createHash as createHash$1 } from 'crypto'; +import require$$0$3 from 'events'; +import require$$1$2 from 'https'; +import require$$2$1 from 'http'; +import require$$3 from 'net'; +import require$$4 from 'tls'; +import require$$7, { fileURLToPath } from 'url'; +import _0x10daec, { dirname } from 'path'; +import _0x3e7a5a from 'fs'; +import _0x2cc836, { constants as constants$1 } from 'node:os'; +import _0x5a9d71 from 'node:fs'; +import _0x2fc670 from 'node:path'; + +function _0x41d6(){const _0xce27c3=['13623642xtJDKR','720294ERTOnQ','get','has','set','329OWWzYZ','capacity','8UsTMVn','9862oKHjSp','value','keys','cache','delete','11862980eBUtnn','next','152104NDBwMH','254frKgUC','471327cVFkap','8498720ZwnDvN'];_0x41d6=function(){return _0xce27c3;};return _0x41d6();}const _0x37832f=_0x11d9;function _0x11d9(_0x3ea709,_0x3a7a15){const _0x41d614=_0x41d6();return _0x11d9=function(_0x11d999,_0xe6ed7e){_0x11d999=_0x11d999-0x171;let _0x6de7b9=_0x41d614[_0x11d999];return _0x6de7b9;},_0x11d9(_0x3ea709,_0x3a7a15);}(function(_0x4c1e11,_0x44b142){const _0x50c546=_0x11d9,_0x5497f8=_0x4c1e11();while(!![]){try{const _0x46c9bb=parseInt(_0x50c546(0x181))/0x1*(parseInt(_0x50c546(0x176))/0x2)+parseInt(_0x50c546(0x177))/0x3*(-parseInt(_0x50c546(0x180))/0x4)+-parseInt(_0x50c546(0x178))/0x5+-parseInt(_0x50c546(0x17a))/0x6+parseInt(_0x50c546(0x17e))/0x7*(-parseInt(_0x50c546(0x175))/0x8)+parseInt(_0x50c546(0x179))/0x9+parseInt(_0x50c546(0x173))/0xa;if(_0x46c9bb===_0x44b142)break;else _0x5497f8['push'](_0x5497f8['shift']());}catch(_0x2a2c41){_0x5497f8['push'](_0x5497f8['shift']());}}}(_0x41d6,0xe1cd8));class LRUCache{[_0x37832f(0x17f)];[_0x37832f(0x171)];constructor(_0x13648e){const _0x109020=_0x37832f;this['capacity']=_0x13648e,this[_0x109020(0x171)]=new Map();}[_0x37832f(0x17b)](_0x45f06c){const _0x4e148c=_0x37832f,_0x52aa56=this[_0x4e148c(0x171)]['get'](_0x45f06c);return _0x52aa56!==void 0x0&&(this['cache'][_0x4e148c(0x172)](_0x45f06c),this['cache'][_0x4e148c(0x17d)](_0x45f06c,_0x52aa56)),_0x52aa56;}['put'](_0xd434fd,_0x19f64d){const _0x4e8071=_0x37832f;if(this[_0x4e8071(0x171)][_0x4e8071(0x17c)](_0xd434fd))this['cache'][_0x4e8071(0x172)](_0xd434fd);else {if(this[_0x4e8071(0x171)]['size']>=this['capacity']){const _0x4ec25a=this['cache'][_0x4e8071(0x183)]()[_0x4e8071(0x174)]()[_0x4e8071(0x182)];_0x4ec25a!==void 0x0&&this['cache'][_0x4e8071(0x172)](_0x4ec25a);}}this['cache'][_0x4e8071(0x17d)](_0xd434fd,_0x19f64d);}} + +function getDefaultExportFromCjs (x) { + return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; +} + +function getAugmentedNamespace(n) { + if (n.__esModule) return n; + var f = n.default; + if (typeof f == "function") { + var a = function a () { + if (this instanceof a) { + return Reflect.construct(f, arguments, this.constructor); + } + return f.apply(this, arguments); + }; + a.prototype = f.prototype; + } else a = {}; + Object.defineProperty(a, '__esModule', {value: true}); + Object.keys(n).forEach(function (k) { + var d = Object.getOwnPropertyDescriptor(n, k); + Object.defineProperty(a, k, d.get ? d : { + enumerable: true, + get: function () { + return n[k]; + } + }); + }); + return a; +} + +var bufferUtil$1 = {exports: {}}; + +const BINARY_TYPES$2 = ['nodebuffer', 'arraybuffer', 'fragments']; +const hasBlob$1 = typeof Blob !== 'undefined'; + +if (hasBlob$1) BINARY_TYPES$2.push('blob'); + +var constants = { + BINARY_TYPES: BINARY_TYPES$2, + EMPTY_BUFFER: Buffer.alloc(0), + GUID: '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', + hasBlob: hasBlob$1, + kForOnEventAttribute: Symbol('kIsForOnEventAttribute'), + kListener: Symbol('kListener'), + kStatusCode: Symbol('status-code'), + kWebSocket: Symbol('websocket'), + NOOP: () => {} +}; + +const __viteOptionalPeerDep_bufferutil_ws = {}; + +const __viteOptionalPeerDep_bufferutil_ws$1 = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({ + __proto__: null, + default: __viteOptionalPeerDep_bufferutil_ws +}, Symbol.toStringTag, { value: 'Module' })); + +const require$$1 = /*@__PURE__*/getAugmentedNamespace(__viteOptionalPeerDep_bufferutil_ws$1); + +var unmask$1; +var mask; + +const { EMPTY_BUFFER: EMPTY_BUFFER$3 } = constants; + +const FastBuffer$2 = Buffer[Symbol.species]; + +/** + * Merges an array of buffers into a new buffer. + * + * @param {Buffer[]} list The array of buffers to concat + * @param {Number} totalLength The total length of buffers in the list + * @return {Buffer} The resulting buffer + * @public + */ +function concat$1(list, totalLength) { + if (list.length === 0) return EMPTY_BUFFER$3; + if (list.length === 1) return list[0]; + + const target = Buffer.allocUnsafe(totalLength); + let offset = 0; + + for (let i = 0; i < list.length; i++) { + const buf = list[i]; + target.set(buf, offset); + offset += buf.length; + } + + if (offset < totalLength) { + return new FastBuffer$2(target.buffer, target.byteOffset, offset); + } + + return target; +} + +/** + * Masks a buffer using the given mask. + * + * @param {Buffer} source The buffer to mask + * @param {Buffer} mask The mask to use + * @param {Buffer} output The buffer where to store the result + * @param {Number} offset The offset at which to start writing + * @param {Number} length The number of bytes to mask. + * @public + */ +function _mask(source, mask, output, offset, length) { + for (let i = 0; i < length; i++) { + output[offset + i] = source[i] ^ mask[i & 3]; + } +} + +/** + * Unmasks a buffer using the given mask. + * + * @param {Buffer} buffer The buffer to unmask + * @param {Buffer} mask The mask to use + * @public + */ +function _unmask(buffer, mask) { + for (let i = 0; i < buffer.length; i++) { + buffer[i] ^= mask[i & 3]; + } +} + +/** + * Converts a buffer to an `ArrayBuffer`. + * + * @param {Buffer} buf The buffer to convert + * @return {ArrayBuffer} Converted buffer + * @public + */ +function toArrayBuffer$1(buf) { + if (buf.length === buf.buffer.byteLength) { + return buf.buffer; + } + + return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.length); +} + +/** + * Converts `data` to a `Buffer`. + * + * @param {*} data The data to convert + * @return {Buffer} The buffer + * @throws {TypeError} + * @public + */ +function toBuffer$2(data) { + toBuffer$2.readOnly = true; + + if (Buffer.isBuffer(data)) return data; + + let buf; + + if (data instanceof ArrayBuffer) { + buf = new FastBuffer$2(data); + } else if (ArrayBuffer.isView(data)) { + buf = new FastBuffer$2(data.buffer, data.byteOffset, data.byteLength); + } else { + buf = Buffer.from(data); + toBuffer$2.readOnly = false; + } + + return buf; +} + +bufferUtil$1.exports = { + concat: concat$1, + mask: _mask, + toArrayBuffer: toArrayBuffer$1, + toBuffer: toBuffer$2, + unmask: _unmask +}; + +/* istanbul ignore else */ +if (!process.env.WS_NO_BUFFER_UTIL) { + try { + const bufferUtil = require$$1; + + mask = bufferUtil$1.exports.mask = function (source, mask, output, offset, length) { + if (length < 48) _mask(source, mask, output, offset, length); + else bufferUtil.mask(source, mask, output, offset, length); + }; + + unmask$1 = bufferUtil$1.exports.unmask = function (buffer, mask) { + if (buffer.length < 32) _unmask(buffer, mask); + else bufferUtil.unmask(buffer, mask); + }; + } catch (e) { + // Continue regardless of the error. + } +} + +var bufferUtilExports = bufferUtil$1.exports; + +const kDone = Symbol('kDone'); +const kRun = Symbol('kRun'); + +/** + * A very simple job queue with adjustable concurrency. Adapted from + * https://github.com/STRML/async-limiter + */ +let Limiter$1 = class Limiter { + /** + * Creates a new `Limiter`. + * + * @param {Number} [concurrency=Infinity] The maximum number of jobs allowed + * to run concurrently + */ + constructor(concurrency) { + this[kDone] = () => { + this.pending--; + this[kRun](); + }; + this.concurrency = concurrency || Infinity; + this.jobs = []; + this.pending = 0; + } + + /** + * Adds a job to the queue. + * + * @param {Function} job The job to run + * @public + */ + add(job) { + this.jobs.push(job); + this[kRun](); + } + + /** + * Removes a job from the queue and runs it if possible. + * + * @private + */ + [kRun]() { + if (this.pending === this.concurrency) return; + + if (this.jobs.length) { + const job = this.jobs.shift(); + + this.pending++; + job(this[kDone]); + } + } +}; + +var limiter = Limiter$1; + +const zlib = require$$0; + +const bufferUtil = bufferUtilExports; +const Limiter = limiter; +const { kStatusCode: kStatusCode$2 } = constants; + +const FastBuffer$1 = Buffer[Symbol.species]; +const TRAILER = Buffer.from([0x00, 0x00, 0xff, 0xff]); +const kPerMessageDeflate = Symbol('permessage-deflate'); +const kTotalLength = Symbol('total-length'); +const kCallback = Symbol('callback'); +const kBuffers = Symbol('buffers'); +const kError$1 = Symbol('error'); + +// +// We limit zlib concurrency, which prevents severe memory fragmentation +// as documented in https://github.com/nodejs/node/issues/8871#issuecomment-250915913 +// and https://github.com/websockets/ws/issues/1202 +// +// Intentionally global; it's the global thread pool that's an issue. +// +let zlibLimiter; + +/** + * permessage-deflate implementation. + */ +let PerMessageDeflate$3 = class PerMessageDeflate { + /** + * Creates a PerMessageDeflate instance. + * + * @param {Object} [options] Configuration options + * @param {(Boolean|Number)} [options.clientMaxWindowBits] Advertise support + * for, or request, a custom client window size + * @param {Boolean} [options.clientNoContextTakeover=false] Advertise/ + * acknowledge disabling of client context takeover + * @param {Number} [options.concurrencyLimit=10] The number of concurrent + * calls to zlib + * @param {(Boolean|Number)} [options.serverMaxWindowBits] Request/confirm the + * use of a custom server window size + * @param {Boolean} [options.serverNoContextTakeover=false] Request/accept + * disabling of server context takeover + * @param {Number} [options.threshold=1024] Size (in bytes) below which + * messages should not be compressed if context takeover is disabled + * @param {Object} [options.zlibDeflateOptions] Options to pass to zlib on + * deflate + * @param {Object} [options.zlibInflateOptions] Options to pass to zlib on + * inflate + * @param {Boolean} [isServer=false] Create the instance in either server or + * client mode + * @param {Number} [maxPayload=0] The maximum allowed message length + */ + constructor(options, isServer, maxPayload) { + this._maxPayload = maxPayload | 0; + this._options = options || {}; + this._threshold = + this._options.threshold !== undefined ? this._options.threshold : 1024; + this._isServer = !!isServer; + this._deflate = null; + this._inflate = null; + + this.params = null; + + if (!zlibLimiter) { + const concurrency = + this._options.concurrencyLimit !== undefined + ? this._options.concurrencyLimit + : 10; + zlibLimiter = new Limiter(concurrency); + } + } + + /** + * @type {String} + */ + static get extensionName() { + return 'permessage-deflate'; + } + + /** + * Create an extension negotiation offer. + * + * @return {Object} Extension parameters + * @public + */ + offer() { + const params = {}; + + if (this._options.serverNoContextTakeover) { + params.server_no_context_takeover = true; + } + if (this._options.clientNoContextTakeover) { + params.client_no_context_takeover = true; + } + if (this._options.serverMaxWindowBits) { + params.server_max_window_bits = this._options.serverMaxWindowBits; + } + if (this._options.clientMaxWindowBits) { + params.client_max_window_bits = this._options.clientMaxWindowBits; + } else if (this._options.clientMaxWindowBits == null) { + params.client_max_window_bits = true; + } + + return params; + } + + /** + * Accept an extension negotiation offer/response. + * + * @param {Array} configurations The extension negotiation offers/reponse + * @return {Object} Accepted configuration + * @public + */ + accept(configurations) { + configurations = this.normalizeParams(configurations); + + this.params = this._isServer + ? this.acceptAsServer(configurations) + : this.acceptAsClient(configurations); + + return this.params; + } + + /** + * Releases all resources used by the extension. + * + * @public + */ + cleanup() { + if (this._inflate) { + this._inflate.close(); + this._inflate = null; + } + + if (this._deflate) { + const callback = this._deflate[kCallback]; + + this._deflate.close(); + this._deflate = null; + + if (callback) { + callback( + new Error( + 'The deflate stream was closed while data was being processed' + ) + ); + } + } + } + + /** + * Accept an extension negotiation offer. + * + * @param {Array} offers The extension negotiation offers + * @return {Object} Accepted configuration + * @private + */ + acceptAsServer(offers) { + const opts = this._options; + const accepted = offers.find((params) => { + if ( + (opts.serverNoContextTakeover === false && + params.server_no_context_takeover) || + (params.server_max_window_bits && + (opts.serverMaxWindowBits === false || + (typeof opts.serverMaxWindowBits === 'number' && + opts.serverMaxWindowBits > params.server_max_window_bits))) || + (typeof opts.clientMaxWindowBits === 'number' && + !params.client_max_window_bits) + ) { + return false; + } + + return true; + }); + + if (!accepted) { + throw new Error('None of the extension offers can be accepted'); + } + + if (opts.serverNoContextTakeover) { + accepted.server_no_context_takeover = true; + } + if (opts.clientNoContextTakeover) { + accepted.client_no_context_takeover = true; + } + if (typeof opts.serverMaxWindowBits === 'number') { + accepted.server_max_window_bits = opts.serverMaxWindowBits; + } + if (typeof opts.clientMaxWindowBits === 'number') { + accepted.client_max_window_bits = opts.clientMaxWindowBits; + } else if ( + accepted.client_max_window_bits === true || + opts.clientMaxWindowBits === false + ) { + delete accepted.client_max_window_bits; + } + + return accepted; + } + + /** + * Accept the extension negotiation response. + * + * @param {Array} response The extension negotiation response + * @return {Object} Accepted configuration + * @private + */ + acceptAsClient(response) { + const params = response[0]; + + if ( + this._options.clientNoContextTakeover === false && + params.client_no_context_takeover + ) { + throw new Error('Unexpected parameter "client_no_context_takeover"'); + } + + if (!params.client_max_window_bits) { + if (typeof this._options.clientMaxWindowBits === 'number') { + params.client_max_window_bits = this._options.clientMaxWindowBits; + } + } else if ( + this._options.clientMaxWindowBits === false || + (typeof this._options.clientMaxWindowBits === 'number' && + params.client_max_window_bits > this._options.clientMaxWindowBits) + ) { + throw new Error( + 'Unexpected or invalid parameter "client_max_window_bits"' + ); + } + + return params; + } + + /** + * Normalize parameters. + * + * @param {Array} configurations The extension negotiation offers/reponse + * @return {Array} The offers/response with normalized parameters + * @private + */ + normalizeParams(configurations) { + configurations.forEach((params) => { + Object.keys(params).forEach((key) => { + let value = params[key]; + + if (value.length > 1) { + throw new Error(`Parameter "${key}" must have only a single value`); + } + + value = value[0]; + + if (key === 'client_max_window_bits') { + if (value !== true) { + const num = +value; + if (!Number.isInteger(num) || num < 8 || num > 15) { + throw new TypeError( + `Invalid value for parameter "${key}": ${value}` + ); + } + value = num; + } else if (!this._isServer) { + throw new TypeError( + `Invalid value for parameter "${key}": ${value}` + ); + } + } else if (key === 'server_max_window_bits') { + const num = +value; + if (!Number.isInteger(num) || num < 8 || num > 15) { + throw new TypeError( + `Invalid value for parameter "${key}": ${value}` + ); + } + value = num; + } else if ( + key === 'client_no_context_takeover' || + key === 'server_no_context_takeover' + ) { + if (value !== true) { + throw new TypeError( + `Invalid value for parameter "${key}": ${value}` + ); + } + } else { + throw new Error(`Unknown parameter "${key}"`); + } + + params[key] = value; + }); + }); + + return configurations; + } + + /** + * Decompress data. Concurrency limited. + * + * @param {Buffer} data Compressed data + * @param {Boolean} fin Specifies whether or not this is the last fragment + * @param {Function} callback Callback + * @public + */ + decompress(data, fin, callback) { + zlibLimiter.add((done) => { + this._decompress(data, fin, (err, result) => { + done(); + callback(err, result); + }); + }); + } + + /** + * Compress data. Concurrency limited. + * + * @param {(Buffer|String)} data Data to compress + * @param {Boolean} fin Specifies whether or not this is the last fragment + * @param {Function} callback Callback + * @public + */ + compress(data, fin, callback) { + zlibLimiter.add((done) => { + this._compress(data, fin, (err, result) => { + done(); + callback(err, result); + }); + }); + } + + /** + * Decompress data. + * + * @param {Buffer} data Compressed data + * @param {Boolean} fin Specifies whether or not this is the last fragment + * @param {Function} callback Callback + * @private + */ + _decompress(data, fin, callback) { + const endpoint = this._isServer ? 'client' : 'server'; + + if (!this._inflate) { + const key = `${endpoint}_max_window_bits`; + const windowBits = + typeof this.params[key] !== 'number' + ? zlib.Z_DEFAULT_WINDOWBITS + : this.params[key]; + + this._inflate = zlib.createInflateRaw({ + ...this._options.zlibInflateOptions, + windowBits + }); + this._inflate[kPerMessageDeflate] = this; + this._inflate[kTotalLength] = 0; + this._inflate[kBuffers] = []; + this._inflate.on('error', inflateOnError); + this._inflate.on('data', inflateOnData); + } + + this._inflate[kCallback] = callback; + + this._inflate.write(data); + if (fin) this._inflate.write(TRAILER); + + this._inflate.flush(() => { + const err = this._inflate[kError$1]; + + if (err) { + this._inflate.close(); + this._inflate = null; + callback(err); + return; + } + + const data = bufferUtil.concat( + this._inflate[kBuffers], + this._inflate[kTotalLength] + ); + + if (this._inflate._readableState.endEmitted) { + this._inflate.close(); + this._inflate = null; + } else { + this._inflate[kTotalLength] = 0; + this._inflate[kBuffers] = []; + + if (fin && this.params[`${endpoint}_no_context_takeover`]) { + this._inflate.reset(); + } + } + + callback(null, data); + }); + } + + /** + * Compress data. + * + * @param {(Buffer|String)} data Data to compress + * @param {Boolean} fin Specifies whether or not this is the last fragment + * @param {Function} callback Callback + * @private + */ + _compress(data, fin, callback) { + const endpoint = this._isServer ? 'server' : 'client'; + + if (!this._deflate) { + const key = `${endpoint}_max_window_bits`; + const windowBits = + typeof this.params[key] !== 'number' + ? zlib.Z_DEFAULT_WINDOWBITS + : this.params[key]; + + this._deflate = zlib.createDeflateRaw({ + ...this._options.zlibDeflateOptions, + windowBits + }); + + this._deflate[kTotalLength] = 0; + this._deflate[kBuffers] = []; + + this._deflate.on('data', deflateOnData); + } + + this._deflate[kCallback] = callback; + + this._deflate.write(data); + this._deflate.flush(zlib.Z_SYNC_FLUSH, () => { + if (!this._deflate) { + // + // The deflate stream was closed while data was being processed. + // + return; + } + + let data = bufferUtil.concat( + this._deflate[kBuffers], + this._deflate[kTotalLength] + ); + + if (fin) { + data = new FastBuffer$1(data.buffer, data.byteOffset, data.length - 4); + } + + // + // Ensure that the callback will not be called again in + // `PerMessageDeflate#cleanup()`. + // + this._deflate[kCallback] = null; + + this._deflate[kTotalLength] = 0; + this._deflate[kBuffers] = []; + + if (fin && this.params[`${endpoint}_no_context_takeover`]) { + this._deflate.reset(); + } + + callback(null, data); + }); + } +}; + +var permessageDeflate = PerMessageDeflate$3; + +/** + * The listener of the `zlib.DeflateRaw` stream `'data'` event. + * + * @param {Buffer} chunk A chunk of data + * @private + */ +function deflateOnData(chunk) { + this[kBuffers].push(chunk); + this[kTotalLength] += chunk.length; +} + +/** + * The listener of the `zlib.InflateRaw` stream `'data'` event. + * + * @param {Buffer} chunk A chunk of data + * @private + */ +function inflateOnData(chunk) { + this[kTotalLength] += chunk.length; + + if ( + this[kPerMessageDeflate]._maxPayload < 1 || + this[kTotalLength] <= this[kPerMessageDeflate]._maxPayload + ) { + this[kBuffers].push(chunk); + return; + } + + this[kError$1] = new RangeError('Max payload size exceeded'); + this[kError$1].code = 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH'; + this[kError$1][kStatusCode$2] = 1009; + this.removeListener('data', inflateOnData); + this.reset(); +} + +/** + * The listener of the `zlib.InflateRaw` stream `'error'` event. + * + * @param {Error} err The emitted error + * @private + */ +function inflateOnError(err) { + // + // There is no need to call `Zlib#close()` as the handle is automatically + // closed when an error is emitted. + // + this[kPerMessageDeflate]._inflate = null; + err[kStatusCode$2] = 1007; + this[kCallback](err); +} + +var validation = {exports: {}}; + +const __viteOptionalPeerDep_utf8Validate_ws = {}; + +const __viteOptionalPeerDep_utf8Validate_ws$1 = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({ + __proto__: null, + default: __viteOptionalPeerDep_utf8Validate_ws +}, Symbol.toStringTag, { value: 'Module' })); + +const require$$2 = /*@__PURE__*/getAugmentedNamespace(__viteOptionalPeerDep_utf8Validate_ws$1); + +var isValidUTF8_1; + +const { isUtf8 } = require$$0$1; + +const { hasBlob } = constants; + +// +// Allowed token characters: +// +// '!', '#', '$', '%', '&', ''', '*', '+', '-', +// '.', 0-9, A-Z, '^', '_', '`', a-z, '|', '~' +// +// tokenChars[32] === 0 // ' ' +// tokenChars[33] === 1 // '!' +// tokenChars[34] === 0 // '"' +// ... +// +// prettier-ignore +const tokenChars$1 = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31 + 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32 - 47 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 - 63 + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 - 79 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80 - 95 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 111 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0 // 112 - 127 +]; + +/** + * Checks if a status code is allowed in a close frame. + * + * @param {Number} code The status code + * @return {Boolean} `true` if the status code is valid, else `false` + * @public + */ +function isValidStatusCode$2(code) { + return ( + (code >= 1000 && + code <= 1014 && + code !== 1004 && + code !== 1005 && + code !== 1006) || + (code >= 3000 && code <= 4999) + ); +} + +/** + * Checks if a given buffer contains only correct UTF-8. + * Ported from https://www.cl.cam.ac.uk/%7Emgk25/ucs/utf8_check.c by + * Markus Kuhn. + * + * @param {Buffer} buf The buffer to check + * @return {Boolean} `true` if `buf` contains only correct UTF-8, else `false` + * @public + */ +function _isValidUTF8(buf) { + const len = buf.length; + let i = 0; + + while (i < len) { + if ((buf[i] & 0x80) === 0) { + // 0xxxxxxx + i++; + } else if ((buf[i] & 0xe0) === 0xc0) { + // 110xxxxx 10xxxxxx + if ( + i + 1 === len || + (buf[i + 1] & 0xc0) !== 0x80 || + (buf[i] & 0xfe) === 0xc0 // Overlong + ) { + return false; + } + + i += 2; + } else if ((buf[i] & 0xf0) === 0xe0) { + // 1110xxxx 10xxxxxx 10xxxxxx + if ( + i + 2 >= len || + (buf[i + 1] & 0xc0) !== 0x80 || + (buf[i + 2] & 0xc0) !== 0x80 || + (buf[i] === 0xe0 && (buf[i + 1] & 0xe0) === 0x80) || // Overlong + (buf[i] === 0xed && (buf[i + 1] & 0xe0) === 0xa0) // Surrogate (U+D800 - U+DFFF) + ) { + return false; + } + + i += 3; + } else if ((buf[i] & 0xf8) === 0xf0) { + // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + if ( + i + 3 >= len || + (buf[i + 1] & 0xc0) !== 0x80 || + (buf[i + 2] & 0xc0) !== 0x80 || + (buf[i + 3] & 0xc0) !== 0x80 || + (buf[i] === 0xf0 && (buf[i + 1] & 0xf0) === 0x80) || // Overlong + (buf[i] === 0xf4 && buf[i + 1] > 0x8f) || + buf[i] > 0xf4 // > U+10FFFF + ) { + return false; + } + + i += 4; + } else { + return false; + } + } + + return true; +} + +/** + * Determines whether a value is a `Blob`. + * + * @param {*} value The value to be tested + * @return {Boolean} `true` if `value` is a `Blob`, else `false` + * @private + */ +function isBlob$2(value) { + return ( + hasBlob && + typeof value === 'object' && + typeof value.arrayBuffer === 'function' && + typeof value.type === 'string' && + typeof value.stream === 'function' && + (value[Symbol.toStringTag] === 'Blob' || + value[Symbol.toStringTag] === 'File') + ); +} + +validation.exports = { + isBlob: isBlob$2, + isValidStatusCode: isValidStatusCode$2, + isValidUTF8: _isValidUTF8, + tokenChars: tokenChars$1 +}; + +if (isUtf8) { + isValidUTF8_1 = validation.exports.isValidUTF8 = function (buf) { + return buf.length < 24 ? _isValidUTF8(buf) : isUtf8(buf); + }; +} /* istanbul ignore else */ else if (!process.env.WS_NO_UTF_8_VALIDATE) { + try { + const isValidUTF8 = require$$2; + + isValidUTF8_1 = validation.exports.isValidUTF8 = function (buf) { + return buf.length < 32 ? _isValidUTF8(buf) : isValidUTF8(buf); + }; + } catch (e) { + // Continue regardless of the error. + } +} + +var validationExports = validation.exports; + +const { Writable } = require$$0$2; + +const PerMessageDeflate$2 = permessageDeflate; +const { + BINARY_TYPES: BINARY_TYPES$1, + EMPTY_BUFFER: EMPTY_BUFFER$2, + kStatusCode: kStatusCode$1, + kWebSocket: kWebSocket$2 +} = constants; +const { concat, toArrayBuffer, unmask } = bufferUtilExports; +const { isValidStatusCode: isValidStatusCode$1, isValidUTF8 } = validationExports; + +const FastBuffer = Buffer[Symbol.species]; + +const GET_INFO = 0; +const GET_PAYLOAD_LENGTH_16 = 1; +const GET_PAYLOAD_LENGTH_64 = 2; +const GET_MASK = 3; +const GET_DATA = 4; +const INFLATING = 5; +const DEFER_EVENT = 6; + +/** + * HyBi Receiver implementation. + * + * @extends Writable + */ +let Receiver$1 = class Receiver extends Writable { + /** + * Creates a Receiver instance. + * + * @param {Object} [options] Options object + * @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether + * any of the `'message'`, `'ping'`, and `'pong'` events can be emitted + * multiple times in the same tick + * @param {String} [options.binaryType=nodebuffer] The type for binary data + * @param {Object} [options.extensions] An object containing the negotiated + * extensions + * @param {Boolean} [options.isServer=false] Specifies whether to operate in + * client or server mode + * @param {Number} [options.maxPayload=0] The maximum allowed message length + * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or + * not to skip UTF-8 validation for text and close messages + */ + constructor(options = {}) { + super(); + + this._allowSynchronousEvents = + options.allowSynchronousEvents !== undefined + ? options.allowSynchronousEvents + : true; + this._binaryType = options.binaryType || BINARY_TYPES$1[0]; + this._extensions = options.extensions || {}; + this._isServer = !!options.isServer; + this._maxPayload = options.maxPayload | 0; + this._skipUTF8Validation = !!options.skipUTF8Validation; + this[kWebSocket$2] = undefined; + + this._bufferedBytes = 0; + this._buffers = []; + + this._compressed = false; + this._payloadLength = 0; + this._mask = undefined; + this._fragmented = 0; + this._masked = false; + this._fin = false; + this._opcode = 0; + + this._totalPayloadLength = 0; + this._messageLength = 0; + this._fragments = []; + + this._errored = false; + this._loop = false; + this._state = GET_INFO; + } + + /** + * Implements `Writable.prototype._write()`. + * + * @param {Buffer} chunk The chunk of data to write + * @param {String} encoding The character encoding of `chunk` + * @param {Function} cb Callback + * @private + */ + _write(chunk, encoding, cb) { + if (this._opcode === 0x08 && this._state == GET_INFO) return cb(); + + this._bufferedBytes += chunk.length; + this._buffers.push(chunk); + this.startLoop(cb); + } + + /** + * Consumes `n` bytes from the buffered data. + * + * @param {Number} n The number of bytes to consume + * @return {Buffer} The consumed bytes + * @private + */ + consume(n) { + this._bufferedBytes -= n; + + if (n === this._buffers[0].length) return this._buffers.shift(); + + if (n < this._buffers[0].length) { + const buf = this._buffers[0]; + this._buffers[0] = new FastBuffer( + buf.buffer, + buf.byteOffset + n, + buf.length - n + ); + + return new FastBuffer(buf.buffer, buf.byteOffset, n); + } + + const dst = Buffer.allocUnsafe(n); + + do { + const buf = this._buffers[0]; + const offset = dst.length - n; + + if (n >= buf.length) { + dst.set(this._buffers.shift(), offset); + } else { + dst.set(new Uint8Array(buf.buffer, buf.byteOffset, n), offset); + this._buffers[0] = new FastBuffer( + buf.buffer, + buf.byteOffset + n, + buf.length - n + ); + } + + n -= buf.length; + } while (n > 0); + + return dst; + } + + /** + * Starts the parsing loop. + * + * @param {Function} cb Callback + * @private + */ + startLoop(cb) { + this._loop = true; + + do { + switch (this._state) { + case GET_INFO: + this.getInfo(cb); + break; + case GET_PAYLOAD_LENGTH_16: + this.getPayloadLength16(cb); + break; + case GET_PAYLOAD_LENGTH_64: + this.getPayloadLength64(cb); + break; + case GET_MASK: + this.getMask(); + break; + case GET_DATA: + this.getData(cb); + break; + case INFLATING: + case DEFER_EVENT: + this._loop = false; + return; + } + } while (this._loop); + + if (!this._errored) cb(); + } + + /** + * Reads the first two bytes of a frame. + * + * @param {Function} cb Callback + * @private + */ + getInfo(cb) { + if (this._bufferedBytes < 2) { + this._loop = false; + return; + } + + const buf = this.consume(2); + + if ((buf[0] & 0x30) !== 0x00) { + const error = this.createError( + RangeError, + 'RSV2 and RSV3 must be clear', + true, + 1002, + 'WS_ERR_UNEXPECTED_RSV_2_3' + ); + + cb(error); + return; + } + + const compressed = (buf[0] & 0x40) === 0x40; + + if (compressed && !this._extensions[PerMessageDeflate$2.extensionName]) { + const error = this.createError( + RangeError, + 'RSV1 must be clear', + true, + 1002, + 'WS_ERR_UNEXPECTED_RSV_1' + ); + + cb(error); + return; + } + + this._fin = (buf[0] & 0x80) === 0x80; + this._opcode = buf[0] & 0x0f; + this._payloadLength = buf[1] & 0x7f; + + if (this._opcode === 0x00) { + if (compressed) { + const error = this.createError( + RangeError, + 'RSV1 must be clear', + true, + 1002, + 'WS_ERR_UNEXPECTED_RSV_1' + ); + + cb(error); + return; + } + + if (!this._fragmented) { + const error = this.createError( + RangeError, + 'invalid opcode 0', + true, + 1002, + 'WS_ERR_INVALID_OPCODE' + ); + + cb(error); + return; + } + + this._opcode = this._fragmented; + } else if (this._opcode === 0x01 || this._opcode === 0x02) { + if (this._fragmented) { + const error = this.createError( + RangeError, + `invalid opcode ${this._opcode}`, + true, + 1002, + 'WS_ERR_INVALID_OPCODE' + ); + + cb(error); + return; + } + + this._compressed = compressed; + } else if (this._opcode > 0x07 && this._opcode < 0x0b) { + if (!this._fin) { + const error = this.createError( + RangeError, + 'FIN must be set', + true, + 1002, + 'WS_ERR_EXPECTED_FIN' + ); + + cb(error); + return; + } + + if (compressed) { + const error = this.createError( + RangeError, + 'RSV1 must be clear', + true, + 1002, + 'WS_ERR_UNEXPECTED_RSV_1' + ); + + cb(error); + return; + } + + if ( + this._payloadLength > 0x7d || + (this._opcode === 0x08 && this._payloadLength === 1) + ) { + const error = this.createError( + RangeError, + `invalid payload length ${this._payloadLength}`, + true, + 1002, + 'WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH' + ); + + cb(error); + return; + } + } else { + const error = this.createError( + RangeError, + `invalid opcode ${this._opcode}`, + true, + 1002, + 'WS_ERR_INVALID_OPCODE' + ); + + cb(error); + return; + } + + if (!this._fin && !this._fragmented) this._fragmented = this._opcode; + this._masked = (buf[1] & 0x80) === 0x80; + + if (this._isServer) { + if (!this._masked) { + const error = this.createError( + RangeError, + 'MASK must be set', + true, + 1002, + 'WS_ERR_EXPECTED_MASK' + ); + + cb(error); + return; + } + } else if (this._masked) { + const error = this.createError( + RangeError, + 'MASK must be clear', + true, + 1002, + 'WS_ERR_UNEXPECTED_MASK' + ); + + cb(error); + return; + } + + if (this._payloadLength === 126) this._state = GET_PAYLOAD_LENGTH_16; + else if (this._payloadLength === 127) this._state = GET_PAYLOAD_LENGTH_64; + else this.haveLength(cb); + } + + /** + * Gets extended payload length (7+16). + * + * @param {Function} cb Callback + * @private + */ + getPayloadLength16(cb) { + if (this._bufferedBytes < 2) { + this._loop = false; + return; + } + + this._payloadLength = this.consume(2).readUInt16BE(0); + this.haveLength(cb); + } + + /** + * Gets extended payload length (7+64). + * + * @param {Function} cb Callback + * @private + */ + getPayloadLength64(cb) { + if (this._bufferedBytes < 8) { + this._loop = false; + return; + } + + const buf = this.consume(8); + const num = buf.readUInt32BE(0); + + // + // The maximum safe integer in JavaScript is 2^53 - 1. An error is returned + // if payload length is greater than this number. + // + if (num > Math.pow(2, 53 - 32) - 1) { + const error = this.createError( + RangeError, + 'Unsupported WebSocket frame: payload length > 2^53 - 1', + false, + 1009, + 'WS_ERR_UNSUPPORTED_DATA_PAYLOAD_LENGTH' + ); + + cb(error); + return; + } + + this._payloadLength = num * Math.pow(2, 32) + buf.readUInt32BE(4); + this.haveLength(cb); + } + + /** + * Payload length has been read. + * + * @param {Function} cb Callback + * @private + */ + haveLength(cb) { + if (this._payloadLength && this._opcode < 0x08) { + this._totalPayloadLength += this._payloadLength; + if (this._totalPayloadLength > this._maxPayload && this._maxPayload > 0) { + const error = this.createError( + RangeError, + 'Max payload size exceeded', + false, + 1009, + 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH' + ); + + cb(error); + return; + } + } + + if (this._masked) this._state = GET_MASK; + else this._state = GET_DATA; + } + + /** + * Reads mask bytes. + * + * @private + */ + getMask() { + if (this._bufferedBytes < 4) { + this._loop = false; + return; + } + + this._mask = this.consume(4); + this._state = GET_DATA; + } + + /** + * Reads data bytes. + * + * @param {Function} cb Callback + * @private + */ + getData(cb) { + let data = EMPTY_BUFFER$2; + + if (this._payloadLength) { + if (this._bufferedBytes < this._payloadLength) { + this._loop = false; + return; + } + + data = this.consume(this._payloadLength); + + if ( + this._masked && + (this._mask[0] | this._mask[1] | this._mask[2] | this._mask[3]) !== 0 + ) { + unmask(data, this._mask); + } + } + + if (this._opcode > 0x07) { + this.controlMessage(data, cb); + return; + } + + if (this._compressed) { + this._state = INFLATING; + this.decompress(data, cb); + return; + } + + if (data.length) { + // + // This message is not compressed so its length is the sum of the payload + // length of all fragments. + // + this._messageLength = this._totalPayloadLength; + this._fragments.push(data); + } + + this.dataMessage(cb); + } + + /** + * Decompresses data. + * + * @param {Buffer} data Compressed data + * @param {Function} cb Callback + * @private + */ + decompress(data, cb) { + const perMessageDeflate = this._extensions[PerMessageDeflate$2.extensionName]; + + perMessageDeflate.decompress(data, this._fin, (err, buf) => { + if (err) return cb(err); + + if (buf.length) { + this._messageLength += buf.length; + if (this._messageLength > this._maxPayload && this._maxPayload > 0) { + const error = this.createError( + RangeError, + 'Max payload size exceeded', + false, + 1009, + 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH' + ); + + cb(error); + return; + } + + this._fragments.push(buf); + } + + this.dataMessage(cb); + if (this._state === GET_INFO) this.startLoop(cb); + }); + } + + /** + * Handles a data message. + * + * @param {Function} cb Callback + * @private + */ + dataMessage(cb) { + if (!this._fin) { + this._state = GET_INFO; + return; + } + + const messageLength = this._messageLength; + const fragments = this._fragments; + + this._totalPayloadLength = 0; + this._messageLength = 0; + this._fragmented = 0; + this._fragments = []; + + if (this._opcode === 2) { + let data; + + if (this._binaryType === 'nodebuffer') { + data = concat(fragments, messageLength); + } else if (this._binaryType === 'arraybuffer') { + data = toArrayBuffer(concat(fragments, messageLength)); + } else if (this._binaryType === 'blob') { + data = new Blob(fragments); + } else { + data = fragments; + } + + if (this._allowSynchronousEvents) { + this.emit('message', data, true); + this._state = GET_INFO; + } else { + this._state = DEFER_EVENT; + setImmediate(() => { + this.emit('message', data, true); + this._state = GET_INFO; + this.startLoop(cb); + }); + } + } else { + const buf = concat(fragments, messageLength); + + if (!this._skipUTF8Validation && !isValidUTF8(buf)) { + const error = this.createError( + Error, + 'invalid UTF-8 sequence', + true, + 1007, + 'WS_ERR_INVALID_UTF8' + ); + + cb(error); + return; + } + + if (this._state === INFLATING || this._allowSynchronousEvents) { + this.emit('message', buf, false); + this._state = GET_INFO; + } else { + this._state = DEFER_EVENT; + setImmediate(() => { + this.emit('message', buf, false); + this._state = GET_INFO; + this.startLoop(cb); + }); + } + } + } + + /** + * Handles a control message. + * + * @param {Buffer} data Data to handle + * @return {(Error|RangeError|undefined)} A possible error + * @private + */ + controlMessage(data, cb) { + if (this._opcode === 0x08) { + if (data.length === 0) { + this._loop = false; + this.emit('conclude', 1005, EMPTY_BUFFER$2); + this.end(); + } else { + const code = data.readUInt16BE(0); + + if (!isValidStatusCode$1(code)) { + const error = this.createError( + RangeError, + `invalid status code ${code}`, + true, + 1002, + 'WS_ERR_INVALID_CLOSE_CODE' + ); + + cb(error); + return; + } + + const buf = new FastBuffer( + data.buffer, + data.byteOffset + 2, + data.length - 2 + ); + + if (!this._skipUTF8Validation && !isValidUTF8(buf)) { + const error = this.createError( + Error, + 'invalid UTF-8 sequence', + true, + 1007, + 'WS_ERR_INVALID_UTF8' + ); + + cb(error); + return; + } + + this._loop = false; + this.emit('conclude', code, buf); + this.end(); + } + + this._state = GET_INFO; + return; + } + + if (this._allowSynchronousEvents) { + this.emit(this._opcode === 0x09 ? 'ping' : 'pong', data); + this._state = GET_INFO; + } else { + this._state = DEFER_EVENT; + setImmediate(() => { + this.emit(this._opcode === 0x09 ? 'ping' : 'pong', data); + this._state = GET_INFO; + this.startLoop(cb); + }); + } + } + + /** + * Builds an error object. + * + * @param {function(new:Error|RangeError)} ErrorCtor The error constructor + * @param {String} message The error message + * @param {Boolean} prefix Specifies whether or not to add a default prefix to + * `message` + * @param {Number} statusCode The status code + * @param {String} errorCode The exposed error code + * @return {(Error|RangeError)} The error + * @private + */ + createError(ErrorCtor, message, prefix, statusCode, errorCode) { + this._loop = false; + this._errored = true; + + const err = new ErrorCtor( + prefix ? `Invalid WebSocket frame: ${message}` : message + ); + + Error.captureStackTrace(err, this.createError); + err.code = errorCode; + err[kStatusCode$1] = statusCode; + return err; + } +}; + +var receiver = Receiver$1; + +/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^Duplex" }] */ +const { randomFillSync } = require$$1$1; + +const PerMessageDeflate$1 = permessageDeflate; +const { EMPTY_BUFFER: EMPTY_BUFFER$1, kWebSocket: kWebSocket$1, NOOP: NOOP$1 } = constants; +const { isBlob: isBlob$1, isValidStatusCode } = validationExports; +const { mask: applyMask, toBuffer: toBuffer$1 } = bufferUtilExports; + +const kByteLength = Symbol('kByteLength'); +const maskBuffer = Buffer.alloc(4); +const RANDOM_POOL_SIZE = 8 * 1024; +let randomPool; +let randomPoolPointer = RANDOM_POOL_SIZE; + +const DEFAULT = 0; +const DEFLATING = 1; +const GET_BLOB_DATA = 2; + +/** + * HyBi Sender implementation. + */ +let Sender$1 = class Sender { + /** + * Creates a Sender instance. + * + * @param {Duplex} socket The connection socket + * @param {Object} [extensions] An object containing the negotiated extensions + * @param {Function} [generateMask] The function used to generate the masking + * key + */ + constructor(socket, extensions, generateMask) { + this._extensions = extensions || {}; + + if (generateMask) { + this._generateMask = generateMask; + this._maskBuffer = Buffer.alloc(4); + } + + this._socket = socket; + + this._firstFragment = true; + this._compress = false; + + this._bufferedBytes = 0; + this._queue = []; + this._state = DEFAULT; + this.onerror = NOOP$1; + this[kWebSocket$1] = undefined; + } + + /** + * Frames a piece of data according to the HyBi WebSocket protocol. + * + * @param {(Buffer|String)} data The data to frame + * @param {Object} options Options object + * @param {Boolean} [options.fin=false] Specifies whether or not to set the + * FIN bit + * @param {Function} [options.generateMask] The function used to generate the + * masking key + * @param {Boolean} [options.mask=false] Specifies whether or not to mask + * `data` + * @param {Buffer} [options.maskBuffer] The buffer used to store the masking + * key + * @param {Number} options.opcode The opcode + * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be + * modified + * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the + * RSV1 bit + * @return {(Buffer|String)[]} The framed data + * @public + */ + static frame(data, options) { + let mask; + let merge = false; + let offset = 2; + let skipMasking = false; + + if (options.mask) { + mask = options.maskBuffer || maskBuffer; + + if (options.generateMask) { + options.generateMask(mask); + } else { + if (randomPoolPointer === RANDOM_POOL_SIZE) { + /* istanbul ignore else */ + if (randomPool === undefined) { + // + // This is lazily initialized because server-sent frames must not + // be masked so it may never be used. + // + randomPool = Buffer.alloc(RANDOM_POOL_SIZE); + } + + randomFillSync(randomPool, 0, RANDOM_POOL_SIZE); + randomPoolPointer = 0; + } + + mask[0] = randomPool[randomPoolPointer++]; + mask[1] = randomPool[randomPoolPointer++]; + mask[2] = randomPool[randomPoolPointer++]; + mask[3] = randomPool[randomPoolPointer++]; + } + + skipMasking = (mask[0] | mask[1] | mask[2] | mask[3]) === 0; + offset = 6; + } + + let dataLength; + + if (typeof data === 'string') { + if ( + (!options.mask || skipMasking) && + options[kByteLength] !== undefined + ) { + dataLength = options[kByteLength]; + } else { + data = Buffer.from(data); + dataLength = data.length; + } + } else { + dataLength = data.length; + merge = options.mask && options.readOnly && !skipMasking; + } + + let payloadLength = dataLength; + + if (dataLength >= 65536) { + offset += 8; + payloadLength = 127; + } else if (dataLength > 125) { + offset += 2; + payloadLength = 126; + } + + const target = Buffer.allocUnsafe(merge ? dataLength + offset : offset); + + target[0] = options.fin ? options.opcode | 0x80 : options.opcode; + if (options.rsv1) target[0] |= 0x40; + + target[1] = payloadLength; + + if (payloadLength === 126) { + target.writeUInt16BE(dataLength, 2); + } else if (payloadLength === 127) { + target[2] = target[3] = 0; + target.writeUIntBE(dataLength, 4, 6); + } + + if (!options.mask) return [target, data]; + + target[1] |= 0x80; + target[offset - 4] = mask[0]; + target[offset - 3] = mask[1]; + target[offset - 2] = mask[2]; + target[offset - 1] = mask[3]; + + if (skipMasking) return [target, data]; + + if (merge) { + applyMask(data, mask, target, offset, dataLength); + return [target]; + } + + applyMask(data, mask, data, 0, dataLength); + return [target, data]; + } + + /** + * Sends a close message to the other peer. + * + * @param {Number} [code] The status code component of the body + * @param {(String|Buffer)} [data] The message component of the body + * @param {Boolean} [mask=false] Specifies whether or not to mask the message + * @param {Function} [cb] Callback + * @public + */ + close(code, data, mask, cb) { + let buf; + + if (code === undefined) { + buf = EMPTY_BUFFER$1; + } else if (typeof code !== 'number' || !isValidStatusCode(code)) { + throw new TypeError('First argument must be a valid error code number'); + } else if (data === undefined || !data.length) { + buf = Buffer.allocUnsafe(2); + buf.writeUInt16BE(code, 0); + } else { + const length = Buffer.byteLength(data); + + if (length > 123) { + throw new RangeError('The message must not be greater than 123 bytes'); + } + + buf = Buffer.allocUnsafe(2 + length); + buf.writeUInt16BE(code, 0); + + if (typeof data === 'string') { + buf.write(data, 2); + } else { + buf.set(data, 2); + } + } + + const options = { + [kByteLength]: buf.length, + fin: true, + generateMask: this._generateMask, + mask, + maskBuffer: this._maskBuffer, + opcode: 0x08, + readOnly: false, + rsv1: false + }; + + if (this._state !== DEFAULT) { + this.enqueue([this.dispatch, buf, false, options, cb]); + } else { + this.sendFrame(Sender.frame(buf, options), cb); + } + } + + /** + * Sends a ping message to the other peer. + * + * @param {*} data The message to send + * @param {Boolean} [mask=false] Specifies whether or not to mask `data` + * @param {Function} [cb] Callback + * @public + */ + ping(data, mask, cb) { + let byteLength; + let readOnly; + + if (typeof data === 'string') { + byteLength = Buffer.byteLength(data); + readOnly = false; + } else if (isBlob$1(data)) { + byteLength = data.size; + readOnly = false; + } else { + data = toBuffer$1(data); + byteLength = data.length; + readOnly = toBuffer$1.readOnly; + } + + if (byteLength > 125) { + throw new RangeError('The data size must not be greater than 125 bytes'); + } + + const options = { + [kByteLength]: byteLength, + fin: true, + generateMask: this._generateMask, + mask, + maskBuffer: this._maskBuffer, + opcode: 0x09, + readOnly, + rsv1: false + }; + + if (isBlob$1(data)) { + if (this._state !== DEFAULT) { + this.enqueue([this.getBlobData, data, false, options, cb]); + } else { + this.getBlobData(data, false, options, cb); + } + } else if (this._state !== DEFAULT) { + this.enqueue([this.dispatch, data, false, options, cb]); + } else { + this.sendFrame(Sender.frame(data, options), cb); + } + } + + /** + * Sends a pong message to the other peer. + * + * @param {*} data The message to send + * @param {Boolean} [mask=false] Specifies whether or not to mask `data` + * @param {Function} [cb] Callback + * @public + */ + pong(data, mask, cb) { + let byteLength; + let readOnly; + + if (typeof data === 'string') { + byteLength = Buffer.byteLength(data); + readOnly = false; + } else if (isBlob$1(data)) { + byteLength = data.size; + readOnly = false; + } else { + data = toBuffer$1(data); + byteLength = data.length; + readOnly = toBuffer$1.readOnly; + } + + if (byteLength > 125) { + throw new RangeError('The data size must not be greater than 125 bytes'); + } + + const options = { + [kByteLength]: byteLength, + fin: true, + generateMask: this._generateMask, + mask, + maskBuffer: this._maskBuffer, + opcode: 0x0a, + readOnly, + rsv1: false + }; + + if (isBlob$1(data)) { + if (this._state !== DEFAULT) { + this.enqueue([this.getBlobData, data, false, options, cb]); + } else { + this.getBlobData(data, false, options, cb); + } + } else if (this._state !== DEFAULT) { + this.enqueue([this.dispatch, data, false, options, cb]); + } else { + this.sendFrame(Sender.frame(data, options), cb); + } + } + + /** + * Sends a data message to the other peer. + * + * @param {*} data The message to send + * @param {Object} options Options object + * @param {Boolean} [options.binary=false] Specifies whether `data` is binary + * or text + * @param {Boolean} [options.compress=false] Specifies whether or not to + * compress `data` + * @param {Boolean} [options.fin=false] Specifies whether the fragment is the + * last one + * @param {Boolean} [options.mask=false] Specifies whether or not to mask + * `data` + * @param {Function} [cb] Callback + * @public + */ + send(data, options, cb) { + const perMessageDeflate = this._extensions[PerMessageDeflate$1.extensionName]; + let opcode = options.binary ? 2 : 1; + let rsv1 = options.compress; + + let byteLength; + let readOnly; + + if (typeof data === 'string') { + byteLength = Buffer.byteLength(data); + readOnly = false; + } else if (isBlob$1(data)) { + byteLength = data.size; + readOnly = false; + } else { + data = toBuffer$1(data); + byteLength = data.length; + readOnly = toBuffer$1.readOnly; + } + + if (this._firstFragment) { + this._firstFragment = false; + if ( + rsv1 && + perMessageDeflate && + perMessageDeflate.params[ + perMessageDeflate._isServer + ? 'server_no_context_takeover' + : 'client_no_context_takeover' + ] + ) { + rsv1 = byteLength >= perMessageDeflate._threshold; + } + this._compress = rsv1; + } else { + rsv1 = false; + opcode = 0; + } + + if (options.fin) this._firstFragment = true; + + const opts = { + [kByteLength]: byteLength, + fin: options.fin, + generateMask: this._generateMask, + mask: options.mask, + maskBuffer: this._maskBuffer, + opcode, + readOnly, + rsv1 + }; + + if (isBlob$1(data)) { + if (this._state !== DEFAULT) { + this.enqueue([this.getBlobData, data, this._compress, opts, cb]); + } else { + this.getBlobData(data, this._compress, opts, cb); + } + } else if (this._state !== DEFAULT) { + this.enqueue([this.dispatch, data, this._compress, opts, cb]); + } else { + this.dispatch(data, this._compress, opts, cb); + } + } + + /** + * Gets the contents of a blob as binary data. + * + * @param {Blob} blob The blob + * @param {Boolean} [compress=false] Specifies whether or not to compress + * the data + * @param {Object} options Options object + * @param {Boolean} [options.fin=false] Specifies whether or not to set the + * FIN bit + * @param {Function} [options.generateMask] The function used to generate the + * masking key + * @param {Boolean} [options.mask=false] Specifies whether or not to mask + * `data` + * @param {Buffer} [options.maskBuffer] The buffer used to store the masking + * key + * @param {Number} options.opcode The opcode + * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be + * modified + * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the + * RSV1 bit + * @param {Function} [cb] Callback + * @private + */ + getBlobData(blob, compress, options, cb) { + this._bufferedBytes += options[kByteLength]; + this._state = GET_BLOB_DATA; + + blob + .arrayBuffer() + .then((arrayBuffer) => { + if (this._socket.destroyed) { + const err = new Error( + 'The socket was closed while the blob was being read' + ); + + // + // `callCallbacks` is called in the next tick to ensure that errors + // that might be thrown in the callbacks behave like errors thrown + // outside the promise chain. + // + process.nextTick(callCallbacks, this, err, cb); + return; + } + + this._bufferedBytes -= options[kByteLength]; + const data = toBuffer$1(arrayBuffer); + + if (!compress) { + this._state = DEFAULT; + this.sendFrame(Sender.frame(data, options), cb); + this.dequeue(); + } else { + this.dispatch(data, compress, options, cb); + } + }) + .catch((err) => { + // + // `onError` is called in the next tick for the same reason that + // `callCallbacks` above is. + // + process.nextTick(onError, this, err, cb); + }); + } + + /** + * Dispatches a message. + * + * @param {(Buffer|String)} data The message to send + * @param {Boolean} [compress=false] Specifies whether or not to compress + * `data` + * @param {Object} options Options object + * @param {Boolean} [options.fin=false] Specifies whether or not to set the + * FIN bit + * @param {Function} [options.generateMask] The function used to generate the + * masking key + * @param {Boolean} [options.mask=false] Specifies whether or not to mask + * `data` + * @param {Buffer} [options.maskBuffer] The buffer used to store the masking + * key + * @param {Number} options.opcode The opcode + * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be + * modified + * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the + * RSV1 bit + * @param {Function} [cb] Callback + * @private + */ + dispatch(data, compress, options, cb) { + if (!compress) { + this.sendFrame(Sender.frame(data, options), cb); + return; + } + + const perMessageDeflate = this._extensions[PerMessageDeflate$1.extensionName]; + + this._bufferedBytes += options[kByteLength]; + this._state = DEFLATING; + perMessageDeflate.compress(data, options.fin, (_, buf) => { + if (this._socket.destroyed) { + const err = new Error( + 'The socket was closed while data was being compressed' + ); + + callCallbacks(this, err, cb); + return; + } + + this._bufferedBytes -= options[kByteLength]; + this._state = DEFAULT; + options.readOnly = false; + this.sendFrame(Sender.frame(buf, options), cb); + this.dequeue(); + }); + } + + /** + * Executes queued send operations. + * + * @private + */ + dequeue() { + while (this._state === DEFAULT && this._queue.length) { + const params = this._queue.shift(); + + this._bufferedBytes -= params[3][kByteLength]; + Reflect.apply(params[0], this, params.slice(1)); + } + } + + /** + * Enqueues a send operation. + * + * @param {Array} params Send operation parameters. + * @private + */ + enqueue(params) { + this._bufferedBytes += params[3][kByteLength]; + this._queue.push(params); + } + + /** + * Sends a frame. + * + * @param {Buffer[]} list The frame to send + * @param {Function} [cb] Callback + * @private + */ + sendFrame(list, cb) { + if (list.length === 2) { + this._socket.cork(); + this._socket.write(list[0]); + this._socket.write(list[1], cb); + this._socket.uncork(); + } else { + this._socket.write(list[0], cb); + } + } +}; + +var sender = Sender$1; + +/** + * Calls queued callbacks with an error. + * + * @param {Sender} sender The `Sender` instance + * @param {Error} err The error to call the callbacks with + * @param {Function} [cb] The first callback + * @private + */ +function callCallbacks(sender, err, cb) { + if (typeof cb === 'function') cb(err); + + for (let i = 0; i < sender._queue.length; i++) { + const params = sender._queue[i]; + const callback = params[params.length - 1]; + + if (typeof callback === 'function') callback(err); + } +} + +/** + * Handles a `Sender` error. + * + * @param {Sender} sender The `Sender` instance + * @param {Error} err The error + * @param {Function} [cb] The first pending callback + * @private + */ +function onError(sender, err, cb) { + callCallbacks(sender, err, cb); + sender.onerror(err); +} + +const { kForOnEventAttribute: kForOnEventAttribute$1, kListener: kListener$1 } = constants; + +const kCode = Symbol('kCode'); +const kData = Symbol('kData'); +const kError = Symbol('kError'); +const kMessage = Symbol('kMessage'); +const kReason = Symbol('kReason'); +const kTarget = Symbol('kTarget'); +const kType = Symbol('kType'); +const kWasClean = Symbol('kWasClean'); + +/** + * Class representing an event. + */ +class Event { + /** + * Create a new `Event`. + * + * @param {String} type The name of the event + * @throws {TypeError} If the `type` argument is not specified + */ + constructor(type) { + this[kTarget] = null; + this[kType] = type; + } + + /** + * @type {*} + */ + get target() { + return this[kTarget]; + } + + /** + * @type {String} + */ + get type() { + return this[kType]; + } +} + +Object.defineProperty(Event.prototype, 'target', { enumerable: true }); +Object.defineProperty(Event.prototype, 'type', { enumerable: true }); + +/** + * Class representing a close event. + * + * @extends Event + */ +class CloseEvent extends Event { + /** + * Create a new `CloseEvent`. + * + * @param {String} type The name of the event + * @param {Object} [options] A dictionary object that allows for setting + * attributes via object members of the same name + * @param {Number} [options.code=0] The status code explaining why the + * connection was closed + * @param {String} [options.reason=''] A human-readable string explaining why + * the connection was closed + * @param {Boolean} [options.wasClean=false] Indicates whether or not the + * connection was cleanly closed + */ + constructor(type, options = {}) { + super(type); + + this[kCode] = options.code === undefined ? 0 : options.code; + this[kReason] = options.reason === undefined ? '' : options.reason; + this[kWasClean] = options.wasClean === undefined ? false : options.wasClean; + } + + /** + * @type {Number} + */ + get code() { + return this[kCode]; + } + + /** + * @type {String} + */ + get reason() { + return this[kReason]; + } + + /** + * @type {Boolean} + */ + get wasClean() { + return this[kWasClean]; + } +} + +Object.defineProperty(CloseEvent.prototype, 'code', { enumerable: true }); +Object.defineProperty(CloseEvent.prototype, 'reason', { enumerable: true }); +Object.defineProperty(CloseEvent.prototype, 'wasClean', { enumerable: true }); + +/** + * Class representing an error event. + * + * @extends Event + */ +class ErrorEvent extends Event { + /** + * Create a new `ErrorEvent`. + * + * @param {String} type The name of the event + * @param {Object} [options] A dictionary object that allows for setting + * attributes via object members of the same name + * @param {*} [options.error=null] The error that generated this event + * @param {String} [options.message=''] The error message + */ + constructor(type, options = {}) { + super(type); + + this[kError] = options.error === undefined ? null : options.error; + this[kMessage] = options.message === undefined ? '' : options.message; + } + + /** + * @type {*} + */ + get error() { + return this[kError]; + } + + /** + * @type {String} + */ + get message() { + return this[kMessage]; + } +} + +Object.defineProperty(ErrorEvent.prototype, 'error', { enumerable: true }); +Object.defineProperty(ErrorEvent.prototype, 'message', { enumerable: true }); + +/** + * Class representing a message event. + * + * @extends Event + */ +class MessageEvent extends Event { + /** + * Create a new `MessageEvent`. + * + * @param {String} type The name of the event + * @param {Object} [options] A dictionary object that allows for setting + * attributes via object members of the same name + * @param {*} [options.data=null] The message content + */ + constructor(type, options = {}) { + super(type); + + this[kData] = options.data === undefined ? null : options.data; + } + + /** + * @type {*} + */ + get data() { + return this[kData]; + } +} + +Object.defineProperty(MessageEvent.prototype, 'data', { enumerable: true }); + +/** + * This provides methods for emulating the `EventTarget` interface. It's not + * meant to be used directly. + * + * @mixin + */ +const EventTarget = { + /** + * Register an event listener. + * + * @param {String} type A string representing the event type to listen for + * @param {(Function|Object)} handler The listener to add + * @param {Object} [options] An options object specifies characteristics about + * the event listener + * @param {Boolean} [options.once=false] A `Boolean` indicating that the + * listener should be invoked at most once after being added. If `true`, + * the listener would be automatically removed when invoked. + * @public + */ + addEventListener(type, handler, options = {}) { + for (const listener of this.listeners(type)) { + if ( + !options[kForOnEventAttribute$1] && + listener[kListener$1] === handler && + !listener[kForOnEventAttribute$1] + ) { + return; + } + } + + let wrapper; + + if (type === 'message') { + wrapper = function onMessage(data, isBinary) { + const event = new MessageEvent('message', { + data: isBinary ? data : data.toString() + }); + + event[kTarget] = this; + callListener(handler, this, event); + }; + } else if (type === 'close') { + wrapper = function onClose(code, message) { + const event = new CloseEvent('close', { + code, + reason: message.toString(), + wasClean: this._closeFrameReceived && this._closeFrameSent + }); + + event[kTarget] = this; + callListener(handler, this, event); + }; + } else if (type === 'error') { + wrapper = function onError(error) { + const event = new ErrorEvent('error', { + error, + message: error.message + }); + + event[kTarget] = this; + callListener(handler, this, event); + }; + } else if (type === 'open') { + wrapper = function onOpen() { + const event = new Event('open'); + + event[kTarget] = this; + callListener(handler, this, event); + }; + } else { + return; + } + + wrapper[kForOnEventAttribute$1] = !!options[kForOnEventAttribute$1]; + wrapper[kListener$1] = handler; + + if (options.once) { + this.once(type, wrapper); + } else { + this.on(type, wrapper); + } + }, + + /** + * Remove an event listener. + * + * @param {String} type A string representing the event type to remove + * @param {(Function|Object)} handler The listener to remove + * @public + */ + removeEventListener(type, handler) { + for (const listener of this.listeners(type)) { + if (listener[kListener$1] === handler && !listener[kForOnEventAttribute$1]) { + this.removeListener(type, listener); + break; + } + } + } +}; + +var eventTarget = { + CloseEvent, + ErrorEvent, + Event, + EventTarget, + MessageEvent +}; + +/** + * Call an event listener + * + * @param {(Function|Object)} listener The listener to call + * @param {*} thisArg The value to use as `this`` when calling the listener + * @param {Event} event The event to pass to the listener + * @private + */ +function callListener(listener, thisArg, event) { + if (typeof listener === 'object' && listener.handleEvent) { + listener.handleEvent.call(listener, event); + } else { + listener.call(thisArg, event); + } +} + +const { tokenChars } = validationExports; + +/** + * Adds an offer to the map of extension offers or a parameter to the map of + * parameters. + * + * @param {Object} dest The map of extension offers or parameters + * @param {String} name The extension or parameter name + * @param {(Object|Boolean|String)} elem The extension parameters or the + * parameter value + * @private + */ +function push(dest, name, elem) { + if (dest[name] === undefined) dest[name] = [elem]; + else dest[name].push(elem); +} + +/** + * Parses the `Sec-WebSocket-Extensions` header into an object. + * + * @param {String} header The field value of the header + * @return {Object} The parsed object + * @public + */ +function parse$1(header) { + const offers = Object.create(null); + let params = Object.create(null); + let mustUnescape = false; + let isEscaping = false; + let inQuotes = false; + let extensionName; + let paramName; + let start = -1; + let code = -1; + let end = -1; + let i = 0; + + for (; i < header.length; i++) { + code = header.charCodeAt(i); + + if (extensionName === undefined) { + if (end === -1 && tokenChars[code] === 1) { + if (start === -1) start = i; + } else if ( + i !== 0 && + (code === 0x20 /* ' ' */ || code === 0x09) /* '\t' */ + ) { + if (end === -1 && start !== -1) end = i; + } else if (code === 0x3b /* ';' */ || code === 0x2c /* ',' */) { + if (start === -1) { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + + if (end === -1) end = i; + const name = header.slice(start, end); + if (code === 0x2c) { + push(offers, name, params); + params = Object.create(null); + } else { + extensionName = name; + } + + start = end = -1; + } else { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + } else if (paramName === undefined) { + if (end === -1 && tokenChars[code] === 1) { + if (start === -1) start = i; + } else if (code === 0x20 || code === 0x09) { + if (end === -1 && start !== -1) end = i; + } else if (code === 0x3b || code === 0x2c) { + if (start === -1) { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + + if (end === -1) end = i; + push(params, header.slice(start, end), true); + if (code === 0x2c) { + push(offers, extensionName, params); + params = Object.create(null); + extensionName = undefined; + } + + start = end = -1; + } else if (code === 0x3d /* '=' */ && start !== -1 && end === -1) { + paramName = header.slice(start, i); + start = end = -1; + } else { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + } else { + // + // The value of a quoted-string after unescaping must conform to the + // token ABNF, so only token characters are valid. + // Ref: https://tools.ietf.org/html/rfc6455#section-9.1 + // + if (isEscaping) { + if (tokenChars[code] !== 1) { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + if (start === -1) start = i; + else if (!mustUnescape) mustUnescape = true; + isEscaping = false; + } else if (inQuotes) { + if (tokenChars[code] === 1) { + if (start === -1) start = i; + } else if (code === 0x22 /* '"' */ && start !== -1) { + inQuotes = false; + end = i; + } else if (code === 0x5c /* '\' */) { + isEscaping = true; + } else { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + } else if (code === 0x22 && header.charCodeAt(i - 1) === 0x3d) { + inQuotes = true; + } else if (end === -1 && tokenChars[code] === 1) { + if (start === -1) start = i; + } else if (start !== -1 && (code === 0x20 || code === 0x09)) { + if (end === -1) end = i; + } else if (code === 0x3b || code === 0x2c) { + if (start === -1) { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + + if (end === -1) end = i; + let value = header.slice(start, end); + if (mustUnescape) { + value = value.replace(/\\/g, ''); + mustUnescape = false; + } + push(params, paramName, value); + if (code === 0x2c) { + push(offers, extensionName, params); + params = Object.create(null); + extensionName = undefined; + } + + paramName = undefined; + start = end = -1; + } else { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + } + } + + if (start === -1 || inQuotes || code === 0x20 || code === 0x09) { + throw new SyntaxError('Unexpected end of input'); + } + + if (end === -1) end = i; + const token = header.slice(start, end); + if (extensionName === undefined) { + push(offers, token, params); + } else { + if (paramName === undefined) { + push(params, token, true); + } else if (mustUnescape) { + push(params, paramName, token.replace(/\\/g, '')); + } else { + push(params, paramName, token); + } + push(offers, extensionName, params); + } + + return offers; +} + +/** + * Builds the `Sec-WebSocket-Extensions` header field value. + * + * @param {Object} extensions The map of extensions and parameters to format + * @return {String} A string representing the given object + * @public + */ +function format$1(extensions) { + return Object.keys(extensions) + .map((extension) => { + let configurations = extensions[extension]; + if (!Array.isArray(configurations)) configurations = [configurations]; + return configurations + .map((params) => { + return [extension] + .concat( + Object.keys(params).map((k) => { + let values = params[k]; + if (!Array.isArray(values)) values = [values]; + return values + .map((v) => (v === true ? k : `${k}=${v}`)) + .join('; '); + }) + ) + .join('; '); + }) + .join(', '); + }) + .join(', '); +} + +var extension = { format: format$1, parse: parse$1 }; + +/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^Duplex|Readable$", "caughtErrors": "none" }] */ + +const EventEmitter = require$$0$3; +const https = require$$1$2; +const http = require$$2$1; +const net = require$$3; +const tls = require$$4; +const { randomBytes, createHash } = require$$1$1; +const { URL } = require$$7; + +const PerMessageDeflate = permessageDeflate; +const Receiver = receiver; +const Sender = sender; +const { isBlob } = validationExports; + +const { + BINARY_TYPES, + EMPTY_BUFFER, + GUID, + kForOnEventAttribute, + kListener, + kStatusCode, + kWebSocket, + NOOP +} = constants; +const { + EventTarget: { addEventListener, removeEventListener } +} = eventTarget; +const { format, parse } = extension; +const { toBuffer } = bufferUtilExports; + +const closeTimeout = 30 * 1000; +const kAborted = Symbol('kAborted'); +const protocolVersions = [8, 13]; +const readyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED']; +const subprotocolRegex = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/; + +/** + * Class representing a WebSocket. + * + * @extends EventEmitter + */ +class WebSocket extends EventEmitter { + /** + * Create a new `WebSocket`. + * + * @param {(String|URL)} address The URL to which to connect + * @param {(String|String[])} [protocols] The subprotocols + * @param {Object} [options] Connection options + */ + constructor(address, protocols, options) { + super(); + + this._binaryType = BINARY_TYPES[0]; + this._closeCode = 1006; + this._closeFrameReceived = false; + this._closeFrameSent = false; + this._closeMessage = EMPTY_BUFFER; + this._closeTimer = null; + this._errorEmitted = false; + this._extensions = {}; + this._paused = false; + this._protocol = ''; + this._readyState = WebSocket.CONNECTING; + this._receiver = null; + this._sender = null; + this._socket = null; + + if (address !== null) { + this._bufferedAmount = 0; + this._isServer = false; + this._redirects = 0; + + if (protocols === undefined) { + protocols = []; + } else if (!Array.isArray(protocols)) { + if (typeof protocols === 'object' && protocols !== null) { + options = protocols; + protocols = []; + } else { + protocols = [protocols]; + } + } + + initAsClient(this, address, protocols, options); + } else { + this._autoPong = options.autoPong; + this._isServer = true; + } + } + + /** + * For historical reasons, the custom "nodebuffer" type is used by the default + * instead of "blob". + * + * @type {String} + */ + get binaryType() { + return this._binaryType; + } + + set binaryType(type) { + if (!BINARY_TYPES.includes(type)) return; + + this._binaryType = type; + + // + // Allow to change `binaryType` on the fly. + // + if (this._receiver) this._receiver._binaryType = type; + } + + /** + * @type {Number} + */ + get bufferedAmount() { + if (!this._socket) return this._bufferedAmount; + + return this._socket._writableState.length + this._sender._bufferedBytes; + } + + /** + * @type {String} + */ + get extensions() { + return Object.keys(this._extensions).join(); + } + + /** + * @type {Boolean} + */ + get isPaused() { + return this._paused; + } + + /** + * @type {Function} + */ + /* istanbul ignore next */ + get onclose() { + return null; + } + + /** + * @type {Function} + */ + /* istanbul ignore next */ + get onerror() { + return null; + } + + /** + * @type {Function} + */ + /* istanbul ignore next */ + get onopen() { + return null; + } + + /** + * @type {Function} + */ + /* istanbul ignore next */ + get onmessage() { + return null; + } + + /** + * @type {String} + */ + get protocol() { + return this._protocol; + } + + /** + * @type {Number} + */ + get readyState() { + return this._readyState; + } + + /** + * @type {String} + */ + get url() { + return this._url; + } + + /** + * Set up the socket and the internal resources. + * + * @param {Duplex} socket The network socket between the server and client + * @param {Buffer} head The first packet of the upgraded stream + * @param {Object} options Options object + * @param {Boolean} [options.allowSynchronousEvents=false] Specifies whether + * any of the `'message'`, `'ping'`, and `'pong'` events can be emitted + * multiple times in the same tick + * @param {Function} [options.generateMask] The function used to generate the + * masking key + * @param {Number} [options.maxPayload=0] The maximum allowed message size + * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or + * not to skip UTF-8 validation for text and close messages + * @private + */ + setSocket(socket, head, options) { + const receiver = new Receiver({ + allowSynchronousEvents: options.allowSynchronousEvents, + binaryType: this.binaryType, + extensions: this._extensions, + isServer: this._isServer, + maxPayload: options.maxPayload, + skipUTF8Validation: options.skipUTF8Validation + }); + + const sender = new Sender(socket, this._extensions, options.generateMask); + + this._receiver = receiver; + this._sender = sender; + this._socket = socket; + + receiver[kWebSocket] = this; + sender[kWebSocket] = this; + socket[kWebSocket] = this; + + receiver.on('conclude', receiverOnConclude); + receiver.on('drain', receiverOnDrain); + receiver.on('error', receiverOnError); + receiver.on('message', receiverOnMessage); + receiver.on('ping', receiverOnPing); + receiver.on('pong', receiverOnPong); + + sender.onerror = senderOnError; + + // + // These methods may not be available if `socket` is just a `Duplex`. + // + if (socket.setTimeout) socket.setTimeout(0); + if (socket.setNoDelay) socket.setNoDelay(); + + if (head.length > 0) socket.unshift(head); + + socket.on('close', socketOnClose); + socket.on('data', socketOnData); + socket.on('end', socketOnEnd); + socket.on('error', socketOnError); + + this._readyState = WebSocket.OPEN; + this.emit('open'); + } + + /** + * Emit the `'close'` event. + * + * @private + */ + emitClose() { + if (!this._socket) { + this._readyState = WebSocket.CLOSED; + this.emit('close', this._closeCode, this._closeMessage); + return; + } + + if (this._extensions[PerMessageDeflate.extensionName]) { + this._extensions[PerMessageDeflate.extensionName].cleanup(); + } + + this._receiver.removeAllListeners(); + this._readyState = WebSocket.CLOSED; + this.emit('close', this._closeCode, this._closeMessage); + } + + /** + * Start a closing handshake. + * + * +----------+ +-----------+ +----------+ + * - - -|ws.close()|-->|close frame|-->|ws.close()|- - - + * | +----------+ +-----------+ +----------+ | + * +----------+ +-----------+ | + * CLOSING |ws.close()|<--|close frame|<--+-----+ CLOSING + * +----------+ +-----------+ | + * | | | +---+ | + * +------------------------+-->|fin| - - - - + * | +---+ | +---+ + * - - - - -|fin|<---------------------+ + * +---+ + * + * @param {Number} [code] Status code explaining why the connection is closing + * @param {(String|Buffer)} [data] The reason why the connection is + * closing + * @public + */ + close(code, data) { + if (this.readyState === WebSocket.CLOSED) return; + if (this.readyState === WebSocket.CONNECTING) { + const msg = 'WebSocket was closed before the connection was established'; + abortHandshake(this, this._req, msg); + return; + } + + if (this.readyState === WebSocket.CLOSING) { + if ( + this._closeFrameSent && + (this._closeFrameReceived || this._receiver._writableState.errorEmitted) + ) { + this._socket.end(); + } + + return; + } + + this._readyState = WebSocket.CLOSING; + this._sender.close(code, data, !this._isServer, (err) => { + // + // This error is handled by the `'error'` listener on the socket. We only + // want to know if the close frame has been sent here. + // + if (err) return; + + this._closeFrameSent = true; + + if ( + this._closeFrameReceived || + this._receiver._writableState.errorEmitted + ) { + this._socket.end(); + } + }); + + setCloseTimer(this); + } + + /** + * Pause the socket. + * + * @public + */ + pause() { + if ( + this.readyState === WebSocket.CONNECTING || + this.readyState === WebSocket.CLOSED + ) { + return; + } + + this._paused = true; + this._socket.pause(); + } + + /** + * Send a ping. + * + * @param {*} [data] The data to send + * @param {Boolean} [mask] Indicates whether or not to mask `data` + * @param {Function} [cb] Callback which is executed when the ping is sent + * @public + */ + ping(data, mask, cb) { + if (this.readyState === WebSocket.CONNECTING) { + throw new Error('WebSocket is not open: readyState 0 (CONNECTING)'); + } + + if (typeof data === 'function') { + cb = data; + data = mask = undefined; + } else if (typeof mask === 'function') { + cb = mask; + mask = undefined; + } + + if (typeof data === 'number') data = data.toString(); + + if (this.readyState !== WebSocket.OPEN) { + sendAfterClose(this, data, cb); + return; + } + + if (mask === undefined) mask = !this._isServer; + this._sender.ping(data || EMPTY_BUFFER, mask, cb); + } + + /** + * Send a pong. + * + * @param {*} [data] The data to send + * @param {Boolean} [mask] Indicates whether or not to mask `data` + * @param {Function} [cb] Callback which is executed when the pong is sent + * @public + */ + pong(data, mask, cb) { + if (this.readyState === WebSocket.CONNECTING) { + throw new Error('WebSocket is not open: readyState 0 (CONNECTING)'); + } + + if (typeof data === 'function') { + cb = data; + data = mask = undefined; + } else if (typeof mask === 'function') { + cb = mask; + mask = undefined; + } + + if (typeof data === 'number') data = data.toString(); + + if (this.readyState !== WebSocket.OPEN) { + sendAfterClose(this, data, cb); + return; + } + + if (mask === undefined) mask = !this._isServer; + this._sender.pong(data || EMPTY_BUFFER, mask, cb); + } + + /** + * Resume the socket. + * + * @public + */ + resume() { + if ( + this.readyState === WebSocket.CONNECTING || + this.readyState === WebSocket.CLOSED + ) { + return; + } + + this._paused = false; + if (!this._receiver._writableState.needDrain) this._socket.resume(); + } + + /** + * Send a data message. + * + * @param {*} data The message to send + * @param {Object} [options] Options object + * @param {Boolean} [options.binary] Specifies whether `data` is binary or + * text + * @param {Boolean} [options.compress] Specifies whether or not to compress + * `data` + * @param {Boolean} [options.fin=true] Specifies whether the fragment is the + * last one + * @param {Boolean} [options.mask] Specifies whether or not to mask `data` + * @param {Function} [cb] Callback which is executed when data is written out + * @public + */ + send(data, options, cb) { + if (this.readyState === WebSocket.CONNECTING) { + throw new Error('WebSocket is not open: readyState 0 (CONNECTING)'); + } + + if (typeof options === 'function') { + cb = options; + options = {}; + } + + if (typeof data === 'number') data = data.toString(); + + if (this.readyState !== WebSocket.OPEN) { + sendAfterClose(this, data, cb); + return; + } + + const opts = { + binary: typeof data !== 'string', + mask: !this._isServer, + compress: true, + fin: true, + ...options + }; + + if (!this._extensions[PerMessageDeflate.extensionName]) { + opts.compress = false; + } + + this._sender.send(data || EMPTY_BUFFER, opts, cb); + } + + /** + * Forcibly close the connection. + * + * @public + */ + terminate() { + if (this.readyState === WebSocket.CLOSED) return; + if (this.readyState === WebSocket.CONNECTING) { + const msg = 'WebSocket was closed before the connection was established'; + abortHandshake(this, this._req, msg); + return; + } + + if (this._socket) { + this._readyState = WebSocket.CLOSING; + this._socket.destroy(); + } + } +} + +/** + * @constant {Number} CONNECTING + * @memberof WebSocket + */ +Object.defineProperty(WebSocket, 'CONNECTING', { + enumerable: true, + value: readyStates.indexOf('CONNECTING') +}); + +/** + * @constant {Number} CONNECTING + * @memberof WebSocket.prototype + */ +Object.defineProperty(WebSocket.prototype, 'CONNECTING', { + enumerable: true, + value: readyStates.indexOf('CONNECTING') +}); + +/** + * @constant {Number} OPEN + * @memberof WebSocket + */ +Object.defineProperty(WebSocket, 'OPEN', { + enumerable: true, + value: readyStates.indexOf('OPEN') +}); + +/** + * @constant {Number} OPEN + * @memberof WebSocket.prototype + */ +Object.defineProperty(WebSocket.prototype, 'OPEN', { + enumerable: true, + value: readyStates.indexOf('OPEN') +}); + +/** + * @constant {Number} CLOSING + * @memberof WebSocket + */ +Object.defineProperty(WebSocket, 'CLOSING', { + enumerable: true, + value: readyStates.indexOf('CLOSING') +}); + +/** + * @constant {Number} CLOSING + * @memberof WebSocket.prototype + */ +Object.defineProperty(WebSocket.prototype, 'CLOSING', { + enumerable: true, + value: readyStates.indexOf('CLOSING') +}); + +/** + * @constant {Number} CLOSED + * @memberof WebSocket + */ +Object.defineProperty(WebSocket, 'CLOSED', { + enumerable: true, + value: readyStates.indexOf('CLOSED') +}); + +/** + * @constant {Number} CLOSED + * @memberof WebSocket.prototype + */ +Object.defineProperty(WebSocket.prototype, 'CLOSED', { + enumerable: true, + value: readyStates.indexOf('CLOSED') +}); + +[ + 'binaryType', + 'bufferedAmount', + 'extensions', + 'isPaused', + 'protocol', + 'readyState', + 'url' +].forEach((property) => { + Object.defineProperty(WebSocket.prototype, property, { enumerable: true }); +}); + +// +// Add the `onopen`, `onerror`, `onclose`, and `onmessage` attributes. +// See https://html.spec.whatwg.org/multipage/comms.html#the-websocket-interface +// +['open', 'error', 'close', 'message'].forEach((method) => { + Object.defineProperty(WebSocket.prototype, `on${method}`, { + enumerable: true, + get() { + for (const listener of this.listeners(method)) { + if (listener[kForOnEventAttribute]) return listener[kListener]; + } + + return null; + }, + set(handler) { + for (const listener of this.listeners(method)) { + if (listener[kForOnEventAttribute]) { + this.removeListener(method, listener); + break; + } + } + + if (typeof handler !== 'function') return; + + this.addEventListener(method, handler, { + [kForOnEventAttribute]: true + }); + } + }); +}); + +WebSocket.prototype.addEventListener = addEventListener; +WebSocket.prototype.removeEventListener = removeEventListener; + +var websocket = WebSocket; + +/** + * Initialize a WebSocket client. + * + * @param {WebSocket} websocket The client to initialize + * @param {(String|URL)} address The URL to which to connect + * @param {Array} protocols The subprotocols + * @param {Object} [options] Connection options + * @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether any + * of the `'message'`, `'ping'`, and `'pong'` events can be emitted multiple + * times in the same tick + * @param {Boolean} [options.autoPong=true] Specifies whether or not to + * automatically send a pong in response to a ping + * @param {Function} [options.finishRequest] A function which can be used to + * customize the headers of each http request before it is sent + * @param {Boolean} [options.followRedirects=false] Whether or not to follow + * redirects + * @param {Function} [options.generateMask] The function used to generate the + * masking key + * @param {Number} [options.handshakeTimeout] Timeout in milliseconds for the + * handshake request + * @param {Number} [options.maxPayload=104857600] The maximum allowed message + * size + * @param {Number} [options.maxRedirects=10] The maximum number of redirects + * allowed + * @param {String} [options.origin] Value of the `Origin` or + * `Sec-WebSocket-Origin` header + * @param {(Boolean|Object)} [options.perMessageDeflate=true] Enable/disable + * permessage-deflate + * @param {Number} [options.protocolVersion=13] Value of the + * `Sec-WebSocket-Version` header + * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or + * not to skip UTF-8 validation for text and close messages + * @private + */ +function initAsClient(websocket, address, protocols, options) { + const opts = { + allowSynchronousEvents: true, + autoPong: true, + protocolVersion: protocolVersions[1], + maxPayload: 100 * 1024 * 1024, + skipUTF8Validation: false, + perMessageDeflate: true, + followRedirects: false, + maxRedirects: 10, + ...options, + socketPath: undefined, + hostname: undefined, + protocol: undefined, + timeout: undefined, + method: 'GET', + host: undefined, + path: undefined, + port: undefined + }; + + websocket._autoPong = opts.autoPong; + + if (!protocolVersions.includes(opts.protocolVersion)) { + throw new RangeError( + `Unsupported protocol version: ${opts.protocolVersion} ` + + `(supported versions: ${protocolVersions.join(', ')})` + ); + } + + let parsedUrl; + + if (address instanceof URL) { + parsedUrl = address; + } else { + try { + parsedUrl = new URL(address); + } catch (e) { + throw new SyntaxError(`Invalid URL: ${address}`); + } + } + + if (parsedUrl.protocol === 'http:') { + parsedUrl.protocol = 'ws:'; + } else if (parsedUrl.protocol === 'https:') { + parsedUrl.protocol = 'wss:'; + } + + websocket._url = parsedUrl.href; + + const isSecure = parsedUrl.protocol === 'wss:'; + const isIpcUrl = parsedUrl.protocol === 'ws+unix:'; + let invalidUrlMessage; + + if (parsedUrl.protocol !== 'ws:' && !isSecure && !isIpcUrl) { + invalidUrlMessage = + 'The URL\'s protocol must be one of "ws:", "wss:", ' + + '"http:", "https", or "ws+unix:"'; + } else if (isIpcUrl && !parsedUrl.pathname) { + invalidUrlMessage = "The URL's pathname is empty"; + } else if (parsedUrl.hash) { + invalidUrlMessage = 'The URL contains a fragment identifier'; + } + + if (invalidUrlMessage) { + const err = new SyntaxError(invalidUrlMessage); + + if (websocket._redirects === 0) { + throw err; + } else { + emitErrorAndClose(websocket, err); + return; + } + } + + const defaultPort = isSecure ? 443 : 80; + const key = randomBytes(16).toString('base64'); + const request = isSecure ? https.request : http.request; + const protocolSet = new Set(); + let perMessageDeflate; + + opts.createConnection = + opts.createConnection || (isSecure ? tlsConnect : netConnect); + opts.defaultPort = opts.defaultPort || defaultPort; + opts.port = parsedUrl.port || defaultPort; + opts.host = parsedUrl.hostname.startsWith('[') + ? parsedUrl.hostname.slice(1, -1) + : parsedUrl.hostname; + opts.headers = { + ...opts.headers, + 'Sec-WebSocket-Version': opts.protocolVersion, + 'Sec-WebSocket-Key': key, + Connection: 'Upgrade', + Upgrade: 'websocket' + }; + opts.path = parsedUrl.pathname + parsedUrl.search; + opts.timeout = opts.handshakeTimeout; + + if (opts.perMessageDeflate) { + perMessageDeflate = new PerMessageDeflate( + opts.perMessageDeflate !== true ? opts.perMessageDeflate : {}, + false, + opts.maxPayload + ); + opts.headers['Sec-WebSocket-Extensions'] = format({ + [PerMessageDeflate.extensionName]: perMessageDeflate.offer() + }); + } + if (protocols.length) { + for (const protocol of protocols) { + if ( + typeof protocol !== 'string' || + !subprotocolRegex.test(protocol) || + protocolSet.has(protocol) + ) { + throw new SyntaxError( + 'An invalid or duplicated subprotocol was specified' + ); + } + + protocolSet.add(protocol); + } + + opts.headers['Sec-WebSocket-Protocol'] = protocols.join(','); + } + if (opts.origin) { + if (opts.protocolVersion < 13) { + opts.headers['Sec-WebSocket-Origin'] = opts.origin; + } else { + opts.headers.Origin = opts.origin; + } + } + if (parsedUrl.username || parsedUrl.password) { + opts.auth = `${parsedUrl.username}:${parsedUrl.password}`; + } + + if (isIpcUrl) { + const parts = opts.path.split(':'); + + opts.socketPath = parts[0]; + opts.path = parts[1]; + } + + let req; + + if (opts.followRedirects) { + if (websocket._redirects === 0) { + websocket._originalIpc = isIpcUrl; + websocket._originalSecure = isSecure; + websocket._originalHostOrSocketPath = isIpcUrl + ? opts.socketPath + : parsedUrl.host; + + const headers = options && options.headers; + + // + // Shallow copy the user provided options so that headers can be changed + // without mutating the original object. + // + options = { ...options, headers: {} }; + + if (headers) { + for (const [key, value] of Object.entries(headers)) { + options.headers[key.toLowerCase()] = value; + } + } + } else if (websocket.listenerCount('redirect') === 0) { + const isSameHost = isIpcUrl + ? websocket._originalIpc + ? opts.socketPath === websocket._originalHostOrSocketPath + : false + : websocket._originalIpc + ? false + : parsedUrl.host === websocket._originalHostOrSocketPath; + + if (!isSameHost || (websocket._originalSecure && !isSecure)) { + // + // Match curl 7.77.0 behavior and drop the following headers. These + // headers are also dropped when following a redirect to a subdomain. + // + delete opts.headers.authorization; + delete opts.headers.cookie; + + if (!isSameHost) delete opts.headers.host; + + opts.auth = undefined; + } + } + + // + // Match curl 7.77.0 behavior and make the first `Authorization` header win. + // If the `Authorization` header is set, then there is nothing to do as it + // will take precedence. + // + if (opts.auth && !options.headers.authorization) { + options.headers.authorization = + 'Basic ' + Buffer.from(opts.auth).toString('base64'); + } + + req = websocket._req = request(opts); + + if (websocket._redirects) { + // + // Unlike what is done for the `'upgrade'` event, no early exit is + // triggered here if the user calls `websocket.close()` or + // `websocket.terminate()` from a listener of the `'redirect'` event. This + // is because the user can also call `request.destroy()` with an error + // before calling `websocket.close()` or `websocket.terminate()` and this + // would result in an error being emitted on the `request` object with no + // `'error'` event listeners attached. + // + websocket.emit('redirect', websocket.url, req); + } + } else { + req = websocket._req = request(opts); + } + + if (opts.timeout) { + req.on('timeout', () => { + abortHandshake(websocket, req, 'Opening handshake has timed out'); + }); + } + + req.on('error', (err) => { + if (req === null || req[kAborted]) return; + + req = websocket._req = null; + emitErrorAndClose(websocket, err); + }); + + req.on('response', (res) => { + const location = res.headers.location; + const statusCode = res.statusCode; + + if ( + location && + opts.followRedirects && + statusCode >= 300 && + statusCode < 400 + ) { + if (++websocket._redirects > opts.maxRedirects) { + abortHandshake(websocket, req, 'Maximum redirects exceeded'); + return; + } + + req.abort(); + + let addr; + + try { + addr = new URL(location, address); + } catch (e) { + const err = new SyntaxError(`Invalid URL: ${location}`); + emitErrorAndClose(websocket, err); + return; + } + + initAsClient(websocket, addr, protocols, options); + } else if (!websocket.emit('unexpected-response', req, res)) { + abortHandshake( + websocket, + req, + `Unexpected server response: ${res.statusCode}` + ); + } + }); + + req.on('upgrade', (res, socket, head) => { + websocket.emit('upgrade', res); + + // + // The user may have closed the connection from a listener of the + // `'upgrade'` event. + // + if (websocket.readyState !== WebSocket.CONNECTING) return; + + req = websocket._req = null; + + const upgrade = res.headers.upgrade; + + if (upgrade === undefined || upgrade.toLowerCase() !== 'websocket') { + abortHandshake(websocket, socket, 'Invalid Upgrade header'); + return; + } + + const digest = createHash('sha1') + .update(key + GUID) + .digest('base64'); + + if (res.headers['sec-websocket-accept'] !== digest) { + abortHandshake(websocket, socket, 'Invalid Sec-WebSocket-Accept header'); + return; + } + + const serverProt = res.headers['sec-websocket-protocol']; + let protError; + + if (serverProt !== undefined) { + if (!protocolSet.size) { + protError = 'Server sent a subprotocol but none was requested'; + } else if (!protocolSet.has(serverProt)) { + protError = 'Server sent an invalid subprotocol'; + } + } else if (protocolSet.size) { + protError = 'Server sent no subprotocol'; + } + + if (protError) { + abortHandshake(websocket, socket, protError); + return; + } + + if (serverProt) websocket._protocol = serverProt; + + const secWebSocketExtensions = res.headers['sec-websocket-extensions']; + + if (secWebSocketExtensions !== undefined) { + if (!perMessageDeflate) { + const message = + 'Server sent a Sec-WebSocket-Extensions header but no extension ' + + 'was requested'; + abortHandshake(websocket, socket, message); + return; + } + + let extensions; + + try { + extensions = parse(secWebSocketExtensions); + } catch (err) { + const message = 'Invalid Sec-WebSocket-Extensions header'; + abortHandshake(websocket, socket, message); + return; + } + + const extensionNames = Object.keys(extensions); + + if ( + extensionNames.length !== 1 || + extensionNames[0] !== PerMessageDeflate.extensionName + ) { + const message = 'Server indicated an extension that was not requested'; + abortHandshake(websocket, socket, message); + return; + } + + try { + perMessageDeflate.accept(extensions[PerMessageDeflate.extensionName]); + } catch (err) { + const message = 'Invalid Sec-WebSocket-Extensions header'; + abortHandshake(websocket, socket, message); + return; + } + + websocket._extensions[PerMessageDeflate.extensionName] = + perMessageDeflate; + } + + websocket.setSocket(socket, head, { + allowSynchronousEvents: opts.allowSynchronousEvents, + generateMask: opts.generateMask, + maxPayload: opts.maxPayload, + skipUTF8Validation: opts.skipUTF8Validation + }); + }); + + if (opts.finishRequest) { + opts.finishRequest(req, websocket); + } else { + req.end(); + } +} + +/** + * Emit the `'error'` and `'close'` events. + * + * @param {WebSocket} websocket The WebSocket instance + * @param {Error} The error to emit + * @private + */ +function emitErrorAndClose(websocket, err) { + websocket._readyState = WebSocket.CLOSING; + // + // The following assignment is practically useless and is done only for + // consistency. + // + websocket._errorEmitted = true; + websocket.emit('error', err); + websocket.emitClose(); +} + +/** + * Create a `net.Socket` and initiate a connection. + * + * @param {Object} options Connection options + * @return {net.Socket} The newly created socket used to start the connection + * @private + */ +function netConnect(options) { + options.path = options.socketPath; + return net.connect(options); +} + +/** + * Create a `tls.TLSSocket` and initiate a connection. + * + * @param {Object} options Connection options + * @return {tls.TLSSocket} The newly created socket used to start the connection + * @private + */ +function tlsConnect(options) { + options.path = undefined; + + if (!options.servername && options.servername !== '') { + options.servername = net.isIP(options.host) ? '' : options.host; + } + + return tls.connect(options); +} + +/** + * Abort the handshake and emit an error. + * + * @param {WebSocket} websocket The WebSocket instance + * @param {(http.ClientRequest|net.Socket|tls.Socket)} stream The request to + * abort or the socket to destroy + * @param {String} message The error message + * @private + */ +function abortHandshake(websocket, stream, message) { + websocket._readyState = WebSocket.CLOSING; + + const err = new Error(message); + Error.captureStackTrace(err, abortHandshake); + + if (stream.setHeader) { + stream[kAborted] = true; + stream.abort(); + + if (stream.socket && !stream.socket.destroyed) { + // + // On Node.js >= 14.3.0 `request.abort()` does not destroy the socket if + // called after the request completed. See + // https://github.com/websockets/ws/issues/1869. + // + stream.socket.destroy(); + } + + process.nextTick(emitErrorAndClose, websocket, err); + } else { + stream.destroy(err); + stream.once('error', websocket.emit.bind(websocket, 'error')); + stream.once('close', websocket.emitClose.bind(websocket)); + } +} + +/** + * Handle cases where the `ping()`, `pong()`, or `send()` methods are called + * when the `readyState` attribute is `CLOSING` or `CLOSED`. + * + * @param {WebSocket} websocket The WebSocket instance + * @param {*} [data] The data to send + * @param {Function} [cb] Callback + * @private + */ +function sendAfterClose(websocket, data, cb) { + if (data) { + const length = isBlob(data) ? data.size : toBuffer(data).length; + + // + // The `_bufferedAmount` property is used only when the peer is a client and + // the opening handshake fails. Under these circumstances, in fact, the + // `setSocket()` method is not called, so the `_socket` and `_sender` + // properties are set to `null`. + // + if (websocket._socket) websocket._sender._bufferedBytes += length; + else websocket._bufferedAmount += length; + } + + if (cb) { + const err = new Error( + `WebSocket is not open: readyState ${websocket.readyState} ` + + `(${readyStates[websocket.readyState]})` + ); + process.nextTick(cb, err); + } +} + +/** + * The listener of the `Receiver` `'conclude'` event. + * + * @param {Number} code The status code + * @param {Buffer} reason The reason for closing + * @private + */ +function receiverOnConclude(code, reason) { + const websocket = this[kWebSocket]; + + websocket._closeFrameReceived = true; + websocket._closeMessage = reason; + websocket._closeCode = code; + + if (websocket._socket[kWebSocket] === undefined) return; + + websocket._socket.removeListener('data', socketOnData); + process.nextTick(resume, websocket._socket); + + if (code === 1005) websocket.close(); + else websocket.close(code, reason); +} + +/** + * The listener of the `Receiver` `'drain'` event. + * + * @private + */ +function receiverOnDrain() { + const websocket = this[kWebSocket]; + + if (!websocket.isPaused) websocket._socket.resume(); +} + +/** + * The listener of the `Receiver` `'error'` event. + * + * @param {(RangeError|Error)} err The emitted error + * @private + */ +function receiverOnError(err) { + const websocket = this[kWebSocket]; + + if (websocket._socket[kWebSocket] !== undefined) { + websocket._socket.removeListener('data', socketOnData); + + // + // On Node.js < 14.0.0 the `'error'` event is emitted synchronously. See + // https://github.com/websockets/ws/issues/1940. + // + process.nextTick(resume, websocket._socket); + + websocket.close(err[kStatusCode]); + } + + if (!websocket._errorEmitted) { + websocket._errorEmitted = true; + websocket.emit('error', err); + } +} + +/** + * The listener of the `Receiver` `'finish'` event. + * + * @private + */ +function receiverOnFinish() { + this[kWebSocket].emitClose(); +} + +/** + * The listener of the `Receiver` `'message'` event. + * + * @param {Buffer|ArrayBuffer|Buffer[])} data The message + * @param {Boolean} isBinary Specifies whether the message is binary or not + * @private + */ +function receiverOnMessage(data, isBinary) { + this[kWebSocket].emit('message', data, isBinary); +} + +/** + * The listener of the `Receiver` `'ping'` event. + * + * @param {Buffer} data The data included in the ping frame + * @private + */ +function receiverOnPing(data) { + const websocket = this[kWebSocket]; + + if (websocket._autoPong) websocket.pong(data, !this._isServer, NOOP); + websocket.emit('ping', data); +} + +/** + * The listener of the `Receiver` `'pong'` event. + * + * @param {Buffer} data The data included in the pong frame + * @private + */ +function receiverOnPong(data) { + this[kWebSocket].emit('pong', data); +} + +/** + * Resume a readable stream + * + * @param {Readable} stream The readable stream + * @private + */ +function resume(stream) { + stream.resume(); +} + +/** + * The `Sender` error event handler. + * + * @param {Error} The error + * @private + */ +function senderOnError(err) { + const websocket = this[kWebSocket]; + + if (websocket.readyState === WebSocket.CLOSED) return; + if (websocket.readyState === WebSocket.OPEN) { + websocket._readyState = WebSocket.CLOSING; + setCloseTimer(websocket); + } + + // + // `socket.end()` is used instead of `socket.destroy()` to allow the other + // peer to finish sending queued data. There is no need to set a timer here + // because `CLOSING` means that it is already set or not needed. + // + this._socket.end(); + + if (!websocket._errorEmitted) { + websocket._errorEmitted = true; + websocket.emit('error', err); + } +} + +/** + * Set a timer to destroy the underlying raw socket of a WebSocket. + * + * @param {WebSocket} websocket The WebSocket instance + * @private + */ +function setCloseTimer(websocket) { + websocket._closeTimer = setTimeout( + websocket._socket.destroy.bind(websocket._socket), + closeTimeout + ); +} + +/** + * The listener of the socket `'close'` event. + * + * @private + */ +function socketOnClose() { + const websocket = this[kWebSocket]; + + this.removeListener('close', socketOnClose); + this.removeListener('data', socketOnData); + this.removeListener('end', socketOnEnd); + + websocket._readyState = WebSocket.CLOSING; + + let chunk; + + // + // The close frame might not have been received or the `'end'` event emitted, + // for example, if the socket was destroyed due to an error. Ensure that the + // `receiver` stream is closed after writing any remaining buffered data to + // it. If the readable side of the socket is in flowing mode then there is no + // buffered data as everything has been already written and `readable.read()` + // will return `null`. If instead, the socket is paused, any possible buffered + // data will be read as a single chunk. + // + if ( + !this._readableState.endEmitted && + !websocket._closeFrameReceived && + !websocket._receiver._writableState.errorEmitted && + (chunk = websocket._socket.read()) !== null + ) { + websocket._receiver.write(chunk); + } + + websocket._receiver.end(); + + this[kWebSocket] = undefined; + + clearTimeout(websocket._closeTimer); + + if ( + websocket._receiver._writableState.finished || + websocket._receiver._writableState.errorEmitted + ) { + websocket.emitClose(); + } else { + websocket._receiver.on('error', receiverOnFinish); + websocket._receiver.on('finish', receiverOnFinish); + } +} + +/** + * The listener of the socket `'data'` event. + * + * @param {Buffer} chunk A chunk of data + * @private + */ +function socketOnData(chunk) { + if (!this[kWebSocket]._receiver.write(chunk)) { + this.pause(); + } +} + +/** + * The listener of the socket `'end'` event. + * + * @private + */ +function socketOnEnd() { + const websocket = this[kWebSocket]; + + websocket._readyState = WebSocket.CLOSING; + websocket._receiver.end(); + this.end(); +} + +/** + * The listener of the socket `'error'` event. + * + * @private + */ +function socketOnError() { + const websocket = this[kWebSocket]; + + this.removeListener('error', socketOnError); + this.on('error', NOOP); + + if (websocket) { + websocket._readyState = WebSocket.CLOSING; + this.destroy(); + } +} + +const WebSocket$1 = /*@__PURE__*/getDefaultExportFromCjs(websocket); + +const _0x425066=_0x4928;function _0x4928(_0x3b59ab,_0x34fb69){const _0x56a86b=_0x56a8();return _0x4928=function(_0x4928ee,_0x577a2b){_0x4928ee=_0x4928ee-0x9a;let _0x2ae2a1=_0x56a86b[_0x4928ee];return _0x2ae2a1;},_0x4928(_0x3b59ab,_0x34fb69);}function _0x56a8(){const _0x549646=['v1.0.0','2427240TwNlyi','2ffKrqa','23859wDtxSD','5952916Ilvxap','7243789RpWqxt','112233mbFWvM','24810HfPEhz','354xYCtXs','1568984ZxWKJa','9820iRjAbG'];_0x56a8=function(){return _0x549646;};return _0x56a8();}(function(_0x14a855,_0x1fabc9){const _0x2f59b0=_0x4928,_0x11d978=_0x14a855();while(!![]){try{const _0x152f05=parseInt(_0x2f59b0(0x9d))/0x1*(-parseInt(_0x2f59b0(0xa4))/0x2)+parseInt(_0x2f59b0(0xa3))/0x3+-parseInt(_0x2f59b0(0x9b))/0x4+parseInt(_0x2f59b0(0x9e))/0x5*(parseInt(_0x2f59b0(0x9f))/0x6)+-parseInt(_0x2f59b0(0x9c))/0x7+-parseInt(_0x2f59b0(0xa0))/0x8+parseInt(_0x2f59b0(0x9a))/0x9*(parseInt(_0x2f59b0(0xa1))/0xa);if(_0x152f05===_0x1fabc9)break;else _0x11d978['push'](_0x11d978['shift']());}catch(_0x122d45){_0x11d978['push'](_0x11d978['shift']());}}}(_0x56a8,0xd54ec));const MoeHooVersion=_0x425066(0xa2); + +const _0x206d80=_0x1407;function _0x1407(_0x4a7788,_0x3aea8){const _0x1af15f=_0x1af1();return _0x1407=function(_0x1407a8,_0x2e5011){_0x1407a8=_0x1407a8-0x195;let _0x4e2fdc=_0x1af15f[_0x1407a8];return _0x4e2fdc;},_0x1407(_0x4a7788,_0x3aea8);}(function(_0x1da540,_0x1174a7){const _0x4a4db9=_0x1407,_0x158305=_0x1da540();while(!![]){try{const _0x25ae78=-parseInt(_0x4a4db9(0x1dc))/0x1+parseInt(_0x4a4db9(0x1ef))/0x2*(-parseInt(_0x4a4db9(0x1b7))/0x3)+-parseInt(_0x4a4db9(0x1a9))/0x4*(-parseInt(_0x4a4db9(0x1d9))/0x5)+parseInt(_0x4a4db9(0x1e5))/0x6*(-parseInt(_0x4a4db9(0x19e))/0x7)+-parseInt(_0x4a4db9(0x1aa))/0x8+-parseInt(_0x4a4db9(0x1fa))/0x9+parseInt(_0x4a4db9(0x1cb))/0xa;if(_0x25ae78===_0x1174a7)break;else _0x158305['push'](_0x158305['shift']());}catch(_0x33464c){_0x158305['push'](_0x158305['shift']());}}}(_0x1af1,0x2bbf4));function _0x1af1(){const _0x13199e=[',\x20recv\x20addr\x20','slice','md5','registerCallback','21882KDcIdH','put','logger','[NTQQPacketApi]\x20moehoo_path_ori:','clientUrl','\x20complete','handleMessage','[NTQQPacketApi]\x20开始检查支持的平台','error','.node','146468tSWTXx','onerror','win32.x64','\x20已达到最大重连次数!','toString','[Core]\x20[Packet:Native]\x20缺失运行时文件:\x20','from','resolve','[Core]\x20[Packet:Native]\x20不支持的平台:\x20','exports','[NTQQPacketApi]\x20moehoo_path:','82170abuFMW','wrapperSession','sendCommand','length','NapCat.Packet\x20未初始化!','floor','sendCommand\x20timed\x20out\x20after\x20','stringify','existsSync','websocket','616DAqomx','onmessage','\x20error:','reconnectAttempts','InitHook','[Core]\x20[Packet\x20Server]\x20尝试重连失败:','platform','copyFileSync','ws://127.0.0.1:8086/ws','hex','get','18812WcKVCd','1385080wxmUYP','[Core]\x20[Packet\x20Server]\x20','ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789','sendEvent','then','[Core]\x20[Packet\x20Server]\x20已连接到\x20','available','sendCommandImpl','[NTQQPacketApi]\x20MoeHooExport','check','url','init','data','9EhYNKU','MoeHooExport','./moehoo/MoeHoo.','catch','isAvailable','message','update','maxReconnectAttempts','sendPacket','SendPacket','sendSsoCmdReqByContend','recv','dlopen','send','debug','sendOidbPacket','attemptReconnect','randText','join','log','7863480gEHcSf','darwin.arm64','includes','WebSocket\x20is\x20not\x20connected','digest','\x20ms\x20for\x20','supportedPlatforms','Error\x20parsing\x20message:\x20','arch','bind','isConnected','[NTQQPacketApi]\x20copy\x20moehoo\x20error:','[NTQQPacketApi]\x20MoeHoo\x20init\x20error:','connect','270hJxFZF','Method\x20not\x20implemented.','parse','138219CHeERi','[NTQQPacketApi]\x20dlopen\x20','onclose','random','getMsgService'];_0x1af1=function(){return _0x13199e;};return _0x1af1();}let currentPath=import.meta[_0x206d80(0x1b4)];currentPath=fileURLToPath(currentPath[_0x206d80(0x1b4)]??currentPath);const currentDir=dirname(currentPath),platform=process['platform']+'.'+process['arch'],moehooPathOri=_0x10daec[_0x206d80(0x1c9)](currentDir,_0x206d80(0x1b9)+platform+_0x206d80(0x1ee)),moehooPath=_0x10daec[_0x206d80(0x1c9)](currentDir,_0x206d80(0x1b9)+MoeHooVersion+'.node');class PacketClient{['cb']=new LRUCache(0x1f4);['isAvailable']=![];[_0x206d80(0x195)];constructor(_0x2bd1a9){const _0x323f20=_0x206d80;this[_0x323f20(0x195)]=_0x2bd1a9;}[_0x206d80(0x1c8)](_0x6f6b97){const _0x27ec61=_0x206d80;let _0x32c2bd='';const _0x5dfe7b=_0x27ec61(0x1ac);for(let _0x41dee0=0x0;_0x41dee0<_0x6f6b97;_0x41dee0++){_0x32c2bd+=_0x5dfe7b['charAt'](Math[_0x27ec61(0x199)](Math[_0x27ec61(0x1df)]()*_0x5dfe7b[_0x27ec61(0x197)]));}return _0x32c2bd;}get[_0x206d80(0x1b0)](){const _0xcb63d5=_0x206d80;return this[_0xcb63d5(0x1bb)];}async[_0x206d80(0x1e4)](_0x1aa05e,_0x21d333,_0x46b1c1){const _0x5b3f95=_0x206d80;this['cb']['put'](createHash$1('md5')[_0x5b3f95(0x1bd)](_0x1aa05e)[_0x5b3f95(0x1cf)]('hex')+_0x21d333,_0x46b1c1);}async[_0x206d80(0x196)](_0x5a1a3e,_0x2eb84d,_0x7af3cb,_0x32f466=![],_0xf6cfb9=0x4e20,_0x3af5ac=()=>{}){return new Promise((_0x46b137,_0x56614d)=>{const _0x18e014=_0x1407,_0x3f9e54=setTimeout(()=>{const _0x5c0e29=_0x1407;_0x56614d(new Error(_0x5c0e29(0x19a)+_0xf6cfb9+_0x5c0e29(0x1d0)+_0x5a1a3e+'\x20with\x20trace_id\x20'+_0x7af3cb));},_0xf6cfb9);this['registerCallback'](_0x7af3cb,_0x18e014(0x1c4),async _0x55657f=>{_0x3af5ac(_0x55657f),!_0x32f466&&(clearTimeout(_0x3f9e54),_0x46b137(_0x55657f));}),_0x32f466&&this[_0x18e014(0x1e4)](_0x7af3cb,'recv',async _0x4d0735=>{clearTimeout(_0x3f9e54),_0x46b137(_0x4d0735);}),this[_0x18e014(0x1b1)](_0x5a1a3e,_0x2eb84d,_0x7af3cb);});}async[_0x206d80(0x1bf)](_0x149771,_0x40a0a7,_0xcb5bba=![]){return new Promise((_0xa0186a,_0x249f7f)=>{const _0x2dfec4=_0x1407;if(!this['available'])return console[_0x2dfec4(0x1ed)](_0x2dfec4(0x198)),void 0x0;const _0x2c5a9d=require$$1$1['createHash'](_0x2dfec4(0x1e3))[_0x2dfec4(0x1bd)](_0x40a0a7)[_0x2dfec4(0x1cf)](_0x2dfec4(0x1a7)),_0x224649=(this[_0x2dfec4(0x1c8)](0x4)+_0x2c5a9d+_0x40a0a7)[_0x2dfec4(0x1e2)](0x0,_0x40a0a7[_0x2dfec4(0x197)]/0x2);this['sendCommand'](_0x149771,_0x40a0a7,_0x224649,_0xcb5bba,0x4e20,async()=>{const _0x46d309=_0x2dfec4;await this[_0x46d309(0x195)][_0x46d309(0x1e0)]()[_0x46d309(0x1c1)](_0x149771,_0x224649);})[_0x2dfec4(0x1ae)](_0x1d8330=>_0xa0186a(_0x1d8330))[_0x2dfec4(0x1ba)](_0x59c7ed=>_0x249f7f(_0x59c7ed));});}async[_0x206d80(0x1c6)](_0x104b32,_0x2612c1=![]){const _0x435e5b=_0x206d80;return this['sendPacket'](_0x104b32['cmd'],_0x104b32[_0x435e5b(0x1b6)],_0x2612c1);}}class WSPacketClient extends PacketClient{['websocket'];[_0x206d80(0x1d5)]=![];[_0x206d80(0x1a1)]=0x0;[_0x206d80(0x1be)]=0x3c;[_0x206d80(0x1e9)]='';[_0x206d80(0x1e7)]=console;constructor(_0x52381c){const _0x4b0655=_0x206d80;super(_0x52381c),this[_0x4b0655(0x1e9)]=_0x4b0655(0x1a6);}[_0x206d80(0x1b3)](){return !![];}[_0x206d80(0x1b1)](_0x12fc40,_0x1e62d7,_0x531277){const _0x2bb9df=_0x206d80;throw new Error(_0x2bb9df(0x1da));}[_0x206d80(0x1d8)](_0xb7703a){return new Promise((_0x1f95e7,_0x25a255)=>{const _0x1fe55f=_0x1407;this[_0x1fe55f(0x19d)]=new WebSocket$1(this[_0x1fe55f(0x1e9)]),this[_0x1fe55f(0x19d)]['on'](_0x1fe55f(0x1ed),_0x3d7a9d=>{}),this[_0x1fe55f(0x19d)]['onopen']=()=>{const _0x2daac6=_0x1fe55f;this['isConnected']=!![],this[_0x2daac6(0x1a1)]=0x0,this[_0x2daac6(0x1e7)][_0x2daac6(0x1ca)]['bind'](this['logger'])(_0x2daac6(0x1af)+this[_0x2daac6(0x1e9)]),_0xb7703a(),_0x1f95e7();},this[_0x1fe55f(0x19d)][_0x1fe55f(0x1f0)]=_0x398b0b=>{const _0x5af8d7=_0x1fe55f;_0x25a255(new Error(''+_0x398b0b[_0x5af8d7(0x1bc)]));},this[_0x1fe55f(0x19d)][_0x1fe55f(0x19f)]=_0x27fc80=>{const _0x593a01=_0x1fe55f;this[_0x593a01(0x1eb)](_0x27fc80[_0x593a01(0x1b6)])[_0x593a01(0x1ae)]()[_0x593a01(0x1ba)]();},this[_0x1fe55f(0x19d)][_0x1fe55f(0x1de)]=()=>{const _0xbb3d4d=_0x1fe55f;this[_0xbb3d4d(0x1d5)]=![],this[_0xbb3d4d(0x1c7)](_0xb7703a);};});}[_0x206d80(0x1c7)](_0xdc28cc){const _0x128462=_0x206d80;try{this[_0x128462(0x1a1)]{const _0x2d8c1b=_0x128462;this[_0x2d8c1b(0x1d8)](_0xdc28cc)['catch'](_0x1febdc=>{const _0x3e2345=_0x2d8c1b;this['logger'][_0x3e2345(0x1ed)]['bind'](this[_0x3e2345(0x1e7)])(_0x3e2345(0x1a3)+_0x1febdc[_0x3e2345(0x1bc)]);});},0x1388*this['reconnectAttempts'])):this['logger'][_0x128462(0x1ed)]['bind'](this[_0x128462(0x1e7)])(_0x128462(0x1ab)+this[_0x128462(0x1e9)]+_0x128462(0x1f2));}catch(_0x381c60){this['logger']['error'][_0x128462(0x1d4)](this[_0x128462(0x1e7)])('[Core]\x20[Packet\x20Server]\x20重连时出错:\x20'+_0x381c60['message']);}}async[_0x206d80(0x1b5)](_0x4d9a93,_0x15f194,_0x546227){const _0x272baf=_0x206d80;if(!this['isConnected']||!this[_0x272baf(0x19d)])throw new Error(_0x272baf(0x1ce));const _0x32f4c9={'action':_0x272baf(0x1b5),'pid':_0x4d9a93,'recv':_0x15f194,'send':_0x546227};this[_0x272baf(0x19d)][_0x272baf(0x1c4)](JSON[_0x272baf(0x19b)](_0x32f4c9));}async[_0x206d80(0x1eb)](_0x535151){const _0x3a5bb7=_0x206d80;try{const _0x31de25=JSON[_0x3a5bb7(0x1db)](_0x535151[_0x3a5bb7(0x1f3)]()),_0x17eb74=_0x31de25['trace_id_md5'],_0x8a129e=_0x31de25?.['type']??_0x3a5bb7(0x1b5),_0xf457c9=this['cb'][_0x3a5bb7(0x1a8)](_0x17eb74+_0x8a129e);_0xf457c9&&await _0xf457c9(_0x31de25[_0x3a5bb7(0x1b6)]);}catch(_0x157fd0){this[_0x3a5bb7(0x1e7)]['error']['bind'](this[_0x3a5bb7(0x1e7)])(_0x3a5bb7(0x1d2)+_0x157fd0);}}}class NativePacketClient extends PacketClient{[_0x206d80(0x1d1)]=[_0x206d80(0x1f1),'linux.x64','linux.arm64',_0x206d80(0x1cc)];[_0x206d80(0x1b8)]={'exports':{}};[_0x206d80(0x1ad)]=new LRUCache(0x1f4);constructor(_0x487191){super(_0x487191);}get[_0x206d80(0x1b0)](){const _0x48a4cf=_0x206d80;return this[_0x48a4cf(0x1bb)];}[_0x206d80(0x1b3)](){const _0x3ad80c=_0x206d80;console['log'](_0x3ad80c(0x1ec));const _0x39d159=process[_0x3ad80c(0x1a4)]+'.'+process[_0x3ad80c(0x1d3)];if(!this[_0x3ad80c(0x1d1)][_0x3ad80c(0x1cd)](_0x39d159))return console[_0x3ad80c(0x1ca)](_0x3ad80c(0x1f7)+_0x39d159),![];if(!_0x3e7a5a[_0x3ad80c(0x19c)](moehooPathOri))return console[_0x3ad80c(0x1ca)](_0x3ad80c(0x1f4)+moehooPath),![];return !![];}async[_0x206d80(0x1b5)](_0x3f2f5e,_0x27a887,_0x4f293c){const _0x4743f8=_0x206d80;console[_0x4743f8(0x1ca)](_0x4743f8(0x1e8),moehooPathOri),console['log'](_0x4743f8(0x1f9),moehooPath);if(_0x3e7a5a['existsSync'](moehooPathOri)&&!_0x3e7a5a['existsSync'](moehooPath))try{_0x3e7a5a[_0x4743f8(0x1a5)](moehooPathOri,moehooPath);}catch(_0x6d7228){console[_0x4743f8(0x1ca)](_0x4743f8(0x1d6),_0x6d7228);}try{process[_0x4743f8(0x1c3)](this[_0x4743f8(0x1b8)],moehooPath,constants$1[_0x4743f8(0x1c3)]['RTLD_LAZY']),console[_0x4743f8(0x1ca)](_0x4743f8(0x1dd)+moehooPath+_0x4743f8(0x1ea));}catch(_0xee5027){console[_0x4743f8(0x1ed)](_0x4743f8(0x1dd)+moehooPath+_0x4743f8(0x1a0),_0xee5027);}try{console['log'](_0x4743f8(0x1b2),this[_0x4743f8(0x1b8)]),console[_0x4743f8(0x1ca)]('[NTQQPacketApi]\x20MoeHoo\x20init\x20hook\x20send\x20addr\x20'+_0x4f293c+_0x4743f8(0x1e1)+_0x27a887),this[_0x4743f8(0x1b8)][_0x4743f8(0x1f8)][_0x4743f8(0x1a2)]?.(_0x4f293c,_0x27a887,(_0x230e79,_0x127d9f,_0xccdced,_0x631559,_0x279b89)=>{const _0x2304d9=_0x4743f8,_0x135e34=createHash$1('md5')[_0x2304d9(0x1bd)](Buffer[_0x2304d9(0x1f5)](_0x279b89,_0x2304d9(0x1a7)))['digest'](_0x2304d9(0x1a7));_0x230e79===0x0&&this['cb'][_0x2304d9(0x1a8)](_0x135e34+'recv')&&this[_0x2304d9(0x1ad)][_0x2304d9(0x1e6)](_0x631559,_0x135e34);if(_0x230e79===0x1&&this[_0x2304d9(0x1ad)][_0x2304d9(0x1a8)](_0x631559)){const _0x1a873c=this[_0x2304d9(0x1ad)][_0x2304d9(0x1a8)](_0x631559),_0x555de8=this['cb']['get'](_0x1a873c+_0x2304d9(0x1c2));_0x555de8?.({'seq':_0x631559,'cmd':_0xccdced,'hex_data':_0x279b89});}}),console[_0x4743f8(0x1c5)]('[NTQQPacketApi]\x20MoeHoo\x20init\x20complete');}catch(_0x296b48){console[_0x4743f8(0x1ed)](_0x4743f8(0x1d7),_0x296b48);}this['isAvailable']=!![];}[_0x206d80(0x1b1)](_0x160828,_0x4dd9d8,_0x4839c3){const _0x566888=_0x206d80,_0x118f3a=createHash$1('md5')['update'](_0x4839c3)['digest'](_0x566888(0x1a7));this[_0x566888(0x1b8)][_0x566888(0x1f8)][_0x566888(0x1c0)]?.(_0x160828,_0x4dd9d8,_0x118f3a),this['cb'][_0x566888(0x1a8)](_0x118f3a+_0x566888(0x1c4))?.({'seq':0x0,'cmd':_0x160828,'hex_data':''});}['connect'](_0x2aad05){const _0x232a2a=_0x206d80;return _0x2aad05(),Promise[_0x232a2a(0x1f6)]();}} + +/** + * Get the type of a JSON value. + * Distinguishes between array, null and object. + */ +function typeofJsonValue(value) { + let t = typeof value; + if (t == "object") { + if (Array.isArray(value)) + return "array"; + if (value === null) + return "null"; + } + return t; +} +/** + * Is this a JSON object (instead of an array or null)? + */ +function isJsonObject(value) { + return value !== null && typeof value == "object" && !Array.isArray(value); +} + +// lookup table from base64 character to byte +let encTable = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split(''); +// lookup table from base64 character *code* to byte because lookup by number is fast +let decTable = []; +for (let i = 0; i < encTable.length; i++) + decTable[encTable[i].charCodeAt(0)] = i; +// support base64url variants +decTable["-".charCodeAt(0)] = encTable.indexOf("+"); +decTable["_".charCodeAt(0)] = encTable.indexOf("/"); +/** + * Decodes a base64 string to a byte array. + * + * - ignores white-space, including line breaks and tabs + * - allows inner padding (can decode concatenated base64 strings) + * - does not require padding + * - understands base64url encoding: + * "-" instead of "+", + * "_" instead of "/", + * no padding + */ +function base64decode(base64Str) { + // estimate byte size, not accounting for inner padding and whitespace + let es = base64Str.length * 3 / 4; + // if (es % 3 !== 0) + // throw new Error('invalid base64 string'); + if (base64Str[base64Str.length - 2] == '=') + es -= 2; + else if (base64Str[base64Str.length - 1] == '=') + es -= 1; + let bytes = new Uint8Array(es), bytePos = 0, // position in byte array + groupPos = 0, // position in base64 group + b, // current byte + p = 0 // previous byte + ; + for (let i = 0; i < base64Str.length; i++) { + b = decTable[base64Str.charCodeAt(i)]; + if (b === undefined) { + // noinspection FallThroughInSwitchStatementJS + switch (base64Str[i]) { + case '=': + groupPos = 0; // reset state when padding found + case '\n': + case '\r': + case '\t': + case ' ': + continue; // skip white-space, and padding + default: + throw Error(`invalid base64 string.`); + } + } + switch (groupPos) { + case 0: + p = b; + groupPos = 1; + break; + case 1: + bytes[bytePos++] = p << 2 | (b & 48) >> 4; + p = b; + groupPos = 2; + break; + case 2: + bytes[bytePos++] = (p & 15) << 4 | (b & 60) >> 2; + p = b; + groupPos = 3; + break; + case 3: + bytes[bytePos++] = (p & 3) << 6 | b; + groupPos = 0; + break; + } + } + if (groupPos == 1) + throw Error(`invalid base64 string.`); + return bytes.subarray(0, bytePos); +} +/** + * Encodes a byte array to a base64 string. + * Adds padding at the end. + * Does not insert newlines. + */ +function base64encode(bytes) { + let base64 = '', groupPos = 0, // position in base64 group + b, // current byte + p = 0; // carry over from previous byte + for (let i = 0; i < bytes.length; i++) { + b = bytes[i]; + switch (groupPos) { + case 0: + base64 += encTable[b >> 2]; + p = (b & 3) << 4; + groupPos = 1; + break; + case 1: + base64 += encTable[p | b >> 4]; + p = (b & 15) << 2; + groupPos = 2; + break; + case 2: + base64 += encTable[p | b >> 6]; + base64 += encTable[b & 63]; + groupPos = 0; + break; + } + } + // padding required? + if (groupPos) { + base64 += encTable[p]; + base64 += '='; + if (groupPos == 1) + base64 += '='; + } + return base64; +} + +/** + * This handler implements the default behaviour for unknown fields. + * When reading data, unknown fields are stored on the message, in a + * symbol property. + * When writing data, the symbol property is queried and unknown fields + * are serialized into the output again. + */ +var UnknownFieldHandler; +(function (UnknownFieldHandler) { + /** + * The symbol used to store unknown fields for a message. + * The property must conform to `UnknownFieldContainer`. + */ + UnknownFieldHandler.symbol = Symbol.for("protobuf-ts/unknown"); + /** + * Store an unknown field during binary read directly on the message. + * This method is compatible with `BinaryReadOptions.readUnknownField`. + */ + UnknownFieldHandler.onRead = (typeName, message, fieldNo, wireType, data) => { + let container = is(message) ? message[UnknownFieldHandler.symbol] : message[UnknownFieldHandler.symbol] = []; + container.push({ no: fieldNo, wireType, data }); + }; + /** + * Write unknown fields stored for the message to the writer. + * This method is compatible with `BinaryWriteOptions.writeUnknownFields`. + */ + UnknownFieldHandler.onWrite = (typeName, message, writer) => { + for (let { no, wireType, data } of UnknownFieldHandler.list(message)) + writer.tag(no, wireType).raw(data); + }; + /** + * List unknown fields stored for the message. + * Note that there may be multiples fields with the same number. + */ + UnknownFieldHandler.list = (message, fieldNo) => { + if (is(message)) { + let all = message[UnknownFieldHandler.symbol]; + return fieldNo ? all.filter(uf => uf.no == fieldNo) : all; + } + return []; + }; + /** + * Returns the last unknown field by field number. + */ + UnknownFieldHandler.last = (message, fieldNo) => UnknownFieldHandler.list(message, fieldNo).slice(-1)[0]; + const is = (message) => message && Array.isArray(message[UnknownFieldHandler.symbol]); +})(UnknownFieldHandler || (UnknownFieldHandler = {})); +/** + * Protobuf binary format wire types. + * + * A wire type provides just enough information to find the length of the + * following value. + * + * See https://developers.google.com/protocol-buffers/docs/encoding#structure + */ +var WireType; +(function (WireType) { + /** + * Used for int32, int64, uint32, uint64, sint32, sint64, bool, enum + */ + WireType[WireType["Varint"] = 0] = "Varint"; + /** + * Used for fixed64, sfixed64, double. + * Always 8 bytes with little-endian byte order. + */ + WireType[WireType["Bit64"] = 1] = "Bit64"; + /** + * Used for string, bytes, embedded messages, packed repeated fields + * + * Only repeated numeric types (types which use the varint, 32-bit, + * or 64-bit wire types) can be packed. In proto3, such fields are + * packed by default. + */ + WireType[WireType["LengthDelimited"] = 2] = "LengthDelimited"; + /** + * Used for groups + * @deprecated + */ + WireType[WireType["StartGroup"] = 3] = "StartGroup"; + /** + * Used for groups + * @deprecated + */ + WireType[WireType["EndGroup"] = 4] = "EndGroup"; + /** + * Used for fixed32, sfixed32, float. + * Always 4 bytes with little-endian byte order. + */ + WireType[WireType["Bit32"] = 5] = "Bit32"; +})(WireType || (WireType = {})); + +// Copyright 2008 Google Inc. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Code generated by the Protocol Buffer compiler is owned by the owner +// of the input file used when generating it. This code is not +// standalone and requires a support library to be linked with it. This +// support library is itself covered by the above license. +/** + * Read a 64 bit varint as two JS numbers. + * + * Returns tuple: + * [0]: low bits + * [0]: high bits + * + * Copyright 2008 Google Inc. All rights reserved. + * + * See https://github.com/protocolbuffers/protobuf/blob/8a71927d74a4ce34efe2d8769fda198f52d20d12/js/experimental/runtime/kernel/buffer_decoder.js#L175 + */ +function varint64read() { + let lowBits = 0; + let highBits = 0; + for (let shift = 0; shift < 28; shift += 7) { + let b = this.buf[this.pos++]; + lowBits |= (b & 0x7F) << shift; + if ((b & 0x80) == 0) { + this.assertBounds(); + return [lowBits, highBits]; + } + } + let middleByte = this.buf[this.pos++]; + // last four bits of the first 32 bit number + lowBits |= (middleByte & 0x0F) << 28; + // 3 upper bits are part of the next 32 bit number + highBits = (middleByte & 0x70) >> 4; + if ((middleByte & 0x80) == 0) { + this.assertBounds(); + return [lowBits, highBits]; + } + for (let shift = 3; shift <= 31; shift += 7) { + let b = this.buf[this.pos++]; + highBits |= (b & 0x7F) << shift; + if ((b & 0x80) == 0) { + this.assertBounds(); + return [lowBits, highBits]; + } + } + throw new Error('invalid varint'); +} +/** + * Write a 64 bit varint, given as two JS numbers, to the given bytes array. + * + * Copyright 2008 Google Inc. All rights reserved. + * + * See https://github.com/protocolbuffers/protobuf/blob/8a71927d74a4ce34efe2d8769fda198f52d20d12/js/experimental/runtime/kernel/writer.js#L344 + */ +function varint64write(lo, hi, bytes) { + for (let i = 0; i < 28; i = i + 7) { + const shift = lo >>> i; + const hasNext = !((shift >>> 7) == 0 && hi == 0); + const byte = (hasNext ? shift | 0x80 : shift) & 0xFF; + bytes.push(byte); + if (!hasNext) { + return; + } + } + const splitBits = ((lo >>> 28) & 0x0F) | ((hi & 0x07) << 4); + const hasMoreBits = !((hi >> 3) == 0); + bytes.push((hasMoreBits ? splitBits | 0x80 : splitBits) & 0xFF); + if (!hasMoreBits) { + return; + } + for (let i = 3; i < 31; i = i + 7) { + const shift = hi >>> i; + const hasNext = !((shift >>> 7) == 0); + const byte = (hasNext ? shift | 0x80 : shift) & 0xFF; + bytes.push(byte); + if (!hasNext) { + return; + } + } + bytes.push((hi >>> 31) & 0x01); +} +// constants for binary math +const TWO_PWR_32_DBL$1 = (1 << 16) * (1 << 16); +/** + * Parse decimal string of 64 bit integer value as two JS numbers. + * + * Returns tuple: + * [0]: minus sign? + * [1]: low bits + * [2]: high bits + * + * Copyright 2008 Google Inc. + */ +function int64fromString(dec) { + // Check for minus sign. + let minus = dec[0] == '-'; + if (minus) + dec = dec.slice(1); + // Work 6 decimal digits at a time, acting like we're converting base 1e6 + // digits to binary. This is safe to do with floating point math because + // Number.isSafeInteger(ALL_32_BITS * 1e6) == true. + const base = 1e6; + let lowBits = 0; + let highBits = 0; + function add1e6digit(begin, end) { + // Note: Number('') is 0. + const digit1e6 = Number(dec.slice(begin, end)); + highBits *= base; + lowBits = lowBits * base + digit1e6; + // Carry bits from lowBits to highBits + if (lowBits >= TWO_PWR_32_DBL$1) { + highBits = highBits + ((lowBits / TWO_PWR_32_DBL$1) | 0); + lowBits = lowBits % TWO_PWR_32_DBL$1; + } + } + add1e6digit(-24, -18); + add1e6digit(-18, -12); + add1e6digit(-12, -6); + add1e6digit(-6); + return [minus, lowBits, highBits]; +} +/** + * Format 64 bit integer value (as two JS numbers) to decimal string. + * + * Copyright 2008 Google Inc. + */ +function int64toString(bitsLow, bitsHigh) { + // Skip the expensive conversion if the number is small enough to use the + // built-in conversions. + if ((bitsHigh >>> 0) <= 0x1FFFFF) { + return '' + (TWO_PWR_32_DBL$1 * bitsHigh + (bitsLow >>> 0)); + } + // What this code is doing is essentially converting the input number from + // base-2 to base-1e7, which allows us to represent the 64-bit range with + // only 3 (very large) digits. Those digits are then trivial to convert to + // a base-10 string. + // The magic numbers used here are - + // 2^24 = 16777216 = (1,6777216) in base-1e7. + // 2^48 = 281474976710656 = (2,8147497,6710656) in base-1e7. + // Split 32:32 representation into 16:24:24 representation so our + // intermediate digits don't overflow. + let low = bitsLow & 0xFFFFFF; + let mid = (((bitsLow >>> 24) | (bitsHigh << 8)) >>> 0) & 0xFFFFFF; + let high = (bitsHigh >> 16) & 0xFFFF; + // Assemble our three base-1e7 digits, ignoring carries. The maximum + // value in a digit at this step is representable as a 48-bit integer, which + // can be stored in a 64-bit floating point number. + let digitA = low + (mid * 6777216) + (high * 6710656); + let digitB = mid + (high * 8147497); + let digitC = (high * 2); + // Apply carries from A to B and from B to C. + let base = 10000000; + if (digitA >= base) { + digitB += Math.floor(digitA / base); + digitA %= base; + } + if (digitB >= base) { + digitC += Math.floor(digitB / base); + digitB %= base; + } + // Convert base-1e7 digits to base-10, with optional leading zeroes. + function decimalFrom1e7(digit1e7, needLeadingZeros) { + let partial = digit1e7 ? String(digit1e7) : ''; + if (needLeadingZeros) { + return '0000000'.slice(partial.length) + partial; + } + return partial; + } + return decimalFrom1e7(digitC, /*needLeadingZeros=*/ 0) + + decimalFrom1e7(digitB, /*needLeadingZeros=*/ digitC) + + // If the final 1e7 digit didn't need leading zeros, we would have + // returned via the trivial code path at the top. + decimalFrom1e7(digitA, /*needLeadingZeros=*/ 1); +} +/** + * Write a 32 bit varint, signed or unsigned. Same as `varint64write(0, value, bytes)` + * + * Copyright 2008 Google Inc. All rights reserved. + * + * See https://github.com/protocolbuffers/protobuf/blob/1b18833f4f2a2f681f4e4a25cdf3b0a43115ec26/js/binary/encoder.js#L144 + */ +function varint32write(value, bytes) { + if (value >= 0) { + // write value as varint 32 + while (value > 0x7f) { + bytes.push((value & 0x7f) | 0x80); + value = value >>> 7; + } + bytes.push(value); + } + else { + for (let i = 0; i < 9; i++) { + bytes.push(value & 127 | 128); + value = value >> 7; + } + bytes.push(1); + } +} +/** + * Read an unsigned 32 bit varint. + * + * See https://github.com/protocolbuffers/protobuf/blob/8a71927d74a4ce34efe2d8769fda198f52d20d12/js/experimental/runtime/kernel/buffer_decoder.js#L220 + */ +function varint32read() { + let b = this.buf[this.pos++]; + let result = b & 0x7F; + if ((b & 0x80) == 0) { + this.assertBounds(); + return result; + } + b = this.buf[this.pos++]; + result |= (b & 0x7F) << 7; + if ((b & 0x80) == 0) { + this.assertBounds(); + return result; + } + b = this.buf[this.pos++]; + result |= (b & 0x7F) << 14; + if ((b & 0x80) == 0) { + this.assertBounds(); + return result; + } + b = this.buf[this.pos++]; + result |= (b & 0x7F) << 21; + if ((b & 0x80) == 0) { + this.assertBounds(); + return result; + } + // Extract only last 4 bits + b = this.buf[this.pos++]; + result |= (b & 0x0F) << 28; + for (let readBytes = 5; ((b & 0x80) !== 0) && readBytes < 10; readBytes++) + b = this.buf[this.pos++]; + if ((b & 0x80) != 0) + throw new Error('invalid varint'); + this.assertBounds(); + // Result can have 32 bits, convert it to unsigned + return result >>> 0; +} + +let BI; +function detectBi() { + const dv = new DataView(new ArrayBuffer(8)); + const ok = globalThis.BigInt !== undefined + && typeof dv.getBigInt64 === "function" + && typeof dv.getBigUint64 === "function" + && typeof dv.setBigInt64 === "function" + && typeof dv.setBigUint64 === "function"; + BI = ok ? { + MIN: BigInt("-9223372036854775808"), + MAX: BigInt("9223372036854775807"), + UMIN: BigInt("0"), + UMAX: BigInt("18446744073709551615"), + C: BigInt, + V: dv, + } : undefined; +} +detectBi(); +function assertBi(bi) { + if (!bi) + throw new Error("BigInt unavailable, see https://github.com/timostamm/protobuf-ts/blob/v1.0.8/MANUAL.md#bigint-support"); +} +// used to validate from(string) input (when bigint is unavailable) +const RE_DECIMAL_STR = /^-?[0-9]+$/; +// constants for binary math +const TWO_PWR_32_DBL = 0x100000000; +const HALF_2_PWR_32 = 0x080000000; +// base class for PbLong and PbULong provides shared code +class SharedPbLong { + /** + * Create a new instance with the given bits. + */ + constructor(lo, hi) { + this.lo = lo | 0; + this.hi = hi | 0; + } + /** + * Is this instance equal to 0? + */ + isZero() { + return this.lo == 0 && this.hi == 0; + } + /** + * Convert to a native number. + */ + toNumber() { + let result = this.hi * TWO_PWR_32_DBL + (this.lo >>> 0); + if (!Number.isSafeInteger(result)) + throw new Error("cannot convert to safe number"); + return result; + } +} +/** + * 64-bit unsigned integer as two 32-bit values. + * Converts between `string`, `number` and `bigint` representations. + */ +class PbULong extends SharedPbLong { + /** + * Create instance from a `string`, `number` or `bigint`. + */ + static from(value) { + if (BI) + // noinspection FallThroughInSwitchStatementJS + switch (typeof value) { + case "string": + if (value == "0") + return this.ZERO; + if (value == "") + throw new Error('string is no integer'); + value = BI.C(value); + case "number": + if (value === 0) + return this.ZERO; + value = BI.C(value); + case "bigint": + if (!value) + return this.ZERO; + if (value < BI.UMIN) + throw new Error('signed value for ulong'); + if (value > BI.UMAX) + throw new Error('ulong too large'); + BI.V.setBigUint64(0, value, true); + return new PbULong(BI.V.getInt32(0, true), BI.V.getInt32(4, true)); + } + else + switch (typeof value) { + case "string": + if (value == "0") + return this.ZERO; + value = value.trim(); + if (!RE_DECIMAL_STR.test(value)) + throw new Error('string is no integer'); + let [minus, lo, hi] = int64fromString(value); + if (minus) + throw new Error('signed value for ulong'); + return new PbULong(lo, hi); + case "number": + if (value == 0) + return this.ZERO; + if (!Number.isSafeInteger(value)) + throw new Error('number is no integer'); + if (value < 0) + throw new Error('signed value for ulong'); + return new PbULong(value, value / TWO_PWR_32_DBL); + } + throw new Error('unknown value ' + typeof value); + } + /** + * Convert to decimal string. + */ + toString() { + return BI ? this.toBigInt().toString() : int64toString(this.lo, this.hi); + } + /** + * Convert to native bigint. + */ + toBigInt() { + assertBi(BI); + BI.V.setInt32(0, this.lo, true); + BI.V.setInt32(4, this.hi, true); + return BI.V.getBigUint64(0, true); + } +} +/** + * ulong 0 singleton. + */ +PbULong.ZERO = new PbULong(0, 0); +/** + * 64-bit signed integer as two 32-bit values. + * Converts between `string`, `number` and `bigint` representations. + */ +class PbLong extends SharedPbLong { + /** + * Create instance from a `string`, `number` or `bigint`. + */ + static from(value) { + if (BI) + // noinspection FallThroughInSwitchStatementJS + switch (typeof value) { + case "string": + if (value == "0") + return this.ZERO; + if (value == "") + throw new Error('string is no integer'); + value = BI.C(value); + case "number": + if (value === 0) + return this.ZERO; + value = BI.C(value); + case "bigint": + if (!value) + return this.ZERO; + if (value < BI.MIN) + throw new Error('signed long too small'); + if (value > BI.MAX) + throw new Error('signed long too large'); + BI.V.setBigInt64(0, value, true); + return new PbLong(BI.V.getInt32(0, true), BI.V.getInt32(4, true)); + } + else + switch (typeof value) { + case "string": + if (value == "0") + return this.ZERO; + value = value.trim(); + if (!RE_DECIMAL_STR.test(value)) + throw new Error('string is no integer'); + let [minus, lo, hi] = int64fromString(value); + if (minus) { + if (hi > HALF_2_PWR_32 || (hi == HALF_2_PWR_32 && lo != 0)) + throw new Error('signed long too small'); + } + else if (hi >= HALF_2_PWR_32) + throw new Error('signed long too large'); + let pbl = new PbLong(lo, hi); + return minus ? pbl.negate() : pbl; + case "number": + if (value == 0) + return this.ZERO; + if (!Number.isSafeInteger(value)) + throw new Error('number is no integer'); + return value > 0 + ? new PbLong(value, value / TWO_PWR_32_DBL) + : new PbLong(-value, -value / TWO_PWR_32_DBL).negate(); + } + throw new Error('unknown value ' + typeof value); + } + /** + * Do we have a minus sign? + */ + isNegative() { + return (this.hi & HALF_2_PWR_32) !== 0; + } + /** + * Negate two's complement. + * Invert all the bits and add one to the result. + */ + negate() { + let hi = ~this.hi, lo = this.lo; + if (lo) + lo = ~lo + 1; + else + hi += 1; + return new PbLong(lo, hi); + } + /** + * Convert to decimal string. + */ + toString() { + if (BI) + return this.toBigInt().toString(); + if (this.isNegative()) { + let n = this.negate(); + return '-' + int64toString(n.lo, n.hi); + } + return int64toString(this.lo, this.hi); + } + /** + * Convert to native bigint. + */ + toBigInt() { + assertBi(BI); + BI.V.setInt32(0, this.lo, true); + BI.V.setInt32(4, this.hi, true); + return BI.V.getBigInt64(0, true); + } +} +/** + * long 0 singleton. + */ +PbLong.ZERO = new PbLong(0, 0); + +const defaultsRead$1 = { + readUnknownField: true, + readerFactory: bytes => new BinaryReader(bytes), +}; +/** + * Make options for reading binary data form partial options. + */ +function binaryReadOptions(options) { + return options ? Object.assign(Object.assign({}, defaultsRead$1), options) : defaultsRead$1; +} +class BinaryReader { + constructor(buf, textDecoder) { + this.varint64 = varint64read; // dirty cast for `this` + /** + * Read a `uint32` field, an unsigned 32 bit varint. + */ + this.uint32 = varint32read; // dirty cast for `this` and access to protected `buf` + this.buf = buf; + this.len = buf.length; + this.pos = 0; + this.view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength); + this.textDecoder = textDecoder !== null && textDecoder !== void 0 ? textDecoder : new TextDecoder("utf-8", { + fatal: true, + ignoreBOM: true, + }); + } + /** + * Reads a tag - field number and wire type. + */ + tag() { + let tag = this.uint32(), fieldNo = tag >>> 3, wireType = tag & 7; + if (fieldNo <= 0 || wireType < 0 || wireType > 5) + throw new Error("illegal tag: field no " + fieldNo + " wire type " + wireType); + return [fieldNo, wireType]; + } + /** + * Skip one element on the wire and return the skipped data. + * Supports WireType.StartGroup since v2.0.0-alpha.23. + */ + skip(wireType) { + let start = this.pos; + // noinspection FallThroughInSwitchStatementJS + switch (wireType) { + case WireType.Varint: + while (this.buf[this.pos++] & 0x80) { + // ignore + } + break; + case WireType.Bit64: + this.pos += 4; + case WireType.Bit32: + this.pos += 4; + break; + case WireType.LengthDelimited: + let len = this.uint32(); + this.pos += len; + break; + case WireType.StartGroup: + // From descriptor.proto: Group type is deprecated, not supported in proto3. + // But we must still be able to parse and treat as unknown. + let t; + while ((t = this.tag()[1]) !== WireType.EndGroup) { + this.skip(t); + } + break; + default: + throw new Error("cant skip wire type " + wireType); + } + this.assertBounds(); + return this.buf.subarray(start, this.pos); + } + /** + * Throws error if position in byte array is out of range. + */ + assertBounds() { + if (this.pos > this.len) + throw new RangeError("premature EOF"); + } + /** + * Read a `int32` field, a signed 32 bit varint. + */ + int32() { + return this.uint32() | 0; + } + /** + * Read a `sint32` field, a signed, zigzag-encoded 32-bit varint. + */ + sint32() { + let zze = this.uint32(); + // decode zigzag + return (zze >>> 1) ^ -(zze & 1); + } + /** + * Read a `int64` field, a signed 64-bit varint. + */ + int64() { + return new PbLong(...this.varint64()); + } + /** + * Read a `uint64` field, an unsigned 64-bit varint. + */ + uint64() { + return new PbULong(...this.varint64()); + } + /** + * Read a `sint64` field, a signed, zig-zag-encoded 64-bit varint. + */ + sint64() { + let [lo, hi] = this.varint64(); + // decode zig zag + let s = -(lo & 1); + lo = ((lo >>> 1 | (hi & 1) << 31) ^ s); + hi = (hi >>> 1 ^ s); + return new PbLong(lo, hi); + } + /** + * Read a `bool` field, a variant. + */ + bool() { + let [lo, hi] = this.varint64(); + return lo !== 0 || hi !== 0; + } + /** + * Read a `fixed32` field, an unsigned, fixed-length 32-bit integer. + */ + fixed32() { + return this.view.getUint32((this.pos += 4) - 4, true); + } + /** + * Read a `sfixed32` field, a signed, fixed-length 32-bit integer. + */ + sfixed32() { + return this.view.getInt32((this.pos += 4) - 4, true); + } + /** + * Read a `fixed64` field, an unsigned, fixed-length 64 bit integer. + */ + fixed64() { + return new PbULong(this.sfixed32(), this.sfixed32()); + } + /** + * Read a `fixed64` field, a signed, fixed-length 64-bit integer. + */ + sfixed64() { + return new PbLong(this.sfixed32(), this.sfixed32()); + } + /** + * Read a `float` field, 32-bit floating point number. + */ + float() { + return this.view.getFloat32((this.pos += 4) - 4, true); + } + /** + * Read a `double` field, a 64-bit floating point number. + */ + double() { + return this.view.getFloat64((this.pos += 8) - 8, true); + } + /** + * Read a `bytes` field, length-delimited arbitrary data. + */ + bytes() { + let len = this.uint32(); + let start = this.pos; + this.pos += len; + this.assertBounds(); + return this.buf.subarray(start, start + len); + } + /** + * Read a `string` field, length-delimited data converted to UTF-8 text. + */ + string() { + return this.textDecoder.decode(this.bytes()); + } +} + +/** + * assert that condition is true or throw error (with message) + */ +function assert(condition, msg) { + if (!condition) { + throw new Error(msg); + } +} +const FLOAT32_MAX = 3.4028234663852886e+38, FLOAT32_MIN = -3.4028234663852886e+38, UINT32_MAX = 0xFFFFFFFF, INT32_MAX = 0X7FFFFFFF, INT32_MIN = -0X80000000; +function assertInt32(arg) { + if (typeof arg !== "number") + throw new Error('invalid int 32: ' + typeof arg); + if (!Number.isInteger(arg) || arg > INT32_MAX || arg < INT32_MIN) + throw new Error('invalid int 32: ' + arg); +} +function assertUInt32(arg) { + if (typeof arg !== "number") + throw new Error('invalid uint 32: ' + typeof arg); + if (!Number.isInteger(arg) || arg > UINT32_MAX || arg < 0) + throw new Error('invalid uint 32: ' + arg); +} +function assertFloat32(arg) { + if (typeof arg !== "number") + throw new Error('invalid float 32: ' + typeof arg); + if (!Number.isFinite(arg)) + return; + if (arg > FLOAT32_MAX || arg < FLOAT32_MIN) + throw new Error('invalid float 32: ' + arg); +} + +const defaultsWrite$1 = { + writeUnknownFields: true, + writerFactory: () => new BinaryWriter(), +}; +/** + * Make options for writing binary data form partial options. + */ +function binaryWriteOptions(options) { + return options ? Object.assign(Object.assign({}, defaultsWrite$1), options) : defaultsWrite$1; +} +class BinaryWriter { + constructor(textEncoder) { + /** + * Previous fork states. + */ + this.stack = []; + this.textEncoder = textEncoder !== null && textEncoder !== void 0 ? textEncoder : new TextEncoder(); + this.chunks = []; + this.buf = []; + } + /** + * Return all bytes written and reset this writer. + */ + finish() { + this.chunks.push(new Uint8Array(this.buf)); // flush the buffer + let len = 0; + for (let i = 0; i < this.chunks.length; i++) + len += this.chunks[i].length; + let bytes = new Uint8Array(len); + let offset = 0; + for (let i = 0; i < this.chunks.length; i++) { + bytes.set(this.chunks[i], offset); + offset += this.chunks[i].length; + } + this.chunks = []; + return bytes; + } + /** + * Start a new fork for length-delimited data like a message + * or a packed repeated field. + * + * Must be joined later with `join()`. + */ + fork() { + this.stack.push({ chunks: this.chunks, buf: this.buf }); + this.chunks = []; + this.buf = []; + return this; + } + /** + * Join the last fork. Write its length and bytes, then + * return to the previous state. + */ + join() { + // get chunk of fork + let chunk = this.finish(); + // restore previous state + let prev = this.stack.pop(); + if (!prev) + throw new Error('invalid state, fork stack empty'); + this.chunks = prev.chunks; + this.buf = prev.buf; + // write length of chunk as varint + this.uint32(chunk.byteLength); + return this.raw(chunk); + } + /** + * Writes a tag (field number and wire type). + * + * Equivalent to `uint32( (fieldNo << 3 | type) >>> 0 )`. + * + * Generated code should compute the tag ahead of time and call `uint32()`. + */ + tag(fieldNo, type) { + return this.uint32((fieldNo << 3 | type) >>> 0); + } + /** + * Write a chunk of raw bytes. + */ + raw(chunk) { + if (this.buf.length) { + this.chunks.push(new Uint8Array(this.buf)); + this.buf = []; + } + this.chunks.push(chunk); + return this; + } + /** + * Write a `uint32` value, an unsigned 32 bit varint. + */ + uint32(value) { + assertUInt32(value); + // write value as varint 32, inlined for speed + while (value > 0x7f) { + this.buf.push((value & 0x7f) | 0x80); + value = value >>> 7; + } + this.buf.push(value); + return this; + } + /** + * Write a `int32` value, a signed 32 bit varint. + */ + int32(value) { + assertInt32(value); + varint32write(value, this.buf); + return this; + } + /** + * Write a `bool` value, a variant. + */ + bool(value) { + this.buf.push(value ? 1 : 0); + return this; + } + /** + * Write a `bytes` value, length-delimited arbitrary data. + */ + bytes(value) { + this.uint32(value.byteLength); // write length of chunk as varint + return this.raw(value); + } + /** + * Write a `string` value, length-delimited data converted to UTF-8 text. + */ + string(value) { + let chunk = this.textEncoder.encode(value); + this.uint32(chunk.byteLength); // write length of chunk as varint + return this.raw(chunk); + } + /** + * Write a `float` value, 32-bit floating point number. + */ + float(value) { + assertFloat32(value); + let chunk = new Uint8Array(4); + new DataView(chunk.buffer).setFloat32(0, value, true); + return this.raw(chunk); + } + /** + * Write a `double` value, a 64-bit floating point number. + */ + double(value) { + let chunk = new Uint8Array(8); + new DataView(chunk.buffer).setFloat64(0, value, true); + return this.raw(chunk); + } + /** + * Write a `fixed32` value, an unsigned, fixed-length 32-bit integer. + */ + fixed32(value) { + assertUInt32(value); + let chunk = new Uint8Array(4); + new DataView(chunk.buffer).setUint32(0, value, true); + return this.raw(chunk); + } + /** + * Write a `sfixed32` value, a signed, fixed-length 32-bit integer. + */ + sfixed32(value) { + assertInt32(value); + let chunk = new Uint8Array(4); + new DataView(chunk.buffer).setInt32(0, value, true); + return this.raw(chunk); + } + /** + * Write a `sint32` value, a signed, zigzag-encoded 32-bit varint. + */ + sint32(value) { + assertInt32(value); + // zigzag encode + value = ((value << 1) ^ (value >> 31)) >>> 0; + varint32write(value, this.buf); + return this; + } + /** + * Write a `fixed64` value, a signed, fixed-length 64-bit integer. + */ + sfixed64(value) { + let chunk = new Uint8Array(8); + let view = new DataView(chunk.buffer); + let long = PbLong.from(value); + view.setInt32(0, long.lo, true); + view.setInt32(4, long.hi, true); + return this.raw(chunk); + } + /** + * Write a `fixed64` value, an unsigned, fixed-length 64 bit integer. + */ + fixed64(value) { + let chunk = new Uint8Array(8); + let view = new DataView(chunk.buffer); + let long = PbULong.from(value); + view.setInt32(0, long.lo, true); + view.setInt32(4, long.hi, true); + return this.raw(chunk); + } + /** + * Write a `int64` value, a signed 64-bit varint. + */ + int64(value) { + let long = PbLong.from(value); + varint64write(long.lo, long.hi, this.buf); + return this; + } + /** + * Write a `sint64` value, a signed, zig-zag-encoded 64-bit varint. + */ + sint64(value) { + let long = PbLong.from(value), + // zigzag encode + sign = long.hi >> 31, lo = (long.lo << 1) ^ sign, hi = ((long.hi << 1) | (long.lo >>> 31)) ^ sign; + varint64write(lo, hi, this.buf); + return this; + } + /** + * Write a `uint64` value, an unsigned 64-bit varint. + */ + uint64(value) { + let long = PbULong.from(value); + varint64write(long.lo, long.hi, this.buf); + return this; + } +} + +const defaultsWrite = { + emitDefaultValues: false, + enumAsInteger: false, + useProtoFieldName: false, + prettySpaces: 0, +}, defaultsRead = { + ignoreUnknownFields: false, +}; +/** + * Make options for reading JSON data from partial options. + */ +function jsonReadOptions(options) { + return options ? Object.assign(Object.assign({}, defaultsRead), options) : defaultsRead; +} +/** + * Make options for writing JSON data from partial options. + */ +function jsonWriteOptions(options) { + return options ? Object.assign(Object.assign({}, defaultsWrite), options) : defaultsWrite; +} + +/** + * The symbol used as a key on message objects to store the message type. + * + * Note that this is an experimental feature - it is here to stay, but + * implementation details may change without notice. + */ +const MESSAGE_TYPE = Symbol.for("protobuf-ts/message-type"); + +/** + * Converts snake_case to lowerCamelCase. + * + * Should behave like protoc: + * https://github.com/protocolbuffers/protobuf/blob/e8ae137c96444ea313485ed1118c5e43b2099cf1/src/google/protobuf/compiler/java/java_helpers.cc#L118 + */ +function lowerCamelCase(snakeCase) { + let capNext = false; + const sb = []; + for (let i = 0; i < snakeCase.length; i++) { + let next = snakeCase.charAt(i); + if (next == '_') { + capNext = true; + } + else if (/\d/.test(next)) { + sb.push(next); + capNext = true; + } + else if (capNext) { + sb.push(next.toUpperCase()); + capNext = false; + } + else if (i == 0) { + sb.push(next.toLowerCase()); + } + else { + sb.push(next); + } + } + return sb.join(''); +} + +/** + * Scalar value types. This is a subset of field types declared by protobuf + * enum google.protobuf.FieldDescriptorProto.Type The types GROUP and MESSAGE + * are omitted, but the numerical values are identical. + */ +var ScalarType; +(function (ScalarType) { + // 0 is reserved for errors. + // Order is weird for historical reasons. + ScalarType[ScalarType["DOUBLE"] = 1] = "DOUBLE"; + ScalarType[ScalarType["FLOAT"] = 2] = "FLOAT"; + // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if + // negative values are likely. + ScalarType[ScalarType["INT64"] = 3] = "INT64"; + ScalarType[ScalarType["UINT64"] = 4] = "UINT64"; + // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if + // negative values are likely. + ScalarType[ScalarType["INT32"] = 5] = "INT32"; + ScalarType[ScalarType["FIXED64"] = 6] = "FIXED64"; + ScalarType[ScalarType["FIXED32"] = 7] = "FIXED32"; + ScalarType[ScalarType["BOOL"] = 8] = "BOOL"; + ScalarType[ScalarType["STRING"] = 9] = "STRING"; + // Tag-delimited aggregate. + // Group type is deprecated and not supported in proto3. However, Proto3 + // implementations should still be able to parse the group wire format and + // treat group fields as unknown fields. + // TYPE_GROUP = 10, + // TYPE_MESSAGE = 11, // Length-delimited aggregate. + // New in version 2. + ScalarType[ScalarType["BYTES"] = 12] = "BYTES"; + ScalarType[ScalarType["UINT32"] = 13] = "UINT32"; + // TYPE_ENUM = 14, + ScalarType[ScalarType["SFIXED32"] = 15] = "SFIXED32"; + ScalarType[ScalarType["SFIXED64"] = 16] = "SFIXED64"; + ScalarType[ScalarType["SINT32"] = 17] = "SINT32"; + ScalarType[ScalarType["SINT64"] = 18] = "SINT64"; +})(ScalarType || (ScalarType = {})); +/** + * JavaScript representation of 64 bit integral types. Equivalent to the + * field option "jstype". + * + * By default, protobuf-ts represents 64 bit types as `bigint`. + * + * You can change the default behaviour by enabling the plugin parameter + * `long_type_string`, which will represent 64 bit types as `string`. + * + * Alternatively, you can change the behaviour for individual fields + * with the field option "jstype": + * + * ```protobuf + * uint64 my_field = 1 [jstype = JS_STRING]; + * uint64 other_field = 2 [jstype = JS_NUMBER]; + * ``` + */ +var LongType; +(function (LongType) { + /** + * Use JavaScript `bigint`. + * + * Field option `[jstype = JS_NORMAL]`. + */ + LongType[LongType["BIGINT"] = 0] = "BIGINT"; + /** + * Use JavaScript `string`. + * + * Field option `[jstype = JS_STRING]`. + */ + LongType[LongType["STRING"] = 1] = "STRING"; + /** + * Use JavaScript `number`. + * + * Large values will loose precision. + * + * Field option `[jstype = JS_NUMBER]`. + */ + LongType[LongType["NUMBER"] = 2] = "NUMBER"; +})(LongType || (LongType = {})); +/** + * Protobuf 2.1.0 introduced packed repeated fields. + * Setting the field option `[packed = true]` enables packing. + * + * In proto3, all repeated fields are packed by default. + * Setting the field option `[packed = false]` disables packing. + * + * Packed repeated fields are encoded with a single tag, + * then a length-delimiter, then the element values. + * + * Unpacked repeated fields are encoded with a tag and + * value for each element. + * + * `bytes` and `string` cannot be packed. + */ +var RepeatType; +(function (RepeatType) { + /** + * The field is not repeated. + */ + RepeatType[RepeatType["NO"] = 0] = "NO"; + /** + * The field is repeated and should be packed. + * Invalid for `bytes` and `string`, they cannot be packed. + */ + RepeatType[RepeatType["PACKED"] = 1] = "PACKED"; + /** + * The field is repeated but should not be packed. + * The only valid repeat type for repeated `bytes` and `string`. + */ + RepeatType[RepeatType["UNPACKED"] = 2] = "UNPACKED"; +})(RepeatType || (RepeatType = {})); +/** + * Turns PartialFieldInfo into FieldInfo. + */ +function normalizeFieldInfo(field) { + var _a, _b, _c, _d; + field.localName = (_a = field.localName) !== null && _a !== void 0 ? _a : lowerCamelCase(field.name); + field.jsonName = (_b = field.jsonName) !== null && _b !== void 0 ? _b : lowerCamelCase(field.name); + field.repeat = (_c = field.repeat) !== null && _c !== void 0 ? _c : RepeatType.NO; + field.opt = (_d = field.opt) !== null && _d !== void 0 ? _d : (field.repeat ? false : field.oneof ? false : field.kind == "message"); + return field; +} + +/** + * Is the given value a valid oneof group? + * + * We represent protobuf `oneof` as algebraic data types (ADT) in generated + * code. But when working with messages of unknown type, the ADT does not + * help us. + * + * This type guard checks if the given object adheres to the ADT rules, which + * are as follows: + * + * 1) Must be an object. + * + * 2) Must have a "oneofKind" discriminator property. + * + * 3) If "oneofKind" is `undefined`, no member field is selected. The object + * must not have any other properties. + * + * 4) If "oneofKind" is a `string`, the member field with this name is + * selected. + * + * 5) If a member field is selected, the object must have a second property + * with this name. The property must not be `undefined`. + * + * 6) No extra properties are allowed. The object has either one property + * (no selection) or two properties (selection). + * + */ +function isOneofGroup(any) { + if (typeof any != 'object' || any === null || !any.hasOwnProperty('oneofKind')) { + return false; + } + switch (typeof any.oneofKind) { + case "string": + if (any[any.oneofKind] === undefined) + return false; + return Object.keys(any).length == 2; + case "undefined": + return Object.keys(any).length == 1; + default: + return false; + } +} + +// noinspection JSMethodCanBeStatic +class ReflectionTypeCheck { + constructor(info) { + var _a; + this.fields = (_a = info.fields) !== null && _a !== void 0 ? _a : []; + } + prepare() { + if (this.data) + return; + const req = [], known = [], oneofs = []; + for (let field of this.fields) { + if (field.oneof) { + if (!oneofs.includes(field.oneof)) { + oneofs.push(field.oneof); + req.push(field.oneof); + known.push(field.oneof); + } + } + else { + known.push(field.localName); + switch (field.kind) { + case "scalar": + case "enum": + if (!field.opt || field.repeat) + req.push(field.localName); + break; + case "message": + if (field.repeat) + req.push(field.localName); + break; + case "map": + req.push(field.localName); + break; + } + } + } + this.data = { req, known, oneofs: Object.values(oneofs) }; + } + /** + * Is the argument a valid message as specified by the + * reflection information? + * + * Checks all field types recursively. The `depth` + * specifies how deep into the structure the check will be. + * + * With a depth of 0, only the presence of fields + * is checked. + * + * With a depth of 1 or more, the field types are checked. + * + * With a depth of 2 or more, the members of map, repeated + * and message fields are checked. + * + * Message fields will be checked recursively with depth - 1. + * + * The number of map entries / repeated values being checked + * is < depth. + */ + is(message, depth, allowExcessProperties = false) { + if (depth < 0) + return true; + if (message === null || message === undefined || typeof message != 'object') + return false; + this.prepare(); + let keys = Object.keys(message), data = this.data; + // if a required field is missing in arg, this cannot be a T + if (keys.length < data.req.length || data.req.some(n => !keys.includes(n))) + return false; + if (!allowExcessProperties) { + // if the arg contains a key we dont know, this is not a literal T + if (keys.some(k => !data.known.includes(k))) + return false; + } + // "With a depth of 0, only the presence and absence of fields is checked." + // "With a depth of 1 or more, the field types are checked." + if (depth < 1) { + return true; + } + // check oneof group + for (const name of data.oneofs) { + const group = message[name]; + if (!isOneofGroup(group)) + return false; + if (group.oneofKind === undefined) + continue; + const field = this.fields.find(f => f.localName === group.oneofKind); + if (!field) + return false; // we found no field, but have a kind, something is wrong + if (!this.field(group[group.oneofKind], field, allowExcessProperties, depth)) + return false; + } + // check types + for (const field of this.fields) { + if (field.oneof !== undefined) + continue; + if (!this.field(message[field.localName], field, allowExcessProperties, depth)) + return false; + } + return true; + } + field(arg, field, allowExcessProperties, depth) { + let repeated = field.repeat; + switch (field.kind) { + case "scalar": + if (arg === undefined) + return field.opt; + if (repeated) + return this.scalars(arg, field.T, depth, field.L); + return this.scalar(arg, field.T, field.L); + case "enum": + if (arg === undefined) + return field.opt; + if (repeated) + return this.scalars(arg, ScalarType.INT32, depth); + return this.scalar(arg, ScalarType.INT32); + case "message": + if (arg === undefined) + return true; + if (repeated) + return this.messages(arg, field.T(), allowExcessProperties, depth); + return this.message(arg, field.T(), allowExcessProperties, depth); + case "map": + if (typeof arg != 'object' || arg === null) + return false; + if (depth < 2) + return true; + if (!this.mapKeys(arg, field.K, depth)) + return false; + switch (field.V.kind) { + case "scalar": + return this.scalars(Object.values(arg), field.V.T, depth, field.V.L); + case "enum": + return this.scalars(Object.values(arg), ScalarType.INT32, depth); + case "message": + return this.messages(Object.values(arg), field.V.T(), allowExcessProperties, depth); + } + break; + } + return true; + } + message(arg, type, allowExcessProperties, depth) { + if (allowExcessProperties) { + return type.isAssignable(arg, depth); + } + return type.is(arg, depth); + } + messages(arg, type, allowExcessProperties, depth) { + if (!Array.isArray(arg)) + return false; + if (depth < 2) + return true; + if (allowExcessProperties) { + for (let i = 0; i < arg.length && i < depth; i++) + if (!type.isAssignable(arg[i], depth - 1)) + return false; + } + else { + for (let i = 0; i < arg.length && i < depth; i++) + if (!type.is(arg[i], depth - 1)) + return false; + } + return true; + } + scalar(arg, type, longType) { + let argType = typeof arg; + switch (type) { + case ScalarType.UINT64: + case ScalarType.FIXED64: + case ScalarType.INT64: + case ScalarType.SFIXED64: + case ScalarType.SINT64: + switch (longType) { + case LongType.BIGINT: + return argType == "bigint"; + case LongType.NUMBER: + return argType == "number" && !isNaN(arg); + default: + return argType == "string"; + } + case ScalarType.BOOL: + return argType == 'boolean'; + case ScalarType.STRING: + return argType == 'string'; + case ScalarType.BYTES: + return arg instanceof Uint8Array; + case ScalarType.DOUBLE: + case ScalarType.FLOAT: + return argType == 'number' && !isNaN(arg); + default: + // case ScalarType.UINT32: + // case ScalarType.FIXED32: + // case ScalarType.INT32: + // case ScalarType.SINT32: + // case ScalarType.SFIXED32: + return argType == 'number' && Number.isInteger(arg); + } + } + scalars(arg, type, depth, longType) { + if (!Array.isArray(arg)) + return false; + if (depth < 2) + return true; + if (Array.isArray(arg)) + for (let i = 0; i < arg.length && i < depth; i++) + if (!this.scalar(arg[i], type, longType)) + return false; + return true; + } + mapKeys(map, type, depth) { + let keys = Object.keys(map); + switch (type) { + case ScalarType.INT32: + case ScalarType.FIXED32: + case ScalarType.SFIXED32: + case ScalarType.SINT32: + case ScalarType.UINT32: + return this.scalars(keys.slice(0, depth).map(k => parseInt(k)), type, depth); + case ScalarType.BOOL: + return this.scalars(keys.slice(0, depth).map(k => k == 'true' ? true : k == 'false' ? false : k), type, depth); + default: + return this.scalars(keys, type, depth, LongType.STRING); + } + } +} + +/** + * Utility method to convert a PbLong or PbUlong to a JavaScript + * representation during runtime. + * + * Works with generated field information, `undefined` is equivalent + * to `STRING`. + */ +function reflectionLongConvert(long, type) { + switch (type) { + case LongType.BIGINT: + return long.toBigInt(); + case LongType.NUMBER: + return long.toNumber(); + default: + // case undefined: + // case LongType.STRING: + return long.toString(); + } +} + +/** + * Reads proto3 messages in canonical JSON format using reflection information. + * + * https://developers.google.com/protocol-buffers/docs/proto3#json + */ +class ReflectionJsonReader { + constructor(info) { + this.info = info; + } + prepare() { + var _a; + if (this.fMap === undefined) { + this.fMap = {}; + const fieldsInput = (_a = this.info.fields) !== null && _a !== void 0 ? _a : []; + for (const field of fieldsInput) { + this.fMap[field.name] = field; + this.fMap[field.jsonName] = field; + this.fMap[field.localName] = field; + } + } + } + // Cannot parse JSON for #. + assert(condition, fieldName, jsonValue) { + if (!condition) { + let what = typeofJsonValue(jsonValue); + if (what == "number" || what == "boolean") + what = jsonValue.toString(); + throw new Error(`Cannot parse JSON ${what} for ${this.info.typeName}#${fieldName}`); + } + } + /** + * Reads a message from canonical JSON format into the target message. + * + * Repeated fields are appended. Map entries are added, overwriting + * existing keys. + * + * If a message field is already present, it will be merged with the + * new data. + */ + read(input, message, options) { + this.prepare(); + const oneofsHandled = []; + for (const [jsonKey, jsonValue] of Object.entries(input)) { + const field = this.fMap[jsonKey]; + if (!field) { + if (!options.ignoreUnknownFields) + throw new Error(`Found unknown field while reading ${this.info.typeName} from JSON format. JSON key: ${jsonKey}`); + continue; + } + const localName = field.localName; + // handle oneof ADT + let target; // this will be the target for the field value, whether it is member of a oneof or not + if (field.oneof) { + if (jsonValue === null && (field.kind !== 'enum' || field.T()[0] !== 'google.protobuf.NullValue')) { + continue; + } + // since json objects are unordered by specification, it is not possible to take the last of multiple oneofs + if (oneofsHandled.includes(field.oneof)) + throw new Error(`Multiple members of the oneof group "${field.oneof}" of ${this.info.typeName} are present in JSON.`); + oneofsHandled.push(field.oneof); + target = message[field.oneof] = { + oneofKind: localName + }; + } + else { + target = message; + } + // we have handled oneof above. we just have read the value into `target`. + if (field.kind == 'map') { + if (jsonValue === null) { + continue; + } + // check input + this.assert(isJsonObject(jsonValue), field.name, jsonValue); + // our target to put map entries into + const fieldObj = target[localName]; + // read entries + for (const [jsonObjKey, jsonObjValue] of Object.entries(jsonValue)) { + this.assert(jsonObjValue !== null, field.name + " map value", null); + // read value + let val; + switch (field.V.kind) { + case "message": + val = field.V.T().internalJsonRead(jsonObjValue, options); + break; + case "enum": + val = this.enum(field.V.T(), jsonObjValue, field.name, options.ignoreUnknownFields); + if (val === false) + continue; + break; + case "scalar": + val = this.scalar(jsonObjValue, field.V.T, field.V.L, field.name); + break; + } + this.assert(val !== undefined, field.name + " map value", jsonObjValue); + // read key + let key = jsonObjKey; + if (field.K == ScalarType.BOOL) + key = key == "true" ? true : key == "false" ? false : key; + key = this.scalar(key, field.K, LongType.STRING, field.name).toString(); + fieldObj[key] = val; + } + } + else if (field.repeat) { + if (jsonValue === null) + continue; + // check input + this.assert(Array.isArray(jsonValue), field.name, jsonValue); + // our target to put array entries into + const fieldArr = target[localName]; + // read array entries + for (const jsonItem of jsonValue) { + this.assert(jsonItem !== null, field.name, null); + let val; + switch (field.kind) { + case "message": + val = field.T().internalJsonRead(jsonItem, options); + break; + case "enum": + val = this.enum(field.T(), jsonItem, field.name, options.ignoreUnknownFields); + if (val === false) + continue; + break; + case "scalar": + val = this.scalar(jsonItem, field.T, field.L, field.name); + break; + } + this.assert(val !== undefined, field.name, jsonValue); + fieldArr.push(val); + } + } + else { + switch (field.kind) { + case "message": + if (jsonValue === null && field.T().typeName != 'google.protobuf.Value') { + this.assert(field.oneof === undefined, field.name + " (oneof member)", null); + continue; + } + target[localName] = field.T().internalJsonRead(jsonValue, options, target[localName]); + break; + case "enum": + let val = this.enum(field.T(), jsonValue, field.name, options.ignoreUnknownFields); + if (val === false) + continue; + target[localName] = val; + break; + case "scalar": + target[localName] = this.scalar(jsonValue, field.T, field.L, field.name); + break; + } + } + } + } + /** + * Returns `false` for unrecognized string representations. + * + * google.protobuf.NullValue accepts only JSON `null` (or the old `"NULL_VALUE"`). + */ + enum(type, json, fieldName, ignoreUnknownFields) { + if (type[0] == 'google.protobuf.NullValue') + assert(json === null || json === "NULL_VALUE", `Unable to parse field ${this.info.typeName}#${fieldName}, enum ${type[0]} only accepts null.`); + if (json === null) + // we require 0 to be default value for all enums + return 0; + switch (typeof json) { + case "number": + assert(Number.isInteger(json), `Unable to parse field ${this.info.typeName}#${fieldName}, enum can only be integral number, got ${json}.`); + return json; + case "string": + let localEnumName = json; + if (type[2] && json.substring(0, type[2].length) === type[2]) + // lookup without the shared prefix + localEnumName = json.substring(type[2].length); + let enumNumber = type[1][localEnumName]; + if (typeof enumNumber === 'undefined' && ignoreUnknownFields) { + return false; + } + assert(typeof enumNumber == "number", `Unable to parse field ${this.info.typeName}#${fieldName}, enum ${type[0]} has no value for "${json}".`); + return enumNumber; + } + assert(false, `Unable to parse field ${this.info.typeName}#${fieldName}, cannot parse enum value from ${typeof json}".`); + } + scalar(json, type, longType, fieldName) { + let e; + try { + switch (type) { + // float, double: JSON value will be a number or one of the special string values "NaN", "Infinity", and "-Infinity". + // Either numbers or strings are accepted. Exponent notation is also accepted. + case ScalarType.DOUBLE: + case ScalarType.FLOAT: + if (json === null) + return .0; + if (json === "NaN") + return Number.NaN; + if (json === "Infinity") + return Number.POSITIVE_INFINITY; + if (json === "-Infinity") + return Number.NEGATIVE_INFINITY; + if (json === "") { + e = "empty string"; + break; + } + if (typeof json == "string" && json.trim().length !== json.length) { + e = "extra whitespace"; + break; + } + if (typeof json != "string" && typeof json != "number") { + break; + } + let float = Number(json); + if (Number.isNaN(float)) { + e = "not a number"; + break; + } + if (!Number.isFinite(float)) { + // infinity and -infinity are handled by string representation above, so this is an error + e = "too large or small"; + break; + } + if (type == ScalarType.FLOAT) + assertFloat32(float); + return float; + // int32, fixed32, uint32: JSON value will be a decimal number. Either numbers or strings are accepted. + case ScalarType.INT32: + case ScalarType.FIXED32: + case ScalarType.SFIXED32: + case ScalarType.SINT32: + case ScalarType.UINT32: + if (json === null) + return 0; + let int32; + if (typeof json == "number") + int32 = json; + else if (json === "") + e = "empty string"; + else if (typeof json == "string") { + if (json.trim().length !== json.length) + e = "extra whitespace"; + else + int32 = Number(json); + } + if (int32 === undefined) + break; + if (type == ScalarType.UINT32) + assertUInt32(int32); + else + assertInt32(int32); + return int32; + // int64, fixed64, uint64: JSON value will be a decimal string. Either numbers or strings are accepted. + case ScalarType.INT64: + case ScalarType.SFIXED64: + case ScalarType.SINT64: + if (json === null) + return reflectionLongConvert(PbLong.ZERO, longType); + if (typeof json != "number" && typeof json != "string") + break; + return reflectionLongConvert(PbLong.from(json), longType); + case ScalarType.FIXED64: + case ScalarType.UINT64: + if (json === null) + return reflectionLongConvert(PbULong.ZERO, longType); + if (typeof json != "number" && typeof json != "string") + break; + return reflectionLongConvert(PbULong.from(json), longType); + // bool: + case ScalarType.BOOL: + if (json === null) + return false; + if (typeof json !== "boolean") + break; + return json; + // string: + case ScalarType.STRING: + if (json === null) + return ""; + if (typeof json !== "string") { + e = "extra whitespace"; + break; + } + try { + encodeURIComponent(json); + } + catch (e) { + e = "invalid UTF8"; + break; + } + return json; + // bytes: JSON value will be the data encoded as a string using standard base64 encoding with paddings. + // Either standard or URL-safe base64 encoding with/without paddings are accepted. + case ScalarType.BYTES: + if (json === null || json === "") + return new Uint8Array(0); + if (typeof json !== 'string') + break; + return base64decode(json); + } + } + catch (error) { + e = error.message; + } + this.assert(false, fieldName + (e ? " - " + e : ""), json); + } +} + +/** + * Writes proto3 messages in canonical JSON format using reflection + * information. + * + * https://developers.google.com/protocol-buffers/docs/proto3#json + */ +class ReflectionJsonWriter { + constructor(info) { + var _a; + this.fields = (_a = info.fields) !== null && _a !== void 0 ? _a : []; + } + /** + * Converts the message to a JSON object, based on the field descriptors. + */ + write(message, options) { + const json = {}, source = message; + for (const field of this.fields) { + // field is not part of a oneof, simply write as is + if (!field.oneof) { + let jsonValue = this.field(field, source[field.localName], options); + if (jsonValue !== undefined) + json[options.useProtoFieldName ? field.name : field.jsonName] = jsonValue; + continue; + } + // field is part of a oneof + const group = source[field.oneof]; + if (group.oneofKind !== field.localName) + continue; // not selected, skip + const opt = field.kind == 'scalar' || field.kind == 'enum' + ? Object.assign(Object.assign({}, options), { emitDefaultValues: true }) : options; + let jsonValue = this.field(field, group[field.localName], opt); + assert(jsonValue !== undefined); + json[options.useProtoFieldName ? field.name : field.jsonName] = jsonValue; + } + return json; + } + field(field, value, options) { + let jsonValue = undefined; + if (field.kind == 'map') { + assert(typeof value == "object" && value !== null); + const jsonObj = {}; + switch (field.V.kind) { + case "scalar": + for (const [entryKey, entryValue] of Object.entries(value)) { + const val = this.scalar(field.V.T, entryValue, field.name, false, true); + assert(val !== undefined); + jsonObj[entryKey.toString()] = val; // JSON standard allows only (double quoted) string as property key + } + break; + case "message": + const messageType = field.V.T(); + for (const [entryKey, entryValue] of Object.entries(value)) { + const val = this.message(messageType, entryValue, field.name, options); + assert(val !== undefined); + jsonObj[entryKey.toString()] = val; // JSON standard allows only (double quoted) string as property key + } + break; + case "enum": + const enumInfo = field.V.T(); + for (const [entryKey, entryValue] of Object.entries(value)) { + assert(entryValue === undefined || typeof entryValue == 'number'); + const val = this.enum(enumInfo, entryValue, field.name, false, true, options.enumAsInteger); + assert(val !== undefined); + jsonObj[entryKey.toString()] = val; // JSON standard allows only (double quoted) string as property key + } + break; + } + if (options.emitDefaultValues || Object.keys(jsonObj).length > 0) + jsonValue = jsonObj; + } + else if (field.repeat) { + assert(Array.isArray(value)); + const jsonArr = []; + switch (field.kind) { + case "scalar": + for (let i = 0; i < value.length; i++) { + const val = this.scalar(field.T, value[i], field.name, field.opt, true); + assert(val !== undefined); + jsonArr.push(val); + } + break; + case "enum": + const enumInfo = field.T(); + for (let i = 0; i < value.length; i++) { + assert(value[i] === undefined || typeof value[i] == 'number'); + const val = this.enum(enumInfo, value[i], field.name, field.opt, true, options.enumAsInteger); + assert(val !== undefined); + jsonArr.push(val); + } + break; + case "message": + const messageType = field.T(); + for (let i = 0; i < value.length; i++) { + const val = this.message(messageType, value[i], field.name, options); + assert(val !== undefined); + jsonArr.push(val); + } + break; + } + // add converted array to json output + if (options.emitDefaultValues || jsonArr.length > 0 || options.emitDefaultValues) + jsonValue = jsonArr; + } + else { + switch (field.kind) { + case "scalar": + jsonValue = this.scalar(field.T, value, field.name, field.opt, options.emitDefaultValues); + break; + case "enum": + jsonValue = this.enum(field.T(), value, field.name, field.opt, options.emitDefaultValues, options.enumAsInteger); + break; + case "message": + jsonValue = this.message(field.T(), value, field.name, options); + break; + } + } + return jsonValue; + } + /** + * Returns `null` as the default for google.protobuf.NullValue. + */ + enum(type, value, fieldName, optional, emitDefaultValues, enumAsInteger) { + if (type[0] == 'google.protobuf.NullValue') + return !emitDefaultValues && !optional ? undefined : null; + if (value === undefined) { + assert(optional); + return undefined; + } + if (value === 0 && !emitDefaultValues && !optional) + // we require 0 to be default value for all enums + return undefined; + assert(typeof value == 'number'); + assert(Number.isInteger(value)); + if (enumAsInteger || !type[1].hasOwnProperty(value)) + // if we don't now the enum value, just return the number + return value; + if (type[2]) + // restore the dropped prefix + return type[2] + type[1][value]; + return type[1][value]; + } + message(type, value, fieldName, options) { + if (value === undefined) + return options.emitDefaultValues ? null : undefined; + return type.internalJsonWrite(value, options); + } + scalar(type, value, fieldName, optional, emitDefaultValues) { + if (value === undefined) { + assert(optional); + return undefined; + } + const ed = emitDefaultValues || optional; + // noinspection FallThroughInSwitchStatementJS + switch (type) { + // int32, fixed32, uint32: JSON value will be a decimal number. Either numbers or strings are accepted. + case ScalarType.INT32: + case ScalarType.SFIXED32: + case ScalarType.SINT32: + if (value === 0) + return ed ? 0 : undefined; + assertInt32(value); + return value; + case ScalarType.FIXED32: + case ScalarType.UINT32: + if (value === 0) + return ed ? 0 : undefined; + assertUInt32(value); + return value; + // float, double: JSON value will be a number or one of the special string values "NaN", "Infinity", and "-Infinity". + // Either numbers or strings are accepted. Exponent notation is also accepted. + case ScalarType.FLOAT: + assertFloat32(value); + case ScalarType.DOUBLE: + if (value === 0) + return ed ? 0 : undefined; + assert(typeof value == 'number'); + if (Number.isNaN(value)) + return 'NaN'; + if (value === Number.POSITIVE_INFINITY) + return 'Infinity'; + if (value === Number.NEGATIVE_INFINITY) + return '-Infinity'; + return value; + // string: + case ScalarType.STRING: + if (value === "") + return ed ? '' : undefined; + assert(typeof value == 'string'); + return value; + // bool: + case ScalarType.BOOL: + if (value === false) + return ed ? false : undefined; + assert(typeof value == 'boolean'); + return value; + // JSON value will be a decimal string. Either numbers or strings are accepted. + case ScalarType.UINT64: + case ScalarType.FIXED64: + assert(typeof value == 'number' || typeof value == 'string' || typeof value == 'bigint'); + let ulong = PbULong.from(value); + if (ulong.isZero() && !ed) + return undefined; + return ulong.toString(); + // JSON value will be a decimal string. Either numbers or strings are accepted. + case ScalarType.INT64: + case ScalarType.SFIXED64: + case ScalarType.SINT64: + assert(typeof value == 'number' || typeof value == 'string' || typeof value == 'bigint'); + let long = PbLong.from(value); + if (long.isZero() && !ed) + return undefined; + return long.toString(); + // bytes: JSON value will be the data encoded as a string using standard base64 encoding with paddings. + // Either standard or URL-safe base64 encoding with/without paddings are accepted. + case ScalarType.BYTES: + assert(value instanceof Uint8Array); + if (!value.byteLength) + return ed ? "" : undefined; + return base64encode(value); + } + } +} + +/** + * Creates the default value for a scalar type. + */ +function reflectionScalarDefault(type, longType = LongType.STRING) { + switch (type) { + case ScalarType.BOOL: + return false; + case ScalarType.UINT64: + case ScalarType.FIXED64: + return reflectionLongConvert(PbULong.ZERO, longType); + case ScalarType.INT64: + case ScalarType.SFIXED64: + case ScalarType.SINT64: + return reflectionLongConvert(PbLong.ZERO, longType); + case ScalarType.DOUBLE: + case ScalarType.FLOAT: + return 0.0; + case ScalarType.BYTES: + return new Uint8Array(0); + case ScalarType.STRING: + return ""; + default: + // case ScalarType.INT32: + // case ScalarType.UINT32: + // case ScalarType.SINT32: + // case ScalarType.FIXED32: + // case ScalarType.SFIXED32: + return 0; + } +} + +/** + * Reads proto3 messages in binary format using reflection information. + * + * https://developers.google.com/protocol-buffers/docs/encoding + */ +class ReflectionBinaryReader { + constructor(info) { + this.info = info; + } + prepare() { + var _a; + if (!this.fieldNoToField) { + const fieldsInput = (_a = this.info.fields) !== null && _a !== void 0 ? _a : []; + this.fieldNoToField = new Map(fieldsInput.map(field => [field.no, field])); + } + } + /** + * Reads a message from binary format into the target message. + * + * Repeated fields are appended. Map entries are added, overwriting + * existing keys. + * + * If a message field is already present, it will be merged with the + * new data. + */ + read(reader, message, options, length) { + this.prepare(); + const end = length === undefined ? reader.len : reader.pos + length; + while (reader.pos < end) { + // read the tag and find the field + const [fieldNo, wireType] = reader.tag(), field = this.fieldNoToField.get(fieldNo); + if (!field) { + let u = options.readUnknownField; + if (u == "throw") + throw new Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.info.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.info.typeName, message, fieldNo, wireType, d); + continue; + } + // target object for the field we are reading + let target = message, repeated = field.repeat, localName = field.localName; + // if field is member of oneof ADT, use ADT as target + if (field.oneof) { + target = target[field.oneof]; + // if other oneof member selected, set new ADT + if (target.oneofKind !== localName) + target = message[field.oneof] = { + oneofKind: localName + }; + } + // we have handled oneof above, we just have read the value into `target[localName]` + switch (field.kind) { + case "scalar": + case "enum": + let T = field.kind == "enum" ? ScalarType.INT32 : field.T; + let L = field.kind == "scalar" ? field.L : undefined; + if (repeated) { + let arr = target[localName]; // safe to assume presence of array, oneof cannot contain repeated values + if (wireType == WireType.LengthDelimited && T != ScalarType.STRING && T != ScalarType.BYTES) { + let e = reader.uint32() + reader.pos; + while (reader.pos < e) + arr.push(this.scalar(reader, T, L)); + } + else + arr.push(this.scalar(reader, T, L)); + } + else + target[localName] = this.scalar(reader, T, L); + break; + case "message": + if (repeated) { + let arr = target[localName]; // safe to assume presence of array, oneof cannot contain repeated values + let msg = field.T().internalBinaryRead(reader, reader.uint32(), options); + arr.push(msg); + } + else + target[localName] = field.T().internalBinaryRead(reader, reader.uint32(), options, target[localName]); + break; + case "map": + let [mapKey, mapVal] = this.mapEntry(field, reader, options); + // safe to assume presence of map object, oneof cannot contain repeated values + target[localName][mapKey] = mapVal; + break; + } + } + } + /** + * Read a map field, expecting key field = 1, value field = 2 + */ + mapEntry(field, reader, options) { + let length = reader.uint32(); + let end = reader.pos + length; + let key = undefined; // javascript only allows number or string for object properties + let val = undefined; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case 1: + if (field.K == ScalarType.BOOL) + key = reader.bool().toString(); + else + // long types are read as string, number types are okay as number + key = this.scalar(reader, field.K, LongType.STRING); + break; + case 2: + switch (field.V.kind) { + case "scalar": + val = this.scalar(reader, field.V.T, field.V.L); + break; + case "enum": + val = reader.int32(); + break; + case "message": + val = field.V.T().internalBinaryRead(reader, reader.uint32(), options); + break; + } + break; + default: + throw new Error(`Unknown field ${fieldNo} (wire type ${wireType}) in map entry for ${this.info.typeName}#${field.name}`); + } + } + if (key === undefined) { + let keyRaw = reflectionScalarDefault(field.K); + key = field.K == ScalarType.BOOL ? keyRaw.toString() : keyRaw; + } + if (val === undefined) + switch (field.V.kind) { + case "scalar": + val = reflectionScalarDefault(field.V.T, field.V.L); + break; + case "enum": + val = 0; + break; + case "message": + val = field.V.T().create(); + break; + } + return [key, val]; + } + scalar(reader, type, longType) { + switch (type) { + case ScalarType.INT32: + return reader.int32(); + case ScalarType.STRING: + return reader.string(); + case ScalarType.BOOL: + return reader.bool(); + case ScalarType.DOUBLE: + return reader.double(); + case ScalarType.FLOAT: + return reader.float(); + case ScalarType.INT64: + return reflectionLongConvert(reader.int64(), longType); + case ScalarType.UINT64: + return reflectionLongConvert(reader.uint64(), longType); + case ScalarType.FIXED64: + return reflectionLongConvert(reader.fixed64(), longType); + case ScalarType.FIXED32: + return reader.fixed32(); + case ScalarType.BYTES: + return reader.bytes(); + case ScalarType.UINT32: + return reader.uint32(); + case ScalarType.SFIXED32: + return reader.sfixed32(); + case ScalarType.SFIXED64: + return reflectionLongConvert(reader.sfixed64(), longType); + case ScalarType.SINT32: + return reader.sint32(); + case ScalarType.SINT64: + return reflectionLongConvert(reader.sint64(), longType); + } + } +} + +/** + * Writes proto3 messages in binary format using reflection information. + * + * https://developers.google.com/protocol-buffers/docs/encoding + */ +class ReflectionBinaryWriter { + constructor(info) { + this.info = info; + } + prepare() { + if (!this.fields) { + const fieldsInput = this.info.fields ? this.info.fields.concat() : []; + this.fields = fieldsInput.sort((a, b) => a.no - b.no); + } + } + /** + * Writes the message to binary format. + */ + write(message, writer, options) { + this.prepare(); + for (const field of this.fields) { + let value, // this will be our field value, whether it is member of a oneof or not + emitDefault, // whether we emit the default value (only true for oneof members) + repeated = field.repeat, localName = field.localName; + // handle oneof ADT + if (field.oneof) { + const group = message[field.oneof]; + if (group.oneofKind !== localName) + continue; // if field is not selected, skip + value = group[localName]; + emitDefault = true; + } + else { + value = message[localName]; + emitDefault = false; + } + // we have handled oneof above. we just have to honor `emitDefault`. + switch (field.kind) { + case "scalar": + case "enum": + let T = field.kind == "enum" ? ScalarType.INT32 : field.T; + if (repeated) { + assert(Array.isArray(value)); + if (repeated == RepeatType.PACKED) + this.packed(writer, T, field.no, value); + else + for (const item of value) + this.scalar(writer, T, field.no, item, true); + } + else if (value === undefined) + assert(field.opt); + else + this.scalar(writer, T, field.no, value, emitDefault || field.opt); + break; + case "message": + if (repeated) { + assert(Array.isArray(value)); + for (const item of value) + this.message(writer, options, field.T(), field.no, item); + } + else { + this.message(writer, options, field.T(), field.no, value); + } + break; + case "map": + assert(typeof value == 'object' && value !== null); + for (const [key, val] of Object.entries(value)) + this.mapEntry(writer, options, field, key, val); + break; + } + } + let u = options.writeUnknownFields; + if (u !== false) + (u === true ? UnknownFieldHandler.onWrite : u)(this.info.typeName, message, writer); + } + mapEntry(writer, options, field, key, value) { + writer.tag(field.no, WireType.LengthDelimited); + writer.fork(); + // javascript only allows number or string for object properties + // we convert from our representation to the protobuf type + let keyValue = key; + switch (field.K) { + case ScalarType.INT32: + case ScalarType.FIXED32: + case ScalarType.UINT32: + case ScalarType.SFIXED32: + case ScalarType.SINT32: + keyValue = Number.parseInt(key); + break; + case ScalarType.BOOL: + assert(key == 'true' || key == 'false'); + keyValue = key == 'true'; + break; + } + // write key, expecting key field number = 1 + this.scalar(writer, field.K, 1, keyValue, true); + // write value, expecting value field number = 2 + switch (field.V.kind) { + case 'scalar': + this.scalar(writer, field.V.T, 2, value, true); + break; + case 'enum': + this.scalar(writer, ScalarType.INT32, 2, value, true); + break; + case 'message': + this.message(writer, options, field.V.T(), 2, value); + break; + } + writer.join(); + } + message(writer, options, handler, fieldNo, value) { + if (value === undefined) + return; + handler.internalBinaryWrite(value, writer.tag(fieldNo, WireType.LengthDelimited).fork(), options); + writer.join(); + } + /** + * Write a single scalar value. + */ + scalar(writer, type, fieldNo, value, emitDefault) { + let [wireType, method, isDefault] = this.scalarInfo(type, value); + if (!isDefault || emitDefault) { + writer.tag(fieldNo, wireType); + writer[method](value); + } + } + /** + * Write an array of scalar values in packed format. + */ + packed(writer, type, fieldNo, value) { + if (!value.length) + return; + assert(type !== ScalarType.BYTES && type !== ScalarType.STRING); + // write tag + writer.tag(fieldNo, WireType.LengthDelimited); + // begin length-delimited + writer.fork(); + // write values without tags + let [, method,] = this.scalarInfo(type); + for (let i = 0; i < value.length; i++) + writer[method](value[i]); + // end length delimited + writer.join(); + } + /** + * Get information for writing a scalar value. + * + * Returns tuple: + * [0]: appropriate WireType + * [1]: name of the appropriate method of IBinaryWriter + * [2]: whether the given value is a default value + * + * If argument `value` is omitted, [2] is always false. + */ + scalarInfo(type, value) { + let t = WireType.Varint; + let m; + let i = value === undefined; + let d = value === 0; + switch (type) { + case ScalarType.INT32: + m = "int32"; + break; + case ScalarType.STRING: + d = i || !value.length; + t = WireType.LengthDelimited; + m = "string"; + break; + case ScalarType.BOOL: + d = value === false; + m = "bool"; + break; + case ScalarType.UINT32: + m = "uint32"; + break; + case ScalarType.DOUBLE: + t = WireType.Bit64; + m = "double"; + break; + case ScalarType.FLOAT: + t = WireType.Bit32; + m = "float"; + break; + case ScalarType.INT64: + d = i || PbLong.from(value).isZero(); + m = "int64"; + break; + case ScalarType.UINT64: + d = i || PbULong.from(value).isZero(); + m = "uint64"; + break; + case ScalarType.FIXED64: + d = i || PbULong.from(value).isZero(); + t = WireType.Bit64; + m = "fixed64"; + break; + case ScalarType.BYTES: + d = i || !value.byteLength; + t = WireType.LengthDelimited; + m = "bytes"; + break; + case ScalarType.FIXED32: + t = WireType.Bit32; + m = "fixed32"; + break; + case ScalarType.SFIXED32: + t = WireType.Bit32; + m = "sfixed32"; + break; + case ScalarType.SFIXED64: + d = i || PbLong.from(value).isZero(); + t = WireType.Bit64; + m = "sfixed64"; + break; + case ScalarType.SINT32: + m = "sint32"; + break; + case ScalarType.SINT64: + d = i || PbLong.from(value).isZero(); + m = "sint64"; + break; + } + return [t, m, i || d]; + } +} + +/** + * Creates an instance of the generic message, using the field + * information. + */ +function reflectionCreate(type) { + /** + * This ternary can be removed in the next major version. + * The `Object.create()` code path utilizes a new `messagePrototype` + * property on the `IMessageType` which has this same `MESSAGE_TYPE` + * non-enumerable property on it. Doing it this way means that we only + * pay the cost of `Object.defineProperty()` once per `IMessageType` + * class of once per "instance". The falsy code path is only provided + * for backwards compatibility in cases where the runtime library is + * updated without also updating the generated code. + */ + const msg = type.messagePrototype + ? Object.create(type.messagePrototype) + : Object.defineProperty({}, MESSAGE_TYPE, { value: type }); + for (let field of type.fields) { + let name = field.localName; + if (field.opt) + continue; + if (field.oneof) + msg[field.oneof] = { oneofKind: undefined }; + else if (field.repeat) + msg[name] = []; + else + switch (field.kind) { + case "scalar": + msg[name] = reflectionScalarDefault(field.T, field.L); + break; + case "enum": + // we require 0 to be default value for all enums + msg[name] = 0; + break; + case "map": + msg[name] = {}; + break; + } + } + return msg; +} + +/** + * Copy partial data into the target message. + * + * If a singular scalar or enum field is present in the source, it + * replaces the field in the target. + * + * If a singular message field is present in the source, it is merged + * with the target field by calling mergePartial() of the responsible + * message type. + * + * If a repeated field is present in the source, its values replace + * all values in the target array, removing extraneous values. + * Repeated message fields are copied, not merged. + * + * If a map field is present in the source, entries are added to the + * target map, replacing entries with the same key. Entries that only + * exist in the target remain. Entries with message values are copied, + * not merged. + * + * Note that this function differs from protobuf merge semantics, + * which appends repeated fields. + */ +function reflectionMergePartial(info, target, source) { + let fieldValue, // the field value we are working with + input = source, output; // where we want our field value to go + for (let field of info.fields) { + let name = field.localName; + if (field.oneof) { + const group = input[field.oneof]; // this is the oneof`s group in the source + if ((group === null || group === void 0 ? void 0 : group.oneofKind) == undefined) { // the user is free to omit + continue; // we skip this field, and all other members too + } + fieldValue = group[name]; // our value comes from the the oneof group of the source + output = target[field.oneof]; // and our output is the oneof group of the target + output.oneofKind = group.oneofKind; // always update discriminator + if (fieldValue == undefined) { + delete output[name]; // remove any existing value + continue; // skip further work on field + } + } + else { + fieldValue = input[name]; // we are using the source directly + output = target; // we want our field value to go directly into the target + if (fieldValue == undefined) { + continue; // skip further work on field, existing value is used as is + } + } + if (field.repeat) + output[name].length = fieldValue.length; // resize target array to match source array + // now we just work with `fieldValue` and `output` to merge the value + switch (field.kind) { + case "scalar": + case "enum": + if (field.repeat) + for (let i = 0; i < fieldValue.length; i++) + output[name][i] = fieldValue[i]; // not a reference type + else + output[name] = fieldValue; // not a reference type + break; + case "message": + let T = field.T(); + if (field.repeat) + for (let i = 0; i < fieldValue.length; i++) + output[name][i] = T.create(fieldValue[i]); + else if (output[name] === undefined) + output[name] = T.create(fieldValue); // nothing to merge with + else + T.mergePartial(output[name], fieldValue); + break; + case "map": + // Map and repeated fields are simply overwritten, not appended or merged + switch (field.V.kind) { + case "scalar": + case "enum": + Object.assign(output[name], fieldValue); // elements are not reference types + break; + case "message": + let T = field.V.T(); + for (let k of Object.keys(fieldValue)) + output[name][k] = T.create(fieldValue[k]); + break; + } + break; + } + } +} + +/** + * Determines whether two message of the same type have the same field values. + * Checks for deep equality, traversing repeated fields, oneof groups, maps + * and messages recursively. + * Will also return true if both messages are `undefined`. + */ +function reflectionEquals(info, a, b) { + if (a === b) + return true; + if (!a || !b) + return false; + for (let field of info.fields) { + let localName = field.localName; + let val_a = field.oneof ? a[field.oneof][localName] : a[localName]; + let val_b = field.oneof ? b[field.oneof][localName] : b[localName]; + switch (field.kind) { + case "enum": + case "scalar": + let t = field.kind == "enum" ? ScalarType.INT32 : field.T; + if (!(field.repeat + ? repeatedPrimitiveEq(t, val_a, val_b) + : primitiveEq(t, val_a, val_b))) + return false; + break; + case "map": + if (!(field.V.kind == "message" + ? repeatedMsgEq(field.V.T(), objectValues(val_a), objectValues(val_b)) + : repeatedPrimitiveEq(field.V.kind == "enum" ? ScalarType.INT32 : field.V.T, objectValues(val_a), objectValues(val_b)))) + return false; + break; + case "message": + let T = field.T(); + if (!(field.repeat + ? repeatedMsgEq(T, val_a, val_b) + : T.equals(val_a, val_b))) + return false; + break; + } + } + return true; +} +const objectValues = Object.values; +function primitiveEq(type, a, b) { + if (a === b) + return true; + if (type !== ScalarType.BYTES) + return false; + let ba = a; + let bb = b; + if (ba.length !== bb.length) + return false; + for (let i = 0; i < ba.length; i++) + if (ba[i] != bb[i]) + return false; + return true; +} +function repeatedPrimitiveEq(type, a, b) { + if (a.length !== b.length) + return false; + for (let i = 0; i < a.length; i++) + if (!primitiveEq(type, a[i], b[i])) + return false; + return true; +} +function repeatedMsgEq(type, a, b) { + if (a.length !== b.length) + return false; + for (let i = 0; i < a.length; i++) + if (!type.equals(a[i], b[i])) + return false; + return true; +} + +const baseDescriptors = Object.getOwnPropertyDescriptors(Object.getPrototypeOf({})); +/** + * This standard message type provides reflection-based + * operations to work with a message. + */ +class MessageType { + constructor(name, fields, options) { + this.defaultCheckDepth = 16; + this.typeName = name; + this.fields = fields.map(normalizeFieldInfo); + this.options = options !== null && options !== void 0 ? options : {}; + this.messagePrototype = Object.create(null, Object.assign(Object.assign({}, baseDescriptors), { [MESSAGE_TYPE]: { value: this } })); + this.refTypeCheck = new ReflectionTypeCheck(this); + this.refJsonReader = new ReflectionJsonReader(this); + this.refJsonWriter = new ReflectionJsonWriter(this); + this.refBinReader = new ReflectionBinaryReader(this); + this.refBinWriter = new ReflectionBinaryWriter(this); + } + create(value) { + let message = reflectionCreate(this); + if (value !== undefined) { + reflectionMergePartial(this, message, value); + } + return message; + } + /** + * Clone the message. + * + * Unknown fields are discarded. + */ + clone(message) { + let copy = this.create(); + reflectionMergePartial(this, copy, message); + return copy; + } + /** + * Determines whether two message of the same type have the same field values. + * Checks for deep equality, traversing repeated fields, oneof groups, maps + * and messages recursively. + * Will also return true if both messages are `undefined`. + */ + equals(a, b) { + return reflectionEquals(this, a, b); + } + /** + * Is the given value assignable to our message type + * and contains no [excess properties](https://www.typescriptlang.org/docs/handbook/interfaces.html#excess-property-checks)? + */ + is(arg, depth = this.defaultCheckDepth) { + return this.refTypeCheck.is(arg, depth, false); + } + /** + * Is the given value assignable to our message type, + * regardless of [excess properties](https://www.typescriptlang.org/docs/handbook/interfaces.html#excess-property-checks)? + */ + isAssignable(arg, depth = this.defaultCheckDepth) { + return this.refTypeCheck.is(arg, depth, true); + } + /** + * Copy partial data into the target message. + */ + mergePartial(target, source) { + reflectionMergePartial(this, target, source); + } + /** + * Create a new message from binary format. + */ + fromBinary(data, options) { + let opt = binaryReadOptions(options); + return this.internalBinaryRead(opt.readerFactory(data), data.byteLength, opt); + } + /** + * Read a new message from a JSON value. + */ + fromJson(json, options) { + return this.internalJsonRead(json, jsonReadOptions(options)); + } + /** + * Read a new message from a JSON string. + * This is equivalent to `T.fromJson(JSON.parse(json))`. + */ + fromJsonString(json, options) { + let value = JSON.parse(json); + return this.fromJson(value, options); + } + /** + * Write the message to canonical JSON value. + */ + toJson(message, options) { + return this.internalJsonWrite(message, jsonWriteOptions(options)); + } + /** + * Convert the message to canonical JSON string. + * This is equivalent to `JSON.stringify(T.toJson(t))` + */ + toJsonString(message, options) { + var _a; + let value = this.toJson(message, options); + return JSON.stringify(value, null, (_a = options === null || options === void 0 ? void 0 : options.prettySpaces) !== null && _a !== void 0 ? _a : 0); + } + /** + * Write the message to binary format. + */ + toBinary(message, options) { + let opt = binaryWriteOptions(options); + return this.internalBinaryWrite(message, opt.writerFactory(), opt).finish(); + } + /** + * This is an internal method. If you just want to read a message from + * JSON, use `fromJson()` or `fromJsonString()`. + * + * Reads JSON value and merges the fields into the target + * according to protobuf rules. If the target is omitted, + * a new instance is created first. + */ + internalJsonRead(json, options, target) { + if (json !== null && typeof json == "object" && !Array.isArray(json)) { + let message = target !== null && target !== void 0 ? target : this.create(); + this.refJsonReader.read(json, message, options); + return message; + } + throw new Error(`Unable to parse message ${this.typeName} from JSON ${typeofJsonValue(json)}.`); + } + /** + * This is an internal method. If you just want to write a message + * to JSON, use `toJson()` or `toJsonString(). + * + * Writes JSON value and returns it. + */ + internalJsonWrite(message, options) { + return this.refJsonWriter.write(message, options); + } + /** + * This is an internal method. If you just want to write a message + * in binary format, use `toBinary()`. + * + * Serializes the message in binary format and appends it to the given + * writer. Returns passed writer. + */ + internalBinaryWrite(message, writer, options) { + this.refBinWriter.write(message, writer, options); + return writer; + } + /** + * This is an internal method. If you just want to read a message from + * binary data, use `fromBinary()`. + * + * Reads data from binary format and merges the fields into + * the target according to protobuf rules. If the target is + * omitted, a new instance is created first. + */ + internalBinaryRead(reader, length, options, target) { + let message = target !== null && target !== void 0 ? target : this.create(); + this.refBinReader.read(reader, message, options, length); + return message; + } +} + +const _0x2a8541=_0x528d;(function(_0x226253,_0x53a2f7){const _0x3b33be=_0x528d,_0x45587d=_0x226253();while(!![]){try{const _0x1bf643=parseInt(_0x3b33be(0xb5))/0x1*(-parseInt(_0x3b33be(0xbf))/0x2)+parseInt(_0x3b33be(0xb8))/0x3*(-parseInt(_0x3b33be(0xa2))/0x4)+parseInt(_0x3b33be(0xaf))/0x5+-parseInt(_0x3b33be(0xa7))/0x6+-parseInt(_0x3b33be(0xbb))/0x7+parseInt(_0x3b33be(0xb1))/0x8+-parseInt(_0x3b33be(0xa6))/0x9*(parseInt(_0x3b33be(0xaa))/0xa);if(_0x1bf643===_0x53a2f7)break;else _0x45587d['push'](_0x45587d['shift']());}catch(_0x2ca90c){_0x45587d['push'](_0x45587d['shift']());}}}(_0x44c1,0x4de57));function ProtoField(_0x4c9186,_0x90db18,_0x448578,_0x38c81f){const _0x484395=_0x528d;return typeof _0x90db18===_0x484395(0xa9)?{'kind':'message','no':_0x4c9186,'type':_0x90db18,'optional':_0x448578??![],'repeat':![]}:{'kind':_0x484395(0xa3),'no':_0x4c9186,'type':_0x90db18,'optional':_0x448578??![],'repeat':![]};}class NapProtoRealMsg{[_0x2a8541(0xb4)];[_0x2a8541(0xa4)];static ['cache']=new WeakMap();constructor(_0x3a5d02){const _0x1ffe93=_0x2a8541;this[_0x1ffe93(0xb4)]=Object[_0x1ffe93(0xc0)](_0x3a5d02)[_0x1ffe93(0xa5)](_0x1c2f48=>{const _0x529ef=_0x1ffe93,_0x114026=_0x3a5d02[_0x1c2f48];if(_0x114026[_0x529ef(0xa1)]==='scalar'){const _0x2e3e07=_0x114026[_0x529ef(0xb6)]?[ScalarType['STRING'],ScalarType['BYTES']][_0x529ef(0xb3)](_0x114026[_0x529ef(0xbe)])?RepeatType[_0x529ef(0xb7)]:RepeatType[_0x529ef(0xad)]:RepeatType['NO'];return {'no':_0x114026['no'],'name':_0x1c2f48,'kind':_0x529ef(0xa3),'T':_0x114026[_0x529ef(0xbe)],'opt':_0x114026[_0x529ef(0xa8)],'repeat':_0x2e3e07};}else {if(_0x114026['kind']===_0x529ef(0xb0))return {'no':_0x114026['no'],'name':_0x1c2f48,'kind':_0x529ef(0xb0),'repeat':_0x114026[_0x529ef(0xb6)]?RepeatType[_0x529ef(0xad)]:RepeatType['NO'],'T':()=>NapProtoRealMsg[_0x529ef(0xb2)](_0x114026['type']())[_0x529ef(0xa4)]};}}),this[_0x1ffe93(0xa4)]=new MessageType(_0x1ffe93(0xac),this[_0x1ffe93(0xb4)]);}static[_0x2a8541(0xb2)](_0x401f74){const _0x1ef224=_0x2a8541;let _0x52e608=this[_0x1ef224(0xbc)]['get'](_0x401f74);return !_0x52e608&&(_0x52e608=new NapProtoRealMsg(_0x401f74),this[_0x1ef224(0xbc)][_0x1ef224(0xb9)](_0x401f74,_0x52e608)),_0x52e608;}[_0x2a8541(0xba)](_0x10563b){const _0x50c7fe=_0x2a8541;return this[_0x50c7fe(0xa4)]['toBinary'](this['_proto_msg'][_0x50c7fe(0xa0)](_0x10563b));}[_0x2a8541(0xab)](_0x1c9858){const _0x1304c5=_0x2a8541;return this['_proto_msg'][_0x1304c5(0xae)](_0x1c9858);}}class NapProtoMsg{['realMsg'];constructor(_0x371ff9){const _0x2f530b=_0x2a8541;this[_0x2f530b(0xbd)]=NapProtoRealMsg[_0x2f530b(0xb2)](_0x371ff9);}['encode'](_0x5187ab){const _0x208548=_0x2a8541;return this[_0x208548(0xbd)][_0x208548(0xba)](_0x5187ab);}['decode'](_0x1f9be5){const _0x131426=_0x2a8541;return this[_0x131426(0xbd)]['decode'](_0x1f9be5);}}function _0x44c1(){const _0xfd001d=['keys','create','kind','83180zsiAVo','scalar','_proto_msg','map','4842evijcn','1259976vYyfnl','optional','function','580oqrAlT','decode','nya','PACKED','fromBinary','2776300BKqcSU','message','3953512QkNdky','getInstance','includes','_field','58057SqKAxa','repeat','UNPACKED','27DOsGOW','set','encode','488621iqfwcE','cache','realMsg','type','8DprcAx'];_0x44c1=function(){return _0xfd001d;};return _0x44c1();}function _0x528d(_0x350c97,_0x701d39){const _0x44c1d0=_0x44c1();return _0x528d=function(_0x528df8,_0x20e3b4){_0x528df8=_0x528df8-0xa0;let _0x10397a=_0x44c1d0[_0x528df8];return _0x10397a;},_0x528d(_0x350c97,_0x701d39);} + +const _0x52fe6a=_0x23ac;(function(_0x34490d,_0x36d86e){const _0x198560=_0x23ac,_0x4997e1=_0x34490d();while(!![]){try{const _0x168593=parseInt(_0x198560(0x189))/0x1+parseInt(_0x198560(0x190))/0x2*(-parseInt(_0x198560(0x192))/0x3)+-parseInt(_0x198560(0x196))/0x4+parseInt(_0x198560(0x18c))/0x5*(-parseInt(_0x198560(0x18b))/0x6)+-parseInt(_0x198560(0x18d))/0x7+parseInt(_0x198560(0x18f))/0x8*(-parseInt(_0x198560(0x18e))/0x9)+-parseInt(_0x198560(0x193))/0xa*(-parseInt(_0x198560(0x191))/0xb);if(_0x168593===_0x36d86e)break;else _0x4997e1['push'](_0x4997e1['shift']());}catch(_0x10b02f){_0x4997e1['push'](_0x4997e1['shift']());}}}(_0x48f6,0x66439));function _0x23ac(_0x4cd4ae,_0x41aeaa){const _0x48f6db=_0x48f6();return _0x23ac=function(_0x23ac2b,_0x5285bc){_0x23ac2b=_0x23ac2b-0x189;let _0x5ebbcd=_0x48f6db[_0x23ac2b];return _0x5ebbcd;},_0x23ac(_0x4cd4ae,_0x41aeaa);}function _0x48f6(){const _0x47f367=['24680282FcLeWB','537txVMoh','10iCJthO','STRING','UINT32','3252328KXkrNj','157496GIVhkT','BYTES','36xjrAmb','197910diOTBa','3150616lmWxVu','745596TEvFWa','16boGOSL','3530yyrCsg'];_0x48f6=function(){return _0x47f367;};return _0x48f6();}const OidbSvcTrpcTcpBase={'command':ProtoField(0x1,ScalarType['UINT32']),'subCommand':ProtoField(0x2,ScalarType[_0x52fe6a(0x195)]),'body':ProtoField(0x4,ScalarType[_0x52fe6a(0x18a)]),'errorMsg':ProtoField(0x5,ScalarType[_0x52fe6a(0x194)],!![]),'isReserved':ProtoField(0xc,ScalarType['UINT32'])};({'body':ProtoField(0x4,ScalarType['BYTES'])}); + +const _0x332f2e=_0x39c0;(function(_0x305be8,_0x570914){const _0x1e3dc2=_0x39c0,_0x57bbce=_0x305be8();while(!![]){try{const _0x30ea5d=-parseInt(_0x1e3dc2(0x142))/0x1*(parseInt(_0x1e3dc2(0x14a))/0x2)+-parseInt(_0x1e3dc2(0x149))/0x3*(-parseInt(_0x1e3dc2(0x147))/0x4)+-parseInt(_0x1e3dc2(0x141))/0x5+-parseInt(_0x1e3dc2(0x143))/0x6*(parseInt(_0x1e3dc2(0x148))/0x7)+-parseInt(_0x1e3dc2(0x144))/0x8*(-parseInt(_0x1e3dc2(0x14b))/0x9)+-parseInt(_0x1e3dc2(0x146))/0xa*(parseInt(_0x1e3dc2(0x140))/0xb)+parseInt(_0x1e3dc2(0x14c))/0xc;if(_0x30ea5d===_0x570914)break;else _0x57bbce['push'](_0x57bbce['shift']());}catch(_0x1f6b72){_0x57bbce['push'](_0x57bbce['shift']());}}}(_0x57c4,0xba5fa));function _0x39c0(_0x36dae1,_0x28d501){const _0x57c4cf=_0x57c4();return _0x39c0=function(_0x39c004,_0x463fae){_0x39c004=_0x39c004-0x140;let _0xc3b1e6=_0x57c4cf[_0x39c004];return _0xc3b1e6;},_0x39c0(_0x36dae1,_0x28d501);}const OidbSvcTrpcTcp0XED3_1={'uin':ProtoField(0x1,ScalarType['UINT32']),'groupUin':ProtoField(0x2,ScalarType[_0x332f2e(0x145)]),'friendUin':ProtoField(0x5,ScalarType[_0x332f2e(0x145)]),'ext':ProtoField(0x6,ScalarType[_0x332f2e(0x145)],!![])};function _0x57c4(){const _0x2d9821=['6339460xeWCBz','776859gLQCAn','1182SZBRFx','89832WBvSKx','UINT32','200jnyWrl','644IzwQQk','11865QqGIeO','23721VemlVH','2axwUOf','1125EfEcKM','5900400DKWepi','14465QBXMYk'];_0x57c4=function(){return _0x2d9821;};return _0x57c4();} + +const _0x2f7ecf=_0x2473;(function(_0x3d4d19,_0x273cce){const _0x33d6a5=_0x2473,_0x674719=_0x3d4d19();while(!![]){try{const _0x10d23d=-parseInt(_0x33d6a5(0x85))/0x1+-parseInt(_0x33d6a5(0x87))/0x2+-parseInt(_0x33d6a5(0x8e))/0x3+parseInt(_0x33d6a5(0x8b))/0x4*(-parseInt(_0x33d6a5(0x8d))/0x5)+-parseInt(_0x33d6a5(0x86))/0x6+-parseInt(_0x33d6a5(0x8c))/0x7*(parseInt(_0x33d6a5(0x88))/0x8)+parseInt(_0x33d6a5(0x84))/0x9*(parseInt(_0x33d6a5(0x8f))/0xa);if(_0x10d23d===_0x273cce)break;else _0x674719['push'](_0x674719['shift']());}catch(_0x53dd59){_0x674719['push'](_0x674719['shift']());}}}(_0x5c9c,0x19712));function _0x2473(_0x1c74e2,_0x4b9d87){const _0x5c9ca7=_0x5c9c();return _0x2473=function(_0x247342,_0x2705a7){_0x247342=_0x247342-0x84;let _0x1ffc28=_0x5c9ca7[_0x247342];return _0x1ffc28;},_0x2473(_0x1c74e2,_0x4b9d87);}const OidbSvcTrpcTcp0X8FC_2_Body={'targetUid':ProtoField(0x1,ScalarType['STRING']),'specialTitle':ProtoField(0x5,ScalarType[_0x2f7ecf(0x89)]),'expiredTime':ProtoField(0x6,ScalarType['SINT32']),'uinName':ProtoField(0x7,ScalarType['STRING']),'targetName':ProtoField(0x8,ScalarType[_0x2f7ecf(0x89)])};function _0x5c9c(){const _0xe2b4d0=['347592xgmfzP','84674NTQxTi','120JTHBbn','STRING','BYTES','376ljtcch','34930ZzHjrJ','7335wTqYiA','154842eZeiNx','30lhBDFb','1424223irbCrS','5900tJZCXw'];_0x5c9c=function(){return _0xe2b4d0;};return _0x5c9c();}const OidbSvcTrpcTcp0X8FC_2={'groupUin':ProtoField(0x1,ScalarType['UINT32']),'body':ProtoField(0x3,ScalarType[_0x2f7ecf(0x8a)])}; + +const _0xc1142e=_0x5aa8;function _0x36b7(){const _0xd82626=['2236vqnKJJ','3522BZZteT','3378ZQXylY','18391476EcUsSU','1ThyQzP','4352085soNbtL','STRING','6977710aAcPUX','18112140jDAVkg','11xYvJSf','21189vAGLqS','581458EsyohQ','8SfaMFv'];_0x36b7=function(){return _0xd82626;};return _0x36b7();}function _0x5aa8(_0x4d98fc,_0x47dab3){const _0x36b709=_0x36b7();return _0x5aa8=function(_0x5aa8d8,_0x4d65f7){_0x5aa8d8=_0x5aa8d8-0x9a;let _0x3df1b2=_0x36b709[_0x5aa8d8];return _0x3df1b2;},_0x5aa8(_0x4d98fc,_0x47dab3);}(function(_0x577f64,_0x5430ed){const _0x405b42=_0x5aa8,_0x28e4dd=_0x577f64();while(!![]){try{const _0x4d504e=parseInt(_0x405b42(0xa4))/0x1*(-parseInt(_0x405b42(0x9e))/0x2)+parseInt(_0x405b42(0xa2))/0x3*(parseInt(_0x405b42(0xa0))/0x4)+-parseInt(_0x405b42(0x9a))/0x5+-parseInt(_0x405b42(0xa1))/0x6*(-parseInt(_0x405b42(0x9d))/0x7)+-parseInt(_0x405b42(0x9f))/0x8*(-parseInt(_0x405b42(0xa5))/0x9)+parseInt(_0x405b42(0x9b))/0xa*(-parseInt(_0x405b42(0x9c))/0xb)+parseInt(_0x405b42(0xa3))/0xc;if(_0x4d504e===_0x5430ed)break;else _0x28e4dd['push'](_0x28e4dd['shift']());}catch(_0x47f2da){_0x28e4dd['push'](_0x28e4dd['shift']());}}}(_0x36b7,0xe1d3a));const OidbSvcTrpcTcp0XEB7_Body={'uin':ProtoField(0x1,ScalarType[_0xc1142e(0xa6)]),'groupUin':ProtoField(0x2,ScalarType[_0xc1142e(0xa6)]),'version':ProtoField(0x3,ScalarType[_0xc1142e(0xa6)])};const OidbSvcTrpcTcp0XEB7={'body':ProtoField(0x2,()=>OidbSvcTrpcTcp0XEB7_Body)}; + +const _0x39e037=_0x3a92;(function(_0x26c328,_0x238676){const _0x52c32f=_0x3a92,_0x45a1c7=_0x26c328();while(!![]){try{const _0x493678=parseInt(_0x52c32f(0x117))/0x1+-parseInt(_0x52c32f(0x116))/0x2+parseInt(_0x52c32f(0x10a))/0x3+parseInt(_0x52c32f(0x11d))/0x4+-parseInt(_0x52c32f(0x119))/0x5+-parseInt(_0x52c32f(0x10c))/0x6*(parseInt(_0x52c32f(0x112))/0x7)+-parseInt(_0x52c32f(0x10d))/0x8*(-parseInt(_0x52c32f(0x11a))/0x9);if(_0x493678===_0x238676)break;else _0x45a1c7['push'](_0x45a1c7['shift']());}catch(_0xb7d0e0){_0x45a1c7['push'](_0x45a1c7['shift']());}}}(_0x471d,0x71877));function _0x471d(){const _0x19407a=['531238YwWKRV','packetPacket','4590065KuroMx','9gxmHYE','packSetSpecialTittlePacket','from','991104GfcPpy','9.0.90','364557jIGWIc','packGroupSignReq','1434Fpidww','7369528bCamVh','client','OidbSvcTrpcTcp.0x','toUpperCase','hex','5747jIuyux','toString','encode','packOidbPacket','484954aMeKPT'];_0x471d=function(){return _0x19407a;};return _0x471d();}function _0x3a92(_0x45bfdb,_0x5b7654){const _0x471de4=_0x471d();return _0x3a92=function(_0x3a922f,_0x2b55a3){_0x3a922f=_0x3a922f-0x10a;let _0x439509=_0x471de4[_0x3a922f];return _0x439509;},_0x3a92(_0x45bfdb,_0x5b7654);}class PacketPacker{[_0x39e037(0x10e)];constructor(_0x23e876){const _0x1204f6=_0x39e037;this[_0x1204f6(0x10e)]=_0x23e876;}[_0x39e037(0x118)](_0x5633d4){const _0x3ea36e=_0x39e037;return Buffer[_0x3ea36e(0x11c)](_0x5633d4)[_0x3ea36e(0x113)](_0x3ea36e(0x111));}[_0x39e037(0x115)](_0xe9677b,_0xed9105,_0x4a583a,_0x10a678=!![],_0x520f8c=![]){const _0x3887d5=_0x39e037,_0x1bf2f1=new NapProtoMsg(OidbSvcTrpcTcpBase)[_0x3887d5(0x114)]({'command':_0xe9677b,'subCommand':_0xed9105,'body':_0x4a583a,'isReserved':_0x10a678?0x1:0x0});return {'cmd':_0x3887d5(0x10f)+_0xe9677b[_0x3887d5(0x113)](0x10)[_0x3887d5(0x110)]()+'_'+_0xed9105,'data':this[_0x3887d5(0x118)](_0x1bf2f1)};}['packPokePacket'](_0x160665,_0x505665){const _0x3f80c6=_0x39e037,_0x37f3d2=new NapProtoMsg(OidbSvcTrpcTcp0XED3_1)[_0x3f80c6(0x114)]({'uin':_0x160665,'groupUin':_0x505665,'friendUin':_0x505665??_0x160665,'ext':0x0});return this[_0x3f80c6(0x115)](0xed3,0x1,_0x37f3d2);}[_0x39e037(0x11b)](_0x30c69d,_0x271e09,_0x5d45b4){const _0x5b67f8=_0x39e037,_0x2fd368=new NapProtoMsg(OidbSvcTrpcTcp0X8FC_2_Body)[_0x5b67f8(0x114)]({'targetUid':_0x271e09,'specialTitle':_0x5d45b4,'expiredTime':-0x1,'uinName':_0x5d45b4}),_0x1600da=new NapProtoMsg(OidbSvcTrpcTcp0X8FC_2)[_0x5b67f8(0x114)]({'groupUin':+_0x30c69d,'body':_0x2fd368});return this['packOidbPacket'](0x8fc,0x2,_0x1600da,![],![]);}[_0x39e037(0x10b)](_0xb87d67,_0x5d95c1){const _0x2c7c4e=_0x39e037;return this[_0x2c7c4e(0x115)](0xeb7,0x1,new NapProtoMsg(OidbSvcTrpcTcp0XEB7)[_0x2c7c4e(0x114)]({'body':{'uin':_0xb87d67,'groupUin':_0x5d95c1,'version':_0x2c7c4e(0x11e)}}),![],![]);}} + +function _0xaf77(){const _0x1b0086=['1396566fvjGoS','client','error','score',',请检查配置文件!','14110qjRsSJ','12RlvvjC','packer','555lskxeU','3TJzBwT','frida','log','map','[Core]\x20[Packet]\x20未知的PacketBackend\x20','3713880kHIVim','11061611gUnGHO','1096734HbAHCX','[NTQQPacketApi]\x20client\x20初始化','constructor','auto','[Core]\x20[Packet]\x20无可用的后端,NapCat.Packet将不会加载!','judgeClient','name','21FtoRlH','check','[Core]\x20[Packet]\x20使用指定的\x20NativePacketClient\x20作为后端','230219WKmqzS','[Core]\x20[Packet]\x20使用指定的\x20FridaPacketClient\x20作为后端','filter','2439rAfbkw','newClient','sort','wrapperSession','11348eOqKSV','native'];_0xaf77=function(){return _0x1b0086;};return _0xaf77();}function _0x20e4(_0x596c91,_0x52d90a){const _0xaf7718=_0xaf77();return _0x20e4=function(_0x20e485,_0x2170bf){_0x20e485=_0x20e485-0x1a8;let _0x564f21=_0xaf7718[_0x20e485];return _0x564f21;},_0x20e4(_0x596c91,_0x52d90a);}const _0x4e8b38=_0x20e4;(function(_0x2ae657,_0x25e651){const _0x157fb8=_0x20e4,_0x471934=_0x2ae657();while(!![]){try{const _0x3a15a4=-parseInt(_0x157fb8(0x1b4))/0x1+-parseInt(_0x157fb8(0x1bd))/0x2*(-parseInt(_0x157fb8(0x1c6))/0x3)+-parseInt(_0x157fb8(0x1bb))/0x4*(-parseInt(_0x157fb8(0x1c5))/0x5)+-parseInt(_0x157fb8(0x1aa))/0x6*(parseInt(_0x157fb8(0x1b1))/0x7)+-parseInt(_0x157fb8(0x1a8))/0x8+-parseInt(_0x157fb8(0x1b7))/0x9*(parseInt(_0x157fb8(0x1c2))/0xa)+-parseInt(_0x157fb8(0x1a9))/0xb*(-parseInt(_0x157fb8(0x1c3))/0xc);if(_0x3a15a4===_0x25e651)break;else _0x471934['push'](_0x471934['shift']());}catch(_0x3964c){_0x471934['push'](_0x471934['shift']());}}}(_0xaf77,0x60175));const clientPriority={0xa:_0x4a056c=>new NativePacketClient(_0x4a056c),0x1:_0x463ee6=>new WSPacketClient(_0x463ee6)};class PacketSession{[_0x4e8b38(0x1be)];[_0x4e8b38(0x1c4)];[_0x4e8b38(0x1ba)];constructor(_0x3b049c){const _0xb74016=_0x4e8b38;this[_0xb74016(0x1ba)]=_0x3b049c,this[_0xb74016(0x1be)]=this[_0xb74016(0x1b8)](),this[_0xb74016(0x1c4)]=new PacketPacker(this[_0xb74016(0x1be)]);}[_0x4e8b38(0x1b8)](){const _0x32f233=_0x4e8b38;let _0x12c3cd=_0x32f233(0x1bc),_0xbacb6b;switch(_0x12c3cd){case'native':console[_0x32f233(0x1c8)](_0x32f233(0x1b3)),_0xbacb6b=new NativePacketClient(this['wrapperSession']),console[_0x32f233(0x1c8)](_0x32f233(0x1ab),_0xbacb6b);break;case _0x32f233(0x1c7):console['log'](_0x32f233(0x1b5)),_0xbacb6b=new WSPacketClient(this['wrapperSession']);break;case _0x32f233(0x1ad):case void 0x0:_0xbacb6b=this[_0x32f233(0x1af)]();break;default:console[_0x32f233(0x1bf)](_0x32f233(0x1ca)+_0x12c3cd+_0x32f233(0x1c1)),_0xbacb6b=null;}if(!(_0xbacb6b&&_0xbacb6b[_0x32f233(0x1b2)]()))throw new Error('[Core]\x20[Packet]\x20无可用的后端,NapCat.Packet将不会加载!');return _0xbacb6b;}['judgeClient'](){const _0x52e441=_0x4e8b38,_0x388005=Object['entries'](clientPriority)[_0x52e441(0x1c9)](([_0x350713,_0x5d9687])=>{const _0x5c9de9=_0x52e441,_0x14042e=_0x5d9687(this[_0x5c9de9(0x1ba)]),_0x2731fd=+_0x350713*+_0x14042e[_0x5c9de9(0x1b2)]();return {'client':_0x14042e,'score':_0x2731fd};})[_0x52e441(0x1b6)](({score:_0x143766})=>_0x143766>0x0)[_0x52e441(0x1b9)]((_0x32be26,_0xa8e36d)=>_0xa8e36d[_0x52e441(0x1c0)]-_0x32be26['score']),_0x2e4798=_0x388005[0x0]?.[_0x52e441(0x1be)];if(!_0x2e4798)throw new Error(_0x52e441(0x1ae));return console[_0x52e441(0x1c8)]('[Core]\x20[Packet]\x20自动选择\x20'+_0x2e4798[_0x52e441(0x1ac)][_0x52e441(0x1b0)]+'\x20作为后端'),_0x2e4798;}} + +const _0x391e5e = { + "3.2.12-28418-x64": { + recv: "A0723E0", + send: "A06EAE0" +}, + "9.9.15-28418-x64": { + recv: "37A9004", + send: "37A4BD0" +}, + "6.9.56-28418-x64": { + send: "4471360", + recv: "4473BCC" +}, + "6.9.56-28418-arm64": { + send: "3FBDBF8", + recv: "3FC0410" +}, + "9.9.15-28498-x64": { + recv: "37A9004", + send: "37A4BD0" +}, + "9.9.16-28788-x64": { + send: "38076D0", + recv: "380BB04" +}, + "3.2.13-28788-x64": { + send: "A0CEC20", + recv: "A0D2520" +}, + "3.2.13-28788-arm64": { + send: "6E91018", + recv: "6E94850" +}, + "9.9.16-28971-x64": { + send: "38079F0", + recv: "380BE24" +}, + "3.2.13-28971-x64": { + send: "A0CEF60", + recv: "A0D2860" +}, + "3.2.12-28971-arm64": { + send: "6E91318", + recv: "6E94B50" +}, + "6.9.58-28971-x64": { + send: "449ACA0", + recv: "449D50C" +}, + "6.9.58-28971-arm64": { + send: "3FE0DB0", + recv: "3FE35C8" +}, + "9.9.16-29271-x64": { + send: "3833510", + recv: "3837944" +}, + "3.2.13-29271-x64": { + send: "A11E680", + recv: "A121F80" +}, + "3.2.13-29271-arm64": { + send: "6ECA098", + recv: "6ECD8D0" +}, + "9.9.16-29456-x64": { + send: "3835CD0", + recv: "383A104" +}, + "3.2.13-29456-x64": { + send: "A11E820", + recv: "A122120" +}, + "3.2.13-29456-arm64": { + send: "6ECA130", + recv: "6ECD968" +}, + "6.9.59-29456-x64": { + send: "44C57A0", + recv: "44C800C" +}, + "6.9.59-29456-arm64": { + send: "4005FE8", + recv: "4008800" +} +}; + +const _0x57d360=_0x5e35;(function(_0x491e9f,_0x33ecd5){const _0x17606f=_0x5e35,_0x58eece=_0x491e9f();while(!![]){try{const _0x32c57f=parseInt(_0x17606f(0x1de))/0x1*(-parseInt(_0x17606f(0x1df))/0x2)+-parseInt(_0x17606f(0x1c1))/0x3+-parseInt(_0x17606f(0x1d6))/0x4*(-parseInt(_0x17606f(0x1c7))/0x5)+-parseInt(_0x17606f(0x1c4))/0x6*(parseInt(_0x17606f(0x1e0))/0x7)+-parseInt(_0x17606f(0x1cc))/0x8*(-parseInt(_0x17606f(0x1bc))/0x9)+-parseInt(_0x17606f(0x1d9))/0xa+parseInt(_0x17606f(0x1dc))/0xb;if(_0x32c57f===_0x33ecd5)break;else _0x58eece['push'](_0x58eece['shift']());}catch(_0x25aa29){_0x58eece['push'](_0x58eece['shift']());}}}(_0x22ff,0xeac0b));function getQQVersionConfigPath(_0xb98b5=''){const _0x2bc714=_0x5e35;let _0xae3f6a;if(_0x2cc836['platform']()===_0x2bc714(0x1c3))_0xae3f6a=_0x2fc670[_0x2bc714(0x1cb)](_0x2fc670['dirname'](_0xb98b5),'versions',_0x2bc714(0x1d5));else {if(_0x2cc836[_0x2bc714(0x1c8)]()===_0x2bc714(0x1d7)){const _0x25d84b=_0x2cc836[_0x2bc714(0x1cf)](),_0x51fb6d=_0x2fc670[_0x2bc714(0x1d1)](_0x25d84b,'./Library/Application\x20Support/QQ');_0xae3f6a=_0x2fc670[_0x2bc714(0x1d1)](_0x51fb6d,_0x2bc714(0x1d0));}else {const _0x3ba0d0=_0x2cc836[_0x2bc714(0x1cf)](),_0x45fde1=_0x2fc670[_0x2bc714(0x1d1)](_0x3ba0d0,_0x2bc714(0x1d8));_0xae3f6a=_0x2fc670['resolve'](_0x45fde1,_0x2bc714(0x1d0));}}if(typeof _0xae3f6a!==_0x2bc714(0x1c6))return void 0x0;!_0x5a9d71[_0x2bc714(0x1c9)](_0xae3f6a)&&(_0xae3f6a=_0x2fc670[_0x2bc714(0x1cb)](_0x2fc670[_0x2bc714(0x1da)](_0xb98b5),_0x2bc714(0x1d2)));if(!_0x5a9d71['existsSync'](_0xae3f6a))return void 0x0;return _0xae3f6a;}function getDefaultQQVersionConfigInfo(){const _0x2c4116=_0x5e35;if(_0x2cc836[_0x2c4116(0x1c8)]()==='linux')return {'baseVersion':_0x2c4116(0x1ce),'curVersion':_0x2c4116(0x1ce),'prevVersion':'','onErrorVersions':[],'buildId':'27254'};if(_0x2cc836['platform']()===_0x2c4116(0x1d7))return {'baseVersion':'6.9.53.28060','curVersion':_0x2c4116(0x1c0),'prevVersion':'','onErrorVersions':[],'buildId':'28060'};return {'baseVersion':_0x2c4116(0x1db),'curVersion':_0x2c4116(0x1db),'prevVersion':'','onErrorVersions':[],'buildId':_0x2c4116(0x1b4)};}function getQQPackageInfoPath(_0x3ff630='',_0x3b4bd2){const _0x3cf202=_0x5e35;let _0x1c2f43;if(_0x2cc836[_0x3cf202(0x1c8)]()===_0x3cf202(0x1d7))_0x1c2f43=_0x2fc670[_0x3cf202(0x1cb)](_0x2fc670[_0x3cf202(0x1da)](_0x3ff630),'..',_0x3cf202(0x1b6),'app',_0x3cf202(0x1d4));else _0x2cc836[_0x3cf202(0x1c8)]()===_0x3cf202(0x1be)?_0x1c2f43=_0x2fc670[_0x3cf202(0x1cb)](_0x2fc670[_0x3cf202(0x1da)](_0x3ff630),_0x3cf202(0x1bf)):_0x1c2f43=_0x2fc670[_0x3cf202(0x1cb)](_0x2fc670[_0x3cf202(0x1da)](_0x3ff630),_0x3cf202(0x1ba)+_0x3b4bd2+'/resources/app/package.json');return !_0x5a9d71[_0x3cf202(0x1c9)](_0x1c2f43)&&(_0x1c2f43=_0x2fc670[_0x3cf202(0x1cb)](_0x2fc670[_0x3cf202(0x1da)](_0x3ff630),_0x3cf202(0x1b9)+_0x3b4bd2+_0x3cf202(0x1c2))),_0x1c2f43;}class QQBasicInfoWrapper{[_0x57d360(0x1cd)];[_0x57d360(0x1dd)];[_0x57d360(0x1bd)];[_0x57d360(0x1ca)];[_0x57d360(0x1b8)];[_0x57d360(0x1b5)];constructor(){const _0x3b41f2=_0x57d360;this[_0x3b41f2(0x1cd)]=process[_0x3b41f2(0x1bb)],this[_0x3b41f2(0x1bd)]=getQQVersionConfigPath(this[_0x3b41f2(0x1cd)]),this[_0x3b41f2(0x1ca)]=!!this[_0x3b41f2(0x1bd)],this['QQVersionConfig']=this[_0x3b41f2(0x1ca)]?JSON['parse'](_0x5a9d71[_0x3b41f2(0x1d3)](this[_0x3b41f2(0x1bd)])[_0x3b41f2(0x1e2)]()):getDefaultQQVersionConfigInfo(),this[_0x3b41f2(0x1dd)]=getQQPackageInfoPath(this['QQMainPath'],this[_0x3b41f2(0x1b8)]?.[_0x3b41f2(0x1c5)]),this[_0x3b41f2(0x1b5)]=JSON['parse'](_0x5a9d71[_0x3b41f2(0x1d3)](this[_0x3b41f2(0x1dd)])[_0x3b41f2(0x1e2)]());}[_0x57d360(0x1b7)](){const _0x34f291=_0x57d360,_0x5c31d9=this[_0x34f291(0x1ca)]?this['QQVersionConfig']?.[_0x34f291(0x1c5)]:this[_0x34f291(0x1b5)]?.['version'];if(!_0x5c31d9)throw new Error(_0x34f291(0x1e1));return _0x5c31d9;}}function _0x5e35(_0x3c1d15,_0x211536){const _0x22ff9c=_0x22ff();return _0x5e35=function(_0x5e35f7,_0xfe043){_0x5e35f7=_0x5e35f7-0x1b4;let _0x5132ae=_0x22ff9c[_0x5e35f7];return _0x5132ae;},_0x5e35(_0x3c1d15,_0x211536);}function _0x22ff(){const _0x4a19dc=['11854920jmIJOj','dirname','9.9.15-28131','31392691iYOsid','QQPackageInfoPath','4069ANqtdS','446MDjbPK','7jvlQTr','QQ版本获取失败','toString','28131','QQPackageInfo','Resources','getFullQQVersion','QQVersionConfig','./resources/app/versions/','./versions/','execPath','81aBlLuB','QQVersionConfigPath','linux','./resources/app/package.json','6.9.53.28060','1996746DlHmOK','/package.json','win32','1782282dreHtq','curVersion','string','2454145MdLTuA','platform','existsSync','isQuickUpdate','join','597640FAGJYd','QQMainPath','3.2.12.28060','homedir','./versions/config.json','resolve','./resources/app/versions/config.json','readFileSync','package.json','config.json','4stzgjk','darwin','./.config/QQ'];_0x22ff=function(){return _0x4a19dc;};return _0x22ff();} + +const _0x9f4fb=_0x1e82;(function(_0x333529,_0x52fca){const _0x2d7cfb=_0x1e82,_0x2dc69d=_0x333529();while(!![]){try{const _0xa48d05=-parseInt(_0x2d7cfb(0x13a))/0x1*(-parseInt(_0x2d7cfb(0x130))/0x2)+-parseInt(_0x2d7cfb(0x12f))/0x3+parseInt(_0x2d7cfb(0x132))/0x4*(parseInt(_0x2d7cfb(0x141))/0x5)+-parseInt(_0x2d7cfb(0x13b))/0x6+-parseInt(_0x2d7cfb(0x135))/0x7+parseInt(_0x2d7cfb(0x13c))/0x8+-parseInt(_0x2d7cfb(0x139))/0x9*(-parseInt(_0x2d7cfb(0x13f))/0xa);if(_0xa48d05===_0x52fca)break;else _0x2dc69d['push'](_0x2dc69d['shift']());}catch(_0x129f0e){_0x2dc69d['push'](_0x2dc69d['shift']());}}}(_0x1d4e,0x31f03));let Process=require('process'),wrapperSession=null;function _0x1e82(_0x3d65a6,_0x6125c2){const _0x1d4eb8=_0x1d4e();return _0x1e82=function(_0x1e82f3,_0x5aef02){_0x1e82f3=_0x1e82f3-0x12f;let _0x3a68ae=_0x1d4eb8[_0x1e82f3];return _0x3a68ae;},_0x1e82(_0x3d65a6,_0x6125c2);}function _0x1d4e(){const _0x4545ba=['2kmnyxl','exports','935988WxNRIu','NodeIQQNTWrapperSession','dlopen','957334mAxIsb','create','RTLD_LAZY','NodeIQQNTWrapperSession\x20created:','9549VYpsGQ','42865UZYnOt','1477332KfmzeB','973424biEwrl','toString','random','3670pgcTBf','dlopenOri','5xVmhjN','601188eFNPPo'];_0x1d4e=function(){return _0x4545ba;};return _0x1d4e();}const dlopenOriName=_0x9f4fb(0x140)+Math[_0x9f4fb(0x13e)]()[_0x9f4fb(0x13d)](0x24)['substring'](0x7);Process[dlopenOriName]=Process[_0x9f4fb(0x134)],Process['dlopen']=function(_0x170cdc,_0x4a6bdf,_0x3c57cb=_0x2cc836['constants'][_0x9f4fb(0x134)][_0x9f4fb(0x137)]){const _0x204cf8=_0x9f4fb;let _0x55fda6=this[dlopenOriName](_0x170cdc,_0x4a6bdf,_0x3c57cb),_0x112989=_0x170cdc[_0x204cf8(0x131)];return _0x170cdc[_0x204cf8(0x131)]=new Proxy({},{'get':function(_0x1a97ab,_0x5a4f2f,_0x34ba03){if(_0x5a4f2f==='NodeIQQNTWrapperSession')return new Proxy(()=>{},{'get'(_0x26ff97,_0x16ebc2,_0x34f08c){if(_0x16ebc2==='create')return new Proxy(()=>{},{'apply'(_0x3d02dc,_0x296552,_0x55b709){const _0x2cfc6e=_0x1e82;return wrapperSession=_0x112989[_0x2cfc6e(0x133)][_0x2cfc6e(0x136)](..._0x55b709),console['log'](_0x2cfc6e(0x138),wrapperSession),Process[_0x2cfc6e(0x134)]=Process[dlopenOriName],wrapperSession;}});}});return _0x112989[_0x5a4f2f];}}),_0x55fda6;};async function initWrapperSession(){if(wrapperSession)return wrapperSession;return new Promise((_0x4a3fac,_0x34a3e4)=>{let _0x9fb78=setInterval(()=>{wrapperSession&&(clearInterval(_0x9fb78),_0x4a3fac(wrapperSession));},0x64);});} + +const _0x3cd3f1=_0x5b41;(function(_0x4f8714,_0xfe81ea){const _0x40bda7=_0x5b41,_0x920b7c=_0x4f8714();while(!![]){try{const _0x307aaf=parseInt(_0x40bda7(0xde))/0x1*(-parseInt(_0x40bda7(0xee))/0x2)+parseInt(_0x40bda7(0xe6))/0x3+-parseInt(_0x40bda7(0xf3))/0x4+-parseInt(_0x40bda7(0xe2))/0x5+parseInt(_0x40bda7(0xfe))/0x6+parseInt(_0x40bda7(0xfd))/0x7*(parseInt(_0x40bda7(0xff))/0x8)+parseInt(_0x40bda7(0xe9))/0x9;if(_0x307aaf===_0xfe81ea)break;else _0x920b7c['push'](_0x920b7c['shift']());}catch(_0x711c29){_0x920b7c['push'](_0x920b7c['shift']());}}}(_0x147f,0x3a1d6));function _0x5b41(_0x4edc2c,_0x4e00ec){const _0x147f3=_0x147f();return _0x5b41=function(_0x5b41b6,_0x177106){_0x5b41b6=_0x5b41b6-0xdb;let _0x29e353=_0x147f3[_0x5b41b6];return _0x29e353;},_0x5b41(_0x4edc2c,_0x4e00ec);}function _0x147f(){const _0xe0fbf4=['catch','error','sendGroupSignPacket','163401kNYpLU','1282254WRLjaD','56VtxYIl','client','then','sendPokePacket','sendSetSpecialTittlePacket','packetSession','11Yptxry','packer','send','sendPacket','641265KsgPQB','packPokePacket','[NTQQPacketApi]\x20PacketServer\x20Offset\x20table\x20not\x20found\x20for\x20QQVersion:\x20','[NTQQPacketApi]\x20InitSendPacket:\x20','455628hVUpIi','当前\x20QQ\x20版本不支持','log','4626684seUEtF','bind','arch','InitSendPacket','logger','49940dhnwbT','connect','sendOidbPacket','available','checkQQVersion','1608404fGuesM','wrapperSession','qqVersion','getFullQQVersion','pid','packGroupSignReq','data'];_0x147f=function(){return _0xe0fbf4;};return _0x147f();}class NTQQPacketApi{['qqVersion'];[_0x3cd3f1(0xdd)];[_0x3cd3f1(0xed)]=console;[_0x3cd3f1(0xf4)];constructor(_0x8fc3ac){const _0x7d3399=_0x3cd3f1;this[_0x7d3399(0xf4)]=_0x8fc3ac,this[_0x7d3399(0xdd)]=void 0x0,this['InitSendPacket'](new QQBasicInfoWrapper()[_0x7d3399(0xf6)]())[_0x7d3399(0x101)]()[_0x7d3399(0xfa)](console[_0x7d3399(0xfb)]);}get[_0x3cd3f1(0xf1)](){const _0x1861a1=_0x3cd3f1;return this[_0x1861a1(0xdd)]?.[_0x1861a1(0x100)][_0x1861a1(0xf1)]??![];}['checkQQVersion'](){const _0x33b211=_0x3cd3f1,_0x1b3bdc=_0x391e5e,_0x1bf552=_0x1b3bdc[this['qqVersion']+'-'+_0x2378fb[_0x33b211(0xeb)]()];if(!_0x1bf552)throw new Error(_0x33b211(0xe7));}async[_0x3cd3f1(0xec)](_0x42389c){const _0x57b7e2=_0x3cd3f1;this[_0x57b7e2(0xed)]['log'](_0x57b7e2(0xe5),_0x42389c,_0x2378fb[_0x57b7e2(0xeb)]()),this[_0x57b7e2(0xf5)]=_0x42389c;const _0x2ef395=_0x391e5e,_0x4614b3=_0x2ef395[_0x42389c+'-'+_0x2378fb[_0x57b7e2(0xeb)]()];if(!_0x4614b3)return this[_0x57b7e2(0xed)]['log'](_0x57b7e2(0xe4),_0x42389c+'-'+_0x2378fb[_0x57b7e2(0xeb)]()),![];try{this[_0x57b7e2(0xdd)]=new PacketSession(this[_0x57b7e2(0xf4)]),this[_0x57b7e2(0xed)][_0x57b7e2(0xe8)]('[NTQQPacketApi]\x20PacketSession\x20created:\x20',this['packetSession']);}catch(_0x42e80d){return this[_0x57b7e2(0xed)][_0x57b7e2(0xe8)]('[NTQQPacketApi]\x20PacketSession\x20create\x20failed:\x20',_0x42e80d),![];}const _0x4ca443=()=>{const _0x5889bd=_0x57b7e2;this[_0x5889bd(0xdd)]&&this['packetSession'][_0x5889bd(0x100)]&&this[_0x5889bd(0xdd)][_0x5889bd(0x100)]['init'](process[_0x5889bd(0xf7)],_0x4614b3['recv'],_0x4614b3[_0x5889bd(0xe0)])[_0x5889bd(0x101)]()['catch'](this['logger']['error'][_0x5889bd(0xea)](this['logger']));};return await this[_0x57b7e2(0xdd)][_0x57b7e2(0x100)][_0x57b7e2(0xef)](_0x4ca443),!![];}async[_0x3cd3f1(0xe1)](_0x38c0a1,_0x20cbf2,_0x428eea=![]){const _0x136210=_0x3cd3f1;return this[_0x136210(0xf2)](),this[_0x136210(0xdd)][_0x136210(0x100)][_0x136210(0xe1)](_0x38c0a1,_0x20cbf2,_0x428eea);}async[_0x3cd3f1(0xf0)](_0x1d462f,_0x5218b0=![]){const _0x164809=_0x3cd3f1;return this[_0x164809(0xe1)](_0x1d462f['cmd'],_0x1d462f[_0x164809(0xf9)],_0x5218b0);}async[_0x3cd3f1(0xdb)](_0x45c844,_0x5bf0f5){const _0x5daddc=_0x3cd3f1,_0x21429b=this['packetSession']?.['packer'][_0x5daddc(0xe3)](_0x45c844,_0x5bf0f5);await this[_0x5daddc(0xf0)](_0x21429b,![]);}async[_0x3cd3f1(0xfc)](_0x1aeadb,_0x266129){const _0x41d5a6=_0x3cd3f1,_0x1fd408=this[_0x41d5a6(0xdd)]?.[_0x41d5a6(0xdf)][_0x41d5a6(0xf8)](_0x1aeadb,_0x266129);await this['sendOidbPacket'](_0x1fd408,!![]);}async[_0x3cd3f1(0xdc)](_0xeda5c5,_0x5d42f2,_0x2c976d){const _0xc2eec6=_0x3cd3f1,_0x35fea6=this[_0xc2eec6(0xdd)]?.['packer']['packSetSpecialTittlePacket'](_0xeda5c5,_0x5d42f2,_0x2c976d);await this['sendOidbPacket'](_0x35fea6,!![]);}} + +export { NTQQPacketApi, initWrapperSession }; diff --git a/src/ntqqapi/native/napcat-protocol-packet/wrapper-session/index.d.ts b/src/ntqqapi/native/napcat-protocol-packet/wrapper-session/index.d.ts new file mode 100644 index 0000000..7939cf4 --- /dev/null +++ b/src/ntqqapi/native/napcat-protocol-packet/wrapper-session/index.d.ts @@ -0,0 +1 @@ +export declare function initWrapperSession(): Promise; diff --git a/src/ntqqapi/native/napcat-protocol-packet/wrapper-session/types.d.ts b/src/ntqqapi/native/napcat-protocol-packet/wrapper-session/types.d.ts new file mode 100644 index 0000000..a3ecdb0 --- /dev/null +++ b/src/ntqqapi/native/napcat-protocol-packet/wrapper-session/types.d.ts @@ -0,0 +1,6 @@ +export interface MsgService { + sendSsoCmdReqByContend: (cmd: string, trace_id: string) => Promise; +} +export type WrapperSession = { + getMsgService(): MsgService; +}; diff --git a/src/ntqqapi/ntcall.ts b/src/ntqqapi/ntcall.ts index 0e1c64e..431937b 100644 --- a/src/ntqqapi/ntcall.ts +++ b/src/ntqqapi/ntcall.ts @@ -1,6 +1,6 @@ import { ipcMain } from 'electron' -import { hookApiCallbacks, ReceiveCmdS, registerReceiveHook, removeReceiveHook } from './hook' -import { getBuildVersion, log } from '../common/utils' +import { hookApiCallbacks, registerReceiveHook, removeReceiveHook } from './hook' +import { log } from '../common/utils' import { randomUUID } from 'node:crypto' import { GeneralCallResult, @@ -17,8 +17,6 @@ import { NodeIKernelRobotService, NodeIKernelNodeMiscService } from './services' -import { CategoryFriend, SimpleInfo, UserDetailInfoByUin } from '@/ntqqapi/types' -import { selfInfo } from '@/common/globalVars' export enum NTClass { NT_API = 'ns-ntApi', @@ -110,38 +108,26 @@ interface InvokeOptions { timeout?: number } -let availableChannel: NTChannel | undefined = undefined; +let channel: NTChannel -export async function checkChanelId(){ - async function testChannel(channel: NTChannel) { - await invoke( - 'nodeIKernelProfileService/getUserDetailInfoByUin', - [{ uin: selfInfo.uin }], - { timeout: 1000, channel } - ) +function getChannel() { + if (channel) { + return channel } - - for (const channel of [NTChannel.IPC_UP_2, NTChannel.IPC_UP_3]) { - // const channel = `IPC_UP_${windowId}` as NTChannel - try { - await testChannel(channel) - log(`check channel ${channel} success`) - availableChannel = channel - return - } catch (e) { - log(`check channel ${channel} failed`, e) - } + if (ipcMain.eventNames().includes(NTChannel.IPC_UP_2)) { + return channel = NTChannel.IPC_UP_2 + } else { + return channel = NTChannel.IPC_UP_3 } - availableChannel = getBuildVersion() >= 28788 ? NTChannel.IPC_UP_3 : NTChannel.IPC_UP_2 } + export function invoke< R extends Awaited unknown>>>, S extends keyof NTService = any, M extends keyof NTService[S] & string = any >(method: Extract | string, args: unknown[], options: InvokeOptions = {}) { const className = options.className ?? NTClass.NT_API - // const channel = options.channel ?? getBuildVersion() >= 28788 ? NTChannel.IPC_UP_3 : NTChannel.IPC_UP_2 - const channel = options.channel ?? availableChannel! + const channel = options.channel ?? getChannel() const timeout = options.timeout ?? 5000 const afterFirstCmd = options.afterFirstCmd ?? true let eventName = className + '-' + channel[channel.length - 1] diff --git a/src/onebot11/action/go-cqhttp/GetStrangerInfo.ts b/src/onebot11/action/go-cqhttp/GetStrangerInfo.ts index 35a403b..8509a12 100644 --- a/src/onebot11/action/go-cqhttp/GetStrangerInfo.ts +++ b/src/onebot11/action/go-cqhttp/GetStrangerInfo.ts @@ -12,6 +12,7 @@ interface Payload { interface Response extends OB11User { reg_time: number long_nick: string + city: string } export class GetStrangerInfo extends BaseAction { @@ -33,7 +34,8 @@ export class GetStrangerInfo extends BaseAction { level: data.detail.commonExt.qqLevel && calcQQLevel(data.detail.commonExt.qqLevel) || 0, login_days: 0, reg_time: data.detail.commonExt.regTime, - long_nick: data.detail.simpleInfo.baseInfo.longNick + long_nick: data.detail.simpleInfo.baseInfo.longNick, + city: data.detail.commonExt.city } } else { const data = await this.ctx.ntUserApi.getUserDetailInfoByUin(uin) @@ -46,7 +48,8 @@ export class GetStrangerInfo extends BaseAction { level: data.info.qqLevel && calcQQLevel(data.info.qqLevel) || 0, login_days: 0, reg_time: data.info.regTime, - long_nick: data.info.longNick + long_nick: data.info.longNick, + city: data.info.city } } } diff --git a/src/onebot11/action/go-cqhttp/SendGroupSign.ts b/src/onebot11/action/go-cqhttp/SendGroupSign.ts new file mode 100644 index 0000000..c80b693 --- /dev/null +++ b/src/onebot11/action/go-cqhttp/SendGroupSign.ts @@ -0,0 +1,20 @@ +import { BaseAction, Schema } from '../BaseAction' +import { ActionName } from '../types' +import { getBuildVersion } from '@/common/utils/misc' +import { selfInfo } from '@/common/globalVars' + +interface Payload { + group_id: number | string +} + +export class SendGroupSign extends BaseAction { + actionName = ActionName.GoCQHTTP_SendGroupSign + payloadSchema = Schema.object({ + group_id: Schema.union([Number, String]).required(), + }) + + async _handle(payload: Payload) { + await this.ctx.app.ntqqPacketApi.sendGroupSignPacket(selfInfo.uin, payload.group_id.toString()) + return null + } +} diff --git a/src/onebot11/action/go-cqhttp/SetGroupSpecialTitle.ts b/src/onebot11/action/go-cqhttp/SetGroupSpecialTitle.ts new file mode 100644 index 0000000..0344939 --- /dev/null +++ b/src/onebot11/action/go-cqhttp/SetGroupSpecialTitle.ts @@ -0,0 +1,31 @@ +import { BaseAction, Schema } from '../BaseAction' +import { ActionName } from '../types' +import { getBuildVersion } from '@/common/utils/misc' +import { selfInfo } from '@/common/globalVars' +import { GroupMemberRole } from '@/ntqqapi/types' + +interface Payload { + group_id: number | string + user_id: number | string + special_title?: string +} + +export class SetGroupSpecialTitle extends BaseAction { + actionName = ActionName.GoCQHTTP_SetGroupSpecialTitle + payloadSchema = Schema.object({ + group_id: Schema.union([Number, String]).required(), + user_id: Schema.union([Number, String]).required(), + special_title: Schema.string() + }) + + async _handle(payload: Payload) { + const uid = await this.ctx.ntUserApi.getUidByUin(payload.user_id.toString(), payload.group_id.toString()) + if (!uid) throw new Error(`用户${payload.user_id}的uid获取失败`) + const self = await this.ctx.ntGroupApi.getGroupMember(payload.group_id.toString(), selfInfo.uid, false) + if (self.role !== GroupMemberRole.Owner){ + throw new Error(`不是群${payload.group_id}的群主,无法设置群头衔`) + } + await this.ctx.app.ntqqPacketApi.sendSetSpecialTittlePacket(payload.group_id.toString(), uid, payload.special_title || "") + return null + } +} diff --git a/src/onebot11/action/index.ts b/src/onebot11/action/index.ts index 744d74a..665698e 100644 --- a/src/onebot11/action/index.ts +++ b/src/onebot11/action/index.ts @@ -78,6 +78,8 @@ import { GroupPoke } from './llonebot/GroupPoke' import { FriendPoke } from './llonebot/FriendPoke' import { GetGroupFileSystemInfo } from './go-cqhttp/GetGroupFileSystemInfo' import { GetCredentials } from './system/GetCredentials' +import { SetGroupSpecialTitle } from '@/onebot11/action/go-cqhttp/SetGroupSpecialTitle' +import { SendGroupSign } from '@/onebot11/action/go-cqhttp/SendGroupSign' export function initActionMap(adapter: Adapter) { const actionHandlers = [ @@ -161,6 +163,8 @@ export function initActionMap(adapter: Adapter) { new DeleteFriend(adapter), new OCRImage(adapter), new GetGroupFileSystemInfo(adapter), + new SetGroupSpecialTitle(adapter), + new SendGroupSign(adapter), ] const actionMap = new Map() for (const action of actionHandlers) { diff --git a/src/onebot11/action/llonebot/FriendPoke.ts b/src/onebot11/action/llonebot/FriendPoke.ts index 80a4ae5..3b2f7b2 100644 --- a/src/onebot11/action/llonebot/FriendPoke.ts +++ b/src/onebot11/action/llonebot/FriendPoke.ts @@ -13,13 +13,15 @@ export class FriendPoke extends BaseAction { }) async _handle(payload: Payload) { - if (!this.ctx.app.native.checkPlatform()) { - throw new Error('当前系统平台或架构不支持') - } - if (!this.ctx.app.native.checkVersion()) { - throw new Error(`当前 QQ 版本 ${getBuildVersion()} 不支持,可尝试其他版本 27333—27597`) - } - await this.ctx.app.native.sendFriendPoke(+payload.user_id) + // if (!this.ctx.app.native.checkPlatform()) { + // throw new Error('当前系统平台或架构不支持') + // } + // if (!this.ctx.app.native.checkVersion()) { + // throw new Error(`当前 QQ 版本 ${getBuildVersion()} 不支持,可尝试其他版本 27333—27597`) + // } + // await this.ctx.app.native.sendFriendPoke(+payload.user_id) + + await this.ctx.app.ntqqPacketApi.sendPokePacket(+payload.user_id) return null } } diff --git a/src/onebot11/action/llonebot/GroupPoke.ts b/src/onebot11/action/llonebot/GroupPoke.ts index 89f8787..1025378 100644 --- a/src/onebot11/action/llonebot/GroupPoke.ts +++ b/src/onebot11/action/llonebot/GroupPoke.ts @@ -1,6 +1,7 @@ import { BaseAction, Schema } from '../BaseAction' import { ActionName } from '../types' import { getBuildVersion } from '@/common/utils/misc' +import {NTQQPacketApi} from '@/ntqqapi/native/napcat-protocol-packet' interface Payload { group_id: number | string @@ -15,13 +16,14 @@ export class GroupPoke extends BaseAction { }) async _handle(payload: Payload) { - if (!this.ctx.app.native.checkPlatform()) { - throw new Error('当前系统平台或架构不支持') - } - if (!this.ctx.app.native.checkVersion()) { - throw new Error(`当前 QQ 版本 ${getBuildVersion()} 不支持,可尝试其他版本 27333—27597`) - } - await this.ctx.app.native.sendGroupPoke(+payload.group_id, +payload.user_id) + // if (!this.ctx.app.native.checkPlatform()) { + // throw new Error('当前系统平台或架构不支持') + // } + // if (!this.ctx.app.native.checkVersion()) { + // throw new Error(`当前 QQ 版本 ${getBuildVersion()} 不支持,可尝试其他版本 27333—27597`) + // } + // await this.ctx.app.native.sendGroupPoke(+payload.group_id, +payload.user_id) + await this.ctx.app.ntqqPacketApi.sendPokePacket(+payload.user_id, +payload.group_id) return null } } diff --git a/src/onebot11/action/types.ts b/src/onebot11/action/types.ts index 74c8048..126db44 100644 --- a/src/onebot11/action/types.ts +++ b/src/onebot11/action/types.ts @@ -91,4 +91,6 @@ export enum ActionName { GoCQHTTP_DeleteFriend = 'delete_friend', GoCQHTTP_OCRImage = 'ocr_image', GoCQHTTP_GetGroupFileSystemInfo = 'get_group_file_system_info', + GoCQHTTP_SetGroupSpecialTitle = 'set_group_special_title', + GoCQHTTP_SendGroupSign = 'send_group_sign', } diff --git a/src/version.ts b/src/version.ts index 94bbc74..e646a57 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const version = '4.1.4' +export const version = '4.2.0'