leancloud-realtime
Version:
LeanCloud Realtime Message JavaScript SDK
2,986 lines • 64.8 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var protobufLight = _interopDefault(require('protobufjs/dist/protobuf-light'));
var EventEmitter = _interopDefault(require('eventemitter3'));
var d = _interopDefault(require('debug'));
var shuffle = _interopDefault(require('lodash/shuffle'));
var values = _interopDefault(require('lodash/values'));
var StateMachine = _interopDefault(require('javascript-state-machine'));
var isPlainObject = _interopDefault(require('lodash/isPlainObject'));
var promiseTimeout = require('promise-timeout');
var messageCompiled = protobufLight.newBuilder({})['import']({
package: 'push_server.messages2',
syntax: 'proto2',
options: {
objc_class_prefix: 'AVIM'
},
messages: [{
name: 'JsonObjectMessage',
syntax: 'proto2',
fields: [{
rule: 'required',
type: 'string',
name: 'data',
id: 1
}]
}, {
name: 'UnreadTuple',
syntax: 'proto2',
fields: [{
rule: 'required',
type: 'string',
name: 'cid',
id: 1
}, {
rule: 'required',
type: 'int32',
name: 'unread',
id: 2
}, {
rule: 'optional',
type: 'string',
name: 'mid',
id: 3
}, {
rule: 'optional',
type: 'int64',
name: 'timestamp',
id: 4
}, {
rule: 'optional',
type: 'string',
name: 'from',
id: 5
}, {
rule: 'optional',
type: 'string',
name: 'data',
id: 6
}, {
rule: 'optional',
type: 'int64',
name: 'patchTimestamp',
id: 7
}, {
rule: 'optional',
type: 'bool',
name: 'mentioned',
id: 8
}, {
rule: 'optional',
type: 'bytes',
name: 'binaryMsg',
id: 9
}, {
rule: 'optional',
type: 'int32',
name: 'convType',
id: 10
}]
}, {
name: 'LogItem',
syntax: 'proto2',
fields: [{
rule: 'optional',
type: 'string',
name: 'from',
id: 1
}, {
rule: 'optional',
type: 'string',
name: 'data',
id: 2
}, {
rule: 'optional',
type: 'int64',
name: 'timestamp',
id: 3
}, {
rule: 'optional',
type: 'string',
name: 'msgId',
id: 4
}, {
rule: 'optional',
type: 'int64',
name: 'ackAt',
id: 5
}, {
rule: 'optional',
type: 'int64',
name: 'readAt',
id: 6
}, {
rule: 'optional',
type: 'int64',
name: 'patchTimestamp',
id: 7
}, {
rule: 'optional',
type: 'bool',
name: 'mentionAll',
id: 8
}, {
rule: 'repeated',
type: 'string',
name: 'mentionPids',
id: 9
}, {
rule: 'optional',
type: 'bool',
name: 'bin',
id: 10
}, {
rule: 'optional',
type: 'int32',
name: 'convType',
id: 11
}]
}, {
name: 'ConvMemberInfo',
syntax: 'proto2',
fields: [{
rule: 'optional',
type: 'string',
name: 'pid',
id: 1
}, {
rule: 'optional',
type: 'string',
name: 'role',
id: 2
}, {
rule: 'optional',
type: 'string',
name: 'infoId',
id: 3
}]
}, {
name: 'DataCommand',
syntax: 'proto2',
fields: [{
rule: 'repeated',
type: 'string',
name: 'ids',
id: 1
}, {
rule: 'repeated',
type: 'JsonObjectMessage',
name: 'msg',
id: 2
}, {
rule: 'optional',
type: 'bool',
name: 'offline',
id: 3
}]
}, {
name: 'SessionCommand',
syntax: 'proto2',
fields: [{
rule: 'optional',
type: 'int64',
name: 't',
id: 1
}, {
rule: 'optional',
type: 'string',
name: 'n',
id: 2
}, {
rule: 'optional',
type: 'string',
name: 's',
id: 3
}, {
rule: 'optional',
type: 'string',
name: 'ua',
id: 4
}, {
rule: 'optional',
type: 'bool',
name: 'r',
id: 5
}, {
rule: 'optional',
type: 'string',
name: 'tag',
id: 6
}, {
rule: 'optional',
type: 'string',
name: 'deviceId',
id: 7
}, {
rule: 'repeated',
type: 'string',
name: 'sessionPeerIds',
id: 8
}, {
rule: 'repeated',
type: 'string',
name: 'onlineSessionPeerIds',
id: 9
}, {
rule: 'optional',
type: 'string',
name: 'st',
id: 10
}, {
rule: 'optional',
type: 'int32',
name: 'stTtl',
id: 11
}, {
rule: 'optional',
type: 'int32',
name: 'code',
id: 12
}, {
rule: 'optional',
type: 'string',
name: 'reason',
id: 13
}, {
rule: 'optional',
type: 'string',
name: 'deviceToken',
id: 14
}, {
rule: 'optional',
type: 'bool',
name: 'sp',
id: 15
}, {
rule: 'optional',
type: 'string',
name: 'detail',
id: 16
}, {
rule: 'optional',
type: 'int64',
name: 'lastUnreadNotifTime',
id: 17
}, {
rule: 'optional',
type: 'int64',
name: 'lastPatchTime',
id: 18
}, {
rule: 'optional',
type: 'int64',
name: 'configBitmap',
id: 19
}]
}, {
name: 'ErrorCommand',
syntax: 'proto2',
fields: [{
rule: 'required',
type: 'int32',
name: 'code',
id: 1
}, {
rule: 'required',
type: 'string',
name: 'reason',
id: 2
}, {
rule: 'optional',
type: 'int32',
name: 'appCode',
id: 3
}, {
rule: 'optional',
type: 'string',
name: 'detail',
id: 4
}, {
rule: 'repeated',
type: 'string',
name: 'pids',
id: 5
}, {
rule: 'optional',
type: 'string',
name: 'appMsg',
id: 6
}]
}, {
name: 'DirectCommand',
syntax: 'proto2',
fields: [{
rule: 'optional',
type: 'string',
name: 'msg',
id: 1
}, {
rule: 'optional',
type: 'string',
name: 'uid',
id: 2
}, {
rule: 'optional',
type: 'string',
name: 'fromPeerId',
id: 3
}, {
rule: 'optional',
type: 'int64',
name: 'timestamp',
id: 4
}, {
rule: 'optional',
type: 'bool',
name: 'offline',
id: 5
}, {
rule: 'optional',
type: 'bool',
name: 'hasMore',
id: 6
}, {
rule: 'repeated',
type: 'string',
name: 'toPeerIds',
id: 7
}, {
rule: 'optional',
type: 'bool',
name: 'r',
id: 10
}, {
rule: 'optional',
type: 'string',
name: 'cid',
id: 11
}, {
rule: 'optional',
type: 'string',
name: 'id',
id: 12
}, {
rule: 'optional',
type: 'bool',
name: 'transient',
id: 13
}, {
rule: 'optional',
type: 'string',
name: 'dt',
id: 14
}, {
rule: 'optional',
type: 'string',
name: 'roomId',
id: 15
}, {
rule: 'optional',
type: 'string',
name: 'pushData',
id: 16
}, {
rule: 'optional',
type: 'bool',
name: 'will',
id: 17
}, {
rule: 'optional',
type: 'int64',
name: 'patchTimestamp',
id: 18
}, {
rule: 'optional',
type: 'bytes',
name: 'binaryMsg',
id: 19
}, {
rule: 'repeated',
type: 'string',
name: 'mentionPids',
id: 20
}, {
rule: 'optional',
type: 'bool',
name: 'mentionAll',
id: 21
}, {
rule: 'optional',
type: 'int32',
name: 'convType',
id: 22
}]
}, {
name: 'AckCommand',
syntax: 'proto2',
fields: [{
rule: 'optional',
type: 'int32',
name: 'code',
id: 1
}, {
rule: 'optional',
type: 'string',
name: 'reason',
id: 2
}, {
rule: 'optional',
type: 'string',
name: 'mid',
id: 3
}, {
rule: 'optional',
type: 'string',
name: 'cid',
id: 4
}, {
rule: 'optional',
type: 'int64',
name: 't',
id: 5
}, {
rule: 'optional',
type: 'string',
name: 'uid',
id: 6
}, {
rule: 'optional',
type: 'int64',
name: 'fromts',
id: 7
}, {
rule: 'optional',
type: 'int64',
name: 'tots',
id: 8
}, {
rule: 'optional',
type: 'string',
name: 'type',
id: 9
}, {
rule: 'repeated',
type: 'string',
name: 'ids',
id: 10
}, {
rule: 'optional',
type: 'int32',
name: 'appCode',
id: 11
}, {
rule: 'optional',
type: 'string',
name: 'appMsg',
id: 12
}]
}, {
name: 'UnreadCommand',
syntax: 'proto2',
fields: [{
rule: 'repeated',
type: 'UnreadTuple',
name: 'convs',
id: 1
}, {
rule: 'optional',
type: 'int64',
name: 'notifTime',
id: 2
}]
}, {
name: 'ConvCommand',
syntax: 'proto2',
fields: [{
rule: 'repeated',
type: 'string',
name: 'm',
id: 1
}, {
rule: 'optional',
type: 'bool',
name: 'transient',
id: 2
}, {
rule: 'optional',
type: 'bool',
name: 'unique',
id: 3
}, {
rule: 'optional',
type: 'string',
name: 'cid',
id: 4
}, {
rule: 'optional',
type: 'string',
name: 'cdate',
id: 5
}, {
rule: 'optional',
type: 'string',
name: 'initBy',
id: 6
}, {
rule: 'optional',
type: 'string',
name: 'sort',
id: 7
}, {
rule: 'optional',
type: 'int32',
name: 'limit',
id: 8
}, {
rule: 'optional',
type: 'int32',
name: 'skip',
id: 9
}, {
rule: 'optional',
type: 'int32',
name: 'flag',
id: 10
}, {
rule: 'optional',
type: 'int32',
name: 'count',
id: 11
}, {
rule: 'optional',
type: 'string',
name: 'udate',
id: 12
}, {
rule: 'optional',
type: 'int64',
name: 't',
id: 13
}, {
rule: 'optional',
type: 'string',
name: 'n',
id: 14
}, {
rule: 'optional',
type: 'string',
name: 's',
id: 15
}, {
rule: 'optional',
type: 'bool',
name: 'statusSub',
id: 16
}, {
rule: 'optional',
type: 'bool',
name: 'statusPub',
id: 17
}, {
rule: 'optional',
type: 'int32',
name: 'statusTTL',
id: 18
}, {
rule: 'optional',
type: 'string',
name: 'uniqueId',
id: 19
}, {
rule: 'optional',
type: 'string',
name: 'targetClientId',
id: 20
}, {
rule: 'optional',
type: 'int64',
name: 'maxReadTimestamp',
id: 21
}, {
rule: 'optional',
type: 'int64',
name: 'maxAckTimestamp',
id: 22
}, {
rule: 'optional',
type: 'bool',
name: 'queryAllMembers',
id: 23
}, {
rule: 'repeated',
type: 'MaxReadTuple',
name: 'maxReadTuples',
id: 24
}, {
rule: 'repeated',
type: 'string',
name: 'cids',
id: 25
}, {
rule: 'optional',
type: 'ConvMemberInfo',
name: 'info',
id: 26
}, {
rule: 'optional',
type: 'bool',
name: 'tempConv',
id: 27
}, {
rule: 'optional',
type: 'int32',
name: 'tempConvTTL',
id: 28
}, {
rule: 'repeated',
type: 'string',
name: 'tempConvIds',
id: 29
}, {
rule: 'repeated',
type: 'string',
name: 'allowedPids',
id: 30
}, {
rule: 'repeated',
type: 'ErrorCommand',
name: 'failedPids',
id: 31
}, {
rule: 'optional',
type: 'string',
name: 'next',
id: 40
}, {
rule: 'optional',
type: 'JsonObjectMessage',
name: 'results',
id: 100
}, {
rule: 'optional',
type: 'JsonObjectMessage',
name: 'where',
id: 101
}, {
rule: 'optional',
type: 'JsonObjectMessage',
name: 'attr',
id: 103
}, {
rule: 'optional',
type: 'JsonObjectMessage',
name: 'attrModified',
id: 104
}]
}, {
name: 'RoomCommand',
syntax: 'proto2',
fields: [{
rule: 'optional',
type: 'string',
name: 'roomId',
id: 1
}, {
rule: 'optional',
type: 'string',
name: 's',
id: 2
}, {
rule: 'optional',
type: 'int64',
name: 't',
id: 3
}, {
rule: 'optional',
type: 'string',
name: 'n',
id: 4
}, {
rule: 'optional',
type: 'bool',
name: 'transient',
id: 5
}, {
rule: 'repeated',
type: 'string',
name: 'roomPeerIds',
id: 6
}, {
rule: 'optional',
type: 'string',
name: 'byPeerId',
id: 7
}]
}, {
name: 'LogsCommand',
syntax: 'proto2',
fields: [{
rule: 'optional',
type: 'string',
name: 'cid',
id: 1
}, {
rule: 'optional',
type: 'int32',
name: 'l',
id: 2
}, {
rule: 'optional',
type: 'int32',
name: 'limit',
id: 3
}, {
rule: 'optional',
type: 'int64',
name: 't',
id: 4
}, {
rule: 'optional',
type: 'int64',
name: 'tt',
id: 5
}, {
rule: 'optional',
type: 'string',
name: 'tmid',
id: 6
}, {
rule: 'optional',
type: 'string',
name: 'mid',
id: 7
}, {
rule: 'optional',
type: 'string',
name: 'checksum',
id: 8
}, {
rule: 'optional',
type: 'bool',
name: 'stored',
id: 9
}, {
rule: 'optional',
type: 'QueryDirection',
name: 'direction',
id: 10,
options: {
default: 'OLD'
}
}, {
rule: 'optional',
type: 'bool',
name: 'tIncluded',
id: 11
}, {
rule: 'optional',
type: 'bool',
name: 'ttIncluded',
id: 12
}, {
rule: 'optional',
type: 'int32',
name: 'lctype',
id: 13
}, {
rule: 'repeated',
type: 'LogItem',
name: 'logs',
id: 105
}],
enums: [{
name: 'QueryDirection',
syntax: 'proto2',
values: [{
name: 'OLD',
id: 1
}, {
name: 'NEW',
id: 2
}]
}]
}, {
name: 'RcpCommand',
syntax: 'proto2',
fields: [{
rule: 'optional',
type: 'string',
name: 'id',
id: 1
}, {
rule: 'optional',
type: 'string',
name: 'cid',
id: 2
}, {
rule: 'optional',
type: 'int64',
name: 't',
id: 3
}, {
rule: 'optional',
type: 'bool',
name: 'read',
id: 4
}, {
rule: 'optional',
type: 'string',
name: 'from',
id: 5
}]
}, {
name: 'ReadTuple',
syntax: 'proto2',
fields: [{
rule: 'required',
type: 'string',
name: 'cid',
id: 1
}, {
rule: 'optional',
type: 'int64',
name: 'timestamp',
id: 2
}, {
rule: 'optional',
type: 'string',
name: 'mid',
id: 3
}]
}, {
name: 'MaxReadTuple',
syntax: 'proto2',
fields: [{
rule: 'optional',
type: 'string',
name: 'pid',
id: 1
}, {
rule: 'optional',
type: 'int64',
name: 'maxAckTimestamp',
id: 2
}, {
rule: 'optional',
type: 'int64',
name: 'maxReadTimestamp',
id: 3
}]
}, {
name: 'ReadCommand',
syntax: 'proto2',
fields: [{
rule: 'optional',
type: 'string',
name: 'cid',
id: 1
}, {
rule: 'repeated',
type: 'string',
name: 'cids',
id: 2
}, {
rule: 'repeated',
type: 'ReadTuple',
name: 'convs',
id: 3
}]
}, {
name: 'PresenceCommand',
syntax: 'proto2',
fields: [{
rule: 'optional',
type: 'StatusType',
name: 'status',
id: 1
}, {
rule: 'repeated',
type: 'string',
name: 'sessionPeerIds',
id: 2
}, {
rule: 'optional',
type: 'string',
name: 'cid',
id: 3
}]
}, {
name: 'ReportCommand',
syntax: 'proto2',
fields: [{
rule: 'optional',
type: 'bool',
name: 'initiative',
id: 1
}, {
rule: 'optional',
type: 'string',
name: 'type',
id: 2
}, {
rule: 'optional',
type: 'string',
name: 'data',
id: 3
}]
}, {
name: 'PatchItem',
syntax: 'proto2',
fields: [{
rule: 'optional',
type: 'string',
name: 'cid',
id: 1
}, {
rule: 'optional',
type: 'string',
name: 'mid',
id: 2
}, {
rule: 'optional',
type: 'int64',
name: 'timestamp',
id: 3
}, {
rule: 'optional',
type: 'bool',
name: 'recall',
id: 4
}, {
rule: 'optional',
type: 'string',
name: 'data',
id: 5
}, {
rule: 'optional',
type: 'int64',
name: 'patchTimestamp',
id: 6
}, {
rule: 'optional',
type: 'string',
name: 'from',
id: 7
}, {
rule: 'optional',
type: 'bytes',
name: 'binaryMsg',
id: 8
}, {
rule: 'optional',
type: 'bool',
name: 'mentionAll',
id: 9
}, {
rule: 'repeated',
type: 'string',
name: 'mentionPids',
id: 10
}, {
rule: 'optional',
type: 'int64',
name: 'patchCode',
id: 11
}, {
rule: 'optional',
type: 'string',
name: 'patchReason',
id: 12
}]
}, {
name: 'PatchCommand',
syntax: 'proto2',
fields: [{
rule: 'repeated',
type: 'PatchItem',
name: 'patches',
id: 1
}, {
rule: 'optional',
type: 'int64',
name: 'lastPatchTime',
id: 2
}]
}, {
name: 'PubsubCommand',
syntax: 'proto2',
fields: [{
rule: 'optional',
type: 'string',
name: 'cid',
id: 1
}, {
rule: 'repeated',
type: 'string',
name: 'cids',
id: 2
}, {
rule: 'optional',
type: 'string',
name: 'topic',
id: 3
}, {
rule: 'optional',
type: 'string',
name: 'subtopic',
id: 4
}, {
rule: 'repeated',
type: 'string',
name: 'topics',
id: 5
}, {
rule: 'repeated',
type: 'string',
name: 'subtopics',
id: 6
}, {
rule: 'optional',
type: 'JsonObjectMessage',
name: 'results',
id: 7
}]
}, {
name: 'BlacklistCommand',
syntax: 'proto2',
fields: [{
rule: 'optional',
type: 'string',
name: 'srcCid',
id: 1
}, {
rule: 'repeated',
type: 'string',
name: 'toPids',
id: 2
}, {
rule: 'optional',
type: 'string',
name: 'srcPid',
id: 3
}, {
rule: 'repeated',
type: 'string',
name: 'toCids',
id: 4
}, {
rule: 'optional',
type: 'int32',
name: 'limit',
id: 5
}, {
rule: 'optional',
type: 'string',
name: 'next',
id: 6
}, {
rule: 'repeated',
type: 'string',
name: 'blockedPids',
id: 8
}, {
rule: 'repeated',
type: 'string',
name: 'blockedCids',
id: 9
}, {
rule: 'repeated',
type: 'string',
name: 'allowedPids',
id: 10
}, {
rule: 'repeated',
type: 'ErrorCommand',
name: 'failedPids',
id: 11
}, {
rule: 'optional',
type: 'int64',
name: 't',
id: 12
}, {
rule: 'optional',
type: 'string',
name: 'n',
id: 13
}, {
rule: 'optional',
type: 'string',
name: 's',
id: 14
}]
}, {
name: 'GenericCommand',
syntax: 'proto2',
fields: [{
rule: 'optional',
type: 'CommandType',
name: 'cmd',
id: 1
}, {
rule: 'optional',
type: 'OpType',
name: 'op',
id: 2
}, {
rule: 'optional',
type: 'string',
name: 'appId',
id: 3
}, {
rule: 'optional',
type: 'string',
name: 'peerId',
id: 4
}, {
rule: 'optional',
type: 'int32',
name: 'i',
id: 5
}, {
rule: 'optional',
type: 'string',
name: 'installationId',
id: 6
}, {
rule: 'optional',
type: 'int32',
name: 'priority',
id: 7
}, {
rule: 'optional',
type: 'int32',
name: 'service',
id: 8
}, {
rule: 'optional',
type: 'int64',
name: 'serverTs',
id: 9
}, {
rule: 'optional',
type: 'int64',
name: 'clientTs',
id: 10
}, {
rule: 'optional',
type: 'int32',
name: 'notificationType',
id: 11
}, {
rule: 'optional',
type: 'DataCommand',
name: 'dataMessage',
id: 101
}, {
rule: 'optional',
type: 'SessionCommand',
name: 'sessionMessage',
id: 102
}, {
rule: 'optional',
type: 'ErrorCommand',
name: 'errorMessage',
id: 103
}, {
rule: 'optional',
type: 'DirectCommand',
name: 'directMessage',
id: 104
}, {
rule: 'optional',
type: 'AckCommand',
name: 'ackMessage',
id: 105
}, {
rule: 'optional',
type: 'UnreadCommand',
name: 'unreadMessage',
id: 106
}, {
rule: 'optional',
type: 'ReadCommand',
name: 'readMessage',
id: 107
}, {
rule: 'optional',
type: 'RcpCommand',
name: 'rcpMessage',
id: 108
}, {
rule: 'optional',
type: 'LogsCommand',
name: 'logsMessage',
id: 109
}, {
rule: 'optional',
type: 'ConvCommand',
name: 'convMessage',
id: 110
}, {
rule: 'optional',
type: 'RoomCommand',
name: 'roomMessage',
id: 111
}, {
rule: 'optional',
type: 'PresenceCommand',
name: 'presenceMessage',
id: 112
}, {
rule: 'optional',
type: 'ReportCommand',
name: 'reportMessage',
id: 113
}, {
rule: 'optional',
type: 'PatchCommand',
name: 'patchMessage',
id: 114
}, {
rule: 'optional',
type: 'PubsubCommand',
name: 'pubsubMessage',
id: 115
}, {
rule: 'optional',
type: 'BlacklistCommand',
name: 'blacklistMessage',
id: 116
}]
}],
enums: [{
name: 'CommandType',
syntax: 'proto2',
values: [{
name: 'session',
id: 0
}, {
name: 'conv',
id: 1
}, {
name: 'direct',
id: 2
}, {
name: 'ack',
id: 3
}, {
name: 'rcp',
id: 4
}, {
name: 'unread',
id: 5
}, {
name: 'logs',
id: 6
}, {
name: 'error',
id: 7
}, {
name: 'login',
id: 8
}, {
name: 'data',
id: 9
}, {
name: 'room',
id: 10
}, {
name: 'read',
id: 11
}, {
name: 'presence',
id: 12
}, {
name: 'report',
id: 13
}, {
name: 'echo',
id: 14
}, {
name: 'loggedin',
id: 15
}, {
name: 'logout',
id: 16
}, {
name: 'loggedout',
id: 17
}, {
name: 'patch',
id: 18
}, {
name: 'pubsub',
id: 19
}, {
name: 'blacklist',
id: 20
}, {
name: 'goaway',
id: 21
}]
}, {
name: 'OpType',
syntax: 'proto2',
values: [{
name: 'open',
id: 1
}, {
name: 'add',
id: 2
}, {
name: 'remove',
id: 3
}, {
name: 'close',
id: 4
}, {
name: 'opened',
id: 5
}, {
name: 'closed',
id: 6
}, {
name: 'query',
id: 7
}, {
name: 'query_result',
id: 8
}, {
name: 'conflict',
id: 9
}, {
name: 'added',
id: 10
}, {
name: 'removed',
id: 11
}, {
name: 'refresh',
id: 12
}, {
name: 'refreshed',
id: 13
}, {
name: 'start',
id: 30
}, {
name: 'started',
id: 31
}, {
name: 'joined',
id: 32
}, {
name: 'members_joined',
id: 33
}, {
name: 'left',
id: 39
}, {
name: 'members_left',
id: 40
}, {
name: 'results',
id: 42
}, {
name: 'count',
id: 43
}, {
name: 'result',
id: 44
}, {
name: 'update',
id: 45
}, {
name: 'updated',
id: 46
}, {
name: 'mute',
id: 47
}, {
name: 'unmute',
id: 48
}, {
name: 'status',
id: 49
}, {
name: 'members',
id: 50
}, {
name: 'max_read',
id: 51
}, {
name: 'is_member',
id: 52
}, {
name: 'member_info_update',
id: 53
}, {
name: 'member_info_updated',
id: 54
}, {
name: 'member_info_changed',
id: 55
}, {
name: 'join',
id: 80
}, {
name: 'invite',
id: 81
}, {
name: 'leave',
id: 82
}, {
name: 'kick',
id: 83
}, {
name: 'reject',
id: 84
}, {
name: 'invited',
id: 85
}, {
name: 'kicked',
id: 86
}, {
name: 'upload',
id: 100
}, {
name: 'uploaded',
id: 101
}, {
name: 'subscribe',
id: 120
}, {
name: 'subscribed',
id: 121
}, {
name: 'unsubscribe',
id: 122
}, {
name: 'unsubscribed',
id: 123
}, {
name: 'is_subscribed',
id: 124
}, {
name: 'modify',
id: 150
}, {
name: 'modified',
id: 151
}, {
name: 'block',
id: 170
}, {
name: 'unblock',
id: 171
}, {
name: 'blocked',
id: 172
}, {
name: 'unblocked',
id: 173
}, {
name: 'members_blocked',
id: 174
}, {
name: 'members_unblocked',
id: 175
}, {
name: 'check_block',
id: 176
}, {
name: 'check_result',
id: 177
}, {
name: 'add_shutup',
id: 180
}, {
name: 'remove_shutup',
id: 181
}, {
name: 'query_shutup',
id: 182
}, {
name: 'shutup_added',
id: 183
}, {
name: 'shutup_removed',
id: 184
}, {
name: 'shutup_result',
id: 185
}, {
name: 'shutuped',
id: 186
}, {
name: 'unshutuped',
id: 187
}, {
name: 'members_shutuped',
id: 188
}, {
name: 'members_unshutuped',
id: 189
}, {
name: 'check_shutup',
id: 190
}]
}, {
name: 'StatusType',
syntax: 'proto2',
values: [{
name: 'on',
id: 1
}, {
name: 'off',
id: 2
}]
}],
isNamespace: true
}).build();
const {
JsonObjectMessage,
UnreadTuple,
LogItem,
DataCommand,
SessionCommand,
ErrorCommand,
DirectCommand,
AckCommand,
UnreadCommand,
ConvCommand,
RoomCommand,
LogsCommand,
RcpCommand,
ReadTuple,
MaxReadTuple,
ReadCommand,
PresenceCommand,
ReportCommand,
GenericCommand,
BlacklistCommand,
PatchCommand,
PatchItem,
ConvMemberInfo,
CommandType,
OpType,
StatusType
} = messageCompiled.push_server.messages2;
var message = /*#__PURE__*/Object.freeze({
__proto__: null,
JsonObjectMessage: JsonObjectMessage,
UnreadTuple: UnreadTuple,
LogItem: LogItem,
DataCommand: DataCommand,
SessionCommand: SessionCommand,
ErrorCommand: ErrorCommand,
DirectCommand: DirectCommand,
AckCommand: AckCommand,
UnreadCommand: UnreadCommand,
ConvCommand: ConvCommand,
RoomCommand: RoomCommand,
LogsCommand: LogsCommand,
RcpCommand: RcpCommand,
ReadTuple: ReadTuple,
MaxReadTuple: MaxReadTuple,
ReadCommand: ReadCommand,
PresenceCommand: PresenceCommand,
ReportCommand: ReportCommand,
GenericCommand: GenericCommand,
BlacklistCommand: BlacklistCommand,
PatchCommand: PatchCommand,
PatchItem: PatchItem,
ConvMemberInfo: ConvMemberInfo,
CommandType: CommandType,
OpType: OpType,
StatusType: StatusType
});
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
var desc = {};
Object.keys(descriptor).forEach(function (key) {
desc[key] = descriptor[key];
});
desc.enumerable = !!desc.enumerable;
desc.configurable = !!desc.configurable;
if ('value' in desc || desc.initializer) {
desc.writable = true;
}
desc = decorators.slice().reverse().reduce(function (desc, decorator) {
return decorator(target, property, desc) || desc;
}, desc);
if (context && desc.initializer !== void 0) {
desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
desc.initializer = undefined;
}
if (desc.initializer === void 0) {
Object.defineProperty(target, property, desc);
desc = null;
}
return desc;
}
const adapters = {};
const getAdapter = name => {
const adapter = adapters[name];
if (adapter === undefined) {
throw new Error(`${name} adapter is not configured`);
}
return adapter;
};
/**
* 指定 Adapters
* @function
* @memberof module:leancloud-realtime
* @param {Adapters} newAdapters Adapters 的类型请参考 {@link https://url.leanapp.cn/adapter-type-definitions @leancloud/adapter-types} 中的定义
*/
const setAdapters = newAdapters => {
Object.assign(adapters, newAdapters);
};
/* eslint-disable */
var global$1 = typeof global !== 'undefined' ? global : typeof window !== 'undefined' ? window : {};
const EXPIRED = Symbol('expired');
const debug = d('LC:Expirable');
class Expirable {
constructor(value, ttl) {
this.originalValue = value;
if (typeof ttl === 'number') {
this.expiredAt = Date.now() + ttl;
}
}
get value() {
const expired = this.expiredAt && this.expiredAt <= Date.now();
if (expired) debug(`expired: ${this.originalValue}`);
return expired ? EXPIRED : this.originalValue;
}
}
Expirable.EXPIRED = EXPIRED;
const debug$1 = d('LC:Cache');
class Cache {
constructor(name = 'anonymous') {
this.name = name;
this._map = {};
}
get(key) {
const cache = this._map[key];
if (cache) {
const {
value
} = cache;
if (value !== Expirable.EXPIRED) {
debug$1('[%s] hit: %s', this.name, key);
return value;
}
delete this._map[key];
}
debug$1(`[${this.name}] missed: ${key}`);
return null;
}
set(key, value, ttl) {
debug$1('[%s] set: %s %d', this.name, key, ttl);
this._map[key] = new Expirable(value, ttl);
}
}
/**
* 调试日志控制器
* @const
* @memberof module:leancloud-realtime
* @example
* debug.enable(); // 启用调试日志
* debug.disable(); // 关闭调试日志
*/
const debug$2 = {
enable: (namespaces = 'LC*') => d.enable(namespaces),
disable: d.disable
};
const tryAll = promiseConstructors => {
const promise = new Promise(promiseConstructors[0]);
if (promiseConstructors.length === 1) {
return promise;
}
return promise.catch(() => tryAll(promiseConstructors.slice(1)));
}; // eslint-disable-next-line no-sequences
const tap = interceptor => value => (interceptor(value), value);
const isIE10 = global$1.navigator && global$1.navigator.userAgent && global$1.navigator.userAgent.indexOf('MSIE 10.') !== -1;
const map = new WeakMap(); // protected property helper
const internal = object => {
if (!map.has(object)) {
map.set(object, {});
}
return map.get(object);
};
const compact = (obj, filter) => {
if (!isPlainObject(obj)) return obj;
const object = { ...obj
};
Object.keys(object).forEach(prop => {
const value = object[prop];
if (value === filter) {
delete object[prop];
} else {
object[prop] = compact(value, filter);
}
});
return object;
}; // debug utility
const removeNull = obj => compact(obj, null);
const trim = message => removeNull(JSON.parse(JSON.stringify(message)));
const ensureArray = target => {
if (Array.isArray(target)) {
return target;
}
if (target === undefined || target === null) {
return [];
}
return [target];
};
const isWeapp = // eslint-disable-next-line no-undef
typeof wx === 'object' && typeof wx.connectSocket === 'function'; // throttle decorator
const isCNApp = appId => appId.slice(-9) !== '-MdYXbMMI';
const equalBuffer = (buffer1, buffer2) => {
if (!buffer1 || !buffer2) return false;
if (buffer1.byteLength !== buffer2.byteLength) return false;
const a = new Uint8Array(buffer1);
const b = new Uint8Array(buffer2);
return !a.some((value, index) => value !== b[index]);
};
var _class;
const debug$3 = d('LC:WebSocketPlus');
const OPEN = 'open';
const DISCONNECT = 'disconnect';
const RECONNECT = 'reconnect';
const RETRY = 'retry';
const SCHEDULE = 'schedule';
const OFFLINE = 'offline';
const ONLINE = 'online';
const ERROR = 'error';
const MESSAGE = 'message';
const HEARTBEAT_TIME = 180000;
const TIMEOUT_TIME = 380000;
const DEFAULT_RETRY_STRATEGY = attempt => Math.min(1000 * 2 ** attempt, 300000);
const requireConnected = (target, name, descriptor) => ({ ...descriptor,
value: function requireConnectedWrapper(...args) {
this.checkConnectionAvailability(name);
return descriptor.value.call(this, ...args);
}
});
let WebSocketPlus = (_class = class WebSocketPlus extends EventEmitter {
get urls() {
return this._urls;
}
set urls(urls) {
this._urls = ensureArray(urls);
}
constructor(getUrls, protocol) {
super();
this.init();
this._protocol = protocol;
Promise.resolve(typeof getUrls === 'function' ? getUrls() : getUrls).then(ensureArray).then(urls => {
this._urls = urls;
return this._open();
}).then(() => {
this.__postponeTimeoutTimer = this._postponeTimeoutTimer.bind(this);
if (global$1.addEventListener) {
this.__pause = () => {
if (this.can('pause')) this.pause();
};
this.__resume = () => {
if (this.can('resume')) this.resume();
};
global$1.addEventListener('offline', this.__pause);
global$1.addEventListener('online', this.__resume);
}
this.open();
}).catch(this.throw.bind(this));
}
_open() {
return this._createWs(this._urls, this._protocol).then(ws => {
const [first, ...reset] = this._urls;
this._urls = [...reset, first];
return ws;
});
}
_createWs(urls, protocol) {
return tryAll(urls.map(url => (resolve, reject) => {
debug$3(`connect [${url}] ${protocol}`);
const WebSocket = getAdapter('WebSocket');
const ws = protocol ? new WebSocket(url, protocol) : new WebSocket(url);
ws.binaryType = this.binaryType || 'arraybuffer';
ws.onopen = () => resolve(ws);
ws.onclose = error => {
if (error instanceof Error) {
return reject(error);
} // in browser, error event is useless
return reject(new Error(`Failed to connect [${url}]`));
};
ws.onerror = ws.onclose;
})).then(ws => {
this._ws = ws;
this._ws.onclose = this._handleClose.bind(this);
this._ws.onmessage = this._handleMessage.bind(this);
return ws;
});
}
_destroyWs() {
const ws = this._ws;
if (!ws) return;
ws.onopen = null;
ws.onclose = null;
ws.onerror = null;
ws.onmessage = null;
this._ws = null;
ws.close();
} // eslint-disable-next-line class-methods-use-this
onbeforeevent(event, from, to, ...payload) {
debug$3(`${event}: ${from} -> ${to} %o`, payload);
}
onopen() {
this.emit(OPEN);
}
onconnected() {
this._startConnectionKeeper();
}
onleaveconnected(event, from, to) {
this._stopConnectionKeeper();
this._destroyWs();
if (to === 'offline' || to === 'disconnected') {
this.emit(DISCONNECT);
}
}
onpause() {
this.emit(OFFLINE);
}
onbeforeresume() {
this.emit(ONLINE);
}
onreconnect() {
this.emit(RECONNECT);
}
ondisconnected(event, from, to, attempt = 0) {
const delay = from === OFFLINE ? 0 : DEFAULT_RETRY_STRATEGY.call(null, attempt);
debug$3(`schedule attempt=${attempt} delay=${delay}`);
this.emit(SCHEDULE, attempt, delay);
if (this.__scheduledRetry) {
clearTimeout(this.__scheduledRetry);
}
this.__scheduledRetry = setTimeout(() => {
if (this.is('disconnected')) {
this.retry(attempt);
}
}, delay);
}
onretry(event, from, to, attempt = 0) {
this.emit(RETRY, attempt);
this._open().then(() => this.can('reconnect') && this.reconnect(), () => this.can('fail') && this.fail(attempt + 1));
}
onerror(event, from, to, error) {
this.emit(ERROR, error);
}
onclose() {
if (global$1.removeEventListener) {
if (this.__pause) global$1.removeEventListener('offline', this.__pause);
if (this.__resume) global$1.removeEventListener('online', this.__resume);
}
}
checkConnectionAvailability(name = 'API') {
if (!this.is('connected')) {
const currentState = this.current;
console.warn(`${name} should not be called when the connection is ${currentState}`);
if (this.is('disconnected') || this.is('reconnecting')) {
console.warn('disconnect and reconnect event should be handled to avoid such calls.');
}
throw new Error('Connection unavailable');
}
} // jsdoc-ignore-start
// jsdoc-ignore-end
_ping() {
debug$3('ping');
try {
this.ping();
} catch (error) {
console.warn(`websocket ping error: ${error.message}`);
}
}
ping() {
if (this._ws.ping) {
this._ws.ping();
} else {
console.warn(`The WebSocket implement does not support sending ping frame.
Override ping method to use application defined ping/pong mechanism.`);
}
}
_postponeTimeoutTimer() {
debug$3('_postponeTimeoutTimer');
this._clearTimeoutTimers();
this._timeoutTimer = setTimeout(() => {
debug$3('timeout');
this.disconnect();
}, TIMEOUT_TIME);
}
_clearTimeoutTimers() {
if (this._timeoutTimer) {
clearTimeout(this._timeoutTimer);
}
}
_startConnectionKeeper() {
debug$3('start connection keeper');
this._heartbeatTimer = setInterval(this._ping.bind(this), HEARTBEAT_TIME);
const addListener = this._ws.addListener || this._ws.addEventListener;
if (!addListener) {
debug$3('connection keeper disabled due to the lack of #addEventListener.');
return;
}
addListener.call(this._ws, 'message', this.__postponeTimeoutTimer);
addListener.call(this._ws, 'pong', this.__postponeTimeoutTimer);
this._postponeTimeoutTimer();
}
_stopConnectionKeeper() {
debug$3('stop connection keeper'); // websockets/ws#489
const removeListener = this._ws.removeListener || this._ws.removeEventListener;
if (removeListener) {
removeListener.call(this._ws, 'message', this.__postponeTimeoutTimer);
removeListener.call(this._ws, 'pong', this.__postponeTimeoutTimer);
this._clearTimeoutTimers();
}
if (this._heartbeatTimer) {
clearInterval(this._heartbeatTimer);
}
}
_handleClose(event) {
debug$3(`ws closed [${event.code}] ${event.reason}`); // socket closed manually, ignore close event.
if (this.isFinished()) return;
this.handleClose(event);
}
handleClose() {
// reconnect
this.disconnect();
} // jsdoc-ignore-start
// jsdoc-ignore-end
send(data) {
debug$3('send', data);
this._ws.send(data);
}
_handleMessage(event) {
debug$3('message', event.data);
this.handleMessage(event.data);
}
handleMessage(message) {
this.emit(MESSAGE, message);
}
}, (_applyDecoratedDescriptor(_class.prototype, "_ping", [requireConnected], Object.getOwnPropertyDescriptor(_class.prototype, "_ping"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "send", [requireConnected], Object.getOwnPropertyDescriptor(_class.prototype, "send"), _class.prototype)), _class);
StateMachine.create({
target: WebSocketPlus.prototype,
initial: {
state: 'initialized',
event: 'init',
defer: true
},
terminal: 'closed',
events: [{
name: 'open',
from: 'initialized',
to: 'connected'
}, {
name: 'disconnect',
from: 'connected',
to: 'disconnected'
}, {
name: 'retry',
from: 'disconnected',
to: 'reconnecting'
}, {
name: 'fail',
from: 'reconnecting',
to: 'disconnected'
}, {
name: 'reconnect',
from: 'reconnecting',
to: 'connected'
}, {
name: 'pause',
from: ['connected', 'disconnected', 'reconnecting'],
to: 'offline'
}, {}, {
name: 'resume',
from: 'offline',
to: 'disconnected'
}, {
name: 'close',
from: ['connected', 'disconnected', 'reconnecting', 'offline'],
to: 'closed'
}, {
name: 'throw',
from: '*',
to: 'error'
}]
});
const error = Object.freeze({
1000: {
name: 'CLOSE_NORMAL'
},
1006: {
name: 'CLOSE_ABNORMAL'
},
4100: {
name: 'APP_NOT_AVAILABLE',
message: 'App not exists or realtime message service is disabled.'
},
4102: {
name: 'SIGNATURE_FAILED',
message: 'Login signature mismatch.'
},
4103: {
name: 'INVALID_LOGIN',
message: 'Malformed clientId.'
},
4105: {
name: 'SESSION_REQUIRED',
message: 'Message sent before session opened.'
},
4107: {
name: 'READ_TIMEOUT'
},
4108: {
name: 'LOGIN_TIMEOUT'
},
4109: {
name: 'FRAME_TOO_LONG'
},
4110: {
name: 'INVALID_ORIGIN',
message: 'Access denied by domain whitelist.'
},
4111: {
name: 'SESSION_CONFLICT'
},
4112: {
name: 'SESSION_TOKEN_EXPIRED'
},
4113: {
name: 'APP_QUOTA_EXCEEDED',
message: 'The daily active users limit exceeded.'
},
4116: {
name: 'MESSAGE_SENT_QUOTA_EXCEEDED',
message: 'Command sent too fast.'
},
4200: {
name: 'INTERNAL_ERROR',
message: 'Internal error, please contact LeanCloud for support.'
},
4301: {
name: 'CONVERSATION_API_FAILED',
message: 'Upstream Conversatoin API failed, see error.detail for details.'
},
4302: {
name: 'CONVERSATION_SIGNATURE_FAILED',
message: 'Conversation action signature mismatch.'
},
4303: {
name: 'CONVERSATION_NOT_FOUND'
},
4304: {
name: 'CONVERSATION_FULL'
},
4305: {
name: 'CONVERSATION_REJECTED_BY_APP',
message: 'Conversation action rejected by hook.'
},
4306: {
name: 'CONVERSATION_UPDATE_FAILED'
},
4307: {
name: 'CONVERSATION_READ_ONLY'
},
4308: {
name: 'CONVERSATION_NOT_ALLOWED'
},
4309: {
name: 'CONVERSATION_UPDATE_REJECTED',
message: 'Conversation update rejected because the client is not a member.'
},
4310: {
name: 'CONVERSATION_QUERY_FAILED',
message: 'Conversation query failed because it is too expansive.'
},
4311: {
name: 'CONVERSATION_LOG_FAILED'
},
4312: {
name: 'CONVERSATION_LOG_REJECTED',
message: 'Message query rejected because the client is not a member of the conversation.'
},
4313: {
name: 'SYSTEM_CONVERSATION_REQUIRED'
},
4314: {
name: 'NORMAL_CONVERSATION_REQUIRED'
},
4315: {
name: 'CONVERSATION_BLACKLISTED',
message: 'Blacklisted in the conversation.'
},
4316: {
name: 'TRANSIENT_CONVERSATION_REQUIRED'
},
4317: {
name: 'CONVERSATION_MEMBERSHIP_REQUIRED'
},
4318: {
name: 'CONVERSATION_API_QUOTA_EXCEEDED',
message: 'LeanCloud API quota exceeded. You may upgrade your plan.'
},
4323: {
name: 'TEMPORARY_CONVERSATION_EXPIRED',
message: 'Temporary conversation expired or does not exist.'
},
4401: {
name: 'INVALID_MESSAGING_TARGET',
message: 'Conversation does not exist or client is not a member.'
},
4402: {
name: 'MESSAGE_REJECTED_BY_APP',
message: 'Message rejected by hook.'
},
4403: {
name: 'MESSAGE_OWNERSHIP_REQUIRED'
},
4404: {
name: 'MESSAGE_NOT_FOUND'
},
4405: {
name: 'MESSAGE_UPDATE_REJECTED_BY_APP',
message: 'Message update rejected by hook.'
},
4406: {
name: 'MESSAGE_EDIT_DISABLED'
},
4407: {
name: 'MESSAGE_RECALL_DISABLED'
},
5130: {
name: 'OWNER_PROMOTION_NOT_ALLOWED',
message: "Updating a member's role to owner is not allowed."
}
});
const ErrorCode = Object.freeze(Object.keys(error).reduce((result, code) => Object.assign(result, {
[error[code].name]: Number(code)
}), {}));
const createError = ({
code,
reason,
appCode,
detail,
error: errorMessage
}) => {
let message = reason || detail || errorMessage;
let name = reason;
if (!message && error[code]) {
({
name
} = error[code]);
message = error[code].message || name;
}
if (!message) {
message = `Unknow Error: ${code}`;
}
const err = new Error(message);
return Object.assign(err, {
code,
appCode,
detail,
name
});
};
const debug$4 = d('LC:Connection');
const COMMAND_TIMEOUT = 20000;
const EXPIRE = Symbol('expire');
const isIdempotentCommand = command => !(command.cmd === CommandType.direct || command.cmd === CommandType.session && command.op === OpType.open || command.cmd === CommandType.conv && (command.op === OpType.start || command.op === OpType.update || command.op === OpType.members));
class Connection extends WebSocketPlus {
constructor(getUrl, {
format,
version
}) {
debug$4('initializing Connection');
const protocolString = `lc.${format}.${version}`;
super(getUrl, protocolString);
this._protocolFormat = format;
this._commands = {};
this._serialId = 0;
}
async send(command, waitingForRespond = true) {
let buffer;
let serialId;
if (waitingForRespond) {
if (isIdempotentCommand(command)) {
buffer = command.toArrayBuffer();
const duplicatedCommand = values(this._commands).find(({
buffer: targetBuffer,
command: targetCommand
}) => targetCommand.cmd === command.cmd && targetCommand.op === command.op && equalBuffer(targetBuffer, buffer));
if (duplicatedCommand) {
console.warn(`Duplicated command [cmd:${command.cmd} op:${command.op}] is throttled.`);
return duplicatedCommand.promise;
}
}
this._serialId += 1;
serialId = this._serialId;
command.i = serialId; // eslint-disable-line no-param-reassign
}
if (debug$4.enabled) debug$4('↑ %O sent', trim(command));
let message;
if (this._protocolFormat === 'proto2base64') {
message = command.toBase64();
} else if (command.toArrayBuffer) {
message = command.toArrayBuffer();
}
if (!message) {
throw new TypeError(`${command} is not a GenericCommand`);
}
super.send(message);
if (!waitingForRespond) return undefined;
const promise = new Promise((resolve, reject) => {
this._commands[serialId] = {
command,
buffer,
resolve,
reject,
timeout: setTimeout(() => {
if (this._commands[serialId]) {
if (debug$4.enabled) debug$4('✗ %O timeout', trim(command));
reject(createError({
error: `Command Timeout [cmd:${command.cmd} op:${command.op}]`,
name: 'COMMAND_TIMEOUT'
}));
delete this._commands[serialId];
}
}, COMMAND_TIMEOUT)
};
});
this._commands[serialId].promise = promise;
return promise;
}
handleMessage(msg) {
let message;
try {
message = GenericCommand.decode(msg);
if (debug$4.enabled) debug$4('↓ %O received', trim(message));
} catch (e) {
console.warn('Decode message failed:', e.message, msg);
return;
}
const serialId = message.i;
if (serialId) {
if (this._commands[serialId]) {
clearTimeout(this._commands[serialId].timeout);
if (message.cmd === CommandType.error) {
this._commands[serialId].reject(createError(message.errorMessage));
} else {
this._commands[serialId].resolve(message);
}
delete this._commands[serialId];
} else {
console.warn(`Unexpected command received with serialId [${serialId}],
which have timed out or never been requested.`);
}
} else {
switch (message.cmd) {
case CommandType.error:
{
this.emit(ERROR, createError(message.errorMessage));
return;
}
case CommandType.goaway:
{
this.emit(EXPIRE);
return;
}
default:
{
this.emit(MESSAGE, message);
}
}
}
}
ping() {
return this.send(new GenericCommand({
cmd: CommandType.echo
})).catch(error => debug$4('ping failed:', error));
}
}
const debug$5 = d('LC:request');
var request = (({
method = 'GET',
url: _url,
query,
headers,
data,
timeout: time
}) => {
let url = _url;
if (query) {
const queryString = Object.keys(query).map(key => {
const value = query[key];
if (value === undefined) return undefined;
const v = isPlainObject(value) ? JSON.stringify(value) : value;
return `${encodeURIComponent(key)}=${encodeURIComponent(v)}`;
}).filter(qs => qs).join('&');
url = `${url}?${queryString}`;
}
debug$5('Req: %O %O %O', method, url, {
headers,
data
});
const request = getAdapter('request');
const promise = request(url, {
method,
headers,
data
}).then(response => {
if (response.ok === false) {
const error = createError(response.data);
error.response = response;
throw error;
}
debug$5('Res: %O %O %O', url, response.status, response.data);
return response.data;
}).catch(error => {
if (error.response) {
debug$5('Error: %O %O %O', url, error.response.status, error.response.data);
}
throw error;
});
return time ? promiseTimeout.timeout(promise, time) : promise;
});
/* eslint-disable max-len */
const applyDecorators = (decorators, target) => {
if (decorators) {
decorators.forEach(decorator => {
try {
decorator(target);
} catch (error) {
if (decorator._pluginName) {
error.message += `[${decorator._pluginName}]`;
}
throw error;
}
});
}
};
const applyDispatcher = (dispatchers, payload) => ensureArray(dispatchers).reduce((resultPromise, dispatcher) => resultPromise.then(shouldDispatch => shouldDispatch === false ? false : dispatcher(...payload)).catch(error => {
if (dispatcher._pluginName) {
// eslint-disable-next-line no-param-reassign
error.message += `[${dispatcher._pluginName}]`;
}
throw error;
}), Promise.resolve(true));
var version = "5.0.0-rc.7";
// eslint-disable-next-line max-classes-per-file
const debug$6 = d('LC:Realtime');
const routerCache = new Cache('push-router');
const initializedApp = {};
class Realtime extends EventEmitter {
/**
* @extends EventEmitter
* @param {Object} options
* @param {String} options.appId
* @param {String} options.appKey (since 4.0.0)
* @param {String|Object} [options.server] 指定服务器域名,中国节点应用此参数必填(since 4.0.0)
* @param {Boolean} [options.noBinary=false] 设置 WebSocket 使用字符串格式收发消息(默认为二进制格式)。
* 适用于 WebSocket 实现不支持二进制数据格式的情况
* @param {Boolean} [options.ssl=true] 使用 wss 进行连接
* @param {String|String[]} [options.RTMServers] 指定私有部署的 RTM 服务器地址(since 4.0.0)
* @param {Plugin[]} [options.plugins] 加载插件(since 3.1.0)
*/
constructor({
plugins,
...options
}) {
debug$6('initializing Realtime %s %O', version, options);
super();
const {
appId
} = options;
if (typeof appId !== 'string') {
throw new TypeError(`appId [${appId}] is not a string`);
}
if (initializedApp[appId]) {
throw new Error(`App [${appId}] is already initialized.`);
}
initializedApp[appId] = true;
if (typeof options.appKey !== 'string') {
throw new TypeError(`appKey [${options.appKey}] is not a string`);
}
if (isCNApp(appId)) {
if (!options.server) {
throw new TypeError(`server option is required for apps from CN region`);
}
}
this._options = {
appId: undefined,
appKey: undefined,
noBinary: false,
ssl: true,
RTMServerName: typeof process !== 'undefined' ? process.env.RTM_SERVER_NAME : undefined,
// undocumented on purpose, internal use only
...options
};
this._cache = new Cache('endpoints');
const _this = internal(this);
_this.clients = new Set();
_this.pendingClients = new Set();
const mergedPlugins = [...ensureArray(Realtime.__preRegisteredPlugins), ...ensureArray(plugins)];
debug$6('Using plugins %o', mergedPlugins.map(plugin => plugin.name));
this._plugins = mergedPlugins.reduce((result, plugin) => {
Object.keys(plugin).forEach(hook => {
if ({}.hasOwnProperty.call(plugin, hook) && hook !== 'name') {
if (plugin.name) {
ensureArray(plugin[hook]).forEach(value => {
// eslint-disable-next-line no-param-reassign
value._pluginName = plugin.name;
});
} // eslint-disable-next-line no-param-reassign
result[hook] = ensureArray(result[hook]).concat(plugin[hook]);
}
});
return result;
}, {}); // onRealtimeCreate hook
applyDecorators(this._plugins.onRealtimeCreate, this);
}
async _request({
method,
url: _url,
version = '1.1',
path,
query,
headers,
data
}) {
let url = _url;
if (!url) {
const {
appId,
server
} = this._options;
const {
api
} = await this.constructor._getServerUrls({
appId,
server
});
url = `${api}/${version}${path}`;
}
return request({
url,
method,
query,
headers: {
'X-LC-Id': this._options.appId,
'X-LC-Key': this._options.appKey,
...headers
},
data
});
}
_open() {
if (this._openPromise) return this._openPromise;
let format = 'protobuf2';
if (this._options.noBinary) {
// 不发送 binary data,fallback to base64 string
format = 'proto2base64';
}
const version = 3;
const protocol = {
format,
version
};
this._openPromise = new Promise((resolve, reject) => {
debug$6('No connection established, create a new one.');
const connection = new Connection(() => this._getRTMServers(this._options), protocol);
connection.on(OPEN, () => resolve(connection)).on(ERROR, error => {
delete this._openPromise;
reject(error);
}).on(EXPIRE, async () => {
debug$6('Connection expired. Refresh endpoints.');
this._cache.set('endpoints', null, 0);
connection.urls = await this._getRTMServers(this._options);
connection.disconnect();
}).on(MESSAGE, this._dispatchCommand.bind(this));
/**
* 连接断开。
* 连接断开可能是因为 SDK 进入了离线状态(see {@link Realtime#event:OFFLINE}),或长时间没有收到服务器心跳。
* 连接断开后所有的网络操作都会失败,请在连接断开后禁用相关的 UI 元素。
* @event Realtime#DISCONNECT
*/
/**
* 计划在一段时间后尝试重新连接
* @event Realtime#SCHEDULE
* @param {Number} attempt 尝试重连的次数
* @param {Number} delay 延迟的毫秒数
*/
/**
* 正在尝试重新连接
* @event Realtime#RETRY
* @param {Number} attempt 尝试重连的次数
*/
/**
* 连接恢复正常。
* 请重新启用在 {@link Realtime#event:DISCONNECT} 事件中禁用的相关 UI 元素
* @event Realtime#RECONNECT
*/
/**
* 客户端连接断开
* @event IMClient#DISCONNECT
* @see Realtime#event:DISCONNECT
* @since 3.2.0
*/
/**
* 计划在一段时间后尝试重新连接
* @event IMClient#SCHEDULE
* @param {Number} attempt 尝试重连的次数
* @param {Number} delay 延迟的毫秒数
* @since 3.2.0
*/
/**
* 正在尝试重新连接
* @event IMClient#RETRY
* @param {Number} attempt 尝试重连的次数
* @since 3.2.0
*/
/**
* 客户端进入离线状态。
* 这通常意味着网络已断开,或者 {@link Realtime#pause} 被调用
* @event Realtime#OFFLINE
* @since 3.4.0
*/
/**
* 客户端恢复在线状态
* 这通常意味着网络已恢复,或者 {@link Realtime#resume} 被调用
* @event Realtime#ONLINE
* @since 3.4.0
*/
/**
* 进入离线状态。
* 这通常意味着网络已断开,或者 {@link Realtime#pause} 被调用
* @event IMClient#OFFLINE
* @since 3.4.0
*/
/**
* 恢复在线状态
* 这通常意味着网络已恢复,或者 {@link Realtime#resume} 被调用
* @event IMClient#ONLINE
* @since 3.4.0
*/
// event proxy
[DISCONNECT, RECONNECT, RETRY, SCHEDULE, OFFLINE, ONLINE].forEach(event => connection.on(event, (...payload) => {
debug$6(`${event} event emitted. %o`, payload);
this.emit(event, ...payload);
if (event !== RECONNECT) {
internal(this).clients.forEach(client => {
client.emit(event, ...payload);
});
}
})); // override handleClose
connection.handleClose = function handleClose(event) {
const isFatal = [ErrorCode.APP_NOT_AVAILABLE, ErrorCode.INVALID_LOGIN, ErrorCode.INVALID_ORIGIN].some(errorCode => errorCode === event.code);
if (isFatal) {
// in these cases, SDK should throw.
this.throw(createError(event));
} else {
// reconnect
this.disconnect();
}
};
internal(this).connection = connection;
});
return this._openPromise;
}
async _getRTMServers(options) {
if (options.RTMServers) return shuffle(ensureArray(options.RTMServers));
let info;
const cachedEndPoints = this._cache.get('endpoints');
if (cachedEndPoints) {
info = cachedEndPoints;
} else {
info = await this.constructor._fetchRTMServers(options);
const {
server,
secondary,
ttl
} = info;
if (typeof server !== 'string' && typeof secondary !== 'string' && typeof ttl !== 'number') {
throw new Error(`malformed RTM route response: ${JSON.stringify(info)}`);
}
this._cache.set('endpoints', info, info.ttl * 1000);
}
debug$6('endpoint info: %O', info);
return [info.server, info.secondary];
}
static async _getServerUrls({
appId,
server
}) {
debug$6('fetch server urls');
if (server) {
if (typeof server !== 'string') return server;
return {
RTMRouter: server,
api: server
};
}
const cachedRouter = routerCache.get(appId);
if (cachedRouter) return cachedRouter;
const defaultProtocol = 'https://';
return request({
url: 'https://app-router.com/2/route',
query: {
appId
},
timeout: 20000
}).then(tap(debug$6)).then(({
rtm_router_server: RTMRouterServer,
api_server: APIServer,
ttl = 3600
}) => {
if (!RTMRouterServer) {
throw new Error('rtm router not exists');
}
const serverUrls = {
RTMRouter: `${defaultProtocol}${RTMRouterServer}`,
api: `${defaultProtocol}${APIServer}`
};
routerCache.set(appId, serverUrls, ttl * 1000);
return serverUrls;
}).catch(() => {
const id = appId.slice(0, 8).toLowerCase();
const domain = 'lncldglobal.com';
return {
RTMRouter: `${defaultProtocol}${id}.rtm.${domain}`,
api: `${defaultProtocol}${id}.api.${domain}`
};
});
}
static _fetchRTMServers({
appId,
ssl,
server,
RTMServerName
}) {
debug$6('fetch endpoint info');
return this._getServerUrls({
appId,
server
}).then(tap(debug$6)).then(({
RTMRouter
}) => request({
url: `${RTMRouter}/v1/route`,
query: {
appId,
secure: ssl,
features: isWeapp ? 'wechat' : undefined,
server: RTMServerName,
_t: Date.now()
},
timeout: 20000
}).then(tap(debug$6)));
}
_close() {
if (this._openPromise) {
this._openPromise.then(connection => connection.close());
}
delete this._openPromise;
}
/**
* 手动进行重连。
* SDK 在网络出现异常时会自动按照一定的时间间隔尝试重连,调用该方法会立即尝试重连并重置重连尝试计数器。
* 只能在 `SCHEDULE` 事件之后,`RETRY` 事件之前调用,如果当前网络正常或者正在进行重连,调用该方法会抛异常。
*/
retry() {
const {
connection
} = internal(this);
if (!connection) {
throw new Error('no connection established');
}
if (connection.cannot('retry')) {
throw new Error(`retrying not allowed when not disconnected. the connection is now ${connection.current}`);
}
return connection.retry();
}
/**
* 暂停,使 SDK 进入离线状态。
* 你可以在网络断开、应用进入后台等时刻调用该方法让 SDK 进入离线状态,离线状态下不会尝试重连。
* 在浏览器中 SDK 会自动监听网络变化,因此无需手动调用该方法。
*
* @since 3.4.0
* @see Realtime#event:OFFLINE
*/
pause() {
// 这个方法常常在网络断开、进入后台时被调用,此时 connection 可能没有建立或者已经 close。
// 因此不像 retry,这个方法应该尽可能 loose
const {
connection
} = internal(this);
if (!connection) return;
if (connection.can('pause')) connection.pause();
}
/**
* 恢复在线状态。
* 你可以在网络恢复、应用回到前台等时刻调用该方法让 SDK 恢复在线状态,恢复在线状态后 SDK 会开始尝试重连。
*
* @since 3.4.0
* @see Realtime#event:ONLINE
*/
resume() {
// 与 pause 一样,这个方法应该尽可能 loose
const {
connection
} = internal(this);
if (!connection) return;
if (connection.can('resume')) connection.resume();
}
_registerPending(value) {
internal(this).pendingClients.add(value);
}
_deregisterPending(client) {
internal(this).pendingClients.delete(client);
}
_register(client) {
internal(this).clients.add(client);
}
_deregister(client) {
const _this = internal(this);
_this.clients.delete(client);
if (_this.clients.size + _this.pendingClients.size === 0) {
this._close();
}
}
_dispatchCommand(command) {
return applyDispatcher(this._plugins.beforeCommandDispatch, [command, this]).then(shouldDispatch => {
// no plugin handled this command
if (shouldDispatch) return debug$6('[WARN] Unexpected message received: %O', trim(command));
return false;
});
}
} // For test purpose only
const polyfilledPromise = Promise;
exports.EventEmitter = EventEmitter;
exports.Promise = polyfilledPromise;
exports.Protocals = message;
exports.Protocols = message;
exports.Realtime = Realtime;
exports.debug = debug$2;
exports.getAdapter = getAdapter;
exports.setAdapters = setAdapters;
//# sourceMappingURL=core.js.map