leancloud-realtime
Advanced tools
Comparing version 3.5.6 to 4.0.0-alpha.0
{ | ||
"opts": { | ||
"template": "node_modules/docdash", | ||
"template": "node_modules/@leeyeh/jsdoc-rtd", | ||
"recurse": true | ||
@@ -5,0 +5,0 @@ }, |
{ | ||
"name": "leancloud-realtime", | ||
"version": "3.5.6", | ||
"version": "4.0.0-alpha.0", | ||
"homepage": "https://github.com/leancloud/js-realtime-sdk/", | ||
@@ -5,0 +5,0 @@ "description": "LeanCloud JavaScript Realtime SDK", |
@@ -1,42 +0,65 @@ | ||
<a name="3.5.6"></a> | ||
## 3.5.6 (2017-08-29) | ||
<a name="4.0.0-alpha.0"></a> | ||
# 4.0.0-alpha.0 (2017-08-31) | ||
### BREAKING CHANGES | ||
### Bug Fixes | ||
* 初始化 `Realtime` 现在需要 `appKey` 参数。 | ||
<details> | ||
<summary>示例</summary> | ||
* **WebSocketPlus:** 修复了在 Electron 的 Chromium 环境中报错的问题 ([15d16e5](https://github.com/leancloud/js-realtime-sdk/commit/5d582ac)), closes [#566](https://github.com/leancloud/js-realtime-sdk/issues/566) | ||
```diff | ||
const realtime = new Realtime({ | ||
appId: 'YOUR_APP_ID', | ||
+ appKey: 'YOUR_APP_KEY', | ||
}); | ||
``` | ||
</details> | ||
* 现在所有异步 API 的异常都是以异步的方式抛出。我们还更新了 API 文档,标出了 API 的异步属性。 | ||
<details> | ||
<summary>示例</summary> | ||
```javascript | ||
// before | ||
try { | ||
conversation.send(message); | ||
} catch (error) { | ||
// hanlde `Connection unavailable` error | ||
} | ||
<a name="3.5.5"></a> | ||
## 3.5.5 (2017-07-28) | ||
// after | ||
conversation.add(members).catch(error => { | ||
// hanlde `Connection unavailable` error | ||
}); | ||
``` | ||
</details> | ||
* 为了更好的隔离服务,我们为每个应用提供了独立的域名。对于小程序用户,请前往 [《小程序域名白名单配置》](https://leancloud.cn/docs/weapp-domains.html) 更新域名白名单。 | ||
* 移除了 v3 中被标记为废弃的 API,包括: | ||
### Bug Fixes | ||
<details> | ||
<summary>移除 API 列表</summary> | ||
* **IMClient:** 修复了 `getConversations` 方法在获取大量会话的时候可能会错误的返回部分 null 的问题 ([4a3d48f](https://github.com/leancloud/js-realtime-sdk/commit/4a3d48f)) | ||
* `IMClient` | ||
* `#markAllAsRead` 方法 | ||
* `createConversation` 的 `options.attributes` 参数 | ||
* `unreadmessages` 事件 | ||
* `Conversation` | ||
* `attributes` 属性 | ||
* `#setAttributes`、`#setAttribute`、`#setName` 与 `#markAsRead` 方法 | ||
* `receipt` 事件 | ||
* `ConversationQuery` | ||
* `#withLastMessages` 方法 | ||
* `Message` | ||
* `needReceipt` 与 `transient` 属性 | ||
* `#setNeedReceipt` 与 `#setTransient` 方法 | ||
</details> | ||
### Features | ||
<a name="3.5.4"></a> | ||
## 3.5.4 (2017-07-28) | ||
* 支持与 LeanCloud 用户系统集成。`Realtime#createIMClient` 方法现在支持使用一个已登录的 `AV.User` 登录 IM,详见 [相关文档](https://url.leanapp.cn/im-login-with-avuser)。 | ||
### Bug Fixes | ||
* **IMClient:** 修复了 v3.5.3 中没有彻底修复的 createIMClient 失败后,使用相同的 id 再次创建会一直失败的问题 ([0cbe268](https://github.com/leancloud/js-realtime-sdk/commit/0cbe268)) | ||
<a name="3.5.3"></a> | ||
## 3.5.3 (2017-07-28) | ||
### Bug Fixes | ||
* **IMClient:** 修复了 createIMClient 失败后,使用相同的 id 再次创建会一直失败的问题 ([#561](https://github.com/leancloud/js-realtime-sdk/issues/561)) ([b8d7c6a](https://github.com/leancloud/js-realtime-sdk/commit/b8d7c6a)) | ||
* **IMClient:** 修复了某些情况下 SDK 会出现 unhandled Promise rejection 的问题 ([#562](https://github.com/leancloud/js-realtime-sdk/issues/562)) ([697bf60](https://github.com/leancloud/js-realtime-sdk/commit/697bf60)) | ||
<a name="3.5.2"></a> | ||
@@ -43,0 +66,0 @@ ## 3.5.2 (2017-06-30) |
@@ -58,3 +58,3 @@ /* eslint-disable */ | ||
], | ||
presets: [["env", { "modules": false }]], | ||
presets: [["env", { "modules": false, debug: true }]], | ||
babelrc: false, | ||
@@ -94,2 +94,3 @@ runtimeHelpers: true, | ||
moduleName: 'AV', | ||
extend: true, | ||
external: ['leancloud-realtime', 'leancloud-realtime/core'], | ||
@@ -137,3 +138,6 @@ globals: { | ||
moduleName: 'AV', | ||
moduleId: 'leancloud-realtime', | ||
extend: true, | ||
amd: { | ||
id: 'leancloud-realtime', | ||
}, | ||
} | ||
@@ -158,3 +162,6 @@ }, | ||
moduleName: 'AV', | ||
moduleId: 'leancloud-realtime', | ||
extend: true, | ||
amd: { | ||
id: 'leancloud-realtime', | ||
}, | ||
}, | ||
@@ -190,3 +197,6 @@ }, | ||
moduleName: 'AV', | ||
moduleId: 'typed-messages', | ||
extend: true, | ||
amd: { | ||
id: 'typed-messages', | ||
}, | ||
external: ['leancloud-realtime', 'leancloud-storage'], | ||
@@ -203,3 +213,5 @@ globals: { | ||
options: Object.assign({}, pluginOptions, { | ||
moduleId: 'webrtc', | ||
amd: { | ||
id: 'webrtc', | ||
}, | ||
}) | ||
@@ -211,3 +223,5 @@ }, | ||
options: Object.assign({}, pluginOptions, { | ||
moduleId: 'groupchat-receipts', | ||
amd: { | ||
id: 'groupchat-receipts', | ||
}, | ||
}) | ||
@@ -219,3 +233,5 @@ }, | ||
options: Object.assign({}, pluginOptions, { | ||
moduleId: 'live-query', | ||
amd: { | ||
id: 'live-query', | ||
}, | ||
}) | ||
@@ -222,0 +238,0 @@ }, |
{ | ||
"name": "leancloud-realtime", | ||
"version": "3.5.6", | ||
"version": "4.0.0-alpha.0", | ||
"description": "LeanCloud Realtime Message JavaScript SDK", | ||
@@ -36,2 +36,3 @@ "main": "./dist/realtime.js", | ||
"devDependencies": { | ||
"@leeyeh/jsdoc-rtd": "^1.0.13", | ||
"@leeyeh/rollup-plugin-node-resolve": "^3.0.0", | ||
@@ -48,5 +49,4 @@ "babel-eslint": "^7.0.0", | ||
"conventional-changelog-cli": "^1.1.1", | ||
"docdash": "git+https://github.com/leeyeh/docdash.git#leancloud", | ||
"eslint": "^3.0.0", | ||
"eslint-config-airbnb-base": "^9.0.0", | ||
"eslint": "^4.3.0", | ||
"eslint-config-airbnb-base": "^11.1.0", | ||
"eslint-plugin-import": "^2.0.0", | ||
@@ -58,7 +58,7 @@ "espree": "^3.1.4", | ||
"grunt-envify": "0.1.0", | ||
"grunt-rollup": "^1.0.1", | ||
"grunt-rollup": "leeyeh/grunt-rollup", | ||
"grunt-saucelabs": "^9.0.0", | ||
"husky": "^0.13.1", | ||
"inherit": "^2.2.3", | ||
"jsdoc": "3.4.0", | ||
"jsdoc": "leeyeh/jsdoc#v3.4.0", | ||
"jsdoc-ignore-future": "^1.1.0", | ||
@@ -70,3 +70,2 @@ "leancloud-storage": "^2.0.0", | ||
"qiniu": "^6.1.9", | ||
"rollup": "^0.41.1", | ||
"rollup-plugin-babel": "^2.4.0", | ||
@@ -73,0 +72,0 @@ "rollup-plugin-commonjs": "^8.0.2", |
LeanCloud JavaScript Realtime SDK | ||
==== | ||
[![npm](https://img.shields.io/npm/v/leancloud-realtime.svg?style=flat-square)](https://www.npmjs.com/package/leancloud-realtime) | ||
[![npm](https://img.shields.io/npm/v/leancloud-realtime/next.svg?style=flat-square)](https://www.npmjs.com/package/leancloud-realtime) | ||
![gzip size](http://img.badgesize.io/leancloud/js-realtime-sdk/dist/dist/realtime.browser.min.js.svg?compression=gzip&style=flat-square) | ||
[![Build Status](https://img.shields.io/travis/leancloud/js-realtime-sdk.svg?style=flat-square)](https://travis-ci.org/leancloud/js-realtime-sdk) | ||
[![Codecov](https://img.shields.io/codecov/c/github/leancloud/js-realtime-sdk.svg?style=flat-square)](https://codecov.io/github/leancloud/js-realtime-sdk) | ||
[![David](https://img.shields.io/david/leancloud/js-realtime-sdk.svg?style=flat-square)](https://david-dm.org/leancloud/js-realtime-sdk) | ||
[![npm](https://img.shields.io/npm/v/leancloud-realtime.svg?style=flat-square)](https://www.npmjs.com/package/leancloud-realtime) | ||
![gzip size](http://img.badgesize.io/leancloud/js-realtime-sdk/dist/dist/realtime.browser.min.js.svg?compression=gzip&style=flat-square) | ||
[![Known Vulnerabilities](https://snyk.io/test/github/leancloud/js-realtime-sdk/badge.svg?style=flat-square)](https://snyk.io/test/github/leancloud/js-realtime-sdk) | ||
@@ -13,6 +14,4 @@ 为您的 JavaScript App 接入 LeanCloud 实时通讯服务。 | ||
---- | ||
master 分支为开发版本。 | ||
v2 分支上为 2.x 版本,将仅得到安全更新,相关文档参见 [2.x 文档](https://leancloud.cn/docs/js_realtime.html)。 | ||
自 v3 起遵循 [语义化版本](http://semver.org/lang/zh-CN/)。 | ||
遵循 [语义化版本](http://semver.org/lang/zh-CN/)。 | ||
@@ -24,7 +23,13 @@ 安装稳定版本: | ||
安装 v2 版本: | ||
安装测试版本: | ||
``` | ||
npm install leancloud-realtime@2 --save | ||
npm install leancloud-realtime@next --save | ||
``` | ||
安装指定版本: | ||
``` | ||
// 安装 v3 版本 | ||
npm install leancloud-realtime@3 --save | ||
``` | ||
支持的运行环境 | ||
@@ -38,5 +43,6 @@ ---- | ||
- Android 4.4+ | ||
- Node.js 0.12+ | ||
- Node.js 4.0+ | ||
- 微信小程序 latest | ||
- React Native 0.26+ | ||
- 微信小程序 latest | ||
- Electron latest | ||
@@ -66,5 +72,4 @@ 文档 | ||
---- | ||
* 如果你发现了新的 bug,或者有新的 feature request,请新建一个 issue | ||
* 在使用过程中遇到了问题时 | ||
* 如果你购买了技术支持服务,请新建一个 ticket。 | ||
* 如果你是商用版用户,请新建一个工单。 | ||
* 也可以在 [论坛](https://forum.leancloud.cn/) 提问、讨论。 | ||
@@ -71,0 +76,0 @@ |
@@ -1,7 +0,9 @@ | ||
declare module LeanCloudRealtime { | ||
interface AVUser { | ||
getSessionToken(): string; | ||
} | ||
export class Realtime extends EventEmitter { | ||
constructor(options: { appId: string, region?: string, pushOfflineMessages?: boolean, noBinary?: boolean, ssl?: boolean, plugins?: Array<Plugin> }); | ||
createIMClient(clientId: string, clientOptions?: { signatureFactory?: Function, conversationSignatureFactory?: Function, tag?: string }): Promise<IMClient>; | ||
createIMClient(client: string|AVUser, clientOptions?: { signatureFactory?: Function, conversationSignatureFactory?: Function, tag?: string }): Promise<IMClient>; | ||
static defineConversationProperty(prop: string, descriptor?: Object); | ||
@@ -79,3 +81,2 @@ register(messageClass: AVMessage[]); | ||
read(): Promise<Conversation>; | ||
markAsRead(): Promise<Conversation>; | ||
fetchReceiptTimestamps(): Promise<Conversation>; | ||
@@ -109,6 +110,4 @@ mute(): Promise<Conversation>; | ||
timestamp: Date; | ||
transient: boolean; | ||
static parse(json: Object, message: Message): Message; | ||
static validate(): boolean; | ||
setTransient(transient: boolean): Message; | ||
toJSON(): Object; | ||
@@ -115,0 +114,0 @@ } |
@@ -19,3 +19,3 @@ import d from 'debug'; | ||
super(getUrl().then(urls => urls.map(url => | ||
`${url}${url.indexOf('?') === -1 ? '?' : '&'}subprotocol=${encodeURIComponent(protocolString)}` | ||
`${url}${url.indexOf('?') === -1 ? '?' : '&'}subprotocol=${encodeURIComponent(protocolString)}`, | ||
))); | ||
@@ -56,13 +56,13 @@ } | ||
reject, | ||
timeout: setTimeout( | ||
() => { | ||
if (this._commands[serialId]) { | ||
debug('✗ %O timeout', trim(command)); | ||
reject(new Error('Command Timeout.')); | ||
delete this._commands[serialId]; | ||
} | ||
}, | ||
COMMAND_TIMEOUT, | ||
), | ||
}; | ||
setTimeout( | ||
() => { | ||
if (this._commands[serialId]) { | ||
debug('✗ %O timeout', trim(command)); | ||
reject(new Error('Command Timeout.')); | ||
delete this._commands[serialId]; | ||
} | ||
}, | ||
COMMAND_TIMEOUT | ||
); | ||
}); | ||
@@ -83,2 +83,3 @@ } | ||
if (this._commands[serialId]) { | ||
clearTimeout(this._commands[serialId].timeout); | ||
if (message.cmd === CommandType.error) { | ||
@@ -85,0 +86,0 @@ this |
@@ -27,3 +27,3 @@ import d from 'debug'; | ||
(prev, key) => (prev << 1) + (Boolean)(options[key]), | ||
0 | ||
0, | ||
); | ||
@@ -362,13 +362,2 @@ } | ||
/** | ||
* @deprecated 请替换为 {@link ConversationQuery#withLastMessagesRefreshed} | ||
* @param {Boolean} [enabled=true] | ||
* @return {ConversationQuery} self | ||
*/ | ||
withLastMessages(enabled) { | ||
console.warn('DEPRECATION ConversationQuery#withLastMessages: ' + | ||
'Use ConversationQuery#withLastMessagesRefreshed instead.'); | ||
return this.withLastMessagesRefreshed(enabled); | ||
} | ||
/** | ||
* 设置返回的 conversations 为精简模式,即不含成员列表 | ||
@@ -387,5 +376,5 @@ * @param {Boolean} [enabled=true] | ||
*/ | ||
find() { | ||
async find() { | ||
return this._client._executeQuery(this); | ||
} | ||
} |
import EventEmitter from 'eventemitter3'; | ||
import isEmpty from 'lodash/isEmpty'; | ||
import isPlainObject from 'lodash/isPlainObject'; | ||
import cloneDeep from 'lodash/cloneDeep'; | ||
@@ -144,3 +143,3 @@ import d from 'debug'; | ||
event, | ||
(...payload) => this._debug(`${event} event emitted. %O`, payload) | ||
(...payload) => this._debug(`${event} event emitted. %O`, payload), | ||
)); | ||
@@ -229,47 +228,2 @@ // onConversationCreate hook | ||
/** | ||
* 对话额外属性,对应 _Conversation 表中的 attr | ||
* @type {Object} | ||
* @deprecated Use {@link Conversation#get conversation.get('attr')}, | ||
* {@link Conversation#set conversation.set('attr', value)} instead | ||
*/ | ||
get attributes() { | ||
console.warn('DEPRECATION Conversation.attributes: Use conversation.get(\'attr\') instead. See https://url.leanapp.cn/DeprecateAttributes for more details.'); | ||
return this.get('attr'); | ||
} | ||
set attributes(value) { | ||
console.warn('DEPRECATION Conversation.attributes: Use conversation.set(\'attr\', value) instead. See https://url.leanapp.cn/DeprecateAttributes for more details.'); | ||
this.set('attr', value); | ||
} | ||
/** | ||
* 设置对话额外属性 | ||
* @param {Object} map key-value 对 | ||
* @param {Boolean} [assign=false] 使用 Object.assign 更新属性,而不是替换整个 attributes | ||
* @return {Conversation} self | ||
* @deprecated Use {@link Conversation#set} instead. See {@link https://url.leanapp.cn/DeprecateAttributes} for more details. | ||
*/ | ||
setAttributes(map, assign = false) { | ||
console.warn('DEPRECATION Conversation#setAttributes: Use conversation.set() instead. See https://url.leanapp.cn/DeprecateAttributes for more details.'); | ||
this._debug(`set attributes: value=${JSON.stringify(map)}, assign=${assign}`); | ||
if (!isPlainObject(map)) { | ||
throw new TypeError('attributes must be a plain object'); | ||
} | ||
if (!assign) { | ||
this.set('attr', map); | ||
} else { | ||
Object.keys(map).forEach(key => this.setAttribute(key, map[key])); | ||
} | ||
return this; | ||
} | ||
/** | ||
* 设置对话额外属性 | ||
* @param {String} key | ||
* @param {Any} value | ||
* @return {Conversation} self | ||
* @deprecated Use {@link Conversation#set conversation.set('attr.key', value)} instead | ||
*/ | ||
setAttribute(key, value) { | ||
console.warn('DEPRECATION Conversation#setAttribute: Use conversation.set(\'attr.key\', value) instead. See https://url.leanapp.cn/DeprecateAttributes for more details.'); | ||
return this.set(`attr.${key}`, value); | ||
} | ||
/** | ||
* 对话名字,对应 _Conversation 表中的 name | ||
@@ -284,12 +238,2 @@ * @type {String} | ||
} | ||
/** | ||
* 设置对话名字 | ||
* @param {String} value | ||
* @return {Conversation} self | ||
* @deprecated Use {@link Conversation#set conversation.set('name', value)} instead | ||
*/ | ||
setName(value) { | ||
console.warn('DEPRECATION Conversation#setName: Use conversation.set(\'name\', value) instead. See https://url.leanapp.cn/DeprecateAttributes for more details.'); | ||
return this.set('name', value); | ||
} | ||
@@ -350,3 +294,3 @@ /** | ||
(target, k) => setValue(target, k, pendingAttributes[k]), | ||
cloneDeep(this._attributes) | ||
cloneDeep(this._attributes), | ||
); | ||
@@ -383,3 +327,3 @@ return this; | ||
*/ | ||
save() { | ||
async save() { | ||
this._debug('save'); | ||
@@ -389,3 +333,3 @@ const attr = internal(this).pendingAttributes; | ||
this._debug('nothing touched, resolve with self'); | ||
return Promise.resolve(this); | ||
return this; | ||
} | ||
@@ -398,13 +342,10 @@ this._debug('attr: %O', attr); | ||
}); | ||
return this | ||
._send(new GenericCommand({ | ||
op: 'update', | ||
convMessage, | ||
})) | ||
.then((resCommand) => { | ||
this.updatedAt = resCommand.convMessage.udate; | ||
this._attributes = internal(this).currentAttributes; | ||
internal(this).pendingAttributes = {}; | ||
return this; | ||
}); | ||
const resCommand = await this._send(new GenericCommand({ | ||
op: 'update', | ||
convMessage, | ||
})); | ||
this.updatedAt = resCommand.convMessage.udate; | ||
this._attributes = internal(this).currentAttributes; | ||
internal(this).pendingAttributes = {}; | ||
return this; | ||
} | ||
@@ -416,9 +357,6 @@ | ||
*/ | ||
fetch() { | ||
return this | ||
._client | ||
.getQuery() | ||
.equalTo('objectId', this.id) | ||
.find() | ||
.then(() => this); | ||
async fetch() { | ||
const query = this._client.getQuery().equalTo('objectId', this.id); | ||
await query.find(); | ||
return this; | ||
} | ||
@@ -430,13 +368,12 @@ | ||
*/ | ||
mute() { | ||
async mute() { | ||
this._debug('mute'); | ||
return this._send(new GenericCommand({ | ||
await this._send(new GenericCommand({ | ||
op: 'mute', | ||
})).then(() => { | ||
if (!this.transient) { | ||
this.muted = true; | ||
this.mutedMembers = union(this.mutedMembers, [this._client.id]); | ||
} | ||
return this; | ||
}); | ||
})); | ||
if (!this.transient) { | ||
this.muted = true; | ||
this.mutedMembers = union(this.mutedMembers, [this._client.id]); | ||
} | ||
return this; | ||
} | ||
@@ -448,13 +385,12 @@ | ||
*/ | ||
unmute() { | ||
async unmute() { | ||
this._debug('unmute'); | ||
return this._send(new GenericCommand({ | ||
await this._send(new GenericCommand({ | ||
op: 'unmute', | ||
})).then(() => { | ||
if (!this.transient) { | ||
this.muted = false; | ||
this.mutedMembers = difference(this.mutedMembers, [this._client.id]); | ||
} | ||
return this; | ||
}); | ||
})); | ||
if (!this.transient) { | ||
this.muted = false; | ||
this.mutedMembers = difference(this.mutedMembers, [this._client.id]); | ||
} | ||
return this; | ||
} | ||
@@ -466,7 +402,8 @@ | ||
*/ | ||
count() { | ||
async count() { | ||
this._debug('unmute'); | ||
return this._send(new GenericCommand({ | ||
const resCommand = await this._send(new GenericCommand({ | ||
op: 'count', | ||
})).then(resCommand => resCommand.convMessage.count); | ||
})); | ||
return resCommand.convMessage.count; | ||
} | ||
@@ -479,3 +416,3 @@ | ||
*/ | ||
add(clientIds) { | ||
async add(clientIds) { | ||
this._debug('add', clientIds); | ||
@@ -488,29 +425,22 @@ if (typeof clientIds === 'string') { | ||
}); | ||
return Promise.resolve( | ||
new GenericCommand({ | ||
op: 'add', | ||
convMessage, | ||
}) | ||
).then((command) => { | ||
if (this._client.options.conversationSignatureFactory) { | ||
const params = [this.id, this._client.id, clientIds.sort(), 'add']; | ||
return runSignatureFactory(this._client.options.conversationSignatureFactory, params) | ||
.then((signatureResult) => { | ||
Object.assign(command.convMessage, keyRemap({ | ||
signature: 's', | ||
timestamp: 't', | ||
nonce: 'n', | ||
}, signatureResult)); | ||
return command; | ||
}); | ||
} | ||
return command; | ||
}).then( | ||
this._send.bind(this) | ||
).then(() => { | ||
if (!this.transient && !this.system) { | ||
this.members = union(this.members, clientIds); | ||
} | ||
return this; | ||
const command = new GenericCommand({ | ||
op: 'add', | ||
convMessage, | ||
}); | ||
if (this._client.options.conversationSignatureFactory) { | ||
const params = [this.id, this._client.id, clientIds.sort(), 'add']; | ||
const signatureResult = await runSignatureFactory( | ||
this._client.options.conversationSignatureFactory, params, | ||
); | ||
Object.assign(command.convMessage, keyRemap({ | ||
signature: 's', | ||
timestamp: 't', | ||
nonce: 'n', | ||
}, signatureResult)); | ||
} | ||
await this._send(command); | ||
if (!this.transient && !this.system) { | ||
this.members = union(this.members, clientIds); | ||
} | ||
return this; | ||
} | ||
@@ -523,3 +453,3 @@ | ||
*/ | ||
remove(clientIds) { | ||
async remove(clientIds) { | ||
this._debug('remove', clientIds); | ||
@@ -532,29 +462,22 @@ if (typeof clientIds === 'string') { | ||
}); | ||
return Promise.resolve( | ||
new GenericCommand({ | ||
op: 'remove', | ||
convMessage, | ||
}) | ||
).then((command) => { | ||
if (this._client.options.conversationSignatureFactory) { | ||
const params = [this.id, this._client.id, clientIds.sort(), 'remove']; | ||
return runSignatureFactory(this._client.options.conversationSignatureFactory, params) | ||
.then((signatureResult) => { | ||
Object.assign(command.convMessage, keyRemap({ | ||
signature: 's', | ||
timestamp: 't', | ||
nonce: 'n', | ||
}, signatureResult)); | ||
return command; | ||
}); | ||
} | ||
return command; | ||
}).then( | ||
this._send.bind(this) | ||
).then(() => { | ||
if (!this.transient && !this.system) { | ||
this.members = difference(this.members, clientIds); | ||
} | ||
return this; | ||
const command = new GenericCommand({ | ||
op: 'remove', | ||
convMessage, | ||
}); | ||
if (this._client.options.conversationSignatureFactory) { | ||
const params = [this.id, this._client.id, clientIds.sort(), 'remove']; | ||
const signatureResult = await runSignatureFactory( | ||
this._client.options.conversationSignatureFactory, params, | ||
); | ||
Object.assign(command.convMessage, keyRemap({ | ||
signature: 's', | ||
timestamp: 't', | ||
nonce: 'n', | ||
}, signatureResult)); | ||
} | ||
await this._send(command); | ||
if (!this.transient && !this.system) { | ||
this.members = difference(this.members, clientIds); | ||
} | ||
return this; | ||
} | ||
@@ -566,3 +489,3 @@ | ||
*/ | ||
join() { | ||
async join() { | ||
this._debug('join'); | ||
@@ -576,3 +499,3 @@ return this.add(this._client.id); | ||
*/ | ||
quit() { | ||
async quit() { | ||
this._debug('quit'); | ||
@@ -587,3 +510,3 @@ return this.remove(this._client.id); | ||
* @param {Boolean} [options.transient] since v3.3.1,是否作为暂态消息发送 | ||
* @param {Boolean} [options.receipt] 是否需要送达回执,仅在普通对话中有效 | ||
* @param {Boolean} [options.receipt] 是否需要回执,仅在普通对话中有效 | ||
* @param {Boolean} [options.will] since v3.4.0,是否指定该消息作为「掉线消息」发送, | ||
@@ -596,3 +519,3 @@ * 「掉线消息」会延迟到当前用户掉线后发送,常用来实现「下线通知」功能 | ||
*/ | ||
send(message, options) { | ||
async send(message, options) { | ||
this._debug(message, 'send'); | ||
@@ -603,7 +526,2 @@ if (!(message instanceof Message)) { | ||
const _options = Object.assign({}, options); | ||
// support typo reciept option | ||
if (_options.reciept !== undefined) { | ||
console.warn('DEPRECATION Conversation#send options.reciept: please use receipt option instead'); | ||
if (_options.receipt === undefined) _options.receipt = _options.reciept; | ||
} | ||
const { | ||
@@ -616,10 +534,6 @@ transient, | ||
} = Object.assign( | ||
// support deprecated attribute: message.needReceipt | ||
{ | ||
transient: message.transient, | ||
receipt: message.needReceipt, | ||
}, | ||
{}, | ||
// support Message static property: sendOptions | ||
message.constructor.sendOptions, | ||
_options | ||
_options, | ||
); | ||
@@ -647,3 +561,3 @@ if (receipt) { | ||
} | ||
let sendPromise = this._send(new GenericCommand({ | ||
const command = new GenericCommand({ | ||
cmd: 'direct', | ||
@@ -660,5 +574,6 @@ directMessage: new DirectCommand({ | ||
priority, | ||
}), !transient); | ||
if (!transient) { | ||
sendPromise = sendPromise.then((resCommand) => { | ||
}); | ||
try { | ||
const resCommand = await this._send(command, !transient); | ||
if (!transient) { | ||
const { | ||
@@ -671,4 +586,4 @@ ackMessage: { | ||
appCode, | ||
}, | ||
} = resCommand; | ||
}, | ||
} = resCommand; | ||
if (code !== null) { | ||
@@ -685,5 +600,3 @@ throw createError({ | ||
this.lastMessageAt = message.timestamp; | ||
}); | ||
} | ||
return sendPromise.then(() => { | ||
} | ||
message._setStatus(MessageStatus.SENT); | ||
@@ -694,9 +607,9 @@ if (receipt) { | ||
return message; | ||
}, (error) => { | ||
} catch (error) { | ||
message._setStatus(MessageStatus.FAILED); | ||
throw error; | ||
}); | ||
} | ||
} | ||
_update(message, newMessage, recall) { | ||
async _update(message, newMessage, recall) { | ||
this._debug('patch %O %O %O', message, newMessage, recall); | ||
@@ -720,3 +633,3 @@ if (message instanceof Message) { | ||
} | ||
return this._send(new GenericCommand({ | ||
await this._send(new GenericCommand({ | ||
cmd: CommandType.patch, | ||
@@ -734,14 +647,13 @@ op: OpType.modify, | ||
}), | ||
})).then(() => { | ||
const { | ||
id, cid, timestamp, from, _status, | ||
} = message; | ||
Object.assign(newMessage, { | ||
id, cid, timestamp, from, _status, | ||
}); | ||
if (this.lastMessage.id === newMessage.id) { | ||
this.lastMessage = newMessage; | ||
} | ||
return newMessage; | ||
})); | ||
const { | ||
id, cid, timestamp, from, _status, | ||
} = message; | ||
Object.assign(newMessage, { | ||
id, cid, timestamp, from, _status, | ||
}); | ||
if (this.lastMessage.id === newMessage.id) { | ||
this.lastMessage = newMessage; | ||
} | ||
return newMessage; | ||
} | ||
@@ -754,3 +666,3 @@ | ||
*/ | ||
update(message, newMessage) { | ||
async update(message, newMessage) { | ||
if (!(newMessage instanceof Message)) { | ||
@@ -766,3 +678,3 @@ throw new TypeError(`${newMessage} is not a Message`); | ||
*/ | ||
recall(message) { | ||
async recall(message) { | ||
return this._update(message, new RecalledMessage(), true); | ||
@@ -782,3 +694,3 @@ } | ||
*/ | ||
queryMessages(options = {}) { | ||
async queryMessages(options = {}) { | ||
this._debug('query messages %O', options); | ||
@@ -804,3 +716,3 @@ if (options.beforeMessageId && !options.beforeTime) { | ||
} | ||
return this._send(new GenericCommand({ | ||
const resCommand = await this._send(new GenericCommand({ | ||
cmd: 'logs', | ||
@@ -810,37 +722,35 @@ logsMessage: new LogsCommand( | ||
cid: this.id, | ||
}) | ||
}), | ||
), | ||
})).then(resCommand => | ||
Promise.all(resCommand.logsMessage.logs.map(({ | ||
msgId, | ||
timestamp, | ||
patchTimestamp, | ||
})); | ||
return Promise.all(resCommand.logsMessage.logs.map(async ({ | ||
msgId, | ||
timestamp, | ||
patchTimestamp, | ||
from, | ||
ackAt, | ||
readAt, | ||
data, | ||
}) => { | ||
const message = await this._client._messageParser.parse(data); | ||
const messageProps = { | ||
id: msgId, | ||
cid: this.id, | ||
timestamp: new Date(timestamp.toNumber()), | ||
from, | ||
ackAt, | ||
readAt, | ||
data, | ||
}) => | ||
this._client._messageParser.parse(data).then((message) => { | ||
const messageProps = { | ||
id: msgId, | ||
cid: this.id, | ||
timestamp: new Date(timestamp.toNumber()), | ||
from, | ||
deliveredAt: ackAt, | ||
}; | ||
if (patchTimestamp) { | ||
messageProps.updatedAt = new Date(patchTimestamp.toNumber()); | ||
} | ||
Object.assign(message, messageProps); | ||
let status = MessageStatus.SENT; | ||
if (this.members.length === 2) { | ||
if (ackAt) status = MessageStatus.DELIVERED; | ||
if (ackAt) this._setLastDeliveredAt(ackAt); | ||
if (readAt) this._setLastReadAt(readAt); | ||
} | ||
message._setStatus(status); | ||
return message; | ||
}) | ||
)) | ||
); | ||
deliveredAt: ackAt, | ||
}; | ||
if (patchTimestamp) { | ||
messageProps.updatedAt = new Date(patchTimestamp.toNumber()); | ||
} | ||
Object.assign(message, messageProps); | ||
let status = MessageStatus.SENT; | ||
if (this.members.length === 2) { | ||
if (ackAt) status = MessageStatus.DELIVERED; | ||
if (ackAt) this._setLastDeliveredAt(ackAt); | ||
if (readAt) this._setLastReadAt(readAt); | ||
} | ||
message._setStatus(status); | ||
return message; | ||
})); | ||
} | ||
@@ -910,6 +820,6 @@ | ||
*/ | ||
read() { | ||
async read() { | ||
this.unreadMessagesCount = 0; | ||
// 跳过暂态会话 | ||
if (this.transient) return Promise.resolve(this); | ||
if (this.transient) return this; | ||
const client = this._client; | ||
@@ -921,15 +831,5 @@ if (!internal(client).readConversationsBuffer) { | ||
client._doSendRead(); | ||
return Promise.resolve(this); | ||
return this; | ||
} | ||
/** | ||
* 将该会话标记为已读 | ||
* @deprecated in favor of {@link Conversation#read} | ||
* @return {Promise.<Conversation>} self | ||
*/ | ||
markAsRead() { | ||
console.warn('DEPRECATION Conversation#markAsRead: Use Conversation#read instead.'); | ||
return this.read(); | ||
} | ||
_handleReceipt({ messageId, timestamp, read }) { | ||
@@ -947,12 +847,2 @@ if (read) { | ||
delete messagesWaitingForReceipt[messageId]; | ||
/** | ||
* 消息已送达。只有在发送时设置了需要回执的情况下才会收到送达回执,该回执并不代表用户已读。 | ||
* @event Conversation#receipt | ||
* @deprecated use {@link Conversation#event:lastdeliveredatupdate} | ||
* and {@link Conversation#event:lastreadatupdate} instead | ||
* @since 3.2.0 | ||
* @param {Object} payload | ||
* @param {Message} payload.message 送达的消息 | ||
*/ | ||
this.emit('receipt', { message }); | ||
} | ||
@@ -965,6 +855,4 @@ | ||
*/ | ||
fetchReceiptTimestamps() { | ||
return this._send(new GenericCommand({ | ||
op: 'max_read', | ||
})).then(({ | ||
async fetchReceiptTimestamps() { | ||
const { | ||
convMessage: { | ||
@@ -974,7 +862,8 @@ maxReadTimestamp, | ||
}, | ||
}) => { | ||
this._setLastDeliveredAt(maxAckTimestamp); | ||
this._setLastReadAt(maxReadTimestamp); | ||
return this; | ||
}); | ||
} = await this._send(new GenericCommand({ | ||
op: 'max_read', | ||
})); | ||
this._setLastDeliveredAt(maxAckTimestamp); | ||
this._setLastReadAt(maxReadTimestamp); | ||
return this; | ||
} | ||
@@ -981,0 +870,0 @@ |
@@ -12,2 +12,4 @@ import './polyfills/polyfills'; | ||
/** | ||
* @name Realtime | ||
* @memberof module:leancloud-realtime | ||
* @see Realtime | ||
@@ -21,2 +23,4 @@ */ | ||
* 错误码,详见 {@link https://leancloud.cn/docs/realtime_v2.html#云端错误码说明} | ||
* @name ErrorCode | ||
* @memberof module:leancloud-realtime | ||
* @enum {Number} | ||
@@ -23,0 +27,0 @@ * @since 3.3.0 |
@@ -78,3 +78,3 @@ export const error = Object.freeze({ | ||
[error[code].name]: Number(code), | ||
}), {}) | ||
}), {}), | ||
); | ||
@@ -81,0 +81,0 @@ |
@@ -17,3 +17,3 @@ import EventEmitter from 'eventemitter3'; | ||
import { ErrorCode } from './error'; | ||
import { tap, Expirable, Cache, keyRemap, union, difference, trim, internal, throttle } from './utils'; | ||
import { Expirable, Cache, keyRemap, union, difference, trim, internal, throttle } from './utils'; | ||
import { applyDecorators, applyDispatcher } from './plugin'; | ||
@@ -73,3 +73,3 @@ import runSignatureFactory from './signature-factory-runner'; | ||
event, | ||
(...payload) => this._debug(`${event} event emitted. %O`, payload) | ||
(...payload) => this._debug(`${event} event emitted. %O`, payload), | ||
)); | ||
@@ -88,3 +88,3 @@ // onIMClientCreate hook | ||
*/ | ||
_dispatchCommand(command) { | ||
async _dispatchCommand(command) { | ||
this._debug(trim(command), 'received'); | ||
@@ -105,8 +105,7 @@ switch (command.cmd) { | ||
default: | ||
this.emit('unhandledmessage', command); | ||
return Promise.resolve(); | ||
return this.emit('unhandledmessage', command); | ||
} | ||
} | ||
_dispatchSessionMessage(message) { | ||
async _dispatchSessionMessage(message) { | ||
const { | ||
@@ -141,3 +140,3 @@ sessionMessage: { | ||
this.emit('unhandledmessage', message); | ||
return Promise.reject(new Error('Unrecognized session command')); | ||
throw new Error('Unrecognized session command'); | ||
} | ||
@@ -157,54 +156,39 @@ } | ||
Promise.all(convs.map(({ | ||
cid, | ||
unread, | ||
mid, | ||
timestamp: ts, | ||
from, | ||
data, | ||
patchTimestamp, | ||
}) => this.getConversation(cid).then((conversation) => { | ||
// deleted conversation | ||
if (!conversation) return null; | ||
let timestamp; | ||
if (ts) { | ||
timestamp = new Date(ts.toNumber()); | ||
conversation.lastMessageAt = timestamp; // eslint-disable-line no-param-reassign | ||
cid, | ||
unread, | ||
mid, | ||
timestamp: ts, | ||
from, | ||
data, | ||
patchTimestamp, | ||
}) => this.getConversation(cid).then((conversation) => { | ||
// deleted conversation | ||
if (!conversation) return null; | ||
let timestamp; | ||
if (ts) { | ||
timestamp = new Date(ts.toNumber()); | ||
conversation.lastMessageAt = timestamp; // eslint-disable-line no-param-reassign | ||
} | ||
return (mid ? this._messageParser.parse(data).then((message) => { | ||
const messageProps = { | ||
id: mid, | ||
cid, | ||
timestamp, | ||
from, | ||
}; | ||
if (patchTimestamp) { | ||
messageProps.updatedAt = new Date(patchTimestamp.toNumber()); | ||
} | ||
return (mid ? this._messageParser.parse(data).then((message) => { | ||
const messageProps = { | ||
id: mid, | ||
cid, | ||
timestamp, | ||
from, | ||
}; | ||
if (patchTimestamp) { | ||
messageProps.updatedAt = new Date(patchTimestamp.toNumber()); | ||
} | ||
Object.assign(message, messageProps); | ||
conversation.lastMessage = message; // eslint-disable-line no-param-reassign | ||
}) : Promise.resolve()).then(() => { | ||
const countNotUpdated = unread === internal(conversation).unreadMessagesCount; | ||
if (countNotUpdated) return null; // to be filtered | ||
// manipulate internal property directly to skip unreadmessagescountupdate event | ||
internal(conversation).unreadMessagesCount = unread; | ||
/** | ||
* 未读消息数目更新 | ||
* @event IMClient#unreadmessages | ||
* @deprecated 请使用新的未读消息数目批量更新事件 {@link IMClient#event:unreadmessagescountupdate} | ||
* @param {Object} payload | ||
* @param {Number} payload.count 未读消息数 | ||
* @param {String} [payload.lastMessageId] 最新一条未读消息 id | ||
* @param {Date} [payload.lastMessageTimestamp] 最新一条未读消息时间戳 | ||
* @param {Conversation} conversation 未读消息数目有更新的对话 | ||
*/ | ||
this.emit('unreadmessages', { | ||
count: unread, | ||
lastMessageId: mid, | ||
lastMessageTimestamp: timestamp, | ||
}, conversation); | ||
return conversation; | ||
}); | ||
}) | ||
// filter conversations without unread count update | ||
)).then(conversations => conversations.filter(conversation => conversation)) | ||
Object.assign(message, messageProps); | ||
conversation.lastMessage = message; // eslint-disable-line no-param-reassign | ||
}) : Promise.resolve()).then(() => { | ||
const countNotUpdated = unread === internal(conversation).unreadMessagesCount; | ||
if (countNotUpdated) return null; // to be filtered | ||
// manipulate internal property directly to skip unreadmessagescountupdate event | ||
internal(conversation).unreadMessagesCount = unread; | ||
return conversation; | ||
}); | ||
}), | ||
// filter conversations without unread count update | ||
)).then(conversations => conversations.filter(conversation => conversation)), | ||
).then((conversations) => { | ||
@@ -223,3 +207,3 @@ if (conversations.length) { | ||
_dispatchRcpMessage(message) { | ||
async _dispatchRcpMessage(message) { | ||
const { | ||
@@ -242,6 +226,6 @@ rcpMessage, | ||
_dispatchPatchMessage({ | ||
patchMessage: { | ||
patches, | ||
}, | ||
}) { | ||
patchMessage: { | ||
patches, | ||
}, | ||
}) { | ||
// ensure all converstions are cached | ||
@@ -303,8 +287,8 @@ return this.getConversations(patches.map(patch => patch.cid)).then(() => | ||
}); | ||
}) | ||
)) | ||
}), | ||
)), | ||
); | ||
} | ||
_dispatchConvMessage(message) { | ||
async _dispatchConvMessage(message) { | ||
const { | ||
@@ -316,116 +300,113 @@ convMessage, | ||
} = message; | ||
const conversation = await this.getConversation(convMessage.cid); | ||
switch (message.op) { | ||
case OpType.joined: { | ||
return this.getConversation(convMessage.cid).then((conversation) => { | ||
if (!conversation.transient) { | ||
// eslint-disable-next-line no-param-reassign | ||
conversation.members = union(conversation.members, [this.id]); | ||
} | ||
const payload = { | ||
invitedBy: initBy, | ||
}; | ||
/** | ||
* 当前用户被添加至某个对话 | ||
* @event IMClient#invited | ||
* @param {Object} payload | ||
* @param {String} payload.invitedBy 邀请者 id | ||
* @param {Conversation} conversation | ||
*/ | ||
this.emit('invited', payload, conversation); | ||
/** | ||
* 当前用户被添加至当前对话 | ||
* @event Conversation#invited | ||
* @param {Object} payload | ||
* @param {String} payload.invitedBy 该移除操作的发起者 id | ||
*/ | ||
conversation.emit('invited', payload); | ||
}); | ||
if (!conversation.transient) { | ||
// eslint-disable-next-line no-param-reassign | ||
conversation.members = union(conversation.members, [this.id]); | ||
} | ||
const payload = { | ||
invitedBy: initBy, | ||
}; | ||
/** | ||
* 当前用户被添加至某个对话 | ||
* @event IMClient#invited | ||
* @param {Object} payload | ||
* @param {String} payload.invitedBy 邀请者 id | ||
* @param {Conversation} conversation | ||
*/ | ||
this.emit('invited', payload, conversation); | ||
/** | ||
* 当前用户被添加至当前对话 | ||
* @event Conversation#invited | ||
* @param {Object} payload | ||
* @param {String} payload.invitedBy 该移除操作的发起者 id | ||
*/ | ||
conversation.emit('invited', payload); | ||
return; | ||
} | ||
case OpType.left: { | ||
return this.getConversation(convMessage.cid).then((conversation) => { | ||
if (!conversation.transient) { | ||
// eslint-disable-next-line no-param-reassign | ||
conversation.members = difference(conversation.members, [this.id]); | ||
} | ||
const payload = { | ||
kickedBy: initBy, | ||
}; | ||
/** | ||
* 当前用户被从某个对话中移除 | ||
* @event IMClient#kicked | ||
* @param {Object} payload | ||
* @param {String} payload.kickedBy 该移除操作的发起者 id | ||
* @param {Conversation} conversation | ||
*/ | ||
this.emit('kicked', payload, conversation); | ||
/** | ||
* 当前用户被从当前对话中移除 | ||
* @event Conversation#kicked | ||
* @param {Object} payload | ||
* @param {String} payload.kickedBy 该移除操作的发起者 id | ||
*/ | ||
conversation.emit('kicked', payload); | ||
}); | ||
if (!conversation.transient) { | ||
// eslint-disable-next-line no-param-reassign | ||
conversation.members = difference(conversation.members, [this.id]); | ||
} | ||
const payload = { | ||
kickedBy: initBy, | ||
}; | ||
/** | ||
* 当前用户被从某个对话中移除 | ||
* @event IMClient#kicked | ||
* @param {Object} payload | ||
* @param {String} payload.kickedBy 该移除操作的发起者 id | ||
* @param {Conversation} conversation | ||
*/ | ||
this.emit('kicked', payload, conversation); | ||
/** | ||
* 当前用户被从当前对话中移除 | ||
* @event Conversation#kicked | ||
* @param {Object} payload | ||
* @param {String} payload.kickedBy 该移除操作的发起者 id | ||
*/ | ||
conversation.emit('kicked', payload); | ||
return; | ||
} | ||
case OpType.members_joined: { | ||
return this.getConversation(convMessage.cid).then((conversation) => { | ||
if (!conversation.transient) { | ||
// eslint-disable-next-line no-param-reassign | ||
conversation.members = union(conversation.members, convMessage.m); | ||
} | ||
const payload = { | ||
invitedBy: initBy, | ||
members: m, | ||
}; | ||
/** | ||
* 有用户被添加至某个对话 | ||
* @event IMClient#membersjoined | ||
* @param {Object} payload | ||
* @param {String[]} payload.members 被添加的用户 id 列表 | ||
* @param {String} payload.invitedBy 邀请者 id | ||
* @param {Conversation} conversation | ||
*/ | ||
this.emit('membersjoined', payload, conversation); | ||
/** | ||
* 有成员被添加至当前对话 | ||
* @event Conversation#membersjoined | ||
* @param {Object} payload | ||
* @param {String[]} payload.members 被添加的成员 id 列表 | ||
* @param {String} payload.invitedBy 邀请者 id | ||
*/ | ||
conversation.emit('membersjoined', payload); | ||
}); | ||
if (!conversation.transient) { | ||
// eslint-disable-next-line no-param-reassign | ||
conversation.members = union(conversation.members, convMessage.m); | ||
} | ||
const payload = { | ||
invitedBy: initBy, | ||
members: m, | ||
}; | ||
/** | ||
* 有用户被添加至某个对话 | ||
* @event IMClient#membersjoined | ||
* @param {Object} payload | ||
* @param {String[]} payload.members 被添加的用户 id 列表 | ||
* @param {String} payload.invitedBy 邀请者 id | ||
* @param {Conversation} conversation | ||
*/ | ||
this.emit('membersjoined', payload, conversation); | ||
/** | ||
* 有成员被添加至当前对话 | ||
* @event Conversation#membersjoined | ||
* @param {Object} payload | ||
* @param {String[]} payload.members 被添加的成员 id 列表 | ||
* @param {String} payload.invitedBy 邀请者 id | ||
*/ | ||
conversation.emit('membersjoined', payload); | ||
return; | ||
} | ||
case OpType.members_left: { | ||
return this.getConversation(convMessage.cid).then((conversation) => { | ||
if (!conversation.transient) { | ||
// eslint-disable-next-line no-param-reassign | ||
conversation.members = difference(conversation.members, convMessage.m); | ||
} | ||
const payload = { | ||
kickedBy: initBy, | ||
members: m, | ||
}; | ||
/** | ||
* 有成员被从某个对话中移除 | ||
* @event IMClient#membersleft | ||
* @param {Object} payload | ||
* @param {String[]} payload.members 被移除的成员 id 列表 | ||
* @param {String} payload.kickedBy 该移除操作的发起者 id | ||
* @param {Conversation} conversation | ||
*/ | ||
this.emit('membersleft', payload, conversation); | ||
/** | ||
* 有成员被从当前对话中移除 | ||
* @event Conversation#membersleft | ||
* @param {Object} payload | ||
* @param {String[]} payload.members 被移除的成员 id 列表 | ||
* @param {String} payload.kickedBy 该移除操作的发起者 id | ||
*/ | ||
conversation.emit('membersleft', payload); | ||
}); | ||
if (!conversation.transient) { | ||
// eslint-disable-next-line no-param-reassign | ||
conversation.members = difference(conversation.members, convMessage.m); | ||
} | ||
const payload = { | ||
kickedBy: initBy, | ||
members: m, | ||
}; | ||
/** | ||
* 有成员被从某个对话中移除 | ||
* @event IMClient#membersleft | ||
* @param {Object} payload | ||
* @param {String[]} payload.members 被移除的成员 id 列表 | ||
* @param {String} payload.kickedBy 该移除操作的发起者 id | ||
* @param {Conversation} conversation | ||
*/ | ||
this.emit('membersleft', payload, conversation); | ||
/** | ||
* 有成员被从当前对话中移除 | ||
* @event Conversation#membersleft | ||
* @param {Object} payload | ||
* @param {String[]} payload.members 被移除的成员 id 列表 | ||
* @param {String} payload.kickedBy 该移除操作的发起者 id | ||
*/ | ||
conversation.emit('membersleft', payload); | ||
return; | ||
} | ||
default: | ||
this.emit('unhandledmessage', message); | ||
return Promise.reject(new Error('Unrecognized conversation command')); | ||
throw new Error('Unrecognized conversation command'); | ||
} | ||
@@ -452,3 +433,2 @@ } | ||
from: fromPeerId, | ||
transient, | ||
}; | ||
@@ -460,2 +440,8 @@ if (patchTimestamp) { | ||
message._setStatus(MessageStatus.SENT); | ||
// filter outgoing message sent from another device | ||
if (message.from !== this.id) { | ||
if (!(transient || conversation.transient)) { | ||
this._sendAck(message); | ||
} | ||
} | ||
return this._dispatchParsedMessage(message, conversation); | ||
@@ -475,5 +461,2 @@ }); | ||
conversation.unreadMessagesCount += 1; // eslint-disable-line no-param-reassign | ||
if (!(message.transient || conversation.transient)) { | ||
this._sendAck(message); | ||
} | ||
} | ||
@@ -581,3 +564,3 @@ /** | ||
if (this.options.signatureFactory) { | ||
return runSignatureFactory(this.options.signatureFactory, [this.id]) | ||
return runSignatureFactory(this.options.signatureFactory, [this._identity]) | ||
.then((signatureResult) => { | ||
@@ -608,2 +591,3 @@ Object.assign(command.sessionMessage, keyRemap({ | ||
this.id = peerId; | ||
if (!this._identity) this._identity = peerId; | ||
if (token) { | ||
@@ -631,3 +615,3 @@ internal(this).sessionToken = new Expirable(token, tokenTTL * 1000); | ||
*/ | ||
close() { | ||
async close() { | ||
this._debug('close session'); | ||
@@ -638,12 +622,9 @@ const command = new GenericCommand({ | ||
}); | ||
return this._send(command).then( | ||
() => { | ||
internal(this)._eventemitter.emit('close', { | ||
code: 0, | ||
}); | ||
this.emit('close', { | ||
code: 0, | ||
}); | ||
} | ||
); | ||
await this._send(command); | ||
internal(this)._eventemitter.emit('close', { | ||
code: 0, | ||
}); | ||
this.emit('close', { | ||
code: 0, | ||
}); | ||
} | ||
@@ -655,3 +636,3 @@ /** | ||
*/ | ||
ping(clientIds) { | ||
async ping(clientIds) { | ||
this._debug('ping'); | ||
@@ -671,4 +652,4 @@ if (!(clientIds instanceof Array)) { | ||
}); | ||
return this._send(command) | ||
.then(resCommand => resCommand.sessionMessage.onlineSessionPeerIds); | ||
const resCommand = await this._send(command); | ||
return resCommand.sessionMessage.onlineSessionPeerIds; | ||
} | ||
@@ -682,3 +663,3 @@ | ||
*/ | ||
getConversation(id, noCache = false) { | ||
async getConversation(id, noCache = false) { | ||
if (typeof id !== 'string') { | ||
@@ -707,10 +688,9 @@ throw new TypeError(`${id} is not a String`); | ||
*/ | ||
getConversations(ids, noCache = false) { | ||
async getConversations(ids, noCache = false) { | ||
const remoteConversationIds = | ||
noCache ? ids : ids.filter(id => this._conversationCache.get(id) === null); | ||
return ( | ||
remoteConversationIds.length ? | ||
this.getQuery().containedIn('objectId', remoteConversationIds).limit(999).find() : | ||
Promise.resolve() | ||
).then(() => ids.map(id => this._conversationCache.get(id))); | ||
if (remoteConversationIds.length) { | ||
await (this.getQuery().containedIn('objectId', remoteConversationIds).limit(999)).find(); | ||
} | ||
return ids.map(id => this._conversationCache.get(id)); | ||
} | ||
@@ -726,3 +706,3 @@ | ||
_executeQuery(query) { | ||
async _executeQuery(query) { | ||
const queryJSON = query.toJSON(); | ||
@@ -737,45 +717,43 @@ queryJSON.where = new JsonObjectMessage({ | ||
}); | ||
return this | ||
._send(command) | ||
.then((resCommand) => { | ||
try { | ||
return JSON.parse(resCommand.convMessage.results.data); | ||
} catch (error) { | ||
const commandString = JSON.stringify(trim(resCommand)); | ||
throw new Error(`Parse query result failed: ${error.message}. Command: ${commandString}`); | ||
} | ||
}) | ||
.then(conversations => Promise.all(conversations.map( | ||
this._parseConversationFromRawData.bind(this) | ||
))) | ||
.then(conversations => conversations.map((fetchedConversation) => { | ||
let conversation = this._conversationCache.get(fetchedConversation.id); | ||
if (!conversation) { | ||
conversation = fetchedConversation; | ||
this._debug('no match, set cache'); | ||
this._conversationCache.set(fetchedConversation.id, fetchedConversation); | ||
} else { | ||
this._debug('update cached conversation'); | ||
[ | ||
'creator', | ||
'createdAt', | ||
'updatedAt', | ||
'lastMessageAt', | ||
'lastMessage', | ||
'mutedMembers', | ||
'members', | ||
'_attributes', | ||
'transient', | ||
'muted', | ||
].forEach((key) => { | ||
const value = fetchedConversation[key]; | ||
if (value !== undefined) conversation[key] = value; | ||
}); | ||
conversation._reset(); | ||
} | ||
return conversation; | ||
})); | ||
const resCommand = await this._send(command); | ||
let conversations; | ||
try { | ||
conversations = JSON.parse(resCommand.convMessage.results.data); | ||
} catch (error) { | ||
const commandString = JSON.stringify(trim(resCommand)); | ||
throw new Error(`Parse query result failed: ${error.message}. Command: ${commandString}`); | ||
} | ||
conversations = await Promise.all(conversations.map( | ||
this._parseConversationFromRawData.bind(this), | ||
)); | ||
return conversations.map((fetchedConversation) => { | ||
let conversation = this._conversationCache.get(fetchedConversation.id); | ||
if (!conversation) { | ||
conversation = fetchedConversation; | ||
this._debug('no match, set cache'); | ||
this._conversationCache.set(fetchedConversation.id, fetchedConversation); | ||
} else { | ||
this._debug('update cached conversation'); | ||
[ | ||
'creator', | ||
'createdAt', | ||
'updatedAt', | ||
'lastMessageAt', | ||
'lastMessage', | ||
'mutedMembers', | ||
'members', | ||
'_attributes', | ||
'transient', | ||
'muted', | ||
].forEach((key) => { | ||
const value = fetchedConversation[key]; | ||
if (value !== undefined) conversation[key] = value; | ||
}); | ||
conversation._reset(); | ||
} | ||
return conversation; | ||
}); | ||
} | ||
_parseConversationFromRawData(rawData) { | ||
async _parseConversationFromRawData(rawData) { | ||
const data = keyRemap({ | ||
@@ -795,25 +773,18 @@ objectId: 'id', | ||
}, rawData); | ||
return Promise.resolve().then(() => { | ||
if (data.lastMessage) { | ||
return this._messageParser.parse(data.lastMessage).then( | ||
(message) => { | ||
/* eslint-disable no-param-reassign */ | ||
data.lastMessage = message; | ||
message.from = data.lastMessageFrom; | ||
message.id = data.lastMessageId; | ||
message.timestamp = new Date(data.lastMessageTimestamp); | ||
if (data.lastMessagePatchTimestamp) { | ||
message.updatedAt = new Date(data.lastMessagePatchTimestamp); | ||
} | ||
message._setStatus(MessageStatus.SENT); | ||
delete data.lastMessageFrom; | ||
delete data.lastMessageId; | ||
delete data.lastMessageTimestamp; | ||
delete data.lastMessagePatchTimestamp; | ||
/* eslint-enable no-param-reassign */ | ||
} | ||
); | ||
if (data.lastMessage) { | ||
const message = await this._messageParser.parse(data.lastMessage); | ||
data.lastMessage = message; | ||
message.from = data.lastMessageFrom; | ||
message.id = data.lastMessageId; | ||
message.timestamp = new Date(data.lastMessageTimestamp); | ||
if (data.lastMessagePatchTimestamp) { | ||
message.updatedAt = new Date(data.lastMessagePatchTimestamp); | ||
} | ||
return Promise.resolve(); | ||
}).then(() => new Conversation(data, this)); | ||
message._setStatus(MessageStatus.SENT); | ||
delete data.lastMessageFrom; | ||
delete data.lastMessageId; | ||
delete data.lastMessageTimestamp; | ||
delete data.lastMessagePatchTimestamp; | ||
} | ||
return new Conversation(data, this); | ||
} | ||
@@ -831,7 +802,6 @@ | ||
*/ | ||
createConversation(options = {}) { | ||
async createConversation(options = {}) { | ||
const { | ||
members: m, | ||
name, | ||
attributes, | ||
transient, | ||
@@ -854,6 +824,2 @@ unique, | ||
} | ||
if (attributes) { | ||
console.warn('DEPRECATION createConversation options.attributes param: Use options[propertyName] instead. See https://url.leanapp.cn/DeprecateAttributes for more details.'); | ||
attr.attr = attributes; | ||
} | ||
attr = new JsonObjectMessage({ | ||
@@ -870,56 +836,36 @@ data: JSON.stringify(attr), | ||
return Promise.resolve( | ||
new GenericCommand({ | ||
cmd: 'conv', | ||
op: 'start', | ||
convMessage: new ConvCommand(startCommandJson), | ||
}) | ||
) | ||
.then((command) => { | ||
if (this.options.conversationSignatureFactory) { | ||
const params = [null, this.id, members, 'create']; | ||
return runSignatureFactory(this.options.conversationSignatureFactory, params) | ||
.then((signatureResult) => { | ||
Object.assign(command.convMessage, keyRemap({ | ||
signature: 's', | ||
timestamp: 't', | ||
nonce: 'n', | ||
}, signatureResult)); | ||
return command; | ||
}); | ||
} | ||
return command; | ||
}) | ||
.then(this._send.bind(this)) | ||
.then(resCommand => new Conversation({ | ||
name, | ||
attr: attributes, | ||
transient, | ||
unique, | ||
id: resCommand.convMessage.cid, | ||
createdAt: resCommand.convMessage.cdate, | ||
updatedAt: resCommand.convMessage.cdate, | ||
lastMessageAt: null, | ||
creator: this.id, | ||
members: transient ? [] : members, | ||
...properties, | ||
}, this)) | ||
.then(tap(conversation => | ||
this._conversationCache.set(conversation.id, conversation) | ||
)); | ||
} | ||
const command = new GenericCommand({ | ||
cmd: 'conv', | ||
op: 'start', | ||
convMessage: new ConvCommand(startCommandJson), | ||
}); | ||
/** | ||
* 将指定的所有会话标记为已读 | ||
* @deprecated 请遍历调用 conversations 的 {@link Conversation#read read} 方法 | ||
* @param {Conversation[]} conversations 指定的会话列表 | ||
* @return {Promise.<Conversation[]>} conversations 返回输入的会话列表 | ||
*/ | ||
// eslint-disable-next-line class-methods-use-this | ||
markAllAsRead(conversations) { | ||
console.warn('DEPRECATION IMClient.markAllAsRead: Use Conversation#read instead.'); | ||
if (!Array.isArray(conversations)) { | ||
throw new TypeError(`${conversations} is not an Array`); | ||
if (this.options.conversationSignatureFactory) { | ||
const params = [null, this._identity, members, 'create']; | ||
const signatureResult = await runSignatureFactory( | ||
this.options.conversationSignatureFactory, | ||
params, | ||
); | ||
Object.assign(command.convMessage, keyRemap({ | ||
signature: 's', | ||
timestamp: 't', | ||
nonce: 'n', | ||
}, signatureResult)); | ||
} | ||
return Promise.all(conversations.map(conversation => conversation.read())); | ||
const resCommand = await this._send(command); | ||
const conversation = new Conversation({ | ||
name, | ||
transient, | ||
unique, | ||
id: resCommand.convMessage.cid, | ||
createdAt: resCommand.convMessage.cdate, | ||
updatedAt: resCommand.convMessage.cdate, | ||
lastMessageAt: null, | ||
creator: this.id, | ||
members: transient ? [] : members, | ||
...properties, | ||
}, this); | ||
this._conversationCache.set(conversation.id, conversation); | ||
return conversation; | ||
} | ||
@@ -926,0 +872,0 @@ |
@@ -65,18 +65,2 @@ import uuid from 'uuid/v4'; | ||
/** | ||
* 标记需要回执 | ||
* @memberof Message# | ||
* @type {Boolean} | ||
* @deprecated 指定是否需要送达回执请使用 {@link Conversation#send} 方法的 `options.receipt` 参数。 | ||
*/ | ||
needReceipt: false, | ||
/** | ||
* 标记暂态消息 | ||
* @memberof Message# | ||
* @type {Boolean} | ||
* @deprecated 指定是否作为暂态消息发送请使用 {@link Conversation#send} 方法的 `options.transient` 参数。 | ||
* 请不要将是否为暂态作为区分某些消息的标记,请使用富媒体消息的属性(attributes)或使用自定义消息类型。 | ||
* 该字段将在 v4.0 中移除。 | ||
*/ | ||
transient: false, | ||
/** | ||
* @var deliveredAt {?Date} 消息送达时间 | ||
@@ -91,26 +75,2 @@ * @memberof Message# | ||
/** | ||
* 设置是否需要送达回执 | ||
* @param {Boolean} needReceipt | ||
* @return {Message} self | ||
* @deprecated 请使用 {@link Conversation#send} 方法的 `options.receipt` 选项代替。 | ||
*/ | ||
setNeedReceipt(needReceipt) { | ||
console.warn('DEPRECATION Message#setNeedReceipt: Use Conversation#send with sendOptions.receipt instead.'); | ||
this.needReceipt = needReceipt; | ||
return this; | ||
} | ||
/** | ||
* 设置是否是暂态消息 | ||
* @param {Boolean} transient | ||
* @return {Message} self | ||
* @deprecated 请使用 {@link Conversation#send} 方法的 `options.transient` 选项代替。 | ||
*/ | ||
setTransient(transient) { | ||
console.warn('DEPRECATION Message#setTransient: Use Conversation#send with sendOptions.transient instead.'); | ||
this.transient = transient; | ||
return this; | ||
} | ||
/** | ||
* 将当前消息序列化为 JSON 对象 | ||
@@ -117,0 +77,0 @@ * @protected |
@@ -119,4 +119,13 @@ /** @module leancloud-realtime */ | ||
realtime._IMClients = {}; | ||
const messageParser = realtime._messageParser = new MessageParser(realtime._plugins); | ||
const messageParser = new MessageParser(realtime._plugins); | ||
realtime._messageParser = messageParser; | ||
const signAVUser = async user => realtime._request({ | ||
method: 'POST', | ||
path: '/rtm/sign', | ||
data: { | ||
session_token: user.getSessionToken(), | ||
}, | ||
}); | ||
/** | ||
@@ -127,3 +136,2 @@ * 注册消息类 | ||
* | ||
* @function register | ||
* @memberof Realtime | ||
@@ -135,11 +143,11 @@ * @instance | ||
*/ | ||
realtime.register = messageClass => | ||
const register = messageClass => | ||
ensureArray(messageClass).map(messageParser.register.bind(messageParser)); | ||
realtime.register(ensureArray(realtime._plugins.messageClasses)); | ||
register(ensureArray(realtime._plugins.messageClasses)); | ||
/** | ||
* 创建一个即时通讯客户端,多次创建相同 id 的客户端会返回同一个实例 | ||
* @function createIMClient | ||
* @memberof Realtime | ||
* @instance | ||
* @param {String} [id] 客户端 id,如果不指定,服务端会随机生成一个 | ||
* @param {String|AV.User} [identity] 客户端 identity,如果不指定该参数,服务端会随机生成一个字符串作为 identity, | ||
* 如果传入一个已登录的 AV.User,则会使用该用户的 id 作为客户端 identity 登录。 | ||
* @param {Object} [clientOptions] 详细参数 @see {@link IMClient} | ||
@@ -149,11 +157,27 @@ * @param {String} [tag] 客户端类型标记,以支持单点登录功能 | ||
*/ | ||
realtime.createIMClient = (id, clientOptions, tag) => { | ||
const idIsString = typeof id === 'string'; | ||
if (idIsString && realtime._IMClients[id] !== undefined) { | ||
return Promise.resolve(realtime._IMClients[id]); | ||
const createIMClient = async (identity, clientOptions, tag) => { | ||
let id; | ||
const buildinOptions = {}; | ||
if (identity) { | ||
if (typeof identity === 'string') { | ||
id = identity; | ||
} else if (identity.id && identity.getSessionToken) { | ||
id = identity.id; | ||
const sessionToken = identity.getSessionToken(); | ||
if (!sessionToken) { | ||
throw new Error('User must be authenticated'); | ||
} | ||
buildinOptions.signatureFactory = signAVUser; | ||
} else { | ||
throw new TypeError('Identity must be a String or an AV.User'); | ||
} | ||
if (realtime._IMClients[id] !== undefined) { | ||
return realtime._IMClients[id]; | ||
} | ||
} | ||
const promise = realtime._open().then((connection) => { | ||
const client = new IMClient(id, clientOptions, connection, { | ||
const client = new IMClient(id, { ...buildinOptions, ...clientOptions }, connection, { | ||
_messageParser: messageParser, | ||
_plugins: realtime._plugins, | ||
_identity: identity, | ||
}); | ||
@@ -175,4 +199,4 @@ connection.on('reconnect', () => | ||
() => client.emit('reconnect'), | ||
error => client.emit('reconnecterror', error) | ||
) | ||
error => client.emit('reconnecterror', error), | ||
), | ||
); | ||
@@ -188,8 +212,8 @@ internal(client)._eventemitter.on('close', () => { | ||
return client; | ||
}).catch((error) => { | ||
delete realtime._IMClients[client.id]; | ||
throw error; | ||
}); | ||
}).catch((error) => { | ||
delete realtime._IMClients[id]; | ||
throw error; | ||
}); | ||
if (idIsString) { | ||
if (identity) { | ||
realtime._IMClients[id] = promise; | ||
@@ -199,2 +223,6 @@ } | ||
}; | ||
Object.assign(realtime, { | ||
register, | ||
createIMClient, | ||
}); | ||
/* eslint-enable no-param-reassign */ | ||
@@ -207,7 +235,7 @@ }; | ||
if (targetClient) { | ||
Promise.resolve(targetClient._dispatchCommand(command)).catch(debug); | ||
targetClient._dispatchCommand(command).catch(debug); | ||
} else { | ||
debug( | ||
'[WARN] Unexpected message received without any live client match: %O', | ||
trim(command) | ||
trim(command), | ||
); | ||
@@ -214,0 +242,0 @@ } |
@@ -142,3 +142,3 @@ /* eslint-disable max-len */ | ||
}), | ||
Promise.resolve(target) | ||
Promise.resolve(target), | ||
); | ||
@@ -149,3 +149,3 @@ | ||
(resultPromise, dispatcher) => resultPromise.then(shouldDispatch => | ||
(shouldDispatch === false ? false : dispatcher(...payload)) | ||
(shouldDispatch === false ? false : dispatcher(...payload)), | ||
).catch((error) => { | ||
@@ -157,3 +157,3 @@ if (dispatcher._pluginName) { | ||
throw error; | ||
}), Promise.resolve(true) | ||
}), Promise.resolve(true), | ||
); |
@@ -11,3 +11,3 @@ import d from 'debug'; | ||
const pushRouterCache = new Cache('push-router'); | ||
const routerCache = new Cache('push-router'); | ||
@@ -19,6 +19,7 @@ export default class Realtime extends EventEmitter { | ||
* @param {String} options.appId | ||
* @param {String} options.appKey (since 4.0.0) | ||
* @param {String} [options.region='cn'] 节点 id | ||
* @param {Boolean} [options.pushOfflineMessages=false] 启用推送离线消息模式(默认为发送未读消息通知模式) | ||
* @param {Boolean} [options.noBinary=false] 设置 WebSocket 使用字符串格式收发消息(默认为二进制格式)。 | ||
* 适用于 WebSocket 实现不支持二进制数据格式的情况(如 React Native) | ||
* 适用于 WebSocket 实现不支持二进制数据格式的情况(如微信小程序) | ||
* @param {Boolean} [options.ssl=true] 使用 wss 进行连接 | ||
@@ -33,4 +34,8 @@ * @param {Plugin[]} [options.plugins] 加载插件(since 3.1.0) | ||
} | ||
if (typeof options.appKey !== 'string') { | ||
throw new TypeError(`appKey [${options.appKey}] is not a string`); | ||
} | ||
this._options = Object.assign({ | ||
appId: undefined, | ||
appKey: undefined, | ||
region: 'cn', | ||
@@ -64,3 +69,3 @@ pushOfflineMessages: false, | ||
}, | ||
{} | ||
{}, | ||
); | ||
@@ -71,2 +76,25 @@ // onRealtimeCreate hook | ||
async _request({ | ||
method, | ||
version = '1.1', | ||
path, | ||
query, | ||
headers, | ||
data = {}, | ||
}) { | ||
const { appId, region } = this._options; | ||
const { api } = await this.constructor._fetchAppRouter({ appId, region }); | ||
const url = `https://${api}/${version}${path}`; | ||
return axios(url, { | ||
method, | ||
params: query, | ||
headers: { | ||
'X-LC-Id': this._options.appId, | ||
'X-LC-Key': this._options.appKey, | ||
...headers, | ||
}, | ||
data, | ||
}).then(response => response.data); | ||
} | ||
_open() { | ||
@@ -93,3 +121,3 @@ if (this._openPromise) return this._openPromise; | ||
() => this._getEndpoints(this._options), | ||
protocol | ||
protocol, | ||
); | ||
@@ -177,3 +205,3 @@ connection.on('open', () => resolve(connection)); | ||
} | ||
}) | ||
}), | ||
); | ||
@@ -196,5 +224,2 @@ // override handleClose | ||
internal(this).connection = connection; | ||
}).catch((error) => { | ||
delete this._openPromise; | ||
throw error; | ||
}); | ||
@@ -212,4 +237,4 @@ | ||
.then( | ||
tap(info => this._cache.set('endpoints', info, info.ttl * 1000)) | ||
) | ||
tap(info => this._cache.set('endpoints', info, info.ttl * 1000)), | ||
), | ||
).then((info) => { | ||
@@ -221,12 +246,12 @@ debug('endpoint info: %O', info); | ||
static _fetchPushRouter({ appId, region }) { | ||
static _fetchAppRouter({ appId, region }) { | ||
debug('fetch router'); | ||
switch (region) { | ||
case 'cn': { | ||
const cachedPushRouter = pushRouterCache.get(appId); | ||
if (cachedPushRouter) { | ||
return Promise.resolve(cachedPushRouter); | ||
const cachedRouter = routerCache.get(appId); | ||
if (cachedRouter) { | ||
return Promise.resolve(cachedRouter); | ||
} | ||
return axios | ||
.get('https://app-router.leancloud.cn/1/route', { | ||
.get('https://app-router.leancloud.cn/2/route', { | ||
params: { | ||
@@ -238,23 +263,35 @@ appId, | ||
.then( | ||
res => res.data | ||
res => res.data, | ||
) | ||
.then(tap(debug)) | ||
.then( | ||
(route) => { | ||
const pushRouter = route.push_router_server; | ||
if (!pushRouter) { | ||
throw new Error('push router not exists'); | ||
({ | ||
rtm_router_server: rtmRouter, | ||
api_server: api, | ||
ttl = 3600, | ||
}) => { | ||
if (!rtmRouter) { | ||
throw new Error('rtm router not exists'); | ||
} | ||
let ttl = route.ttl; | ||
if (typeof ttl !== 'number') { | ||
ttl = 3600; | ||
} | ||
pushRouterCache.set(appId, pushRouter, ttl * 1000); | ||
return pushRouter; | ||
} | ||
const router = { | ||
rtmRouter, | ||
api, | ||
}; | ||
routerCache.set(appId, router, ttl * 1000); | ||
return router; | ||
}, | ||
) | ||
.catch(() => 'router-g0-push.leancloud.cn'); | ||
.catch(() => { | ||
const id = appId.slice(0, 8).toLowerCase(); | ||
return { | ||
rtmRouter: `${id}.rtm.lncld.net`, | ||
api: `${id}.api.lncld.net`, | ||
}; | ||
}); | ||
} | ||
case 'us': | ||
return Promise.resolve('router-a0-push.leancloud.cn'); | ||
return Promise.resolve({ | ||
rtmRouter: 'router-a0-push.leancloud.cn', | ||
api: 'us-api.leancloud.cn', | ||
}); | ||
default: | ||
@@ -267,6 +304,6 @@ throw new Error(`Region [${region}] is not supported.`); | ||
debug('fetch endpoint info'); | ||
return this._fetchPushRouter({ appId, region }) | ||
return this._fetchAppRouter({ appId, region }) | ||
.then(tap(debug)) | ||
.then(router => | ||
axios.get(`https://${router}/v1/route`, { | ||
.then(({ rtmRouter }) => | ||
axios.get(`https://${rtmRouter}/v1/route`, { | ||
params: { | ||
@@ -281,5 +318,5 @@ appId, | ||
}).then( | ||
res => res.data | ||
).then(tap(debug)) | ||
); | ||
res => res.data, | ||
).then(tap(debug)), | ||
); | ||
} | ||
@@ -306,3 +343,3 @@ | ||
throw new Error( | ||
`retrying not allowed when not disconnected. the connection is now ${connection.current}` | ||
`retrying not allowed when not disconnected. the connection is now ${connection.current}`, | ||
); | ||
@@ -309,0 +346,0 @@ } |
@@ -27,3 +27,3 @@ import d from 'debug'; | ||
.then(() => { | ||
debug(`call signatureFactory with ${params}`); | ||
debug('call signatureFactory with %O', params); | ||
return signatureFactory(...params); | ||
@@ -38,4 +38,4 @@ }) | ||
throw error; | ||
} | ||
}, | ||
) | ||
.then(_validateSignature); |
@@ -55,3 +55,3 @@ import isPlainObject from 'lodash/isPlainObject'; | ||
export const difference = (a, b) => Array.from( | ||
(bSet => new Set(a.filter(x => !bSet.has(x))))(new Set(b)) | ||
(bSet => new Set(a.filter(x => !bSet.has(x))))(new Set(b)), | ||
); | ||
@@ -127,7 +127,13 @@ | ||
} = internal(this); | ||
if (!throttleMeta) throttleMeta = internal(this).throttleMeta = {}; | ||
if (!throttleMeta) { | ||
throttleMeta = {}; | ||
internal(this).throttleMeta = throttleMeta; | ||
} | ||
let { | ||
[property]: propertyMeta, | ||
} = throttleMeta; | ||
if (!propertyMeta) propertyMeta = throttleMeta[property] = {}; | ||
if (!propertyMeta) { | ||
propertyMeta = {}; | ||
throttleMeta[property] = propertyMeta; | ||
} | ||
const { | ||
@@ -134,0 +140,0 @@ previouseTimestamp = 0, |
@@ -16,3 +16,3 @@ // WebSocket with auto reconnecting feature, backup endpoint and EventEmitter interface. | ||
const DEFAULT_RETRY_STRATEGY = attempt => Math.min(1000 * Math.pow(2, attempt), 300000); | ||
const DEFAULT_RETRY_STRATEGY = attempt => Math.min(1000 * (2 ** attempt), 300000); | ||
@@ -57,3 +57,3 @@ const requireConnected = (target, name, descriptor) => | ||
this.open(); | ||
} | ||
}, | ||
).catch(this.throw.bind(this)); | ||
@@ -72,7 +72,7 @@ } | ||
const ws = protocol ? new WebSocket( | ||
url, protocol | ||
url, protocol, | ||
) : new WebSocket(url); | ||
ws.binaryType = this.binaryType || 'arraybuffer'; | ||
ws.onopen = () => resolve(ws); | ||
ws.onerror = ws.onclose = (error) => { | ||
ws.onclose = (error) => { | ||
if (error instanceof Error) { | ||
@@ -84,3 +84,4 @@ return reject(error); | ||
}; | ||
}) | ||
ws.onerror = ws.onclose; | ||
}), | ||
).then((ws) => { | ||
@@ -97,3 +98,6 @@ this._ws = ws; | ||
if (!ws) return; | ||
ws.onopen = ws.onclose = ws.onerror = ws.onmessage = null; | ||
ws.onopen = null; | ||
ws.onclose = null; | ||
ws.onerror = null; | ||
ws.onmessage = null; | ||
this._ws = null; | ||
@@ -146,3 +150,3 @@ ws.close(); | ||
() => (this.can('reconnect') ? this.reconnect() : this._destroyWs()), | ||
() => this.can('fail') && this.fail(attempt + 1) | ||
() => this.can('fail') && this.fail(attempt + 1), | ||
); | ||
@@ -149,0 +153,0 @@ } |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
12123671
40
63
92195
186
1
23