123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509 |
- /*!
- * TSRPC Base Client v2.1.15
- * -----------------------------------------
- * Copyright (c) Kingworks Corporation.
- * MIT License
- * https://github.com/k8w/tsrpc-base-client
- */
- import 'k8w-extend-native';
- import { __awaiter, __generator, __assign, __extends } from 'tslib';
- import { TSBuffer } from 'tsbuffer';
- import { TsrpcError, TransportDataProto, TsrpcErrorType, setLogLevel } from 'tsrpc-proto';
- import { SchemaType } from 'tsbuffer-schema';
- /**
- * An auto-increment counter
- */
- var Counter = /** @class */ (function () {
- function Counter(min, max) {
- if (min === void 0) { min = 1; }
- if (max === void 0) { max = Number.MAX_SAFE_INTEGER; }
- this._min = min;
- this._max = max;
- this._last = max;
- }
- /**
- * Reset the counter, makes `getNext()` restart from `0`
- */
- Counter.prototype.reset = function () {
- this._last = this._max;
- };
- /**
- * Get next counter value, and auto increment `1`
- * @param notInc - Just get the next possible value, not actually increasing the sequence
- */
- Counter.prototype.getNext = function (notInc) {
- return this._last >= this._max ? (this._last = this._min) : (notInc ? this._last : ++this._last);
- };
- Object.defineProperty(Counter.prototype, "last", {
- /**
- * Last return of `getNext()`
- */
- get: function () {
- return this._last;
- },
- enumerable: false,
- configurable: true
- });
- return Counter;
- }());
- /**
- * A `Flow` is consists of many `FlowNode`, which is function with the same input and output (like pipeline).
- *
- * @remarks
- * `Flow` is like a hook or event, executed at a specific time.
- * The difference to event is it can be used to **interrupt** an action, by return `undefined` or `null` in a node.
- */
- var Flow = /** @class */ (function () {
- function Flow() {
- /**
- * All node functions, if you want to adjust the sort you can modify this.
- */
- this.nodes = [];
- /**
- * Event when error throwed from a `FlowNode` function.
- * By default, it does nothing except print a `Uncaught FlowError` error log.
- * @param e
- * @param last
- * @param input
- * @param logger
- */
- this.onError = function (e, last, input, logger) {
- logger === null || logger === void 0 ? void 0 : logger.error('Uncaught FlowError:', e);
- };
- }
- /**
- * Execute all node function one by one, the previous output is the next input,
- * until the last output would be return to the caller.
- *
- * @remarks
- * If any node function return `null | undefined`, or throws an error,
- * the latter node functions would not be executed.
- * And it would return `null | undefined` immediately to the caller,
- * which tell the caller it means a interruption,
- * to let the caller stop latter behaviours.
- *
- * @param input The input of the first `FlowNode`
- * @param logger Logger to print log, `undefined` means to hide all log.
- * @returns
- */
- Flow.prototype.exec = function (input, logger) {
- return __awaiter(this, void 0, void 0, function () {
- var res, i, e_1;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0:
- res = input;
- i = 0;
- _a.label = 1;
- case 1:
- if (!(i < this.nodes.length)) return [3 /*break*/, 7];
- _a.label = 2;
- case 2:
- _a.trys.push([2, 4, , 5]);
- return [4 /*yield*/, this.nodes[i](res)];
- case 3:
- res = _a.sent();
- return [3 /*break*/, 5];
- case 4:
- e_1 = _a.sent();
- this.onError(e_1, res, input, logger);
- return [2 /*return*/, undefined];
- case 5:
- // Return 非true 表示不继续后续流程 立即中止
- if (res === null || res === undefined) {
- return [2 /*return*/, res];
- }
- _a.label = 6;
- case 6:
- ++i;
- return [3 /*break*/, 1];
- case 7: return [2 /*return*/, res];
- }
- });
- });
- };
- /**
- * Append a node function to the last
- * @param node
- * @returns
- */
- Flow.prototype.push = function (node) {
- this.nodes.push(node);
- return node;
- };
- /**
- * Remove a node function
- * @param node
- * @returns
- */
- Flow.prototype.remove = function (node) {
- return this.nodes.remove(function (v) { return v === node; });
- };
- return Flow;
- }());
- function getCustomObjectIdTypes(classObjectId) {
- var output = {};
- // string
- if (classObjectId === String) {
- output['?mongodb/ObjectId'] = {
- type: SchemaType.Custom,
- validate: function (value) {
- if (typeof value !== 'string') {
- return { isSucc: false, errMsg: "Expected type to be `string`, actually `".concat(typeof value, "`.") };
- }
- if (!/^[0-9a-fA-F]{24}$/.test(value)) {
- return { isSucc: false, errMsg: 'ObjectId must be a string of 24 hex characters' };
- }
- return { isSucc: true };
- },
- encode: function (value) {
- return new Uint8Array(Array.from({ length: 12 }, function (_, i) { return Number.parseInt('0x' + value.substr(i * 2, 2)); }));
- },
- decode: function (buf) {
- return Array.from(buf, function (v) {
- var str = v.toString(16);
- if (str.length === 1) {
- str = '0' + str;
- }
- return str;
- }).join('');
- }
- };
- }
- // ObjectId
- else {
- output['?mongodb/ObjectId'] = {
- type: SchemaType.Custom,
- validate: function (value) { return (value instanceof classObjectId) ?
- { isSucc: true } :
- { isSucc: false, errMsg: "Expected to be instance of `ObjectId`, actually not." }; },
- encode: function (value) { return new Uint8Array(value.id); },
- decode: function (buf) { return new classObjectId(buf); },
- decodeJSON: function (json) { return new classObjectId(json); }
- };
- }
- output['?mongodb/ObjectID'] = output['?mongodb/ObjectId'];
- output['?bson/ObjectId'] = output['?mongodb/ObjectId'];
- output['?bson/ObjectID'] = output['?mongodb/ObjectId'];
- return output;
- }
- /**
- * A manager for TSRPC receiving messages
- */
- var MsgHandlerManager = /** @class */ (function () {
- function MsgHandlerManager() {
- this._handlers = {};
- }
- /**
- * Execute all handlers parallelly
- * @returns handlers count
- */
- MsgHandlerManager.prototype.forEachHandler = function (msgName, logger) {
- var args = [];
- for (var _i = 2; _i < arguments.length; _i++) {
- args[_i - 2] = arguments[_i];
- }
- var handlers = this._handlers[msgName];
- if (!handlers) {
- return [];
- }
- var output = [];
- for (var _a = 0, handlers_1 = handlers; _a < handlers_1.length; _a++) {
- var handler = handlers_1[_a];
- try {
- output.push(handler.apply(void 0, args));
- }
- catch (e) {
- logger === null || logger === void 0 ? void 0 : logger.error('[MsgHandlerError]', e);
- }
- }
- return output;
- };
- /**
- * Add message handler, duplicate handlers to the same `msgName` would be ignored.
- * @param msgName
- * @param handler
- * @returns
- */
- MsgHandlerManager.prototype.addHandler = function (msgName, handler) {
- var handlers = this._handlers[msgName];
- // 初始化Handlers
- if (!handlers) {
- handlers = this._handlers[msgName] = [];
- }
- // 防止重复监听
- else if (handlers.some(function (v) { return v === handler; })) {
- return;
- }
- handlers.push(handler);
- };
- /**
- * Remove handler from the specific `msgName`
- * @param msgName
- * @param handler
- * @returns
- */
- MsgHandlerManager.prototype.removeHandler = function (msgName, handler) {
- var handlers = this._handlers[msgName];
- if (!handlers) {
- return;
- }
- handlers.removeOne(function (v) { return v === handler; });
- };
- /**
- * Remove all handlers for the specific `msgName`
- * @param msgName
- */
- MsgHandlerManager.prototype.removeAllHandlers = function (msgName) {
- this._handlers[msgName] = undefined;
- };
- return MsgHandlerManager;
- }());
- /** A utility for generate `ServiceMap` */
- var ServiceMapUtil = /** @class */ (function () {
- function ServiceMapUtil() {
- }
- ServiceMapUtil.getServiceMap = function (proto) {
- var map = {
- id2Service: {},
- apiName2Service: {},
- msgName2Service: {}
- };
- for (var _i = 0, _a = proto.services; _i < _a.length; _i++) {
- var v = _a[_i];
- var match = v.name.match(/(.+\/)?([^\/]+)$/);
- var path = match[1] || '';
- var name_1 = match[2];
- if (v.type === 'api') {
- var svc = __assign(__assign({}, v), { reqSchemaId: "".concat(path, "Ptl").concat(name_1, "/Req").concat(name_1), resSchemaId: "".concat(path, "Ptl").concat(name_1, "/Res").concat(name_1) });
- map.apiName2Service[v.name] = svc;
- map.id2Service[v.id] = svc;
- }
- else {
- var svc = __assign(__assign({}, v), { msgSchemaId: "".concat(path, "Msg").concat(name_1, "/Msg").concat(name_1) });
- map.msgName2Service[v.name] = svc;
- map.id2Service[v.id] = svc;
- }
- }
- return map;
- };
- return ServiceMapUtil;
- }());
- var TransportDataUtil = /** @class */ (function () {
- function TransportDataUtil() {
- }
- Object.defineProperty(TransportDataUtil, "tsbuffer", {
- get: function () {
- if (!this._tsbuffer) {
- this._tsbuffer = new TSBuffer(TransportDataProto);
- }
- return this._tsbuffer;
- },
- enumerable: false,
- configurable: true
- });
- TransportDataUtil.encodeClientMsg = function (tsbuffer, service, msg, type, connType) {
- if (type === 'buffer') {
- var op = tsbuffer.encode(msg, service.msgSchemaId);
- if (!op.isSucc) {
- return op;
- }
- var serverInputData = {
- serviceId: service.id,
- buffer: op.buf
- };
- var opOut = this.tsbuffer.encode(serverInputData, 'ServerInputData');
- return opOut.isSucc ? { isSucc: true, output: opOut.buf } : { isSucc: false, errMsg: opOut.errMsg };
- }
- else {
- var op = tsbuffer.encodeJSON(msg, service.msgSchemaId);
- if (!op.isSucc) {
- return op;
- }
- var json = connType === 'SHORT' ? op.json : [service.name, op.json];
- return { isSucc: true, output: type === 'json' ? json : JSON.stringify(json) };
- }
- };
- TransportDataUtil.encodeApiReq = function (tsbuffer, service, req, type, sn) {
- if (type === 'buffer') {
- var op = tsbuffer.encode(req, service.reqSchemaId);
- if (!op.isSucc) {
- return op;
- }
- var serverInputData = {
- serviceId: service.id,
- buffer: op.buf,
- sn: sn
- };
- var opOut = this.tsbuffer.encode(serverInputData, 'ServerInputData');
- return opOut.isSucc ? { isSucc: true, output: opOut.buf } : { isSucc: false, errMsg: opOut.errMsg };
- }
- else {
- var op = tsbuffer.encodeJSON(req, service.reqSchemaId);
- if (!op.isSucc) {
- return op;
- }
- var json = sn === undefined ? op.json : [service.name, op.json, sn];
- return { isSucc: true, output: type === 'json' ? json : JSON.stringify(json) };
- }
- };
- TransportDataUtil.encodeServerMsg = function (tsbuffer, service, msg, type, connType) {
- if (type === 'buffer') {
- var op = tsbuffer.encode(msg, service.msgSchemaId);
- if (!op.isSucc) {
- return op;
- }
- var serverOutputData = {
- serviceId: service.id,
- buffer: op.buf
- };
- var opOut = this.tsbuffer.encode(serverOutputData, 'ServerOutputData');
- return opOut.isSucc ? { isSucc: true, output: opOut.buf } : { isSucc: false, errMsg: opOut.errMsg };
- }
- else {
- var op = tsbuffer.encodeJSON(msg, service.msgSchemaId);
- if (!op.isSucc) {
- return op;
- }
- var json = connType === 'SHORT' ? op.json : [service.name, op.json];
- return { isSucc: true, output: type === 'json' ? json : JSON.stringify(json) };
- }
- };
- TransportDataUtil.parseServerOutout = function (tsbuffer, serviceMap, data, serviceId) {
- if (data instanceof Uint8Array) {
- var opServerOutputData = this.tsbuffer.decode(data, 'ServerOutputData');
- if (!opServerOutputData.isSucc) {
- return opServerOutputData;
- }
- var serverOutputData = opServerOutputData.value;
- serviceId = serviceId !== null && serviceId !== void 0 ? serviceId : serverOutputData.serviceId;
- if (serviceId === undefined) {
- return { isSucc: false, errMsg: "Missing 'serviceId' in ServerOutput" };
- }
- var service = serviceMap.id2Service[serviceId];
- if (!service) {
- return { isSucc: false, errMsg: "Invalid service ID: ".concat(serviceId, " (from ServerOutput)") };
- }
- if (service.type === 'msg') {
- if (!serverOutputData.buffer) {
- return { isSucc: false, errMsg: 'Empty msg buffer (from ServerOutput)' };
- }
- var opMsg = tsbuffer.decode(serverOutputData.buffer, service.msgSchemaId);
- if (!opMsg.isSucc) {
- return opMsg;
- }
- return {
- isSucc: true,
- result: {
- type: 'msg',
- service: service,
- msg: opMsg.value
- }
- };
- }
- else {
- if (serverOutputData.error) {
- return {
- isSucc: true,
- result: {
- type: 'api',
- service: service,
- sn: serverOutputData.sn,
- ret: {
- isSucc: false,
- err: new TsrpcError(serverOutputData.error)
- }
- }
- };
- }
- else {
- if (!serverOutputData.buffer) {
- return { isSucc: false, errMsg: 'Empty API res buffer (from ServerOutput)' };
- }
- var opRes = tsbuffer.decode(serverOutputData.buffer, service.resSchemaId);
- if (!opRes.isSucc) {
- return opRes;
- }
- return {
- isSucc: true,
- result: {
- type: 'api',
- service: service,
- sn: serverOutputData.sn,
- ret: {
- isSucc: true,
- res: opRes.value,
- }
- }
- };
- }
- }
- }
- else {
- var json = void 0;
- if (typeof data === 'string') {
- try {
- json = JSON.parse(data);
- }
- catch (e) {
- return { isSucc: false, errMsg: "Invalid input JSON: ".concat(e.message) };
- }
- }
- else {
- json = data;
- }
- var body = void 0;
- var sn = void 0;
- var service = void 0;
- if (serviceId == undefined) {
- if (!Array.isArray(json)) {
- return { isSucc: false, errMsg: "Invalid server output format" };
- }
- var serviceName = json[0];
- body = json[1];
- sn = json[2];
- // 有 SN 是 Api,没 SN 是 Msg
- service = sn ? serviceMap.apiName2Service[serviceName] : serviceMap.msgName2Service[serviceName];
- if (!service) {
- return { isSucc: false, errMsg: "Invalid service name: ".concat(serviceName, " (from ServerOutputData)") };
- }
- }
- else {
- service = serviceMap.id2Service[serviceId];
- if (!service) {
- return { isSucc: false, errMsg: "Invalid service ID: ".concat(serviceId) };
- }
- body = json;
- }
- if (service.type === 'api') {
- if (body.isSucc && 'res' in body) {
- var op = tsbuffer.decodeJSON(body.res, service.resSchemaId);
- if (!op.isSucc) {
- return op;
- }
- body.res = op.value;
- }
- else if (body.err) {
- body.err = new TsrpcError(body.err);
- }
- else {
- return { isSucc: false, errMsg: "Invalid server output format" };
- }
- return {
- isSucc: true,
- result: {
- type: 'api',
- service: service,
- sn: sn,
- ret: body
- }
- };
- }
- else {
- var op = tsbuffer.decodeJSON(body, service.msgSchemaId);
- if (!op.isSucc) {
- return op;
- }
- return {
- isSucc: true,
- result: {
- type: 'msg',
- service: service,
- msg: op.value
- }
- };
- }
- }
- };
- // 心跳包(Ping & Pong),所有开头为 0 的 Buffer,均为控制指令
- TransportDataUtil.HeartbeatPacket = new Uint8Array([0]);
- return TransportDataUtil;
- }());
- /**
- * An abstract base class for TSRPC Client,
- * which includes some common buffer process flows.
- *
- * @remarks
- * You can implement a client on a specific transportation protocol (like HTTP, WebSocket, QUIP) by extend this.
- *
- * @typeParam ServiceType - `ServiceType` from generated `proto.ts`
- *
- * @see
- * {@link https://github.com/k8w/tsrpc}
- * {@link https://github.com/k8w/tsrpc-browser}
- * {@link https://github.com/k8w/tsrpc-miniapp}
- */
- var BaseClient = /** @class */ (function () {
- function BaseClient(proto, options) {
- this._msgHandlers = new MsgHandlerManager();
- /**
- * {@link Flow} to process `callApi`, `sendMsg`, buffer input/output, etc...
- */
- this.flows = {
- // callApi
- preCallApiFlow: new Flow(),
- preApiReturnFlow: new Flow(),
- postApiReturnFlow: new Flow(),
- // sendMsg
- preSendMsgFlow: new Flow(),
- postSendMsgFlow: new Flow(),
- preRecvMsgFlow: new Flow(),
- postRecvMsgFlow: new Flow(),
- // buffer
- preSendDataFlow: new Flow(),
- preRecvDataFlow: new Flow(),
- /**
- * @deprecated Please use `preSendDataFlow` instead
- */
- preSendBufferFlow: new Flow(),
- /**
- * @deprecated Please use `preRecvDataFlow` instead
- */
- preRecvBufferFlow: new Flow(),
- // Connection Flows (Only for WebSocket)
- /** Before connect to WebSocket server */
- preConnectFlow: new Flow(),
- /** After WebSocket connect successfully */
- postConnectFlow: new Flow(),
- /** After WebSocket disconnected (from connected status) */
- postDisconnectFlow: new Flow(),
- };
- this._apiSnCounter = new Counter(1);
- /**
- * Pending API Requests
- */
- this._pendingApis = [];
- /** @deprecated Please use `_onRecvData` instead */
- this._onRecvBuf = this._onRecvData;
- this.options = options;
- this.serviceMap = ServiceMapUtil.getServiceMap(proto);
- this.dataType = this.options.json ? 'text' : 'buffer';
- var types = __assign({}, proto.types);
- // Custom ObjectId handler
- if (options.customObjectIdClass) {
- types = __assign(__assign({}, types), getCustomObjectIdTypes(options.customObjectIdClass));
- }
- this.tsbuffer = new TSBuffer(types);
- this.logger = this.options.logger;
- if (this.logger) {
- this.logger = setLogLevel(this.logger, this.options.logLevel);
- }
- }
- Object.defineProperty(BaseClient.prototype, "lastSN", {
- /**
- * The `SN` number of the last `callApi()`,
- * which can be passed to `abort()` to abort an API request.
- * @example
- * ```ts
- * client.callApi('xxx', { value: 'xxx' })
- * .then(ret=>{ console.log('succ', ret) });
- * let lastSN = client.lastSN;
- * client.abort(lastSN);
- * ```
- */
- get: function () {
- return this._apiSnCounter.last;
- },
- enumerable: false,
- configurable: true
- });
- Object.defineProperty(BaseClient.prototype, "nextSN", {
- /**
- * The `SN` number of the next `callApi()`,
- * which can be passed to `abort()` to abort an API request.
- * @example
- * ```ts
- * let nextSN = client.nextSN;
- * client.callApi('xxx', { value: 'xxx' })
- * ```
- */
- get: function () {
- return this._apiSnCounter.getNext(true);
- },
- enumerable: false,
- configurable: true
- });
- /**
- * Send request and wait for the return
- * @param apiName
- * @param req - Request body
- * @param options - Transport options
- * @returns return a `ApiReturn`, all error (network error, business error, code exception...) is unified as `TsrpcError`.
- * The promise is never rejected, so you just need to process all error in one place.
- */
- BaseClient.prototype.callApi = function (apiName, req, options) {
- if (options === void 0) { options = {}; }
- return __awaiter(this, void 0, void 0, function () {
- var sn, pendingItem, promise;
- var _this = this;
- return __generator(this, function (_a) {
- sn = this._apiSnCounter.getNext();
- pendingItem = {
- sn: sn,
- abortKey: options.abortKey,
- service: this.serviceMap.apiName2Service[apiName]
- };
- this._pendingApis.push(pendingItem);
- promise = new Promise(function (rs) { return __awaiter(_this, void 0, void 0, function () {
- var pre, ret, preReturn;
- var _a, _b;
- return __generator(this, function (_c) {
- switch (_c.label) {
- case 0: return [4 /*yield*/, this.flows.preCallApiFlow.exec({
- apiName: apiName,
- req: req,
- options: options
- }, this.logger)];
- case 1:
- pre = _c.sent();
- if (!pre || pendingItem.isAborted) {
- this.abort(pendingItem.sn);
- return [2 /*return*/];
- }
- if (!pre.return) return [3 /*break*/, 2];
- ret = pre.return;
- return [3 /*break*/, 4];
- case 2: return [4 /*yield*/, this._doCallApi(pre.apiName, pre.req, pre.options, pendingItem)];
- case 3:
- // do call means it will send buffer via network
- ret = _c.sent();
- _c.label = 4;
- case 4:
- if (pendingItem.isAborted) {
- return [2 /*return*/];
- }
- // Log Original Return
- if (ret.isSucc) {
- this.options.logApi && ((_a = this.logger) === null || _a === void 0 ? void 0 : _a.log("[ApiRes] #".concat(pendingItem.sn, " ").concat(apiName), ret.res));
- }
- else {
- this.options.logApi && ((_b = this.logger) === null || _b === void 0 ? void 0 : _b[ret.err.type === TsrpcError.Type.ApiError ? 'log' : 'error']("[ApiErr] #".concat(pendingItem.sn, " ").concat(apiName), ret.err));
- }
- return [4 /*yield*/, this.flows.preApiReturnFlow.exec(__assign(__assign({}, pre), { return: ret }), this.logger)];
- case 5:
- preReturn = _c.sent();
- if (!preReturn) {
- this.abort(pendingItem.sn);
- return [2 /*return*/];
- }
- rs(preReturn.return);
- // Post Flow
- this.flows.postApiReturnFlow.exec(preReturn, this.logger);
- return [2 /*return*/];
- }
- });
- }); });
- // Finally clear pendings
- promise.catch().then(function () {
- _this._pendingApis.removeOne(function (v) { return v.sn === pendingItem.sn; });
- });
- return [2 /*return*/, promise];
- });
- });
- };
- BaseClient.prototype._doCallApi = function (apiName, req, options, pendingItem) {
- var _a;
- if (options === void 0) { options = {}; }
- return __awaiter(this, void 0, void 0, function () {
- var promise;
- var _this = this;
- return __generator(this, function (_b) {
- this.options.logApi && ((_a = this.logger) === null || _a === void 0 ? void 0 : _a.log("[ApiReq] #".concat(pendingItem.sn), apiName, req));
- promise = new Promise(function (rs) { return __awaiter(_this, void 0, void 0, function () {
- var service, opEncode, promiseReturn, promiseSend, opSend, ret;
- var _a;
- return __generator(this, function (_b) {
- switch (_b.label) {
- case 0:
- service = this.serviceMap.apiName2Service[apiName];
- if (!service) {
- rs({
- isSucc: false,
- err: new TsrpcError('Invalid api name: ' + apiName, {
- code: 'INVALID_API_NAME',
- type: TsrpcErrorType.ClientError
- })
- });
- return [2 /*return*/];
- }
- pendingItem.service = service;
- opEncode = TransportDataUtil.encodeApiReq(this.tsbuffer, service, req, this.dataType, this.type === 'LONG' ? pendingItem.sn : undefined);
- if (!opEncode.isSucc) {
- rs({
- isSucc: false, err: new TsrpcError(opEncode.errMsg, {
- type: TsrpcErrorType.ClientError,
- code: 'INPUT_DATA_ERR'
- })
- });
- return [2 /*return*/];
- }
- promiseReturn = this._waitApiReturn(pendingItem, (_a = options.timeout) !== null && _a !== void 0 ? _a : this.options.timeout);
- promiseSend = this.sendData(opEncode.output, options, service.id, pendingItem);
- return [4 /*yield*/, promiseSend];
- case 1:
- opSend = _b.sent();
- if (opSend.err) {
- rs({
- isSucc: false,
- err: opSend.err
- });
- return [2 /*return*/];
- }
- return [4 /*yield*/, promiseReturn];
- case 2:
- ret = _b.sent();
- if (pendingItem.isAborted) {
- return [2 /*return*/];
- }
- rs(ret);
- return [2 /*return*/];
- }
- });
- }); });
- return [2 /*return*/, promise];
- });
- });
- };
- /**
- * Send message, without response, not ensuring the server is received and processed correctly.
- * @param msgName
- * @param msg - Message body
- * @param options - Transport options
- * @returns If the promise is resolved, it means the request is sent to system kernel successfully.
- * Notice that not means the server received and processed the message correctly.
- */
- BaseClient.prototype.sendMsg = function (msgName, msg, options) {
- var _this = this;
- if (options === void 0) { options = {}; }
- var promise = new Promise(function (rs) { return __awaiter(_this, void 0, void 0, function () {
- var pre, service, opEncode, promiseSend, opSend;
- var _a, _b;
- return __generator(this, function (_c) {
- switch (_c.label) {
- case 0: return [4 /*yield*/, this.flows.preSendMsgFlow.exec({
- msgName: msgName,
- msg: msg,
- options: options
- }, this.logger)];
- case 1:
- pre = _c.sent();
- if (!pre) {
- return [2 /*return*/];
- }
- // The msg is not prevented by pre flow
- this.options.logMsg && ((_a = this.logger) === null || _a === void 0 ? void 0 : _a.log("[SendMsg]", msgName, msg));
- service = this.serviceMap.msgName2Service[msgName];
- if (!service) {
- (_b = this.logger) === null || _b === void 0 ? void 0 : _b.error('Invalid msg name: ' + msgName);
- rs({
- isSucc: false,
- err: new TsrpcError('Invalid msg name: ' + msgName, {
- code: 'INVALID_MSG_NAME',
- type: TsrpcErrorType.ClientError
- })
- });
- return [2 /*return*/];
- }
- opEncode = TransportDataUtil.encodeClientMsg(this.tsbuffer, service, msg, this.dataType, this.type);
- if (!opEncode.isSucc) {
- rs({
- isSucc: false,
- err: new TsrpcError(opEncode.errMsg, {
- type: TsrpcErrorType.ClientError,
- code: 'ENCODE_MSG_ERR'
- })
- });
- return [2 /*return*/];
- }
- promiseSend = this.sendData(opEncode.output, options, service.id);
- return [4 /*yield*/, promiseSend];
- case 2:
- opSend = _c.sent();
- if (opSend.err) {
- rs({
- isSucc: false,
- err: opSend.err
- });
- return [2 /*return*/];
- }
- rs({ isSucc: true });
- // Post Flow
- this.flows.postSendMsgFlow.exec(pre, this.logger);
- return [2 /*return*/];
- }
- });
- }); });
- promise.then(function (v) {
- var _a;
- if (!v.isSucc) {
- ((_a = _this.logger) !== null && _a !== void 0 ? _a : console).error('[SendMsgErr]', v.err);
- }
- });
- return promise;
- };
- /**
- * Add a message handler,
- * duplicate handlers to the same `msgName` would be ignored.
- * @param msgName
- * @param handler
- * @returns
- */
- // listenMsg<T extends keyof ServiceType['msg']>(msgName: T, handler: ClientMsgHandler<ServiceType, T, this>): ClientMsgHandler<ServiceType, T, this>;
- // listenMsg(msgName: RegExp, handler: ClientMsgHandler<ServiceType, keyof ServiceType['msg'], this>): ClientMsgHandler<ServiceType, keyof ServiceType['msg'], this>;
- // listenMsg(msgName: string | RegExp, handler: ClientMsgHandler<ServiceType, string, this>): ClientMsgHandler<ServiceType, string, this> {
- BaseClient.prototype.listenMsg = function (msgName, handler) {
- var _this = this;
- if (msgName instanceof RegExp) {
- Object.keys(this.serviceMap.msgName2Service).filter(function (k) { return msgName.test(k); }).forEach(function (k) {
- _this._msgHandlers.addHandler(k, handler);
- });
- }
- else {
- this._msgHandlers.addHandler(msgName, handler);
- }
- return handler;
- };
- /**
- * Remove a message handler
- */
- BaseClient.prototype.unlistenMsg = function (msgName, handler) {
- var _this = this;
- if (msgName instanceof RegExp) {
- Object.keys(this.serviceMap.msgName2Service).filter(function (k) { return msgName.test(k); }).forEach(function (k) {
- _this._msgHandlers.removeHandler(k, handler);
- });
- }
- else {
- this._msgHandlers.removeHandler(msgName, handler);
- }
- };
- /**
- * Remove all handlers from a message
- */
- BaseClient.prototype.unlistenMsgAll = function (msgName) {
- var _this = this;
- if (msgName instanceof RegExp) {
- Object.keys(this.serviceMap.msgName2Service).filter(function (k) { return msgName.test(k); }).forEach(function (k) {
- _this._msgHandlers.removeAllHandlers(k);
- });
- }
- else {
- this._msgHandlers.removeAllHandlers(msgName);
- }
- };
- /**
- * Abort a pending API request, it makes the promise returned by `callApi()` neither resolved nor rejected forever.
- * @param sn - Every api request has a unique `sn` number, you can get it by `this.lastSN`
- */
- BaseClient.prototype.abort = function (sn) {
- var _a, _b;
- // Find
- var index = this._pendingApis.findIndex(function (v) { return v.sn === sn; });
- if (index === -1) {
- return;
- }
- var pendingItem = this._pendingApis[index];
- // Clear
- this._pendingApis.splice(index, 1);
- pendingItem.onReturn = undefined;
- pendingItem.isAborted = true;
- // Log
- (_a = this.logger) === null || _a === void 0 ? void 0 : _a.log("[ApiAbort] #".concat(pendingItem.sn, " ").concat(pendingItem.service.name));
- // onAbort
- (_b = pendingItem.onAbort) === null || _b === void 0 ? void 0 : _b.call(pendingItem);
- };
- /**
- * Abort all API requests that has the `abortKey`.
- * It makes the promise returned by `callApi` neither resolved nor rejected forever.
- * @param abortKey - The `abortKey` of options when `callApi()`, see {@link TransportOptions.abortKey}.
- * @example
- * ```ts
- * // Send API request many times
- * client.callApi('SendData', { data: 'AAA' }, { abortKey: 'Session#123' });
- * client.callApi('SendData', { data: 'BBB' }, { abortKey: 'Session#123' });
- * client.callApi('SendData', { data: 'CCC' }, { abortKey: 'Session#123' });
- *
- * // And abort the at once
- * client.abortByKey('Session#123');
- * ```
- */
- BaseClient.prototype.abortByKey = function (abortKey) {
- var _this = this;
- this._pendingApis.filter(function (v) { return v.abortKey === abortKey; }).forEach(function (v) { _this.abort(v.sn); });
- };
- /**
- * Abort all pending API requests.
- * It makes the promise returned by `callApi` neither resolved nor rejected forever.
- */
- BaseClient.prototype.abortAll = function () {
- var _this = this;
- this._pendingApis.slice().forEach(function (v) { return _this.abort(v.sn); });
- };
- /**
- * Send data (binary or text)
- * @remarks
- * Long connection: wait res by listenning `conn.onmessage`
- * Short connection: wait res by waitting response
- * @param data
- * @param options
- * @param sn
- */
- BaseClient.prototype.sendData = function (data, options, serviceId, pendingApiItem) {
- var _a, _b, _c;
- return __awaiter(this, void 0, void 0, function () {
- var pre, preBuf;
- return __generator(this, function (_d) {
- switch (_d.label) {
- case 0: return [4 /*yield*/, this.flows.preSendDataFlow.exec({ data: data, sn: pendingApiItem === null || pendingApiItem === void 0 ? void 0 : pendingApiItem.sn }, this.logger)];
- case 1:
- pre = _d.sent();
- if (!pre) {
- return [2 /*return*/, new Promise(function (rs) { })];
- }
- data = pre.data;
- if (!(data instanceof Uint8Array)) return [3 /*break*/, 3];
- return [4 /*yield*/, this.flows.preSendBufferFlow.exec({ buf: data, sn: pendingApiItem === null || pendingApiItem === void 0 ? void 0 : pendingApiItem.sn }, this.logger)];
- case 2:
- preBuf = _d.sent();
- if (!preBuf) {
- return [2 /*return*/, new Promise(function (rs) { })];
- }
- data = preBuf.buf;
- _d.label = 3;
- case 3:
- // debugBuf log
- if (this.options.debugBuf) {
- if (typeof data === 'string') {
- (_a = this.logger) === null || _a === void 0 ? void 0 : _a.debug('[SendText]' + (pendingApiItem ? (' #' + pendingApiItem.sn) : '') + " length=".concat(data.length), data);
- }
- else if (data instanceof Uint8Array) {
- (_b = this.logger) === null || _b === void 0 ? void 0 : _b.debug('[SendBuf]' + (pendingApiItem ? (' #' + pendingApiItem.sn) : '') + " length=".concat(data.length), data);
- }
- else {
- (_c = this.logger) === null || _c === void 0 ? void 0 : _c.debug('[SendJSON]' + (pendingApiItem ? (' #' + pendingApiItem.sn) : ''), data);
- }
- }
- return [2 /*return*/, this._sendData(data, options, serviceId, pendingApiItem)];
- }
- });
- });
- };
- // 信道可传输二进制或字符串
- BaseClient.prototype._onRecvData = function (data, pendingApiItem) {
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
- return __awaiter(this, void 0, void 0, function () {
- var sn, pre, pre_1, opParsed, parsed, pre_2;
- return __generator(this, function (_k) {
- switch (_k.label) {
- case 0:
- sn = pendingApiItem === null || pendingApiItem === void 0 ? void 0 : pendingApiItem.sn;
- return [4 /*yield*/, this.flows.preRecvDataFlow.exec({ data: data, sn: sn }, this.logger)];
- case 1:
- pre = _k.sent();
- if (!pre) {
- return [2 /*return*/];
- }
- data = pre.data;
- if (!(typeof data === 'string')) return [3 /*break*/, 2];
- this.options.debugBuf && ((_a = this.logger) === null || _a === void 0 ? void 0 : _a.debug('[RecvText]' + (sn ? (' #' + sn) : ''), data));
- return [3 /*break*/, 5];
- case 2:
- if (!(data instanceof Uint8Array)) return [3 /*break*/, 4];
- this.options.debugBuf && ((_b = this.logger) === null || _b === void 0 ? void 0 : _b.debug('[RecvBuf]' + (sn ? (' #' + sn) : ''), 'length=' + data.length, data));
- return [4 /*yield*/, this.flows.preRecvBufferFlow.exec({ buf: data, sn: sn }, this.logger)];
- case 3:
- pre_1 = _k.sent();
- if (!pre_1) {
- return [2 /*return*/];
- }
- data = pre_1.buf;
- return [3 /*break*/, 5];
- case 4:
- this.options.debugBuf && ((_c = this.logger) === null || _c === void 0 ? void 0 : _c.debug('[RecvJSON]' + (sn ? (' #' + sn) : ''), data));
- _k.label = 5;
- case 5:
- opParsed = TransportDataUtil.parseServerOutout(this.tsbuffer, this.serviceMap, data, pendingApiItem === null || pendingApiItem === void 0 ? void 0 : pendingApiItem.service.id);
- if (!opParsed.isSucc) {
- (_d = this.logger) === null || _d === void 0 ? void 0 : _d.error('ParseServerOutputError: ' + opParsed.errMsg);
- if (data instanceof Uint8Array) {
- (_e = this.logger) === null || _e === void 0 ? void 0 : _e.error('Please check the version of serviceProto between server and client');
- }
- if (pendingApiItem) {
- (_f = pendingApiItem.onReturn) === null || _f === void 0 ? void 0 : _f.call(pendingApiItem, {
- isSucc: false,
- err: new TsrpcError('Parse server output error', { type: TsrpcErrorType.ServerError })
- });
- }
- return [2 /*return*/];
- }
- parsed = opParsed.result;
- if (!(parsed.type === 'api')) return [3 /*break*/, 6];
- sn = sn !== null && sn !== void 0 ? sn : parsed.sn;
- // call ApiReturn listeners
- (_h = (_g = this._pendingApis.find(function (v) { return v.sn === sn; })) === null || _g === void 0 ? void 0 : _g.onReturn) === null || _h === void 0 ? void 0 : _h.call(_g, parsed.ret);
- return [3 /*break*/, 9];
- case 6:
- if (!(parsed.type === 'msg')) return [3 /*break*/, 9];
- this.options.logMsg && ((_j = this.logger) === null || _j === void 0 ? void 0 : _j.log("[RecvMsg] ".concat(parsed.service.name), parsed.msg));
- return [4 /*yield*/, this.flows.preRecvMsgFlow.exec({ msgName: parsed.service.name, msg: parsed.msg }, this.logger)];
- case 7:
- pre_2 = _k.sent();
- if (!pre_2) {
- return [2 /*return*/];
- }
- this._msgHandlers.forEachHandler(pre_2.msgName, this.logger, pre_2.msg, pre_2.msgName);
- // Post Flow
- return [4 /*yield*/, this.flows.postRecvMsgFlow.exec(pre_2, this.logger)];
- case 8:
- // Post Flow
- _k.sent();
- _k.label = 9;
- case 9: return [2 /*return*/];
- }
- });
- });
- };
- /**
- * @param sn
- * @param timeout
- * @returns `undefined` 代表 canceled
- */
- BaseClient.prototype._waitApiReturn = function (pendingItem, timeout) {
- return __awaiter(this, void 0, void 0, function () {
- var _this = this;
- return __generator(this, function (_a) {
- return [2 /*return*/, new Promise(function (rs) {
- // Timeout
- var timer;
- if (timeout) {
- timer = setTimeout(function () {
- timer = undefined;
- _this._pendingApis.removeOne(function (v) { return v.sn === pendingItem.sn; });
- rs({
- isSucc: false,
- err: new TsrpcError('Request Timeout', {
- type: TsrpcErrorType.NetworkError,
- code: 'TIMEOUT'
- })
- });
- }, timeout);
- }
- // Listener (trigger by `this._onRecvBuf`)
- pendingItem.onReturn = function (ret) {
- if (timer) {
- clearTimeout(timer);
- timer = undefined;
- }
- _this._pendingApis.removeOne(function (v) { return v.sn === pendingItem.sn; });
- rs(ret);
- };
- })];
- });
- });
- };
- return BaseClient;
- }());
- var defaultBaseClientOptions = {
- logLevel: 'debug',
- logApi: true,
- logMsg: true,
- json: false,
- timeout: 15000,
- debugBuf: false
- };
- /**
- * Base HTTP Client
- */
- var BaseHttpClient = /** @class */ (function (_super) {
- __extends(BaseHttpClient, _super);
- function BaseHttpClient(proto, http, options) {
- var _this = this;
- var _a;
- _this = _super.call(this, proto, __assign(__assign({}, defaultBaseHttpClientOptions), options)) || this;
- _this.type = 'SHORT';
- _this._http = http;
- _this._jsonServer = _this.options.server + (_this.options.server.endsWith('/') ? '' : '/');
- (_a = _this.logger) === null || _a === void 0 ? void 0 : _a.log('TSRPC HTTP Client :', _this.options.server);
- return _this;
- }
- BaseHttpClient.prototype._sendData = function (data, options, serviceId, pendingApiItem) {
- return __awaiter(this, void 0, void 0, function () {
- var promise;
- var _this = this;
- return __generator(this, function (_a) {
- promise = (function () { return __awaiter(_this, void 0, void 0, function () {
- var service, urlSearch, url, _a, fetchPromise, abort, fetchRes;
- return __generator(this, function (_b) {
- switch (_b.label) {
- case 0:
- service = this.serviceMap.id2Service[serviceId];
- urlSearch = service.type === 'msg' ? '?type=msg' : '';
- url = typeof data === 'string' ? (this._jsonServer + service.name + urlSearch) : this.options.server;
- _a = this._http.fetch({
- url: url,
- data: data,
- method: 'POST',
- timeout: options.timeout || this.options.timeout,
- headers: { 'Content-Type': typeof data === 'string' ? 'application/json' : 'application/octet-stream' },
- transportOptions: options,
- responseType: typeof data === 'string' ? 'text' : 'arraybuffer',
- }), fetchPromise = _a.promise, abort = _a.abort;
- if (pendingApiItem) {
- pendingApiItem.onAbort = function () {
- abort();
- };
- }
- // Aborted
- if (pendingApiItem === null || pendingApiItem === void 0 ? void 0 : pendingApiItem.isAborted) {
- return [2 /*return*/, new Promise(function (rs) { })];
- }
- return [4 /*yield*/, fetchPromise];
- case 1:
- fetchRes = _b.sent();
- if (!fetchRes.isSucc) {
- return [2 /*return*/, { err: fetchRes.err }];
- }
- return [2 /*return*/, { res: fetchRes.res }];
- }
- });
- }); })();
- promise.then(function (v) {
- // Msg 不需要 onRecvData
- if (pendingApiItem && v.res) {
- _this._onRecvData(v.res, pendingApiItem);
- }
- });
- // Finally
- promise.catch(function (e) { }).then(function () {
- if (pendingApiItem) {
- pendingApiItem.onAbort = undefined;
- }
- });
- return [2 /*return*/, promise];
- });
- });
- };
- return BaseHttpClient;
- }(BaseClient));
- var defaultBaseHttpClientOptions = __assign(__assign({}, defaultBaseClientOptions), { server: 'http://localhost:3000',
- // logger: new TerminalColorLogger(),
- jsonPrune: true });
- /**
- * WebSocket Client for TSRPC.
- * It uses native `WebSocket` of browser.
- * @typeParam ServiceType - `ServiceType` from generated `proto.ts`
- */
- var BaseWsClient = /** @class */ (function (_super) {
- __extends(BaseWsClient, _super);
- function BaseWsClient(proto, wsp, options) {
- var _this = this;
- var _a;
- _this = _super.call(this, proto, __assign(__assign({}, defaultBaseWsClientOptions), options)) || this;
- _this.type = 'LONG';
- _this._onWsOpen = function () {
- var _a;
- if (!_this._connecting) {
- return;
- }
- _this._status = WsClientStatus.Opened;
- _this._connecting.rs({ isSucc: true });
- _this._connecting = undefined;
- (_a = _this.logger) === null || _a === void 0 ? void 0 : _a.log('WebSocket connection to server successful');
- _this.flows.postConnectFlow.exec({}, _this.logger);
- // First heartbeat
- if (_this.options.heartbeat) {
- _this._heartbeat();
- }
- };
- _this._onWsClose = function (code, reason) {
- var _a, _b, _c;
- // 防止重复执行
- if (_this._status === WsClientStatus.Closed) {
- return;
- }
- var isManual = !!_this._rsDisconnecting;
- var isConnectedBefore = _this.isConnected || isManual;
- _this._status = WsClientStatus.Closed;
- // 连接中,返回连接失败
- if (_this._connecting) {
- _this._connecting.rs({
- isSucc: false,
- errMsg: "Failed to connect to WebSocket server: ".concat(_this.options.server)
- });
- _this._connecting = undefined;
- (_a = _this.logger) === null || _a === void 0 ? void 0 : _a.error("Failed to connect to WebSocket server: ".concat(_this.options.server));
- }
- // Clear heartbeat
- if (_this._pendingHeartbeat) {
- clearTimeout(_this._pendingHeartbeat.timeoutTimer);
- _this._pendingHeartbeat = undefined;
- }
- if (_this._nextHeartbeatTimer) {
- clearTimeout(_this._nextHeartbeatTimer);
- }
- // disconnect中,返回成功
- if (_this._rsDisconnecting) {
- _this._rsDisconnecting();
- _this._rsDisconnecting = undefined;
- (_b = _this.logger) === null || _b === void 0 ? void 0 : _b.log('Disconnected succ', "code=".concat(code, " reason=").concat(reason));
- }
- // 非 disconnect 中,从连接中意外断开
- else if (isConnectedBefore) {
- (_c = _this.logger) === null || _c === void 0 ? void 0 : _c.log("Lost connection to ".concat(_this.options.server), "code=".concat(code, " reason=").concat(reason));
- }
- // postDisconnectFlow,仅从连接状态断开时触发
- if (isConnectedBefore) {
- _this.flows.postDisconnectFlow.exec({
- reason: reason,
- isManual: isManual
- }, _this.logger);
- }
- // 对所有请求中的 API 报错
- _this._pendingApis.slice().forEach(function (v) {
- var _a;
- (_a = v.onReturn) === null || _a === void 0 ? void 0 : _a.call(v, {
- isSucc: false,
- err: new TsrpcError(reason || 'Lost connection to server', { type: TsrpcErrorType.NetworkError, code: 'LOST_CONN' })
- });
- });
- };
- _this._onWsError = function (e) {
- var _a, _b;
- (_a = _this.logger) === null || _a === void 0 ? void 0 : _a.error('[WebSocket Error]', e);
- // 连接中,返回连接失败
- if (_this._connecting) {
- _this._connecting.rs({
- isSucc: false,
- errMsg: "Failed to connect to WebSocket server: ".concat(_this.options.server)
- });
- _this._connecting = undefined;
- (_b = _this.logger) === null || _b === void 0 ? void 0 : _b.error("Failed to connect to WebSocket server: ".concat(_this.options.server));
- }
- };
- _this._onWsMessage = function (data) {
- if (_this._status !== WsClientStatus.Opened) {
- return;
- }
- // 心跳包回包
- if (data instanceof Uint8Array && data.length === TransportDataUtil.HeartbeatPacket.length && data.every(function (v, i) { return v === TransportDataUtil.HeartbeatPacket[i]; })) {
- _this._onHeartbeatAnswer(data);
- return;
- }
- _this._onRecvData(data);
- };
- // #region Heartbeat
- /**
- * Last latency time (ms) of heartbeat test
- */
- _this.lastHeartbeatLatency = 0;
- // #endregion
- _this._status = WsClientStatus.Closed;
- _this._wsp = wsp;
- wsp.options = {
- onOpen: _this._onWsOpen,
- onClose: _this._onWsClose,
- onError: _this._onWsError,
- onMessage: _this._onWsMessage,
- logger: _this.logger
- };
- (_a = _this.logger) === null || _a === void 0 ? void 0 : _a.log('TSRPC WebSocket Client :', _this.options.server);
- return _this;
- }
- BaseWsClient.prototype._sendData = function (data) {
- return __awaiter(this, void 0, void 0, function () {
- var _this = this;
- return __generator(this, function (_a) {
- return [2 /*return*/, new Promise(function (rs) { return __awaiter(_this, void 0, void 0, function () {
- return __generator(this, function (_a) {
- if (!this.isConnected) {
- rs({
- err: new TsrpcError('WebSocket is not connected', {
- code: 'WS_NOT_OPEN',
- type: TsrpcError.Type.ClientError
- })
- });
- return [2 /*return*/];
- }
- // Do Send
- rs(this._wsp.send(data));
- return [2 /*return*/];
- });
- }); })];
- });
- });
- };
- /**
- * Send a heartbeat packet
- */
- BaseWsClient.prototype._heartbeat = function () {
- var _this = this;
- var _a;
- if (this._pendingHeartbeat || this._status !== WsClientStatus.Opened || !this.options.heartbeat) {
- return;
- }
- this._pendingHeartbeat = {
- startTime: Date.now(),
- timeoutTimer: setTimeout(function () {
- var _a;
- _this._pendingHeartbeat = undefined;
- // heartbeat timeout, disconnect if still connected
- (_a = _this.logger) === null || _a === void 0 ? void 0 : _a.error('[Heartbeat] Heartbeat timeout, the connection disconnected automatically.');
- _this._wsClose(3000, 'Heartbeat timeout');
- _this._wsp.options.onClose(3000, 'Heartbeat timeout');
- }, this.options.heartbeat.timeout)
- };
- this.options.debugBuf && ((_a = this.logger) === null || _a === void 0 ? void 0 : _a.log('[Heartbeat] Send ping', TransportDataUtil.HeartbeatPacket));
- this._sendData(TransportDataUtil.HeartbeatPacket);
- };
- BaseWsClient.prototype._onHeartbeatAnswer = function (data) {
- var _this = this;
- var _a;
- if (!this._pendingHeartbeat || this._status !== WsClientStatus.Opened || !this.options.heartbeat) {
- return;
- }
- // heartbeat succ
- this.lastHeartbeatLatency = Date.now() - this._pendingHeartbeat.startTime;
- this.options.debugBuf && ((_a = this.logger) === null || _a === void 0 ? void 0 : _a.log("[Heartbeat] Recv pong, latency=".concat(this.lastHeartbeatLatency, "ms"), data));
- clearTimeout(this._pendingHeartbeat.timeoutTimer);
- this._pendingHeartbeat = undefined;
- // next heartbeat timer
- this._nextHeartbeatTimer = setTimeout(function () {
- _this._heartbeat();
- }, this.options.heartbeat.interval);
- };
- Object.defineProperty(BaseWsClient.prototype, "status", {
- get: function () {
- return this._status;
- },
- enumerable: false,
- configurable: true
- });
- Object.defineProperty(BaseWsClient.prototype, "isConnected", {
- get: function () {
- return this._status === WsClientStatus.Opened;
- },
- enumerable: false,
- configurable: true
- });
- /**
- * Start connecting, you must connect first before `callApi()` and `sendMsg()`.
- * @throws never
- */
- BaseWsClient.prototype.connect = function () {
- var _a, _b;
- return __awaiter(this, void 0, void 0, function () {
- var pre, promiseConnect;
- var _this = this;
- return __generator(this, function (_c) {
- switch (_c.label) {
- case 0:
- // 已连接成功
- if (this.isConnected) {
- return [2 /*return*/, { isSucc: true }];
- }
- // 已连接中
- if (this._connecting) {
- return [2 /*return*/, this._connecting.promise];
- }
- return [4 /*yield*/, this.flows.preConnectFlow.exec({}, this.logger)];
- case 1:
- pre = _c.sent();
- // Pre return
- if (pre === null || pre === void 0 ? void 0 : pre.return) {
- return [2 /*return*/, pre.return];
- }
- // Canceled
- if (!pre) {
- return [2 /*return*/, new Promise(function (rs) { })];
- }
- try {
- this._wsp.connect(this.options.server, [this.options.json ? 'text' : 'buffer']);
- }
- catch (e) {
- (_a = this.logger) === null || _a === void 0 ? void 0 : _a.error(e);
- return [2 /*return*/, { isSucc: false, errMsg: e.message }];
- }
- this._status = WsClientStatus.Opening;
- (_b = this.logger) === null || _b === void 0 ? void 0 : _b.log("Start connecting ".concat(this.options.server, "..."));
- this._connecting = {};
- promiseConnect = new Promise(function (rs) {
- _this._connecting.rs = rs;
- });
- this._connecting.promise = promiseConnect;
- return [2 /*return*/, promiseConnect];
- }
- });
- });
- };
- /**
- * Disconnect immediately
- * @throws never
- */
- BaseWsClient.prototype.disconnect = function (code, reason) {
- var _a;
- return __awaiter(this, void 0, void 0, function () {
- var isClosed;
- var _this = this;
- return __generator(this, function (_b) {
- if (this._status === WsClientStatus.Closed) {
- return [2 /*return*/];
- }
- this._status = WsClientStatus.Closing;
- (_a = this.logger) === null || _a === void 0 ? void 0 : _a.log('Start disconnecting...');
- isClosed = false;
- return [2 /*return*/, Promise.race([
- // 正常等待 onClose 关闭
- new Promise(function (rs) {
- _this._rsDisconnecting = function () {
- if (isClosed) {
- return;
- }
- isClosed = true;
- rs();
- };
- _this._wsClose(code !== null && code !== void 0 ? code : 1000, reason !== null && reason !== void 0 ? reason : '');
- }),
- // 超时保护,1 秒未收到关闭请求的,直接 onClose 掉
- new Promise(function (rs) {
- setTimeout(function () {
- if (isClosed) {
- return;
- }
- isClosed = true;
- _this._onWsClose(1005, 'Connection closed, but not received ws.onClose event.');
- }, 1000);
- })
- ])];
- });
- });
- };
- BaseWsClient.prototype._wsClose = function (code, reason) {
- var _a;
- try {
- this._wsp.close(code !== null && code !== void 0 ? code : 1000, reason !== null && reason !== void 0 ? reason : '');
- }
- catch (e) {
- (_a = this.logger) === null || _a === void 0 ? void 0 : _a.error('[WsCloseError]', e);
- }
- };
- return BaseWsClient;
- }(BaseClient));
- var defaultBaseWsClientOptions = __assign(__assign({}, defaultBaseClientOptions), { server: 'ws://localhost:3000' });
- var WsClientStatus;
- (function (WsClientStatus) {
- WsClientStatus["Opening"] = "OPENING";
- WsClientStatus["Opened"] = "OPENED";
- WsClientStatus["Closing"] = "CLOSING";
- WsClientStatus["Closed"] = "CLOSED";
- })(WsClientStatus || (WsClientStatus = {}));
- export { BaseClient, BaseHttpClient, BaseWsClient, Counter, Flow, MsgHandlerManager, ServiceMapUtil, TransportDataUtil, WsClientStatus, defaultBaseClientOptions, defaultBaseHttpClientOptions, defaultBaseWsClientOptions, getCustomObjectIdTypes };
|