main
asd 4 months ago
parent 8910c59468
commit bc59053d11

@ -0,0 +1,31 @@
/*
* Eslint config file
* Documentation: https://eslint.org/docs/user-guide/configuring/
* Install the Eslint extension before using this feature.
*/
module.exports = {
env: {
es6: true,
browser: true,
node: true,
},
ecmaFeatures: {
modules: true,
},
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
},
globals: {
wx: true,
App: true,
Page: true,
getCurrentPages: true,
getApp: true,
Component: true,
requirePlugin: true,
requireMiniProgram: true,
},
// extends: 'eslint:recommended',
rules: {},
}

@ -0,0 +1,6 @@
{
"permissions": {
"openapi": [
]
}
}

@ -0,0 +1,48 @@
const cloud = require('wx-server-sdk')
cloud.init({
  env: 'fzu-rollcall-system-7c9tbd0e8c2b'
})
var xlsx = require('node-xlsx');
const db = cloud.database()
exports.main = async(event, context) => {
  let {
    fileID
  } = event
  //1,通过fileID下载云存储里的excel文件
  const res = await cloud.downloadFile({
    fileID: fileID,
  })
  const buffer = res.fileContent
  const tasks = [] //用来存储所有的添加数据操作
  //2,解析excel文件里的数据
  var sheets = xlsx.parse(buffer); //获取到所有sheets
  sheets.forEach(function(sheet) {
    console.log(sheet['name']);
    for (var rowId in sheet['data']) {
      console.log(rowId);
      var row = sheet['data'][rowId]; //第几行数据
      if (rowId > 0 && row) { //第一行是表格标题所有我们要从第2行开始读
        //3把解析到的数据存到excelList数据表里
        const promise = db.collection('users')
          .add({
            data: {
              num: row[0], 
              name: row[1], 
              score: row[2]
            }
          })
        tasks.push(promise)
      }
    }
  });
  // 等待所有数据添加完成
  let result = await Promise.all(tasks).then(res => {
    return res
  }).catch(function(err) {
    return err
  })
  return result
}

@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../node-xlsx/dist/bin/cli.js" "$@"
else
exec node "$basedir/../node-xlsx/dist/bin/cli.js" "$@"
fi

@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\node-xlsx\dist\bin\cli.js" %*

@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../node-xlsx/dist/bin/cli.js" $args
} else {
& "$basedir/node$exe" "$basedir/../node-xlsx/dist/bin/cli.js" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../node-xlsx/dist/bin/cli.js" $args
} else {
& "node$exe" "$basedir/../node-xlsx/dist/bin/cli.js" $args
}
$ret=$LASTEXITCODE
}
exit $ret

@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../semver/bin/semver" "$@"
else
exec node "$basedir/../semver/bin/semver" "$@"
fi

@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\semver\bin\semver" %*

@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../semver/bin/semver" $args
} else {
& "$basedir/node$exe" "$basedir/../semver/bin/semver" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../semver/bin/semver" $args
} else {
& "node$exe" "$basedir/../semver/bin/semver" $args
}
$ret=$LASTEXITCODE
}
exit $ret

@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../sshpk/bin/sshpk-conv" "$@"
else
exec node "$basedir/../sshpk/bin/sshpk-conv" "$@"
fi

@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\sshpk\bin\sshpk-conv" %*

@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../sshpk/bin/sshpk-conv" $args
} else {
& "$basedir/node$exe" "$basedir/../sshpk/bin/sshpk-conv" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../sshpk/bin/sshpk-conv" $args
} else {
& "node$exe" "$basedir/../sshpk/bin/sshpk-conv" $args
}
$ret=$LASTEXITCODE
}
exit $ret

@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../sshpk/bin/sshpk-sign" "$@"
else
exec node "$basedir/../sshpk/bin/sshpk-sign" "$@"
fi

@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\sshpk\bin\sshpk-sign" %*

@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../sshpk/bin/sshpk-sign" $args
} else {
& "$basedir/node$exe" "$basedir/../sshpk/bin/sshpk-sign" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../sshpk/bin/sshpk-sign" $args
} else {
& "node$exe" "$basedir/../sshpk/bin/sshpk-sign" $args
}
$ret=$LASTEXITCODE
}
exit $ret

@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../sshpk/bin/sshpk-verify" "$@"
else
exec node "$basedir/../sshpk/bin/sshpk-verify" "$@"
fi

@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\sshpk\bin\sshpk-verify" %*

@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../sshpk/bin/sshpk-verify" $args
} else {
& "$basedir/node$exe" "$basedir/../sshpk/bin/sshpk-verify" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../sshpk/bin/sshpk-verify" $args
} else {
& "node$exe" "$basedir/../sshpk/bin/sshpk-verify" $args
}
$ret=$LASTEXITCODE
}
exit $ret

@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../uuid/bin/uuid" "$@"
else
exec node "$basedir/../uuid/bin/uuid" "$@"
fi

@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\uuid\bin\uuid" %*

@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../uuid/bin/uuid" $args
} else {
& "$basedir/node$exe" "$basedir/../uuid/bin/uuid" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../uuid/bin/uuid" $args
} else {
& "node$exe" "$basedir/../uuid/bin/uuid" $args
}
$ret=$LASTEXITCODE
}
exit $ret

@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../xlsx/bin/xlsx.njs" "$@"
else
exec node "$basedir/../xlsx/bin/xlsx.njs" "$@"
fi

@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\xlsx\bin\xlsx.njs" %*

@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../xlsx/bin/xlsx.njs" $args
} else {
& "$basedir/node$exe" "$basedir/../xlsx/bin/xlsx.njs" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../xlsx/bin/xlsx.njs" $args
} else {
& "node$exe" "$basedir/../xlsx/bin/xlsx.njs" $args
}
$ret=$LASTEXITCODE
}
exit $ret

File diff suppressed because it is too large Load Diff

@ -0,0 +1,9 @@
root = true
[!{node_modules}/**]
end_of_line = lf
charset = utf-8
[{*.js,*.ts}]
indent_style = space
indent_size = 2

@ -0,0 +1,43 @@
{
"extends": [
"prettier"
],
"plugins": [
"typescript"
],
"rules": {
"indent": [
"error",
2,
{
"SwitchCase": 1,
"flatTernaryExpressions": true
}
],
"no-unused-vars": "warn",
"typescript/no-unused-vars": "warn",
"semi": [
"error",
"never"
],
"quotes": [
"error",
"single",
{
"avoidEscape": true
}
]
},
"env": {
"es6": true,
"node": true
},
"parser": "typescript-eslint-parser",
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module",
"ecmaFeatures": {
"modules": true
}
}
}

@ -0,0 +1,29 @@
module.exports = {
// 一行最多 100 字符
printWidth: 100,
// 使用 2 个空格缩进
tabWidth: 2,
// 不使用缩进符,而使用空格
useTabs: false,
// 行尾无分号
semi: false,
// 使用单引号
singleQuote: true,
// 对象的 key 仅在必要时用引号
quoteProps: 'as-needed',
// 末尾不需要逗号
trailingComma: 'none',
// 大括号内的首尾需要空格
bracketSpacing: true,
// 每个文件格式化的范围是文件的全部内容
rangeStart: 0,
rangeEnd: Infinity,
// 不需要写文件开头的 @prettier
requirePragma: false,
// 不需要自动在文件开头插入 @prettier
insertPragma: false,
// 使用默认的折行标准
proseWrap: 'preserve',
// 换行符使用 lf
endOfLine: 'lf'
}

@ -0,0 +1,119 @@
# @cloudbase/database
[![NPM Version](https://img.shields.io/npm/v/@cloudbase/database.svg?style=flat)](https://www.npmjs.com/package/@cloudbase/database)
[![](https://img.shields.io/npm/dt/@cloudbase/database.svg)](https://www.npmjs.com/package/@cloudbase/database)
## 介绍
提供 TCB JS SDK 操作数据库的接口。
## 安装
```
yarn add @cloudbase/database
```
## 使用
使用 `@cloudbase/database` 时,需要提供发送请求的类 `reqClass`
```js
// index.js
const database = require('@cloudbase/database/').Db;
const Request = require('./request');
class Tcb {
...
database(dbConfig) {
database.reqClass = Request;
return new database(dbConfig);
}
}
```
实现 `Request` 类,需要提供异步的 `send` 方法。
```js
// request.js
// 进一步处理,鉴权等...
const requestHandler = require('requestHandler')
class Request {
constructor(config) {
this.config = config
}
async send(action, data) {
const params = Object.assign({}, data, {
action
})
const slowQueryWarning = setTimeout(() => {
console.warn(
'Database operation is longer than 3s. Please check query performance and your network environment.'
)
}, 3000)
try {
return await requestHandler({
timeout: this.config.timeout,
config: this.config.config,
params,
method: 'post',
headers: {
'content-type': 'application/json'
}
})
} finally {
clearTimeout(slowQueryWarning)
}
}
}
module.exports = Request
```
请求数据样例
```json
{
"url": "https://tcb-admin.tencentcloudapi.com/admin?eventId=1554896261428_92044",
"method": "post",
"timeout": 15000,
"headers": { "user-agent": "tcb-admin-sdk/1.4.6", "content-type": "application/json" },
"body": {
"collectionName": "coll-1",
"action": "database.addCollection",
"envName": "base-830cab",
"timestamp": 1554896261428,
"eventId": "1554896261428_92044",
"authorization": "q-sign-algorithm=sha1&q-ak=xxx&q-sign-time=1554896260;1554897160&q-key-time=1554896260;1554897160&q-header-list=content-type;user-agent&q-url-param-list=action;collectionname;envname;eventid;timestamp&q-signature=xxxxx",
"sdk_version": "1.4.6"
},
"json": true
}
```
## 开发指南
### 注意
当前 database 库是通过分支来控制了两个不同的版本,主干版提供给@cloudbase/js-sdk 库使用feature/support_db_2.0 分支提供给@cloudbase/node-sdk 库使用。
两个分支区别:
support_db_2.0 分支进行了数据库接口的升级,将原有的 json 协议转换为 mongo 支持的 bson 协议处理,目的是解决旧接口存在的问题,描述如下
- 日期对象 是走约定协议处理,即转换为 {$date: timestamp},这种方式应摒弃(EJSON 协议可解决)
- 无法支持 null, NaN 等特殊类型处理(EJSON 协议可解决)
- serverDate 这种自定义类型的实现,是继续保留 还是摒弃
- 接口实现混杂,普通 CRUD 接口均走 flexdb而聚合事务又用 runcommand 方式
- 原插入文档接口返回 docId 形式混杂
目前仅针对服务端 sdk @cloudbase/node-sdk 完成了升级,而客户端 sdk @cloudbase/js-sdk 以及 小程序端 SDK 仍保留为旧接口形式。
### 开发及发布事项
1. 数据库接口的改动务必补充测试用例验证
2. 发布时,约定 feature/support_db_2.0 分支上发布正式版for @cloudbase/node-sdk使用master 分支上发布 beta 版 (for @cloudbase/js-sdk)使用。两 sdk 均通过锁版本方式依赖该库,不会受自动更新依赖影响。

@ -0,0 +1,11 @@
export declare class ObjectId {
id: string;
constructor({ id }?: {
id?: string;
});
readonly _internalType: import("../utils/symbol").InternalSymbol;
parse(): {
$oid: string;
};
}
export declare function ObjectIdConstructor(opt: any): ObjectId;

@ -0,0 +1,21 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const symbol_1 = require("../helper/symbol");
class ObjectId {
constructor({ id = '' } = {}) {
this.id = id;
}
get _internalType() {
return symbol_1.SYMBOL_OBJECTID;
}
parse() {
return {
$oid: this.id
};
}
}
exports.ObjectId = ObjectId;
function ObjectIdConstructor(opt) {
return new ObjectId(opt);
}
exports.ObjectIdConstructor = ObjectIdConstructor;

@ -0,0 +1,29 @@
export default class Aggregation {
_db: any;
_request: any;
_stages: any[];
_collectionName: string;
constructor(db?: any, collectionName?: any, rawPipeline?: any);
end(): Promise<any>;
unwrap(): any[];
done(): {
[x: number]: any;
}[];
_pipe(stage: any, param: any, raw?: boolean): this;
addFields(param: any): this;
bucket(param: any): this;
bucketAuto(param: any): this;
count(param: any): this;
geoNear(param: any): this;
group(param: any): this;
limit(param: any): this;
match(param: any): this;
project(param: any): this;
lookup(param: any): this;
replaceRoot(param: any): this;
sample(param: any): this;
skip(param: any): this;
sort(param: any): this;
sortByCount(param: any): this;
unwind(param: any): this;
}

@ -0,0 +1,124 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const index_1 = require("./index");
const bson_1 = require("bson");
const query_1 = require("./serializer/query");
const utils_1 = require("./utils/utils");
const type_1 = require("./utils/type");
const validate_1 = require("./validate");
const point_1 = require("./geo/point");
class Aggregation {
constructor(db, collectionName, rawPipeline) {
this._stages = [];
if (db && collectionName) {
this._db = db;
this._request = new index_1.Db.reqClass(this._db.config);
this._collectionName = collectionName;
if (rawPipeline && rawPipeline.length > 0) {
rawPipeline.forEach((stage) => {
validate_1.Validate.isValidAggregation(stage);
const stageName = Object.keys(stage)[0];
this._pipe(stageName, stage[stageName], true);
});
}
}
}
async end() {
if (!this._collectionName || !this._db) {
throw new Error('Aggregation pipeline cannot send request');
}
const result = await this._request.send('database.aggregateDocuments', {
collectionName: this._collectionName,
stages: this._stages
});
if (result && result.data && result.data.list) {
return {
requestId: result.requestId,
data: result.data.list.map(bson_1.EJSON.parse)
};
}
return result;
}
unwrap() {
return this._stages;
}
done() {
return this._stages.map(({ stageKey, stageValue }) => {
return {
[stageKey]: JSON.parse(stageValue)
};
});
}
_pipe(stage, param, raw = false) {
let transformParam = '';
if (type_1.getType(param) === 'object') {
transformParam = utils_1.stringifyByEJSON(param);
}
else {
transformParam = JSON.stringify(param);
}
this._stages.push({
stageKey: raw ? stage : `$${stage}`,
stageValue: transformParam
});
return this;
}
addFields(param) {
return this._pipe('addFields', param);
}
bucket(param) {
return this._pipe('bucket', param);
}
bucketAuto(param) {
return this._pipe('bucketAuto', param);
}
count(param) {
return this._pipe('count', param);
}
geoNear(param) {
if (param.query) {
param.query = query_1.QuerySerializer.encode(param.query);
}
if (param.distanceMultiplier && typeof (param.distanceMultiplier) === 'number') {
param.distanceMultiplier = param.distanceMultiplier;
}
if (param.near) {
param.near = new point_1.Point(param.near.longitude, param.near.latitude).toJSON();
}
return this._pipe('geoNear', param);
}
group(param) {
return this._pipe('group', param);
}
limit(param) {
return this._pipe('limit', param);
}
match(param) {
return this._pipe('match', query_1.QuerySerializer.encode(param));
}
project(param) {
return this._pipe('project', param);
}
lookup(param) {
return this._pipe('lookup', param);
}
replaceRoot(param) {
return this._pipe('replaceRoot', param);
}
sample(param) {
return this._pipe('sample', param);
}
skip(param) {
return this._pipe('skip', param);
}
sort(param) {
return this._pipe('sort', param);
}
sortByCount(param) {
return this._pipe('sortByCount', param);
}
unwind(param) {
return this._pipe('unwind', param);
}
}
exports.default = Aggregation;

@ -0,0 +1,17 @@
import { DocumentReference } from './document';
import { Query, QueryOption, UpdateOption } from './query';
import Aggregation from './aggregate';
export declare class CollectionReference extends Query {
protected _transactionId: string;
readonly name: string;
doc(docID: string | number): DocumentReference;
add(data: any): Promise<{
ids?: string[];
id?: string;
inserted?: number;
ok?: number;
requestId: string;
}>;
aggregate(rawPipeline?: object[]): Aggregation;
options(apiOptions: QueryOption | UpdateOption): CollectionReference;
}

@ -0,0 +1,72 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const document_1 = require("./document");
const query_1 = require("./query");
const aggregate_1 = require("./aggregate");
const datatype_1 = require("./serializer/datatype");
const utils_1 = require("./utils/utils");
const validate_1 = require("./validate");
const type_1 = require("./utils/type");
class CollectionReference extends query_1.Query {
constructor(db, coll, apiOptions, transactionId) {
super(db, coll, '', apiOptions, transactionId);
if (transactionId) {
this._transactionId = transactionId;
}
}
get name() {
return this._coll;
}
doc(docID) {
if (typeof docID !== 'string' && typeof docID !== 'number') {
throw new Error('docId必须为字符串或数字');
}
return new document_1.DocumentReference(this._db, this._coll, this._apiOptions, docID, this._transactionId);
}
async add(data) {
let transformData = data;
if (!type_1.isArray(data)) {
transformData = [data];
}
transformData = transformData.map(item => {
return utils_1.stringifyByEJSON(datatype_1.serialize(item));
});
let params = {
collectionName: this._coll,
data: transformData
};
if (this._transactionId) {
params.transactionId = this._transactionId;
}
const res = await this._request.send('database.insertDocument', params, utils_1.getReqOpts(this._apiOptions));
if (res.code) {
return res;
}
if (!type_1.isArray(data)) {
if (this._transactionId) {
return {
inserted: 1,
ok: 1,
id: res.data.insertedIds[0],
requestId: res.requestId
};
}
return {
id: res.data.insertedIds[0],
requestId: res.requestId
};
}
return {
ids: res.data.insertedIds,
requestId: res.requestId
};
}
aggregate(rawPipeline = []) {
return new aggregate_1.default(this._db, this._coll, (this._apiOptions.raw || false) ? rawPipeline : []);
}
options(apiOptions) {
validate_1.Validate.isValidOptions(apiOptions);
return new CollectionReference(this._db, this._coll, apiOptions, this._transactionId);
}
}
exports.CollectionReference = CollectionReference;

@ -0,0 +1,175 @@
import { QueryCommand } from './commands/query';
import { LogicCommand } from './commands/logic';
import { UpdateCommand } from './commands/update';
import Aggregation from './aggregate';
export declare type IQueryCondition = Record<string, any> | LogicCommand;
export declare const Command: {
eq(val: any): QueryCommand;
neq(val: any): QueryCommand;
lt(val: any): QueryCommand;
lte(val: any): QueryCommand;
gt(val: any): QueryCommand;
gte(val: any): QueryCommand;
in(val: any): QueryCommand;
nin(val: any): QueryCommand;
all(val: any): QueryCommand;
elemMatch(val: any): QueryCommand;
exists(val: boolean): QueryCommand;
size(val: number): QueryCommand;
mod(): QueryCommand;
geoNear(val: any): QueryCommand;
geoWithin(val: any): QueryCommand;
geoIntersects(val: any): QueryCommand;
and(...__expressions__: import("./serializer/datatype").IQueryCondition[]): LogicCommand;
nor(...__expressions__: import("./serializer/datatype").IQueryCondition[]): LogicCommand;
or(...__expressions__: import("./serializer/datatype").IQueryCondition[]): LogicCommand;
not(...__expressions__: import("./serializer/datatype").IQueryCondition[]): LogicCommand;
set(val: any): UpdateCommand;
remove(): UpdateCommand;
inc(val: number): UpdateCommand;
mul(val: number): UpdateCommand;
push(...args: any[]): UpdateCommand;
pull(values: any): UpdateCommand;
pullAll(values: any): UpdateCommand;
pop(): UpdateCommand;
shift(): UpdateCommand;
unshift(...__values__: any[]): UpdateCommand;
addToSet(values: any): UpdateCommand;
rename(values: any): UpdateCommand;
bit(values: any): UpdateCommand;
max(values: any): UpdateCommand;
min(values: any): UpdateCommand;
expr(values: AggregationOperator): {
$expr: AggregationOperator;
};
jsonSchema(schema: any): {
$jsonSchema: any;
};
text(values: string | {
search: string;
language?: string;
caseSensitive?: boolean;
diacriticSensitive: boolean;
}): {
$search: {
(regexp: string | RegExp): number;
(searcher: {
[Symbol.search](string: string): number;
}): number;
};
$language?: undefined;
$caseSensitive?: undefined;
$diacriticSensitive?: undefined;
} | {
$search: string;
$language: string;
$caseSensitive: boolean;
$diacriticSensitive: boolean;
};
aggregate: {
pipeline(): Aggregation;
abs: (param: any) => AggregationOperator;
add: (param: any) => AggregationOperator;
ceil: (param: any) => AggregationOperator;
divide: (param: any) => AggregationOperator;
exp: (param: any) => AggregationOperator;
floor: (param: any) => AggregationOperator;
ln: (param: any) => AggregationOperator;
log: (param: any) => AggregationOperator;
log10: (param: any) => AggregationOperator;
mod: (param: any) => AggregationOperator;
multiply: (param: any) => AggregationOperator;
pow: (param: any) => AggregationOperator;
sqrt: (param: any) => AggregationOperator;
subtract: (param: any) => AggregationOperator;
trunc: (param: any) => AggregationOperator;
arrayElemAt: (param: any) => AggregationOperator;
arrayToObject: (param: any) => AggregationOperator;
concatArrays: (param: any) => AggregationOperator;
filter: (param: any) => AggregationOperator;
in: (param: any) => AggregationOperator;
indexOfArray: (param: any) => AggregationOperator;
isArray: (param: any) => AggregationOperator;
map: (param: any) => AggregationOperator;
range: (param: any) => AggregationOperator;
reduce: (param: any) => AggregationOperator;
reverseArray: (param: any) => AggregationOperator;
size: (param: any) => AggregationOperator;
slice: (param: any) => AggregationOperator;
zip: (param: any) => AggregationOperator;
and: (param: any) => AggregationOperator;
not: (param: any) => AggregationOperator;
or: (param: any) => AggregationOperator;
cmp: (param: any) => AggregationOperator;
eq: (param: any) => AggregationOperator;
gt: (param: any) => AggregationOperator;
gte: (param: any) => AggregationOperator;
lt: (param: any) => AggregationOperator;
lte: (param: any) => AggregationOperator;
neq: (param: any) => AggregationOperator;
cond: (param: any) => AggregationOperator;
ifNull: (param: any) => AggregationOperator;
switch: (param: any) => AggregationOperator;
dateFromParts: (param: any) => AggregationOperator;
dateFromString: (param: any) => AggregationOperator;
dayOfMonth: (param: any) => AggregationOperator;
dayOfWeek: (param: any) => AggregationOperator;
dayOfYear: (param: any) => AggregationOperator;
isoDayOfWeek: (param: any) => AggregationOperator;
isoWeek: (param: any) => AggregationOperator;
isoWeekYear: (param: any) => AggregationOperator;
millisecond: (param: any) => AggregationOperator;
minute: (param: any) => AggregationOperator;
month: (param: any) => AggregationOperator;
second: (param: any) => AggregationOperator;
hour: (param: any) => AggregationOperator;
week: (param: any) => AggregationOperator;
year: (param: any) => AggregationOperator;
literal: (param: any) => AggregationOperator;
mergeObjects: (param: any) => AggregationOperator;
objectToArray: (param: any) => AggregationOperator;
allElementsTrue: (param: any) => AggregationOperator;
anyElementTrue: (param: any) => AggregationOperator;
setDifference: (param: any) => AggregationOperator;
setEquals: (param: any) => AggregationOperator;
setIntersection: (param: any) => AggregationOperator;
setIsSubset: (param: any) => AggregationOperator;
setUnion: (param: any) => AggregationOperator;
concat: (param: any) => AggregationOperator;
dateToString: (param: any) => AggregationOperator;
indexOfBytes: (param: any) => AggregationOperator;
indexOfCP: (param: any) => AggregationOperator;
split: (param: any) => AggregationOperator;
strLenBytes: (param: any) => AggregationOperator;
strLenCP: (param: any) => AggregationOperator;
strcasecmp: (param: any) => AggregationOperator;
substr: (param: any) => AggregationOperator;
substrBytes: (param: any) => AggregationOperator;
substrCP: (param: any) => AggregationOperator;
toLower: (param: any) => AggregationOperator;
toUpper: (param: any) => AggregationOperator;
meta: (param: any) => AggregationOperator;
addToSet: (param: any) => AggregationOperator;
avg: (param: any) => AggregationOperator;
first: (param: any) => AggregationOperator;
last: (param: any) => AggregationOperator;
max: (param: any) => AggregationOperator;
min: (param: any) => AggregationOperator;
push: (param: any) => AggregationOperator;
stdDevPop: (param: any) => AggregationOperator;
stdDevSamp: (param: any) => AggregationOperator;
sum: (param: any) => AggregationOperator;
let: (param: any) => AggregationOperator;
};
project: {
slice: (param: any) => ProjectionOperator;
elemMatch: (param: any) => ProjectionOperator;
};
};
export declare class AggregationOperator {
constructor(name: any, param: any);
}
export declare class ProjectionOperator {
constructor(name: any, param: any);
}
export default Command;

@ -0,0 +1,285 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const query_1 = require("./commands/query");
const logic_1 = require("./commands/logic");
const update_1 = require("./commands/update");
const type_1 = require("./utils/type");
const aggregate_1 = require("./aggregate");
exports.Command = {
eq(val) {
return new query_1.QueryCommand(query_1.QUERY_COMMANDS_LITERAL.EQ, [val]);
},
neq(val) {
return new query_1.QueryCommand(query_1.QUERY_COMMANDS_LITERAL.NEQ, [val]);
},
lt(val) {
return new query_1.QueryCommand(query_1.QUERY_COMMANDS_LITERAL.LT, [val]);
},
lte(val) {
return new query_1.QueryCommand(query_1.QUERY_COMMANDS_LITERAL.LTE, [val]);
},
gt(val) {
return new query_1.QueryCommand(query_1.QUERY_COMMANDS_LITERAL.GT, [val]);
},
gte(val) {
return new query_1.QueryCommand(query_1.QUERY_COMMANDS_LITERAL.GTE, [val]);
},
in(val) {
return new query_1.QueryCommand(query_1.QUERY_COMMANDS_LITERAL.IN, val);
},
nin(val) {
return new query_1.QueryCommand(query_1.QUERY_COMMANDS_LITERAL.NIN, val);
},
all(val) {
return new query_1.QueryCommand(query_1.QUERY_COMMANDS_LITERAL.ALL, val);
},
elemMatch(val) {
return new query_1.QueryCommand(query_1.QUERY_COMMANDS_LITERAL.ELEM_MATCH, [val]);
},
exists(val) {
return new query_1.QueryCommand(query_1.QUERY_COMMANDS_LITERAL.EXISTS, [val]);
},
size(val) {
return new query_1.QueryCommand(query_1.QUERY_COMMANDS_LITERAL.SIZE, [val]);
},
mod() {
if (arguments.length == 1)
return new query_1.QueryCommand(query_1.QUERY_COMMANDS_LITERAL.MOD, [arguments[0]]);
if (arguments.length == 2)
return new query_1.QueryCommand(query_1.QUERY_COMMANDS_LITERAL.MOD, [[arguments[0], arguments[1]]]);
return new query_1.QueryCommand(query_1.QUERY_COMMANDS_LITERAL.MOD, arguments);
},
geoNear(val) {
return new query_1.QueryCommand(query_1.QUERY_COMMANDS_LITERAL.GEO_NEAR, [val]);
},
geoWithin(val) {
return new query_1.QueryCommand(query_1.QUERY_COMMANDS_LITERAL.GEO_WITHIN, [val]);
},
geoIntersects(val) {
return new query_1.QueryCommand(query_1.QUERY_COMMANDS_LITERAL.GEO_INTERSECTS, [val]);
},
and(...__expressions__) {
const expressions = type_1.isArray(arguments[0]) ? arguments[0] : Array.from(arguments);
return new logic_1.LogicCommand(logic_1.LOGIC_COMMANDS_LITERAL.AND, expressions);
},
nor(...__expressions__) {
const expressions = type_1.isArray(arguments[0]) ? arguments[0] : Array.from(arguments);
return new logic_1.LogicCommand(logic_1.LOGIC_COMMANDS_LITERAL.NOR, expressions);
},
or(...__expressions__) {
const expressions = type_1.isArray(arguments[0]) ? arguments[0] : Array.from(arguments);
return new logic_1.LogicCommand(logic_1.LOGIC_COMMANDS_LITERAL.OR, expressions);
},
not(...__expressions__) {
const expressions = type_1.isArray(arguments[0]) ? arguments[0] : Array.from(arguments);
return new logic_1.LogicCommand(logic_1.LOGIC_COMMANDS_LITERAL.NOT, expressions);
},
set(val) {
return new update_1.UpdateCommand(update_1.UPDATE_COMMANDS_LITERAL.SET, [val]);
},
remove() {
return new update_1.UpdateCommand(update_1.UPDATE_COMMANDS_LITERAL.REMOVE, []);
},
inc(val) {
return new update_1.UpdateCommand(update_1.UPDATE_COMMANDS_LITERAL.INC, [val]);
},
mul(val) {
return new update_1.UpdateCommand(update_1.UPDATE_COMMANDS_LITERAL.MUL, [val]);
},
push(...args) {
let values;
if (type_1.isObject(args[0]) && args[0].hasOwnProperty('each')) {
values = {};
const options = args[0];
if (options.each !== undefined) {
values['$each'] = options.each;
}
if (options.position !== undefined) {
values['$position'] = options.position;
}
if (options.sort !== undefined) {
values['$sort'] = options.sort;
}
if (options.slice !== undefined) {
values['$slice'] = options.slice;
}
}
else if (type_1.isArray(args[0])) {
values = args[0];
}
else {
values = Array.from(args);
}
return new update_1.UpdateCommand(update_1.UPDATE_COMMANDS_LITERAL.PUSH, values);
},
pull(values) {
return new update_1.UpdateCommand(update_1.UPDATE_COMMANDS_LITERAL.PULL, values);
},
pullAll(values) {
return new update_1.UpdateCommand(update_1.UPDATE_COMMANDS_LITERAL.PULL_ALL, values);
},
pop() {
return new update_1.UpdateCommand(update_1.UPDATE_COMMANDS_LITERAL.POP, []);
},
shift() {
return new update_1.UpdateCommand(update_1.UPDATE_COMMANDS_LITERAL.SHIFT, []);
},
unshift(...__values__) {
const values = type_1.isArray(arguments[0]) ? arguments[0] : Array.from(arguments);
return new update_1.UpdateCommand(update_1.UPDATE_COMMANDS_LITERAL.UNSHIFT, values);
},
addToSet(values) {
return new update_1.UpdateCommand(update_1.UPDATE_COMMANDS_LITERAL.ADD_TO_SET, values);
},
rename(values) {
return new update_1.UpdateCommand(update_1.UPDATE_COMMANDS_LITERAL.RENAME, [values]);
},
bit(values) {
return new update_1.UpdateCommand(update_1.UPDATE_COMMANDS_LITERAL.BIT, [values]);
},
max(values) {
return new update_1.UpdateCommand(update_1.UPDATE_COMMANDS_LITERAL.MAX, [values]);
},
min(values) {
return new update_1.UpdateCommand(update_1.UPDATE_COMMANDS_LITERAL.MIN, [values]);
},
expr(values) {
return {
$expr: values
};
},
jsonSchema(schema) {
return {
$jsonSchema: schema
};
},
text(values) {
if (type_1.isString(values)) {
return {
$search: values.search
};
}
else {
return {
$search: values.search,
$language: values.language,
$caseSensitive: values.caseSensitive,
$diacriticSensitive: values.diacriticSensitive
};
}
},
aggregate: {
pipeline() {
return new aggregate_1.default();
},
abs: param => new AggregationOperator('abs', param),
add: param => new AggregationOperator('add', param),
ceil: param => new AggregationOperator('ceil', param),
divide: param => new AggregationOperator('divide', param),
exp: param => new AggregationOperator('exp', param),
floor: param => new AggregationOperator('floor', param),
ln: param => new AggregationOperator('ln', param),
log: param => new AggregationOperator('log', param),
log10: param => new AggregationOperator('log10', param),
mod: param => new AggregationOperator('mod', param),
multiply: param => new AggregationOperator('multiply', param),
pow: param => new AggregationOperator('pow', param),
sqrt: param => new AggregationOperator('sqrt', param),
subtract: param => new AggregationOperator('subtract', param),
trunc: param => new AggregationOperator('trunc', param),
arrayElemAt: param => new AggregationOperator('arrayElemAt', param),
arrayToObject: param => new AggregationOperator('arrayToObject', param),
concatArrays: param => new AggregationOperator('concatArrays', param),
filter: param => new AggregationOperator('filter', param),
in: param => new AggregationOperator('in', param),
indexOfArray: param => new AggregationOperator('indexOfArray', param),
isArray: param => new AggregationOperator('isArray', param),
map: param => new AggregationOperator('map', param),
range: param => new AggregationOperator('range', param),
reduce: param => new AggregationOperator('reduce', param),
reverseArray: param => new AggregationOperator('reverseArray', param),
size: param => new AggregationOperator('size', param),
slice: param => new AggregationOperator('slice', param),
zip: param => new AggregationOperator('zip', param),
and: param => new AggregationOperator('and', param),
not: param => new AggregationOperator('not', param),
or: param => new AggregationOperator('or', param),
cmp: param => new AggregationOperator('cmp', param),
eq: param => new AggregationOperator('eq', param),
gt: param => new AggregationOperator('gt', param),
gte: param => new AggregationOperator('gte', param),
lt: param => new AggregationOperator('lt', param),
lte: param => new AggregationOperator('lte', param),
neq: param => new AggregationOperator('ne', param),
cond: param => new AggregationOperator('cond', param),
ifNull: param => new AggregationOperator('ifNull', param),
switch: param => new AggregationOperator('switch', param),
dateFromParts: param => new AggregationOperator('dateFromParts', param),
dateFromString: param => new AggregationOperator('dateFromString', param),
dayOfMonth: param => new AggregationOperator('dayOfMonth', param),
dayOfWeek: param => new AggregationOperator('dayOfWeek', param),
dayOfYear: param => new AggregationOperator('dayOfYear', param),
isoDayOfWeek: param => new AggregationOperator('isoDayOfWeek', param),
isoWeek: param => new AggregationOperator('isoWeek', param),
isoWeekYear: param => new AggregationOperator('isoWeekYear', param),
millisecond: param => new AggregationOperator('millisecond', param),
minute: param => new AggregationOperator('minute', param),
month: param => new AggregationOperator('month', param),
second: param => new AggregationOperator('second', param),
hour: param => new AggregationOperator('hour', param),
week: param => new AggregationOperator('week', param),
year: param => new AggregationOperator('year', param),
literal: param => new AggregationOperator('literal', param),
mergeObjects: param => new AggregationOperator('mergeObjects', param),
objectToArray: param => new AggregationOperator('objectToArray', param),
allElementsTrue: param => new AggregationOperator('allElementsTrue', param),
anyElementTrue: param => new AggregationOperator('anyElementTrue', param),
setDifference: param => new AggregationOperator('setDifference', param),
setEquals: param => new AggregationOperator('setEquals', param),
setIntersection: param => new AggregationOperator('setIntersection', param),
setIsSubset: param => new AggregationOperator('setIsSubset', param),
setUnion: param => new AggregationOperator('setUnion', param),
concat: param => new AggregationOperator('concat', param),
dateToString: param => new AggregationOperator('dateToString', param),
indexOfBytes: param => new AggregationOperator('indexOfBytes', param),
indexOfCP: param => new AggregationOperator('indexOfCP', param),
split: param => new AggregationOperator('split', param),
strLenBytes: param => new AggregationOperator('strLenBytes', param),
strLenCP: param => new AggregationOperator('strLenCP', param),
strcasecmp: param => new AggregationOperator('strcasecmp', param),
substr: param => new AggregationOperator('substr', param),
substrBytes: param => new AggregationOperator('substrBytes', param),
substrCP: param => new AggregationOperator('substrCP', param),
toLower: param => new AggregationOperator('toLower', param),
toUpper: param => new AggregationOperator('toUpper', param),
meta: param => new AggregationOperator('meta', param),
addToSet: param => new AggregationOperator('addToSet', param),
avg: param => new AggregationOperator('avg', param),
first: param => new AggregationOperator('first', param),
last: param => new AggregationOperator('last', param),
max: param => new AggregationOperator('max', param),
min: param => new AggregationOperator('min', param),
push: param => new AggregationOperator('push', param),
stdDevPop: param => new AggregationOperator('stdDevPop', param),
stdDevSamp: param => new AggregationOperator('stdDevSamp', param),
sum: param => new AggregationOperator('sum', param),
let: param => new AggregationOperator('let', param)
},
project: {
slice: param => new ProjectionOperator('slice', param),
elemMatch: param => new ProjectionOperator('elemMatch', param)
}
};
class AggregationOperator {
constructor(name, param) {
this['$' + name] = param;
}
}
exports.AggregationOperator = AggregationOperator;
class ProjectionOperator {
constructor(name, param) {
this['$' + name] = param;
}
}
exports.ProjectionOperator = ProjectionOperator;
exports.default = exports.Command;

@ -0,0 +1,24 @@
import { InternalSymbol } from '../helper/symbol';
export declare const AND = "and";
export declare const OR = "or";
export declare const NOT = "not";
export declare const NOR = "nor";
export declare enum LOGIC_COMMANDS_LITERAL {
AND = "and",
OR = "or",
NOT = "not",
NOR = "nor"
}
export declare class LogicCommand {
fieldName: string | InternalSymbol;
operator: LOGIC_COMMANDS_LITERAL | string;
operands: any[];
_internalType: InternalSymbol;
constructor(operator: LOGIC_COMMANDS_LITERAL | string, operands: any, fieldName?: string | InternalSymbol);
_setFieldName(fieldName: string): LogicCommand;
and(...__expressions__: LogicCommand[]): LogicCommand;
or(...__expressions__: LogicCommand[]): LogicCommand;
}
export declare function isLogicCommand(object: any): object is LogicCommand;
export declare function isKnownLogicCommand(object: any): object is LogicCommand;
export default LogicCommand;

@ -0,0 +1,79 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const symbol_1 = require("../helper/symbol");
const query_1 = require("./query");
exports.AND = 'and';
exports.OR = 'or';
exports.NOT = 'not';
exports.NOR = 'nor';
var LOGIC_COMMANDS_LITERAL;
(function (LOGIC_COMMANDS_LITERAL) {
LOGIC_COMMANDS_LITERAL["AND"] = "and";
LOGIC_COMMANDS_LITERAL["OR"] = "or";
LOGIC_COMMANDS_LITERAL["NOT"] = "not";
LOGIC_COMMANDS_LITERAL["NOR"] = "nor";
})(LOGIC_COMMANDS_LITERAL = exports.LOGIC_COMMANDS_LITERAL || (exports.LOGIC_COMMANDS_LITERAL = {}));
class LogicCommand {
constructor(operator, operands, fieldName) {
this._internalType = symbol_1.SYMBOL_LOGIC_COMMAND;
Object.defineProperties(this, {
_internalType: {
enumerable: false,
configurable: false,
},
});
this.operator = operator;
this.operands = operands;
this.fieldName = fieldName || symbol_1.SYMBOL_UNSET_FIELD_NAME;
if (this.fieldName !== symbol_1.SYMBOL_UNSET_FIELD_NAME) {
if (Array.isArray(operands)) {
operands = operands.slice();
this.operands = operands;
for (let i = 0, len = operands.length; i < len; i++) {
const query = operands[i];
if (isLogicCommand(query) || query_1.isQueryCommand(query)) {
operands[i] = query._setFieldName(this.fieldName);
}
}
}
else {
const query = operands;
if (isLogicCommand(query) || query_1.isQueryCommand(query)) {
operands = query._setFieldName(this.fieldName);
}
}
}
}
_setFieldName(fieldName) {
const operands = this.operands.map(operand => {
if (operand instanceof LogicCommand) {
return operand._setFieldName(fieldName);
}
else {
return operand;
}
});
const command = new LogicCommand(this.operator, operands, fieldName);
return command;
}
and(...__expressions__) {
const expressions = Array.isArray(arguments[0]) ? arguments[0] : Array.from(arguments);
expressions.unshift(this);
return new LogicCommand(LOGIC_COMMANDS_LITERAL.AND, expressions, this.fieldName);
}
or(...__expressions__) {
const expressions = Array.isArray(arguments[0]) ? arguments[0] : Array.from(arguments);
expressions.unshift(this);
return new LogicCommand(LOGIC_COMMANDS_LITERAL.OR, expressions, this.fieldName);
}
}
exports.LogicCommand = LogicCommand;
function isLogicCommand(object) {
return object && (object instanceof LogicCommand) && (object._internalType === symbol_1.SYMBOL_LOGIC_COMMAND);
}
exports.isLogicCommand = isLogicCommand;
function isKnownLogicCommand(object) {
return isLogicCommand && (object.operator.toUpperCase() in LOGIC_COMMANDS_LITERAL);
}
exports.isKnownLogicCommand = isKnownLogicCommand;
exports.default = LogicCommand;

@ -0,0 +1,73 @@
import { LogicCommand } from './logic';
import { InternalSymbol } from '../helper/symbol';
import { Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon } from '../geo/index';
import { CenterSphere } from '../typings';
export declare const EQ = "eq";
export declare const NEQ = "neq";
export declare const GT = "gt";
export declare const GTE = "gte";
export declare const LT = "lt";
export declare const LTE = "lte";
export declare const IN = "in";
export declare const NIN = "nin";
export declare const ALL = "all";
export declare const ELEM_MATCH = "elemMatch";
export declare const EXISTS = "exists";
export declare const SIZE = "size";
export declare const MOD = "mod";
export declare enum QUERY_COMMANDS_LITERAL {
EQ = "eq",
NEQ = "neq",
GT = "gt",
GTE = "gte",
LT = "lt",
LTE = "lte",
IN = "in",
NIN = "nin",
ALL = "all",
ELEM_MATCH = "elemMatch",
EXISTS = "exists",
SIZE = "size",
MOD = "mod",
GEO_NEAR = "geoNear",
GEO_WITHIN = "geoWithin",
GEO_INTERSECTS = "geoIntersects"
}
export declare class QueryCommand extends LogicCommand {
operator: QUERY_COMMANDS_LITERAL;
constructor(operator: QUERY_COMMANDS_LITERAL, operands: any, fieldName?: string | InternalSymbol);
toJSON(): {
['$ne']: any;
} | {
[x: string]: any;
$ne?: undefined;
};
_setFieldName(fieldName: string): QueryCommand;
eq(val: any): LogicCommand;
neq(val: any): LogicCommand;
gt(val: any): LogicCommand;
gte(val: any): LogicCommand;
lt(val: any): LogicCommand;
lte(val: any): LogicCommand;
in(list: any[]): LogicCommand;
nin(list: any[]): LogicCommand;
geoNear(val: IGeoNearOptions): LogicCommand;
geoWithin(val: IGeoWithinOptions): LogicCommand;
geoIntersects(val: IGeoIntersectsOptions): LogicCommand;
}
export declare function isQueryCommand(object: any): object is QueryCommand;
export declare function isKnownQueryCommand(object: any): object is QueryCommand;
export declare function isComparisonCommand(object: any): object is QueryCommand;
export default QueryCommand;
export interface IGeoNearOptions {
geometry: Point;
maxDistance?: number;
minDistance?: number;
}
export interface IGeoWithinOptions {
geometry?: Polygon | MultiPolygon;
centerSphere?: CenterSphere;
}
export interface IGeoIntersectsOptions {
geometry: Point | LineString | Polygon | MultiPoint | MultiLineString | MultiPolygon;
}

@ -0,0 +1,147 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const logic_1 = require("./logic");
const symbol_1 = require("../helper/symbol");
const index_1 = require("../geo/index");
const type_1 = require("../utils/type");
const validate_1 = require("../validate");
exports.EQ = 'eq';
exports.NEQ = 'neq';
exports.GT = 'gt';
exports.GTE = 'gte';
exports.LT = 'lt';
exports.LTE = 'lte';
exports.IN = 'in';
exports.NIN = 'nin';
exports.ALL = 'all';
exports.ELEM_MATCH = 'elemMatch';
exports.EXISTS = 'exists';
exports.SIZE = 'size';
exports.MOD = 'mod';
var QUERY_COMMANDS_LITERAL;
(function (QUERY_COMMANDS_LITERAL) {
QUERY_COMMANDS_LITERAL["EQ"] = "eq";
QUERY_COMMANDS_LITERAL["NEQ"] = "neq";
QUERY_COMMANDS_LITERAL["GT"] = "gt";
QUERY_COMMANDS_LITERAL["GTE"] = "gte";
QUERY_COMMANDS_LITERAL["LT"] = "lt";
QUERY_COMMANDS_LITERAL["LTE"] = "lte";
QUERY_COMMANDS_LITERAL["IN"] = "in";
QUERY_COMMANDS_LITERAL["NIN"] = "nin";
QUERY_COMMANDS_LITERAL["ALL"] = "all";
QUERY_COMMANDS_LITERAL["ELEM_MATCH"] = "elemMatch";
QUERY_COMMANDS_LITERAL["EXISTS"] = "exists";
QUERY_COMMANDS_LITERAL["SIZE"] = "size";
QUERY_COMMANDS_LITERAL["MOD"] = "mod";
QUERY_COMMANDS_LITERAL["GEO_NEAR"] = "geoNear";
QUERY_COMMANDS_LITERAL["GEO_WITHIN"] = "geoWithin";
QUERY_COMMANDS_LITERAL["GEO_INTERSECTS"] = "geoIntersects";
})(QUERY_COMMANDS_LITERAL = exports.QUERY_COMMANDS_LITERAL || (exports.QUERY_COMMANDS_LITERAL = {}));
class QueryCommand extends logic_1.LogicCommand {
constructor(operator, operands, fieldName) {
super(operator, operands, fieldName);
this.operator = operator;
this._internalType = symbol_1.SYMBOL_QUERY_COMMAND;
}
toJSON() {
switch (this.operator) {
case QUERY_COMMANDS_LITERAL.IN:
case QUERY_COMMANDS_LITERAL.NIN:
return {
['$' + this.operator]: this.operands
};
case QUERY_COMMANDS_LITERAL.NEQ:
return {
['$ne']: this.operands[0]
};
default:
return {
['$' + this.operator]: this.operands[0]
};
}
}
_setFieldName(fieldName) {
const command = new QueryCommand(this.operator, this.operands, fieldName);
return command;
}
eq(val) {
const command = new QueryCommand(QUERY_COMMANDS_LITERAL.EQ, [val], this.fieldName);
return this.and(command);
}
neq(val) {
const command = new QueryCommand(QUERY_COMMANDS_LITERAL.NEQ, [val], this.fieldName);
return this.and(command);
}
gt(val) {
const command = new QueryCommand(QUERY_COMMANDS_LITERAL.GT, [val], this.fieldName);
return this.and(command);
}
gte(val) {
const command = new QueryCommand(QUERY_COMMANDS_LITERAL.GTE, [val], this.fieldName);
return this.and(command);
}
lt(val) {
const command = new QueryCommand(QUERY_COMMANDS_LITERAL.LT, [val], this.fieldName);
return this.and(command);
}
lte(val) {
const command = new QueryCommand(QUERY_COMMANDS_LITERAL.LTE, [val], this.fieldName);
return this.and(command);
}
in(list) {
const command = new QueryCommand(QUERY_COMMANDS_LITERAL.IN, list, this.fieldName);
return this.and(command);
}
nin(list) {
const command = new QueryCommand(QUERY_COMMANDS_LITERAL.NIN, list, this.fieldName);
return this.and(command);
}
geoNear(val) {
if (!(val.geometry instanceof index_1.Point)) {
throw new TypeError(`"geometry" must be of type Point. Received type ${typeof val.geometry}`);
}
if (val.maxDistance !== undefined && !type_1.isNumber(val.maxDistance)) {
throw new TypeError(`"maxDistance" must be of type Number. Received type ${typeof val.maxDistance}`);
}
if (val.minDistance !== undefined && !type_1.isNumber(val.minDistance)) {
throw new TypeError(`"minDistance" must be of type Number. Received type ${typeof val.minDistance}`);
}
const command = new QueryCommand(QUERY_COMMANDS_LITERAL.GEO_NEAR, [val], this.fieldName);
return this.and(command);
}
geoWithin(val) {
if (!(val.geometry instanceof index_1.MultiPolygon) &&
!(val.geometry instanceof index_1.Polygon) &&
!validate_1.Validate.isCentersPhere(val.centerSphere)) {
throw new TypeError(`"geometry" must be of type Polygon or MultiPolygon. Received type ${typeof val.geometry}`);
}
const command = new QueryCommand(QUERY_COMMANDS_LITERAL.GEO_WITHIN, [val], this.fieldName);
return this.and(command);
}
geoIntersects(val) {
if (!(val.geometry instanceof index_1.Point) &&
!(val.geometry instanceof index_1.LineString) &&
!(val.geometry instanceof index_1.Polygon) &&
!(val.geometry instanceof index_1.MultiPoint) &&
!(val.geometry instanceof index_1.MultiLineString) &&
!(val.geometry instanceof index_1.MultiPolygon)) {
throw new TypeError(`"geometry" must be of type Point, LineString, Polygon, MultiPoint, MultiLineString or MultiPolygon. Received type ${typeof val.geometry}`);
}
const command = new QueryCommand(QUERY_COMMANDS_LITERAL.GEO_INTERSECTS, [val], this.fieldName);
return this.and(command);
}
}
exports.QueryCommand = QueryCommand;
function isQueryCommand(object) {
return object && object instanceof QueryCommand && object._internalType === symbol_1.SYMBOL_QUERY_COMMAND;
}
exports.isQueryCommand = isQueryCommand;
function isKnownQueryCommand(object) {
return isQueryCommand(object) && object.operator.toUpperCase() in QUERY_COMMANDS_LITERAL;
}
exports.isKnownQueryCommand = isKnownQueryCommand;
function isComparisonCommand(object) {
return isQueryCommand(object);
}
exports.isComparisonCommand = isComparisonCommand;
exports.default = QueryCommand;

@ -0,0 +1,29 @@
import { InternalSymbol } from '../helper/symbol';
export declare enum UPDATE_COMMANDS_LITERAL {
SET = "set",
REMOVE = "remove",
INC = "inc",
MUL = "mul",
PUSH = "push",
PULL = "pull",
PULL_ALL = "pullAll",
POP = "pop",
SHIFT = "shift",
UNSHIFT = "unshift",
ADD_TO_SET = "addToSet",
BIT = "bit",
RENAME = "rename",
MAX = "max",
MIN = "min"
}
export declare class UpdateCommand {
fieldName: string | InternalSymbol;
operator: UPDATE_COMMANDS_LITERAL;
operands: any;
_internalType: InternalSymbol;
constructor(operator: UPDATE_COMMANDS_LITERAL, operands?: any, fieldName?: string | InternalSymbol);
_setFieldName(fieldName: string): UpdateCommand;
}
export declare function isUpdateCommand(object: any): object is UpdateCommand;
export declare function isKnownUpdateCommand(object: any): object is UpdateCommand;
export default UpdateCommand;

@ -0,0 +1,49 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const symbol_1 = require("../helper/symbol");
var UPDATE_COMMANDS_LITERAL;
(function (UPDATE_COMMANDS_LITERAL) {
UPDATE_COMMANDS_LITERAL["SET"] = "set";
UPDATE_COMMANDS_LITERAL["REMOVE"] = "remove";
UPDATE_COMMANDS_LITERAL["INC"] = "inc";
UPDATE_COMMANDS_LITERAL["MUL"] = "mul";
UPDATE_COMMANDS_LITERAL["PUSH"] = "push";
UPDATE_COMMANDS_LITERAL["PULL"] = "pull";
UPDATE_COMMANDS_LITERAL["PULL_ALL"] = "pullAll";
UPDATE_COMMANDS_LITERAL["POP"] = "pop";
UPDATE_COMMANDS_LITERAL["SHIFT"] = "shift";
UPDATE_COMMANDS_LITERAL["UNSHIFT"] = "unshift";
UPDATE_COMMANDS_LITERAL["ADD_TO_SET"] = "addToSet";
UPDATE_COMMANDS_LITERAL["BIT"] = "bit";
UPDATE_COMMANDS_LITERAL["RENAME"] = "rename";
UPDATE_COMMANDS_LITERAL["MAX"] = "max";
UPDATE_COMMANDS_LITERAL["MIN"] = "min";
})(UPDATE_COMMANDS_LITERAL = exports.UPDATE_COMMANDS_LITERAL || (exports.UPDATE_COMMANDS_LITERAL = {}));
class UpdateCommand {
constructor(operator, operands, fieldName) {
this._internalType = symbol_1.SYMBOL_UPDATE_COMMAND;
Object.defineProperties(this, {
_internalType: {
enumerable: false,
configurable: false,
},
});
this.operator = operator;
this.operands = operands;
this.fieldName = fieldName || symbol_1.SYMBOL_UNSET_FIELD_NAME;
}
_setFieldName(fieldName) {
const command = new UpdateCommand(this.operator, this.operands, fieldName);
return command;
}
}
exports.UpdateCommand = UpdateCommand;
function isUpdateCommand(object) {
return object && (object instanceof UpdateCommand) && (object._internalType === symbol_1.SYMBOL_UPDATE_COMMAND);
}
exports.isUpdateCommand = isUpdateCommand;
function isKnownUpdateCommand(object) {
return isUpdateCommand(object) && (object.operator.toUpperCase() in UPDATE_COMMANDS_LITERAL);
}
exports.isKnownUpdateCommand = isKnownUpdateCommand;
exports.default = UpdateCommand;

@ -0,0 +1,6 @@
declare const _default: {
db_doc_size_limit: number;
db_realtime_ping_interval: number;
db_realtime_pong_wait_timeout: number;
};
export default _default;

@ -0,0 +1,7 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = {
db_doc_size_limit: 512 * 1024,
db_realtime_ping_interval: 15 * 1000,
db_realtime_pong_wait_timeout: 15 * 1000,
};

@ -0,0 +1,3 @@
export declare const ERR_CODE: {
[key: string]: string | number;
};

@ -0,0 +1,15 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ERR_CODE = {
UNKNOWN_ERROR: 'UNKNOWN_ERROR',
SDK_DATABASE_REALTIME_LISTENER_INIT_WATCH_FAIL: 'SDK_DATABASE_REALTIME_LISTENER_INIT_WATCH_FAIL',
SDK_DATABASE_REALTIME_LISTENER_RECONNECT_WATCH_FAIL: 'SDK_DATABASE_REALTIME_LISTENER_RECONNECT_WATCH_FAIL',
SDK_DATABASE_REALTIME_LISTENER_REBUILD_WATCH_FAIL: 'SDK_DATABASE_REALTIME_LISTENER_REBUILD_WATCH_FAIL',
SDK_DATABASE_REALTIME_LISTENER_CLOSE_WATCH_FAIL: 'SDK_DATABASE_REALTIME_LISTENER_CLOSE_WATCH_FAIL',
SDK_DATABASE_REALTIME_LISTENER_SERVER_ERROR_MSG: 'SDK_DATABASE_REALTIME_LISTENER_SERVER_ERROR_MSG',
SDK_DATABASE_REALTIME_LISTENER_RECEIVE_INVALID_SERVER_DATA: 'SDK_DATABASE_REALTIME_LISTENER_RECEIVE_INVALID_SERVER_DATA',
SDK_DATABASE_REALTIME_LISTENER_WEBSOCKET_CONNECTION_ERROR: 'SDK_DATABASE_REALTIME_LISTENER_WEBSOCKET_CONNECTION_ERROR',
SDK_DATABASE_REALTIME_LISTENER_WEBSOCKET_CONNECTION_CLOSED: 'SDK_DATABASE_REALTIME_LISTENER_WEBSOCKET_CONNECTION_CLOSED',
SDK_DATABASE_REALTIME_LISTENER_CHECK_LAST_FAIL: 'SDK_DATABASE_REALTIME_LISTENER_CHECK_LAST_FAIL',
SDK_DATABASE_REALTIME_LISTENER_UNEXPECTED_FATAL_ERROR: 'SDK_DATABASE_REALTIME_LISTENER_UNEXPECTED_FATAL_ERROR'
};

@ -0,0 +1,34 @@
export declare const ERRORS: {
CREATE_WATCH_NET_ERROR: {
code: string;
message: string;
};
CREATE_WACTH_EXCEED_ERROR: {
code: string;
message: string;
};
CREATE_WATCH_SERVER_ERROR: {
code: string;
message: string;
};
CONN_ERROR: {
code: string;
message: string;
};
INVALID_PARAM: {
code: string;
message: string;
};
INSERT_DOC_FAIL: {
code: string;
message: string;
};
DATABASE_TRANSACTION_CONFLICT: {
code: string;
message: string;
};
DATABASE_REQUEST_FAILED: {
code: string;
message: string;
};
};

@ -0,0 +1,36 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ERRORS = {
CREATE_WATCH_NET_ERROR: {
code: 'CREATE_WATCH_NET_ERROR',
message: 'create watch network error'
},
CREATE_WACTH_EXCEED_ERROR: {
code: 'CREATE_WACTH_EXCEED_ERROR',
message: 'maximum connections exceed'
},
CREATE_WATCH_SERVER_ERROR: {
code: 'CREATE_WATCH_SERVER_ERROR',
message: 'create watch server error'
},
CONN_ERROR: {
code: 'CONN_ERROR',
message: 'connection error'
},
INVALID_PARAM: {
code: 'INVALID_PARAM',
message: 'Invalid request param'
},
INSERT_DOC_FAIL: {
code: 'INSERT_DOC_FAIL',
message: 'insert document failed'
},
DATABASE_TRANSACTION_CONFLICT: {
code: 'DATABASE_TRANSACTION_CONFLICT',
message: 'database transaction conflict'
},
DATABASE_REQUEST_FAILED: {
code: 'DATABASE_REQUEST_FAILED',
message: 'database request failed'
}
};

@ -0,0 +1,54 @@
declare enum ErrorCode {
DocIDError = "\u6587\u6863ID\u4E0D\u5408\u6CD5",
CollNameError = "\u96C6\u5408\u540D\u79F0\u4E0D\u5408\u6CD5",
OpStrError = "\u64CD\u4F5C\u7B26\u4E0D\u5408\u6CD5",
DirectionError = "\u6392\u5E8F\u5B57\u7B26\u4E0D\u5408\u6CD5",
IntergerError = "must be integer",
BooleanError = "must be boolean",
ArrayError = "must be array",
QueryParamTypeError = "\u67E5\u8BE2\u53C2\u6570\u5FC5\u987B\u4E3A\u5BF9\u8C61",
QueryParamValueError = "\u67E5\u8BE2\u53C2\u6570\u5BF9\u8C61\u503C\u4E0D\u80FD\u5747\u4E3Aundefined",
CentersPhereError = "centersPhere\u7ED3\u6784\u4E0D\u5408\u6CD5"
}
declare const FieldType: {
String: string;
Number: string;
Object: string;
Array: string;
Boolean: string;
Null: string;
GeoPoint: string;
GeoLineString: string;
GeoPolygon: string;
GeoMultiPoint: string;
GeoMultiLineString: string;
GeoMultiPolygon: string;
Date: string;
Command: string;
ServerDate: string;
BsonDate: string;
};
declare type OrderByDirection = 'desc' | 'asc';
declare const OrderDirectionList: string[];
declare type WhereFilterOp = '<' | '<=' | '==' | '>=' | '>';
declare const WhereFilterOpList: string[];
declare enum Opeartor {
lt = "<",
gt = ">",
lte = "<=",
gte = ">=",
eq = "=="
}
declare const OperatorMap: {
[Opeartor.eq]: string;
[Opeartor.lt]: string;
[Opeartor.lte]: string;
[Opeartor.gt]: string;
[Opeartor.gte]: string;
};
declare const UpdateOperatorList: string[];
declare enum QueryType {
WHERE = "WHERE",
DOC = "DOC"
}
export { ErrorCode, FieldType, WhereFilterOp, WhereFilterOpList, Opeartor, OperatorMap, OrderByDirection, OrderDirectionList, UpdateOperatorList, QueryType };

@ -0,0 +1,76 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var ErrorCode;
(function (ErrorCode) {
ErrorCode["DocIDError"] = "\u6587\u6863ID\u4E0D\u5408\u6CD5";
ErrorCode["CollNameError"] = "\u96C6\u5408\u540D\u79F0\u4E0D\u5408\u6CD5";
ErrorCode["OpStrError"] = "\u64CD\u4F5C\u7B26\u4E0D\u5408\u6CD5";
ErrorCode["DirectionError"] = "\u6392\u5E8F\u5B57\u7B26\u4E0D\u5408\u6CD5";
ErrorCode["IntergerError"] = "must be integer";
ErrorCode["BooleanError"] = "must be boolean";
ErrorCode["ArrayError"] = "must be array";
ErrorCode["QueryParamTypeError"] = "\u67E5\u8BE2\u53C2\u6570\u5FC5\u987B\u4E3A\u5BF9\u8C61";
ErrorCode["QueryParamValueError"] = "\u67E5\u8BE2\u53C2\u6570\u5BF9\u8C61\u503C\u4E0D\u80FD\u5747\u4E3Aundefined";
ErrorCode["CentersPhereError"] = "centersPhere\u7ED3\u6784\u4E0D\u5408\u6CD5";
})(ErrorCode || (ErrorCode = {}));
exports.ErrorCode = ErrorCode;
const FieldType = {
String: 'String',
Number: 'Number',
Object: 'Object',
Array: 'Array',
Boolean: 'Boolean',
Null: 'Null',
GeoPoint: 'GeoPoint',
GeoLineString: 'GeoLineString',
GeoPolygon: 'GeoPolygon',
GeoMultiPoint: 'GeoMultiPoint',
GeoMultiLineString: 'GeoMultiLineString',
GeoMultiPolygon: 'GeoMultiPolygon',
Date: 'Date',
Command: 'Command',
ServerDate: 'ServerDate',
BsonDate: 'BsonDate'
};
exports.FieldType = FieldType;
const OrderDirectionList = ['desc', 'asc'];
exports.OrderDirectionList = OrderDirectionList;
const WhereFilterOpList = ['<', '<=', '==', '>=', '>'];
exports.WhereFilterOpList = WhereFilterOpList;
var Opeartor;
(function (Opeartor) {
Opeartor["lt"] = "<";
Opeartor["gt"] = ">";
Opeartor["lte"] = "<=";
Opeartor["gte"] = ">=";
Opeartor["eq"] = "==";
})(Opeartor || (Opeartor = {}));
exports.Opeartor = Opeartor;
const OperatorMap = {
[Opeartor.eq]: '$eq',
[Opeartor.lt]: '$lt',
[Opeartor.lte]: '$lte',
[Opeartor.gt]: '$gt',
[Opeartor.gte]: '$gte'
};
exports.OperatorMap = OperatorMap;
const UpdateOperatorList = [
'$set',
'$inc',
'$mul',
'$unset',
'$push',
'$pop',
'$unshift',
'$shift',
'$currentDate',
'$each',
'$position'
];
exports.UpdateOperatorList = UpdateOperatorList;
var QueryType;
(function (QueryType) {
QueryType["WHERE"] = "WHERE";
QueryType["DOC"] = "DOC";
})(QueryType || (QueryType = {}));
exports.QueryType = QueryType;

@ -0,0 +1,14 @@
import { IWatchOptions, DBRealtimeListener } from './typings/index';
export declare class DocumentReference {
readonly id: string | number;
readonly _transactionId: string;
readonly projection: Object;
private _apiOptions;
set(data: Object): Promise<any>;
update(data: Object): Promise<any>;
delete(): Promise<any>;
remove(): Promise<any>;
get(): Promise<any>;
field(projection: Object): DocumentReference;
watch: (options: IWatchOptions) => DBRealtimeListener;
}

@ -0,0 +1,219 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const index_1 = require("./index");
const util_1 = require("./util");
const update_1 = require("./serializer/update");
const datatype_1 = require("./serializer/datatype");
const update_2 = require("./commands/update");
const websocket_client_1 = require("./realtime/websocket-client");
const constant_1 = require("./constant");
const utils_1 = require("./utils/utils");
const code_1 = require("./const/code");
const bson_1 = require("bson");
class DocumentReference {
constructor(db, coll, apiOptions, docID, transactionId) {
this.watch = (options) => {
if (!index_1.Db.ws) {
index_1.Db.ws = new websocket_client_1.RealtimeWebSocketClient({
context: {
appConfig: {
docSizeLimit: 1000,
realtimePingInterval: 10000,
realtimePongWaitTimeout: 5000,
request: this.request
}
}
});
}
return index_1.Db.ws.watch(Object.assign(Object.assign({}, options), { envId: this._db.config.env, collectionName: this._coll, query: JSON.stringify({
_id: this.id
}) }));
};
this._db = db;
this._coll = coll;
this.id = docID;
this._transactionId = transactionId;
this.request = new index_1.Db.reqClass(this._db.config);
this._apiOptions = apiOptions;
}
async create(data) {
if (this.id) {
data['_id'] = this.id;
}
let params = {
collectionName: this._coll,
data: [utils_1.stringifyByEJSON(datatype_1.serialize(data))],
transactionId: this._transactionId
};
const res = await this.request.send('database.insertDocument', params, utils_1.getReqOpts(this._apiOptions));
if (res.code) {
return res;
}
if (this._transactionId) {
return {
inserted: 1,
ok: 1,
id: res.data.insertedIds[0],
requestId: res.requestId
};
}
return {
id: res.data.insertedIds[0],
requestId: res.requestId
};
}
async set(data) {
if (!this.id) {
return utils_1.processReturn(this._db.config.throwOnCode, Object.assign(Object.assign({}, code_1.ERRORS.INVALID_PARAM), { message: 'docId不能为空' }));
}
if (!data || typeof data !== 'object') {
return utils_1.processReturn(this._db.config.throwOnCode, Object.assign(Object.assign({}, code_1.ERRORS.INVALID_PARAM), { message: '参数必需是非空对象' }));
}
if (data.hasOwnProperty('_id')) {
return utils_1.processReturn(this._db.config.throwOnCode, Object.assign(Object.assign({}, code_1.ERRORS.INVALID_PARAM), { message: '不能更新_id的值' }));
}
let hasOperator = false;
const checkMixed = objs => {
if (typeof objs === 'object') {
for (let key in objs) {
if (objs[key] instanceof update_2.UpdateCommand) {
hasOperator = true;
}
else if (typeof objs[key] === 'object') {
checkMixed(objs[key]);
}
}
}
};
checkMixed(data);
if (hasOperator) {
return utils_1.processReturn(this._db.config.throwOnCode, Object.assign(Object.assign({}, code_1.ERRORS.DATABASE_REQUEST_FAILED), { message: 'update operator complicit' }));
}
let param = {
collectionName: this._coll,
queryType: constant_1.QueryType.DOC,
data: utils_1.stringifyByEJSON(datatype_1.serialize(data)),
transactionId: this._transactionId,
multi: false,
merge: false,
upsert: true
};
if (this.id) {
param['query'] = utils_1.stringifyByEJSON({ _id: this.id });
}
const res = await this.request.send('database.modifyDocument', param, utils_1.getReqOpts(this._apiOptions));
if (res.code) {
return res;
}
if (this._transactionId) {
return {
updated: res.data.updated,
upserted: [{ _id: res.data.upsert_id }],
requestId: res.requestId
};
}
return {
updated: res.data.updated,
upsertedId: res.data.upsert_id,
requestId: res.requestId
};
}
async update(data) {
if (!data || typeof data !== 'object') {
return utils_1.processReturn(this._db.config.throwOnCode, Object.assign(Object.assign({}, code_1.ERRORS.INVALID_PARAM), { message: '参数必需是非空对象' }));
}
if (data.hasOwnProperty('_id')) {
return utils_1.processReturn(this._db.config.throwOnCode, Object.assign(Object.assign({}, code_1.ERRORS.INVALID_PARAM), { message: '不能更新_id的值' }));
}
const query = utils_1.stringifyByEJSON({ _id: this.id });
const param = {
collectionName: this._coll,
transactionId: this._transactionId,
data: update_1.UpdateSerializer.encodeEJSON(data, this._apiOptions.raw || false),
query,
queryType: constant_1.QueryType.DOC,
multi: false,
merge: true,
upsert: false
};
const res = await this.request.send('database.modifyDocument', param, utils_1.getReqOpts(this._apiOptions));
if (res.code) {
return res;
}
return {
updated: res.data.updated,
requestId: res.requestId
};
}
async delete() {
return this.remove();
}
async remove() {
const query = utils_1.stringifyByEJSON({ _id: this.id });
const param = {
collectionName: this._coll,
transactionId: this._transactionId,
query: query,
queryType: constant_1.QueryType.DOC,
multi: false
};
const res = await this.request.send('database.removeDocument', param, utils_1.getReqOpts(this._apiOptions));
if (res.code) {
return res;
}
return {
deleted: res.data.deleted,
requestId: res.requestId
};
}
async get() {
const query = utils_1.stringifyByEJSON({ _id: this.id });
const { projection } = this._apiOptions;
const param = {
collectionName: this._coll,
query,
transactionId: this._transactionId,
queryType: constant_1.QueryType.DOC,
multi: false
};
if (projection) {
param.projection = utils_1.stringifyByEJSON(projection);
}
const res = await this.request.send('database.getDocument', param, utils_1.getReqOpts(this._apiOptions));
if (res.code) {
return res;
}
const list = res.data.list.map(item => bson_1.EJSON.parse(item));
const documents = util_1.Util.formatResDocumentData(list);
if (this._transactionId) {
return {
data: documents[0] || null,
requestId: res.requestId
};
}
return {
data: documents,
requestId: res.requestId,
offset: res.data.offset,
limit: res.data.limit
};
}
field(projection) {
let transformProjection = {};
for (let k in projection) {
if (typeof projection[k] === 'boolean') {
transformProjection[k] = projection[k] === true ? 1 : 0;
}
if (typeof projection[k] === 'number') {
transformProjection[k] = projection[k] > 0 ? 1 : 0;
}
if (typeof projection[k] === 'object') {
transformProjection[k] = projection[k];
}
}
let newApiOption = Object.assign({}, this._apiOptions);
newApiOption.projection = transformProjection;
return new DocumentReference(this._db, this._coll, newApiOption, this.id, this._transactionId);
}
}
exports.DocumentReference = DocumentReference;

@ -0,0 +1,6 @@
export * from './point';
export * from './lineString';
export * from './polygon';
export * from './multiPoint';
export * from './multiLineString';
export * from './multiPolygon';

@ -0,0 +1,11 @@
"use strict";
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
Object.defineProperty(exports, "__esModule", { value: true });
__export(require("./point"));
__export(require("./lineString"));
__export(require("./polygon"));
__export(require("./multiPoint"));
__export(require("./multiLineString"));
__export(require("./multiPolygon"));

@ -0,0 +1,24 @@
export interface ISerializedPoint {
type: string;
coordinates: [number, number];
}
export interface ISerializedLineString {
type: string;
coordinates: [number, number][];
}
export interface ISerializedPolygon {
type: string;
coordinates: [number, number][][];
}
export interface ISerializedMultiPoint {
type: string;
coordinates: [number, number][];
}
export interface ISerializedMultiLineString {
type: string;
coordinates: [number, number][][];
}
export interface ISerializedMultiPolygon {
type: string;
coordinates: [number, number][][][];
}

@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

@ -0,0 +1,19 @@
import { Point } from './point';
import { ISerializedLineString } from './interface';
export declare class LineString {
readonly points: Point[];
constructor(points: Point[]);
parse(key: any): {
[x: number]: {
type: string;
coordinates: number[][];
};
};
toJSON(): {
type: string;
coordinates: number[][];
};
static validate(lineString: ISerializedLineString): boolean;
static isClosed(lineString: LineString): boolean;
readonly _internalType: import("../utils/symbol").InternalSymbol;
}

@ -0,0 +1,57 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const symbol_1 = require("../helper/symbol");
const point_1 = require("./point");
const type_1 = require("../utils/type");
class LineString {
constructor(points) {
if (!type_1.isArray(points)) {
throw new TypeError(`"points" must be of type Point[]. Received type ${typeof points}`);
}
if (points.length < 2) {
throw new Error('"points" must contain 2 points at least');
}
points.forEach(point => {
if (!(point instanceof point_1.Point)) {
throw new TypeError(`"points" must be of type Point[]. Received type ${typeof point}[]`);
}
});
this.points = points;
}
parse(key) {
return {
[key]: {
type: 'LineString',
coordinates: this.points.map(point => point.toJSON().coordinates)
}
};
}
toJSON() {
return {
type: 'LineString',
coordinates: this.points.map(point => point.toJSON().coordinates)
};
}
static validate(lineString) {
if (lineString.type !== 'LineString' || !type_1.isArray(lineString.coordinates)) {
return false;
}
for (let point of lineString.coordinates) {
if (!type_1.isNumber(point[0]) || !type_1.isNumber(point[1])) {
return false;
}
}
return true;
}
static isClosed(lineString) {
const firstPoint = lineString.points[0];
const lastPoint = lineString.points[lineString.points.length - 1];
if (firstPoint.latitude === lastPoint.latitude && firstPoint.longitude === lastPoint.longitude) {
return true;
}
}
get _internalType() {
return symbol_1.SYMBOL_GEO_LINE_STRING;
}
}
exports.LineString = LineString;

@ -0,0 +1,18 @@
import { LineString } from './lineString';
import { ISerializedMultiLineString } from './interface';
export declare class MultiLineString {
readonly lines: LineString[];
constructor(lines: LineString[]);
parse(key: any): {
[x: number]: {
type: string;
coordinates: number[][][];
};
};
toJSON(): {
type: string;
coordinates: number[][][];
};
static validate(multiLineString: ISerializedMultiLineString): boolean;
readonly _internalType: import("../utils/symbol").InternalSymbol;
}

@ -0,0 +1,56 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const symbol_1 = require("../helper/symbol");
const type_1 = require("../utils/type");
const lineString_1 = require("./lineString");
class MultiLineString {
constructor(lines) {
if (!type_1.isArray(lines)) {
throw new TypeError(`"lines" must be of type LineString[]. Received type ${typeof lines}`);
}
if (lines.length === 0) {
throw new Error('Polygon must contain 1 linestring at least');
}
lines.forEach(line => {
if (!(line instanceof lineString_1.LineString)) {
throw new TypeError(`"lines" must be of type LineString[]. Received type ${typeof line}[]`);
}
});
this.lines = lines;
}
parse(key) {
return {
[key]: {
type: 'MultiLineString',
coordinates: this.lines.map(line => {
return line.points.map(point => [point.longitude, point.latitude]);
})
}
};
}
toJSON() {
return {
type: 'MultiLineString',
coordinates: this.lines.map(line => {
return line.points.map(point => [point.longitude, point.latitude]);
})
};
}
static validate(multiLineString) {
if (multiLineString.type !== 'MultiLineString' || !type_1.isArray(multiLineString.coordinates)) {
return false;
}
for (let line of multiLineString.coordinates) {
for (let point of line) {
if (!type_1.isNumber(point[0]) || !type_1.isNumber(point[1])) {
return false;
}
}
}
return true;
}
get _internalType() {
return symbol_1.SYMBOL_GEO_MULTI_LINE_STRING;
}
}
exports.MultiLineString = MultiLineString;

@ -0,0 +1,18 @@
import { Point } from './point';
import { ISerializedMultiPoint } from './interface';
export declare class MultiPoint {
readonly points: Point[];
constructor(points: Point[]);
parse(key: any): {
[x: number]: {
type: string;
coordinates: number[][];
};
};
toJSON(): {
type: string;
coordinates: number[][];
};
static validate(multiPoint: ISerializedMultiPoint): boolean;
readonly _internalType: import("../utils/symbol").InternalSymbol;
}

@ -0,0 +1,50 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const symbol_1 = require("../helper/symbol");
const point_1 = require("./point");
const type_1 = require("../utils/type");
class MultiPoint {
constructor(points) {
if (!type_1.isArray(points)) {
throw new TypeError(`"points" must be of type Point[]. Received type ${typeof points}`);
}
if (points.length === 0) {
throw new Error('"points" must contain 1 point at least');
}
points.forEach(point => {
if (!(point instanceof point_1.Point)) {
throw new TypeError(`"points" must be of type Point[]. Received type ${typeof point}[]`);
}
});
this.points = points;
}
parse(key) {
return {
[key]: {
type: 'MultiPoint',
coordinates: this.points.map(point => point.toJSON().coordinates)
}
};
}
toJSON() {
return {
type: 'MultiPoint',
coordinates: this.points.map(point => point.toJSON().coordinates)
};
}
static validate(multiPoint) {
if (multiPoint.type !== 'MultiPoint' || !type_1.isArray(multiPoint.coordinates)) {
return false;
}
for (let point of multiPoint.coordinates) {
if (!type_1.isNumber(point[0]) || !type_1.isNumber(point[1])) {
return false;
}
}
return true;
}
get _internalType() {
return symbol_1.SYMBOL_GEO_MULTI_POINT;
}
}
exports.MultiPoint = MultiPoint;

@ -0,0 +1,18 @@
import { Polygon } from './polygon';
import { ISerializedMultiPolygon } from './interface';
export declare class MultiPolygon {
readonly polygons: Polygon[];
constructor(polygons: Polygon[]);
parse(key: any): {
[x: number]: {
type: string;
coordinates: number[][][][];
};
};
toJSON(): {
type: string;
coordinates: number[][][][];
};
static validate(multiPolygon: ISerializedMultiPolygon): boolean;
readonly _internalType: import("../utils/symbol").InternalSymbol;
}

@ -0,0 +1,62 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const symbol_1 = require("../helper/symbol");
const type_1 = require("../utils/type");
const polygon_1 = require("./polygon");
class MultiPolygon {
constructor(polygons) {
if (!type_1.isArray(polygons)) {
throw new TypeError(`"polygons" must be of type Polygon[]. Received type ${typeof polygons}`);
}
if (polygons.length === 0) {
throw new Error('MultiPolygon must contain 1 polygon at least');
}
for (let polygon of polygons) {
if (!(polygon instanceof polygon_1.Polygon)) {
throw new TypeError(`"polygon" must be of type Polygon[]. Received type ${typeof polygon}[]`);
}
}
this.polygons = polygons;
}
parse(key) {
return {
[key]: {
type: 'MultiPolygon',
coordinates: this.polygons.map(polygon => {
return polygon.lines.map(line => {
return line.points.map(point => [point.longitude, point.latitude]);
});
})
}
};
}
toJSON() {
return {
type: 'MultiPolygon',
coordinates: this.polygons.map(polygon => {
return polygon.lines.map(line => {
return line.points.map(point => [point.longitude, point.latitude]);
});
})
};
}
static validate(multiPolygon) {
if (multiPolygon.type !== 'MultiPolygon' || !type_1.isArray(multiPolygon.coordinates)) {
return false;
}
for (let polygon of multiPolygon.coordinates) {
for (let line of polygon) {
for (let point of line) {
if (!type_1.isNumber(point[0]) || !type_1.isNumber(point[1])) {
return false;
}
}
}
}
return true;
}
get _internalType() {
return symbol_1.SYMBOL_GEO_MULTI_POLYGON;
}
}
exports.MultiPolygon = MultiPolygon;

@ -0,0 +1,19 @@
import { ISerializedPoint } from './interface';
export declare class Point {
readonly latitude: number;
readonly longitude: number;
constructor(longitude: number, latitude: number);
parse(key: any): {
[x: number]: {
type: string;
coordinates: number[];
};
};
toJSON(): {
type: string;
coordinates: number[];
};
toReadableString(): string;
static validate(point: ISerializedPoint): boolean;
readonly _internalType: import("../utils/symbol").InternalSymbol;
}

@ -0,0 +1,43 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const validate_1 = require("../validate");
const symbol_1 = require("../helper/symbol");
const type_1 = require("../utils/type");
class Point {
constructor(longitude, latitude) {
validate_1.Validate.isGeopoint('longitude', longitude);
validate_1.Validate.isGeopoint('latitude', latitude);
this.longitude = longitude;
this.latitude = latitude;
}
parse(key) {
return {
[key]: {
type: 'Point',
coordinates: [this.longitude, this.latitude]
}
};
}
toJSON() {
return {
type: 'Point',
coordinates: [
this.longitude,
this.latitude,
],
};
}
toReadableString() {
return `[${this.longitude},${this.latitude}]`;
}
static validate(point) {
return point.type === 'Point' &&
type_1.isArray(point.coordinates) &&
validate_1.Validate.isGeopoint('longitude', point.coordinates[0]) &&
validate_1.Validate.isGeopoint('latitude', point.coordinates[1]);
}
get _internalType() {
return symbol_1.SYMBOL_GEO_POINT;
}
}
exports.Point = Point;

@ -0,0 +1,19 @@
import { LineString } from './lineString';
import { ISerializedPolygon } from './interface';
export declare class Polygon {
readonly lines: LineString[];
constructor(lines: LineString[]);
parse(key: any): {
[x: number]: {
type: string;
coordinates: number[][][];
};
};
toJSON(): {
type: string;
coordinates: number[][][];
};
static validate(polygon: ISerializedPolygon): boolean;
static isCloseLineString(lineString: any): boolean;
readonly _internalType: import("../utils/symbol").InternalSymbol;
}

@ -0,0 +1,70 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const symbol_1 = require("../helper/symbol");
const type_1 = require("../utils/type");
const lineString_1 = require("./lineString");
class Polygon {
constructor(lines) {
if (!type_1.isArray(lines)) {
throw new TypeError(`"lines" must be of type LineString[]. Received type ${typeof lines}`);
}
if (lines.length === 0) {
throw new Error('Polygon must contain 1 linestring at least');
}
lines.forEach(line => {
if (!(line instanceof lineString_1.LineString)) {
throw new TypeError(`"lines" must be of type LineString[]. Received type ${typeof line}[]`);
}
if (!lineString_1.LineString.isClosed(line)) {
throw new Error(`LineString ${line.points.map(p => p.toReadableString())} is not a closed cycle`);
}
});
this.lines = lines;
}
parse(key) {
return {
[key]: {
type: 'Polygon',
coordinates: this.lines.map(line => {
return line.points.map(point => [point.longitude, point.latitude]);
})
}
};
}
toJSON() {
return {
type: 'Polygon',
coordinates: this.lines.map(line => {
return line.points.map(point => [point.longitude, point.latitude]);
})
};
}
static validate(polygon) {
if (polygon.type !== 'Polygon' || !type_1.isArray(polygon.coordinates)) {
return false;
}
for (let line of polygon.coordinates) {
if (!this.isCloseLineString(line)) {
return false;
}
for (let point of line) {
if (!type_1.isNumber(point[0]) || !type_1.isNumber(point[1])) {
return false;
}
}
}
return true;
}
static isCloseLineString(lineString) {
const firstPoint = lineString[0];
const lastPoint = lineString[lineString.length - 1];
if (firstPoint[0] !== lastPoint[0] || firstPoint[1] !== lastPoint[1]) {
return false;
}
return true;
}
get _internalType() {
return symbol_1.SYMBOL_GEO_POLYGON;
}
}
exports.Polygon = Polygon;

@ -0,0 +1,15 @@
import InternalSymbol from '../utils/symbol';
export * from '../utils/symbol';
export declare const SYMBOL_UNSET_FIELD_NAME: InternalSymbol;
export declare const SYMBOL_UPDATE_COMMAND: InternalSymbol;
export declare const SYMBOL_QUERY_COMMAND: InternalSymbol;
export declare const SYMBOL_LOGIC_COMMAND: InternalSymbol;
export declare const SYMBOL_GEO_POINT: InternalSymbol;
export declare const SYMBOL_GEO_LINE_STRING: InternalSymbol;
export declare const SYMBOL_GEO_POLYGON: InternalSymbol;
export declare const SYMBOL_GEO_MULTI_POINT: InternalSymbol;
export declare const SYMBOL_GEO_MULTI_LINE_STRING: InternalSymbol;
export declare const SYMBOL_GEO_MULTI_POLYGON: InternalSymbol;
export declare const SYMBOL_SERVER_DATE: InternalSymbol;
export declare const SYMBOL_REGEXP: InternalSymbol;
export declare const SYMBOL_OBJECTID: InternalSymbol;

@ -0,0 +1,20 @@
"use strict";
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
Object.defineProperty(exports, "__esModule", { value: true });
const symbol_1 = require("../utils/symbol");
__export(require("../utils/symbol"));
exports.SYMBOL_UNSET_FIELD_NAME = symbol_1.default.for('UNSET_FIELD_NAME');
exports.SYMBOL_UPDATE_COMMAND = symbol_1.default.for('UPDATE_COMMAND');
exports.SYMBOL_QUERY_COMMAND = symbol_1.default.for('QUERY_COMMAND');
exports.SYMBOL_LOGIC_COMMAND = symbol_1.default.for('LOGIC_COMMAND');
exports.SYMBOL_GEO_POINT = symbol_1.default.for('GEO_POINT');
exports.SYMBOL_GEO_LINE_STRING = symbol_1.default.for('SYMBOL_GEO_LINE_STRING');
exports.SYMBOL_GEO_POLYGON = symbol_1.default.for('SYMBOL_GEO_POLYGON');
exports.SYMBOL_GEO_MULTI_POINT = symbol_1.default.for('SYMBOL_GEO_MULTI_POINT');
exports.SYMBOL_GEO_MULTI_LINE_STRING = symbol_1.default.for('SYMBOL_GEO_MULTI_LINE_STRING');
exports.SYMBOL_GEO_MULTI_POLYGON = symbol_1.default.for('SYMBOL_GEO_MULTI_POLYGON');
exports.SYMBOL_SERVER_DATE = symbol_1.default.for('SERVER_DATE');
exports.SYMBOL_REGEXP = symbol_1.default.for('REGEXP');
exports.SYMBOL_OBJECTID = symbol_1.default.for('OBJECTID');

@ -0,0 +1,50 @@
import { Point } from './geo/point';
import { CollectionReference } from './collection';
import { Command } from './command';
interface GeoTeyp {
Point: typeof Point;
}
export { Query } from './query';
export { CollectionReference } from './collection';
export { DocumentReference } from './document';
interface ICloudBaseConfig {
timeout?: number;
isHttp?: boolean;
secretId?: string;
secretKey?: string;
envName?: string;
env?: string;
sessionToken?: string;
serviceUrl?: string;
headers?: any;
proxy?: string;
version?: string;
credentials?: ICredentialsInfo;
_useFeature?: boolean;
throwOnCode?: boolean;
}
interface ICredentialsInfo {
private_key_id: string;
private_key: string;
}
export declare class Db {
Geo: GeoTeyp;
command: typeof Command;
RegExp: any;
serverDate: any;
ObjectId: any;
startTransaction: any;
runTransaction: any;
config: ICloudBaseConfig;
static ws: any;
static reqClass: any;
static wsClass: any;
static createSign: Function;
static getAccessToken: Function;
static dataVersion: string;
static runtime: string;
static appSecretInfo: any;
constructor(config?: any);
collection(collName: string): CollectionReference;
createCollection(collName: string): any;
}

@ -0,0 +1,41 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const Geo = require("./geo/index");
const collection_1 = require("./collection");
const command_1 = require("./command");
const index_1 = require("./serverDate/index");
const index_2 = require("./regexp/index");
const index_3 = require("./transaction/index");
const index_4 = require("./ObjectId/index");
var query_1 = require("./query");
exports.Query = query_1.Query;
var collection_2 = require("./collection");
exports.CollectionReference = collection_2.CollectionReference;
var document_1 = require("./document");
exports.DocumentReference = document_1.DocumentReference;
class Db {
constructor(config) {
this.config = config;
this.Geo = Geo;
this.serverDate = index_1.ServerDateConstructor;
this.command = command_1.Command;
this.RegExp = index_2.RegExpConstructor;
this.ObjectId = index_4.ObjectIdConstructor;
this.startTransaction = index_3.startTransaction;
this.runTransaction = index_3.runTransaction;
}
collection(collName) {
if (!collName) {
throw new Error('Collection name is required');
}
return new collection_1.CollectionReference(this, collName);
}
createCollection(collName) {
let request = new Db.reqClass(this.config);
const params = {
collectionName: collName
};
return request.send('database.addCollection', params);
}
}
exports.Db = Db;

@ -0,0 +1,4 @@
export declare const OperatorMap: {
[key: string]: string;
};
export declare function operatorToString(operator: string): string;

@ -0,0 +1,23 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const query_1 = require("./commands/query");
const logic_1 = require("./commands/logic");
const update_1 = require("./commands/update");
exports.OperatorMap = {};
for (const key in query_1.QUERY_COMMANDS_LITERAL) {
exports.OperatorMap[key] = '$' + key;
}
for (const key in logic_1.LOGIC_COMMANDS_LITERAL) {
exports.OperatorMap[key] = '$' + key;
}
for (const key in update_1.UPDATE_COMMANDS_LITERAL) {
exports.OperatorMap[key] = '$' + key;
}
exports.OperatorMap[query_1.QUERY_COMMANDS_LITERAL.NEQ] = '$ne';
exports.OperatorMap[update_1.UPDATE_COMMANDS_LITERAL.REMOVE] = '$unset';
exports.OperatorMap[update_1.UPDATE_COMMANDS_LITERAL.SHIFT] = '$pop';
exports.OperatorMap[update_1.UPDATE_COMMANDS_LITERAL.UNSHIFT] = '$push';
function operatorToString(operator) {
return exports.OperatorMap[operator] || '$' + operator;
}
exports.operatorToString = operatorToString;

@ -0,0 +1,38 @@
import { OrderByDirection } from './constant';
import { IWatchOptions, DBRealtimeListener } from './typings/index';
interface GetRes {
data: any[];
requestId: string;
total: number;
limit: number;
offset: number;
}
interface BaseOption {
timeout?: number;
raw?: boolean;
}
export interface QueryOption extends BaseOption {
limit?: number;
offset?: number;
projection?: Object;
order?: Record<string, any>[];
}
export interface UpdateOption extends BaseOption {
multiple?: boolean;
}
export declare class Query {
protected _transactionId: string;
get(): Promise<GetRes>;
count(): Promise<any>;
where(query: object): Query;
options(apiOptions: QueryOption | UpdateOption): Query;
orderBy(fieldPath: string, directionStr: OrderByDirection): Query;
limit(limit: number): Query;
skip(offset: number): Query;
update(data: Object): Promise<any>;
field(projection: any): Query;
remove(): Promise<any>;
updateAndReturn(data: Object): Promise<any>;
watch: (options: IWatchOptions) => DBRealtimeListener;
}
export {};

@ -0,0 +1,247 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const constant_1 = require("./constant");
const index_1 = require("./index");
const validate_1 = require("./validate");
const util_1 = require("./util");
const query_1 = require("./serializer/query");
const update_1 = require("./serializer/update");
const websocket_client_1 = require("./realtime/websocket-client");
const constant_2 = require("./constant");
const utils_1 = require("./utils/utils");
const code_1 = require("./const/code");
const bson_1 = require("bson");
class Query {
constructor(db, coll, fieldFilters, apiOptions, transactionId) {
this.watch = (options) => {
if (!index_1.Db.ws) {
index_1.Db.ws = new websocket_client_1.RealtimeWebSocketClient({
context: {
appConfig: {
docSizeLimit: 1000,
realtimePingInterval: 10000,
realtimePongWaitTimeout: 5000,
request: this._request
}
}
});
}
const { limit, order } = this._apiOptions;
return index_1.Db.ws.watch(Object.assign(Object.assign({}, options), { envId: this._db.config.env, collectionName: this._coll, query: JSON.stringify(this._fieldFilters), limit, orderBy: order
? order.reduce((acc, cur) => {
acc[cur.field] = cur.direction;
return acc;
}, {})
: undefined }));
};
this._db = db;
this._coll = coll;
this._fieldFilters = fieldFilters;
this._apiOptions = apiOptions || {};
this._request = new index_1.Db.reqClass(this._db.config);
this._transactionId = transactionId;
}
async get() {
const order = this._apiOptions.order;
let param = {
collectionName: this._coll,
queryType: constant_1.QueryType.WHERE,
transactionId: this._transactionId
};
if (this._fieldFilters) {
param.query = this._fieldFilters;
}
if (order) {
param.order = utils_1.stringifyByEJSON(order);
}
const offset = this._apiOptions.offset;
if (offset) {
param.offset = offset;
}
const limit = this._apiOptions.limit;
if (limit) {
param.limit = limit < 1000 ? limit : 1000;
}
else {
param.limit = 100;
}
const projection = this._apiOptions.projection;
if (projection) {
param.projection = utils_1.stringifyByEJSON(projection);
}
const res = await this._request.send('database.getDocument', param, utils_1.getReqOpts(this._apiOptions));
if (res.code) {
return res;
}
const list = res.data.list.map(item => bson_1.EJSON.parse(item));
const documents = util_1.Util.formatResDocumentData(list);
const result = {
data: documents,
requestId: res.requestId
};
if (res.limit)
result.limit = res.limit;
if (res.offset)
result.offset = res.offset;
return result;
}
async count() {
let param = {
collectionName: this._coll,
queryType: constant_1.QueryType.WHERE
};
if (this._fieldFilters) {
param.query = this._fieldFilters;
}
const res = await this._request.send('database.calculateDocument', param, utils_1.getReqOpts(this._apiOptions));
if (res.code) {
return res;
}
return {
requestId: res.requestId,
total: res.data.total
};
}
where(query) {
if (Object.prototype.toString.call(query).slice(8, -1) !== 'Object') {
throw Error(constant_2.ErrorCode.QueryParamTypeError);
}
const keys = Object.keys(query);
const checkFlag = keys.some(item => {
return query[item] !== undefined;
});
if (keys.length && !checkFlag) {
throw Error(constant_2.ErrorCode.QueryParamValueError);
}
return new Query(this._db, this._coll, query_1.QuerySerializer.encodeEJSON(query, this._apiOptions.raw || false), this._apiOptions, this._transactionId);
}
options(apiOptions) {
validate_1.Validate.isValidOptions(apiOptions);
return new Query(this._db, this._coll, this._fieldFilters, apiOptions, this._transactionId);
}
orderBy(fieldPath, directionStr) {
validate_1.Validate.isFieldPath(fieldPath);
validate_1.Validate.isFieldOrder(directionStr);
const newOrder = {
[fieldPath]: directionStr === 'desc' ? -1 : 1
};
const order = this._apiOptions.order || {};
const newApiOption = Object.assign({}, this._apiOptions, {
order: Object.assign({}, order, newOrder)
});
return new Query(this._db, this._coll, this._fieldFilters, newApiOption, this._transactionId);
}
limit(limit) {
validate_1.Validate.isInteger('limit', limit);
let newApiOption = Object.assign({}, this._apiOptions);
newApiOption.limit = limit;
return new Query(this._db, this._coll, this._fieldFilters, newApiOption, this._transactionId);
}
skip(offset) {
validate_1.Validate.isInteger('offset', offset);
let newApiOption = Object.assign({}, this._apiOptions);
newApiOption.offset = offset;
return new Query(this._db, this._coll, this._fieldFilters, newApiOption, this._transactionId);
}
async update(data) {
if (!data || typeof data !== 'object') {
return utils_1.processReturn(this._db.config.throwOnCode, Object.assign(Object.assign({}, code_1.ERRORS.INVALID_PARAM), { message: '参数必需是非空对象' }));
}
if (data.hasOwnProperty('_id')) {
return utils_1.processReturn(this._db.config.throwOnCode, Object.assign(Object.assign({}, code_1.ERRORS.INVALID_PARAM), { message: '不能更新_id的值' }));
}
let { multiple } = this._apiOptions;
const multi = multiple === undefined ? true : multiple;
let param = {
collectionName: this._coll,
queryType: constant_1.QueryType.WHERE,
multi,
merge: true,
upsert: false,
data: update_1.UpdateSerializer.encodeEJSON(data, this._apiOptions.raw || false)
};
if (this._fieldFilters) {
param.query = this._fieldFilters;
}
const res = await this._request.send('database.modifyDocument', param, utils_1.getReqOpts(this._apiOptions));
if (res.code) {
return res;
}
return {
requestId: res.requestId,
updated: res.data.updated,
upsertId: res.data.upsert_id
};
}
field(projection) {
let transformProjection = {};
for (let k in projection) {
if (typeof projection[k] === 'boolean') {
transformProjection[k] = projection[k] === true ? 1 : 0;
}
if (typeof projection[k] === 'number') {
transformProjection[k] = projection[k] > 0 ? 1 : 0;
}
if (typeof projection[k] === 'object') {
transformProjection[k] = projection[k];
}
}
let newApiOption = Object.assign({}, this._apiOptions);
newApiOption.projection = transformProjection;
return new Query(this._db, this._coll, this._fieldFilters, newApiOption, this._transactionId);
}
async remove() {
const { offset, limit, projection, order } = this._apiOptions;
if (offset !== undefined ||
limit !== undefined ||
projection !== undefined ||
order !== undefined) {
console.warn('`offset`, `limit`, `projection`, `orderBy` are not supported in remove() operation');
}
let { multiple } = this._apiOptions;
const multi = multiple === undefined ? true : multiple;
const param = {
collectionName: this._coll,
query: this._fieldFilters,
queryType: constant_1.QueryType.WHERE,
multi
};
const res = await this._request.send('database.removeDocument', param, utils_1.getReqOpts(this._apiOptions));
if (res.code) {
return res;
}
return {
requestId: res.requestId,
deleted: res.data.deleted
};
}
async updateAndReturn(data) {
if (!data || typeof data !== 'object') {
return utils_1.processReturn(this._db.config.throwOnCode, Object.assign(Object.assign({}, code_1.ERRORS.INVALID_PARAM), { message: '参数必需是非空对象' }));
}
if (data.hasOwnProperty('_id')) {
return utils_1.processReturn(this._db.config.throwOnCode, Object.assign(Object.assign({}, code_1.ERRORS.INVALID_PARAM), { message: '不能更新_id的值' }));
}
let param = {
collectionName: this._coll,
queryType: constant_1.QueryType.WHERE,
data: update_1.UpdateSerializer.encodeEJSON(data, false)
};
if (this._transactionId) {
param.transactionId = this._transactionId;
}
if (this._fieldFilters) {
param.query = this._fieldFilters;
}
const res = await this._request.send('database.modifyAndReturnDoc', param, utils_1.getReqOpts(this._apiOptions));
if (res.code) {
return res;
}
return {
requestId: res.requestId,
updated: res.data.updated,
doc: res.data.doc && bson_1.EJSON.parse(res.data.doc)
};
}
}
exports.Query = Query;

@ -0,0 +1,7 @@
import { IResponseMessageErrorMsg } from '../typings/realtime';
export declare class RealtimeErrorMessageError extends Error {
isRealtimeErrorMessageError: boolean;
payload: IResponseMessageErrorMsg;
constructor(serverErrorMsg: IResponseMessageErrorMsg);
}
export declare const isRealtimeErrorMessageError: (e: any) => e is RealtimeErrorMessageError;

@ -0,0 +1,11 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
class RealtimeErrorMessageError extends Error {
constructor(serverErrorMsg) {
super(`Watch Error ${JSON.stringify(serverErrorMsg.msgData)} (requestid: ${serverErrorMsg.requestId})`);
this.isRealtimeErrorMessageError = true;
this.payload = serverErrorMsg;
}
}
exports.RealtimeErrorMessageError = RealtimeErrorMessageError;
exports.isRealtimeErrorMessageError = (e) => e && e.isRealtimeErrorMessageError;

@ -0,0 +1,14 @@
import { VirtualWebSocketClient } from './virtual-websocket-client';
import { IRealtimeListenerConstructorOptions, DBRealtimeListener } from '../typings/index';
interface IRealtimeListenerOptions extends IRealtimeListenerConstructorOptions {
close: () => void;
debug?: boolean;
virtualClient?: VirtualWebSocketClient;
}
export declare class RealtimeListener implements DBRealtimeListener {
close: () => void;
onChange: (res: any) => void;
onError: (error: any) => void;
constructor(options: IRealtimeListenerOptions);
}
export {};

@ -0,0 +1,17 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
class RealtimeListener {
constructor(options) {
this.close = options.close;
this.onChange = options.onChange;
this.onError = options.onError;
if (options.debug) {
Object.defineProperty(this, 'virtualClient', {
get: () => {
return options.virtualClient;
}
});
}
}
}
exports.RealtimeListener = RealtimeListener;

@ -0,0 +1,3 @@
import { IResponseMessage, IResponseMessageInitEventMsg } from '../typings/realtime';
export declare function genRequestId(prefix?: string): string;
export declare function isInitEventMessage(msg: IResponseMessage): msg is IResponseMessageInitEventMsg;

@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function genRequestId(prefix = '') {
return `${prefix ? `${prefix}_` : ''}${+new Date()}_${Math.random()}`;
}
exports.genRequestId = genRequestId;
function isInitEventMessage(msg) {
return msg.msgType === 'INIT_EVENT';
}
exports.isInitEventMessage = isInitEventMessage;

@ -0,0 +1,16 @@
import { ISingleDBEvent, SnapshotType, ISnapshot } from '../typings/index';
interface ISnapshotConstructorOptions {
id: number;
docChanges: ISingleDBEvent[];
docs: Record<string, any>[];
type?: SnapshotType;
msgType?: String;
}
export declare class Snapshot implements ISnapshot {
id: number;
docChanges: ISingleDBEvent[];
docs: Record<string, any>[];
type?: 'init';
constructor(options: ISnapshotConstructorOptions);
}
export {};

@ -0,0 +1,42 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
class Snapshot {
constructor(options) {
const { id, docChanges, docs, msgType, type } = options;
let cachedDocChanges;
let cachedDocs;
Object.defineProperties(this, {
id: {
get: () => id,
enumerable: true
},
docChanges: {
get: () => {
if (!cachedDocChanges) {
cachedDocChanges = JSON.parse(JSON.stringify(docChanges));
}
return cachedDocChanges;
},
enumerable: true
},
docs: {
get: () => {
if (!cachedDocs) {
cachedDocs = JSON.parse(JSON.stringify(docs));
}
return cachedDocs;
},
enumerable: true
},
msgType: {
get: () => msgType,
enumerable: true
},
type: {
get: () => type,
enumerable: true
}
});
}
}
exports.Snapshot = Snapshot;

@ -0,0 +1,63 @@
import { IResponseMessage } from '../typings/realtime';
import { RealtimeListener } from './listener';
import { IWSSendOptions } from './websocket-client';
import { IWatchOptions } from '../typings/index';
interface IVirtualWebSocketClientConstructorOptions extends IWatchOptions {
envId?: string;
collectionName: string;
query: string;
limit?: number;
orderBy?: Record<string, string>;
send: <T = any>(opts: IWSSendOptions) => Promise<T>;
login: (envId?: string, refresh?: boolean) => Promise<any>;
isWSConnected: () => boolean;
onceWSConnected: () => Promise<void>;
getWaitExpectedTimeoutLength: () => number;
onWatchStart: (client: VirtualWebSocketClient, queryID: string) => void;
onWatchClose: (client: VirtualWebSocketClient, queryID: string) => void;
debug?: boolean;
}
export declare class VirtualWebSocketClient {
watchId: string;
private envId?;
private collectionName;
private query;
private limit;
private orderBy;
private send;
private login;
private isWSConnected;
private onceWSConnected;
private getWaitExpectedTimeoutLength;
private onWatchStart;
private onWatchClose;
private debug?;
listener: RealtimeListener;
private watchStatus;
private _availableRetries;
private _ackTimeoutId?;
private _initWatchPromise?;
private _rebuildWatchPromise?;
private sessionInfo?;
private _waitExpectedTimeoutId?;
constructor(options: IVirtualWebSocketClientConstructorOptions);
private _login;
private initWatch;
private rebuildWatch;
private handleWatchEstablishmentError;
private closeWatch;
private scheduleSendACK;
private clearACKSchedule;
private sendACK;
private handleCommonError;
private useRetryTicket;
private handleServerEvents;
private _handleServerEvents;
private _postHandleServerEventsValidityCheck;
private clearWaitExpectedEvent;
onMessage(msg: IResponseMessage): void;
closeWithError(error: any): void;
pause(): void;
resume(): Promise<void>;
}
export {};

@ -0,0 +1,736 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const lodash_set_1 = require("lodash.set");
const lodash_unset_1 = require("lodash.unset");
const lodash_clonedeep_1 = require("lodash.clonedeep");
const message_1 = require("./message");
const error_1 = require("../utils/error");
const error_config_1 = require("../config/error.config");
const utils_1 = require("../utils/utils");
const listener_1 = require("./listener");
const snapshot_1 = require("./snapshot");
const error_2 = require("./error");
var WATCH_STATUS;
(function (WATCH_STATUS) {
WATCH_STATUS["LOGGINGIN"] = "LOGGINGIN";
WATCH_STATUS["INITING"] = "INITING";
WATCH_STATUS["REBUILDING"] = "REBUILDING";
WATCH_STATUS["ACTIVE"] = "ACTIVE";
WATCH_STATUS["ERRORED"] = "ERRORED";
WATCH_STATUS["CLOSING"] = "CLOSING";
WATCH_STATUS["CLOSED"] = "CLOSED";
WATCH_STATUS["PAUSED"] = "PAUSED";
WATCH_STATUS["RESUMING"] = "RESUMING";
})(WATCH_STATUS || (WATCH_STATUS = {}));
const DEFAULT_WAIT_TIME_ON_UNKNOWN_ERROR = 100;
const DEFAULT_MAX_AUTO_RETRY_ON_ERROR = 2;
const DEFAULT_MAX_SEND_ACK_AUTO_RETRY_ON_ERROR = 2;
const DEFAULT_SEND_ACK_DEBOUNCE_TIMEOUT = 10 * 1000;
const DEFAULT_INIT_WATCH_TIMEOUT = 10 * 1000;
const DEFAULT_REBUILD_WATCH_TIMEOUT = 10 * 1000;
class VirtualWebSocketClient {
constructor(options) {
this.watchStatus = WATCH_STATUS.INITING;
this._login = async (envId, refresh) => {
this.watchStatus = WATCH_STATUS.LOGGINGIN;
const loginResult = await this.login(envId, refresh);
if (!this.envId) {
this.envId = loginResult.envId;
}
return loginResult;
};
this.initWatch = async (forceRefreshLogin) => {
if (this._initWatchPromise) {
return this._initWatchPromise;
}
this._initWatchPromise = new Promise(async (resolve, reject) => {
try {
if (this.watchStatus === WATCH_STATUS.PAUSED) {
console.log('[realtime] initWatch cancelled on pause');
return resolve();
}
const { envId } = await this._login(this.envId, forceRefreshLogin);
if (this.watchStatus === WATCH_STATUS.PAUSED) {
console.log('[realtime] initWatch cancelled on pause');
return resolve();
}
this.watchStatus = WATCH_STATUS.INITING;
const initWatchMsg = {
watchId: this.watchId,
requestId: message_1.genRequestId(),
msgType: 'INIT_WATCH',
msgData: {
envId,
collName: this.collectionName,
query: this.query,
limit: this.limit,
orderBy: this.orderBy
}
};
const initEventMsg = await this.send({
msg: initWatchMsg,
waitResponse: true,
skipOnMessage: true,
timeout: DEFAULT_INIT_WATCH_TIMEOUT
});
const { events, currEvent } = initEventMsg.msgData;
this.sessionInfo = {
queryID: initEventMsg.msgData.queryID,
currentEventId: currEvent - 1,
currentDocs: []
};
if (events.length > 0) {
for (const e of events) {
e.ID = currEvent;
}
this.handleServerEvents(initEventMsg);
}
else {
this.sessionInfo.currentEventId = currEvent;
const snapshot = new snapshot_1.Snapshot({
id: currEvent,
docChanges: [],
docs: [],
type: 'init'
});
this.listener.onChange(snapshot);
this.scheduleSendACK();
}
this.onWatchStart(this, this.sessionInfo.queryID);
this.watchStatus = WATCH_STATUS.ACTIVE;
this._availableRetries.INIT_WATCH = DEFAULT_MAX_AUTO_RETRY_ON_ERROR;
resolve();
}
catch (e) {
this.handleWatchEstablishmentError(e, {
operationName: 'INIT_WATCH',
resolve,
reject
});
}
});
let success = false;
try {
await this._initWatchPromise;
success = true;
}
finally {
this._initWatchPromise = undefined;
}
console.log(`[realtime] initWatch ${success ? 'success' : 'fail'}`);
};
this.rebuildWatch = async (forceRefreshLogin) => {
if (this._rebuildWatchPromise) {
return this._rebuildWatchPromise;
}
this._rebuildWatchPromise = new Promise(async (resolve, reject) => {
try {
if (this.watchStatus === WATCH_STATUS.PAUSED) {
console.log('[realtime] rebuildWatch cancelled on pause');
return resolve();
}
const { envId } = await this._login(this.envId, forceRefreshLogin);
if (!this.sessionInfo) {
throw new Error('can not rebuildWatch without a successful initWatch (lack of sessionInfo)');
}
if (this.watchStatus === WATCH_STATUS.PAUSED) {
console.log('[realtime] rebuildWatch cancelled on pause');
return resolve();
}
this.watchStatus = WATCH_STATUS.REBUILDING;
const rebuildWatchMsg = {
watchId: this.watchId,
requestId: message_1.genRequestId(),
msgType: 'REBUILD_WATCH',
msgData: {
envId,
collName: this.collectionName,
queryID: this.sessionInfo.queryID,
eventID: this.sessionInfo.currentEventId
}
};
const nextEventMsg = await this.send({
msg: rebuildWatchMsg,
waitResponse: true,
skipOnMessage: false,
timeout: DEFAULT_REBUILD_WATCH_TIMEOUT
});
this.handleServerEvents(nextEventMsg);
this.watchStatus = WATCH_STATUS.ACTIVE;
this._availableRetries.REBUILD_WATCH = DEFAULT_MAX_AUTO_RETRY_ON_ERROR;
resolve();
}
catch (e) {
this.handleWatchEstablishmentError(e, {
operationName: 'REBUILD_WATCH',
resolve,
reject
});
}
});
let success = false;
try {
await this._rebuildWatchPromise;
success = true;
}
finally {
this._rebuildWatchPromise = undefined;
}
console.log(`[realtime] rebuildWatch ${success ? 'success' : 'fail'}`);
};
this.handleWatchEstablishmentError = async (e, options) => {
const isInitWatch = options.operationName === 'INIT_WATCH';
const abortWatch = () => {
this.closeWithError(new error_1.CloudSDKError({
errCode: isInitWatch
? error_config_1.ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_INIT_WATCH_FAIL
: error_config_1.ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_REBUILD_WATCH_FAIL,
errMsg: e
}));
options.reject(e);
};
const retry = (refreshLogin) => {
if (this.useRetryTicket(options.operationName)) {
if (isInitWatch) {
this._initWatchPromise = undefined;
options.resolve(this.initWatch(refreshLogin));
}
else {
this._rebuildWatchPromise = undefined;
options.resolve(this.rebuildWatch(refreshLogin));
}
}
else {
abortWatch();
}
};
this.handleCommonError(e, {
onSignError: () => retry(true),
onTimeoutError: () => retry(false),
onNotRetryableError: abortWatch,
onCancelledError: options.reject,
onUnknownError: async () => {
try {
const onWSDisconnected = async () => {
this.pause();
await this.onceWSConnected();
retry(true);
};
if (!this.isWSConnected()) {
await onWSDisconnected();
}
else {
await utils_1.sleep(DEFAULT_WAIT_TIME_ON_UNKNOWN_ERROR);
if (this.watchStatus === WATCH_STATUS.PAUSED) {
options.reject(new error_1.CancelledError(`${options.operationName} cancelled due to pause after unknownError`));
}
else if (!this.isWSConnected()) {
await onWSDisconnected();
}
else {
retry(false);
}
}
}
catch (e) {
retry(true);
}
}
});
};
this.closeWatch = async () => {
const queryId = this.sessionInfo ? this.sessionInfo.queryID : '';
if (this.watchStatus !== WATCH_STATUS.ACTIVE) {
this.watchStatus = WATCH_STATUS.CLOSED;
this.onWatchClose(this, queryId);
return;
}
try {
this.watchStatus = WATCH_STATUS.CLOSING;
const closeWatchMsg = {
watchId: this.watchId,
requestId: message_1.genRequestId(),
msgType: 'CLOSE_WATCH',
msgData: null
};
await this.send({
msg: closeWatchMsg
});
this.sessionInfo = undefined;
this.watchStatus = WATCH_STATUS.CLOSED;
}
catch (e) {
this.closeWithError(new error_1.CloudSDKError({
errCode: error_config_1.ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_CLOSE_WATCH_FAIL,
errMsg: e
}));
}
finally {
this.onWatchClose(this, queryId);
}
};
this.scheduleSendACK = () => {
this.clearACKSchedule();
this._ackTimeoutId = setTimeout(() => {
if (this._waitExpectedTimeoutId) {
this.scheduleSendACK();
}
else {
this.sendACK();
}
}, DEFAULT_SEND_ACK_DEBOUNCE_TIMEOUT);
};
this.clearACKSchedule = () => {
if (this._ackTimeoutId) {
clearTimeout(this._ackTimeoutId);
}
};
this.sendACK = async () => {
try {
if (this.watchStatus !== WATCH_STATUS.ACTIVE) {
this.scheduleSendACK();
return;
}
if (!this.sessionInfo) {
console.warn('[realtime listener] can not send ack without a successful initWatch (lack of sessionInfo)');
return;
}
const ackMsg = {
watchId: this.watchId,
requestId: message_1.genRequestId(),
msgType: 'CHECK_LAST',
msgData: {
queryID: this.sessionInfo.queryID,
eventID: this.sessionInfo.currentEventId
}
};
await this.send({
msg: ackMsg
});
this.scheduleSendACK();
}
catch (e) {
if (error_2.isRealtimeErrorMessageError(e)) {
const msg = e.payload;
switch (msg.msgData.code) {
case 'CHECK_LOGIN_FAILED':
case 'SIGN_EXPIRED_ERROR':
case 'SIGN_INVALID_ERROR':
case 'SIGN_PARAM_INVALID': {
this.rebuildWatch();
return;
}
case 'QUERYID_INVALID_ERROR':
case 'SYS_ERR':
case 'INVALIID_ENV':
case 'COLLECTION_PERMISSION_DENIED': {
this.closeWithError(new error_1.CloudSDKError({
errCode: error_config_1.ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_CHECK_LAST_FAIL,
errMsg: msg.msgData.code
}));
return;
}
default: {
break;
}
}
}
if (this._availableRetries.CHECK_LAST &&
this._availableRetries.CHECK_LAST > 0) {
this._availableRetries.CHECK_LAST--;
this.scheduleSendACK();
}
else {
this.closeWithError(new error_1.CloudSDKError({
errCode: error_config_1.ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_CHECK_LAST_FAIL,
errMsg: e
}));
}
}
};
this.handleCommonError = (e, options) => {
if (error_2.isRealtimeErrorMessageError(e)) {
const msg = e.payload;
switch (msg.msgData.code) {
case 'CHECK_LOGIN_FAILED':
case 'SIGN_EXPIRED_ERROR':
case 'SIGN_INVALID_ERROR':
case 'SIGN_PARAM_INVALID': {
options.onSignError(e);
return;
}
case 'QUERYID_INVALID_ERROR':
case 'SYS_ERR':
case 'INVALIID_ENV':
case 'COLLECTION_PERMISSION_DENIED': {
options.onNotRetryableError(e);
return;
}
default: {
options.onNotRetryableError(e);
return;
}
}
}
else if (error_1.isTimeoutError(e)) {
options.onTimeoutError(e);
return;
}
else if (error_1.isCancelledError(e)) {
options.onCancelledError(e);
return;
}
options.onUnknownError(e);
};
this.watchId = `watchid_${+new Date()}_${Math.random()}`;
this.envId = options.envId;
this.collectionName = options.collectionName;
this.query = options.query;
this.limit = options.limit;
this.orderBy = options.orderBy;
this.send = options.send;
this.login = options.login;
this.isWSConnected = options.isWSConnected;
this.onceWSConnected = options.onceWSConnected;
this.getWaitExpectedTimeoutLength = options.getWaitExpectedTimeoutLength;
this.onWatchStart = options.onWatchStart;
this.onWatchClose = options.onWatchClose;
this.debug = options.debug;
this._availableRetries = {
INIT_WATCH: DEFAULT_MAX_AUTO_RETRY_ON_ERROR,
REBUILD_WATCH: DEFAULT_MAX_AUTO_RETRY_ON_ERROR,
CHECK_LAST: DEFAULT_MAX_SEND_ACK_AUTO_RETRY_ON_ERROR
};
this.listener = new listener_1.RealtimeListener({
close: this.closeWatch,
onChange: options.onChange,
onError: options.onError,
debug: this.debug,
virtualClient: this
});
this.initWatch();
}
useRetryTicket(operationName) {
if (this._availableRetries[operationName] &&
this._availableRetries[operationName] > 0) {
this._availableRetries[operationName]--;
console.log(`[realtime] ${operationName} use a retry ticket, now only ${this._availableRetries[operationName]} retry left`);
return true;
}
return false;
}
async handleServerEvents(msg) {
try {
this.scheduleSendACK();
await this._handleServerEvents(msg);
this._postHandleServerEventsValidityCheck(msg);
}
catch (e) {
console.error('[realtime listener] internal non-fatal error: handle server events failed with error: ', e);
throw e;
}
}
async _handleServerEvents(msg) {
const { requestId } = msg;
const { events } = msg.msgData;
const { msgType } = msg;
if (!events.length || !this.sessionInfo) {
return;
}
const sessionInfo = this.sessionInfo;
let allChangeEvents;
try {
allChangeEvents = events.map(getPublicEvent);
}
catch (e) {
this.closeWithError(new error_1.CloudSDKError({
errCode: error_config_1.ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_RECEIVE_INVALID_SERVER_DATA,
errMsg: e
}));
return;
}
let docs = [...sessionInfo.currentDocs];
let initEncountered = false;
for (let i = 0, len = allChangeEvents.length; i < len; i++) {
const change = allChangeEvents[i];
if (sessionInfo.currentEventId >= change.id) {
if (!allChangeEvents[i - 1] || change.id > allChangeEvents[i - 1].id) {
console.warn(`[realtime] duplicate event received, cur ${sessionInfo.currentEventId} but got ${change.id}`);
}
else {
console.error(`[realtime listener] server non-fatal error: events out of order (the latter event's id is smaller than that of the former) (requestId ${requestId})`);
}
continue;
}
else if (sessionInfo.currentEventId === change.id - 1) {
switch (change.dataType) {
case 'update': {
if (!change.doc) {
switch (change.queueType) {
case 'update':
case 'dequeue': {
const localDoc = docs.find(doc => doc._id === change.docId);
if (localDoc) {
const doc = lodash_clonedeep_1.default(localDoc);
if (change.updatedFields) {
for (const fieldPath in change.updatedFields) {
lodash_set_1.default(doc, fieldPath, change.updatedFields[fieldPath]);
}
}
if (change.removedFields) {
for (const fieldPath of change.removedFields) {
lodash_unset_1.default(doc, fieldPath);
}
}
change.doc = doc;
}
else {
console.error('[realtime listener] internal non-fatal server error: unexpected update dataType event where no doc is associated.');
}
break;
}
case 'enqueue': {
const err = new error_1.CloudSDKError({
errCode: error_config_1.ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_UNEXPECTED_FATAL_ERROR,
errMsg: `HandleServerEvents: full doc is not provided with dataType="update" and queueType="enqueue" (requestId ${msg.requestId})`
});
this.closeWithError(err);
throw err;
}
default: {
break;
}
}
}
break;
}
case 'replace': {
if (!change.doc) {
const err = new error_1.CloudSDKError({
errCode: error_config_1.ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_UNEXPECTED_FATAL_ERROR,
errMsg: `HandleServerEvents: full doc is not provided with dataType="replace" (requestId ${msg.requestId})`
});
this.closeWithError(err);
throw err;
}
break;
}
case 'remove': {
const doc = docs.find(doc => doc._id === change.docId);
if (doc) {
change.doc = doc;
}
else {
console.error('[realtime listener] internal non-fatal server error: unexpected remove event where no doc is associated.');
}
break;
}
case 'limit': {
if (!change.doc) {
switch (change.queueType) {
case 'dequeue': {
const doc = docs.find(doc => doc._id === change.docId);
if (doc) {
change.doc = doc;
}
else {
console.error('[realtime listener] internal non-fatal server error: unexpected limit dataType event where no doc is associated.');
}
break;
}
case 'enqueue': {
const err = new error_1.CloudSDKError({
errCode: error_config_1.ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_UNEXPECTED_FATAL_ERROR,
errMsg: `HandleServerEvents: full doc is not provided with dataType="limit" and queueType="enqueue" (requestId ${msg.requestId})`
});
this.closeWithError(err);
throw err;
}
default: {
break;
}
}
}
break;
}
}
switch (change.queueType) {
case 'init': {
if (!initEncountered) {
initEncountered = true;
docs = [change.doc];
}
else {
docs.push(change.doc);
}
break;
}
case 'enqueue': {
docs.push(change.doc);
break;
}
case 'dequeue': {
const ind = docs.findIndex(doc => doc._id === change.docId);
if (ind > -1) {
docs.splice(ind, 1);
}
else {
console.error('[realtime listener] internal non-fatal server error: unexpected dequeue event where no doc is associated.');
}
break;
}
case 'update': {
const ind = docs.findIndex(doc => doc._id === change.docId);
if (ind > -1) {
docs[ind] = change.doc;
}
else {
console.error('[realtime listener] internal non-fatal server error: unexpected queueType update event where no doc is associated.');
}
break;
}
}
if (i === len - 1 ||
(allChangeEvents[i + 1] && allChangeEvents[i + 1].id !== change.id)) {
const docsSnapshot = [...docs];
const docChanges = allChangeEvents
.slice(0, i + 1)
.filter(c => c.id === change.id);
this.sessionInfo.currentEventId = change.id;
this.sessionInfo.currentDocs = docs;
const snapshot = new snapshot_1.Snapshot({
id: change.id,
docChanges,
docs: docsSnapshot,
msgType
});
this.listener.onChange(snapshot);
}
}
else {
console.warn(`[realtime listener] event received is out of order, cur ${this.sessionInfo.currentEventId} but got ${change.id}`);
await this.rebuildWatch();
return;
}
}
}
_postHandleServerEventsValidityCheck(msg) {
if (!this.sessionInfo) {
console.error('[realtime listener] internal non-fatal error: sessionInfo lost after server event handling, this should never occur');
return;
}
if (this.sessionInfo.expectEventId &&
this.sessionInfo.currentEventId >= this.sessionInfo.expectEventId) {
this.clearWaitExpectedEvent();
}
if (this.sessionInfo.currentEventId < msg.msgData.currEvent) {
console.warn('[realtime listener] internal non-fatal error: client eventId does not match with server event id after server event handling');
return;
}
}
clearWaitExpectedEvent() {
if (this._waitExpectedTimeoutId) {
clearTimeout(this._waitExpectedTimeoutId);
this._waitExpectedTimeoutId = undefined;
}
}
onMessage(msg) {
switch (this.watchStatus) {
case WATCH_STATUS.PAUSED: {
if (msg.msgType !== 'ERROR') {
return;
}
break;
}
case WATCH_STATUS.LOGGINGIN:
case WATCH_STATUS.INITING:
case WATCH_STATUS.REBUILDING: {
console.warn(`[realtime listener] internal non-fatal error: unexpected message received while ${this.watchStatus}`);
return;
}
case WATCH_STATUS.CLOSED: {
console.warn('[realtime listener] internal non-fatal error: unexpected message received when the watch has closed');
return;
}
case WATCH_STATUS.ERRORED: {
console.warn('[realtime listener] internal non-fatal error: unexpected message received when the watch has ended with error');
return;
}
}
if (!this.sessionInfo) {
console.warn('[realtime listener] internal non-fatal error: sessionInfo not found while message is received.');
return;
}
this.scheduleSendACK();
switch (msg.msgType) {
case 'NEXT_EVENT': {
console.warn(`nextevent ${msg.msgData.currEvent} ignored`, msg);
this.handleServerEvents(msg);
break;
}
case 'CHECK_EVENT': {
if (this.sessionInfo.currentEventId < msg.msgData.currEvent) {
this.sessionInfo.expectEventId = msg.msgData.currEvent;
this.clearWaitExpectedEvent();
this._waitExpectedTimeoutId = setTimeout(() => {
this.rebuildWatch();
}, this.getWaitExpectedTimeoutLength());
console.log(`[realtime] waitExpectedTimeoutLength ${this.getWaitExpectedTimeoutLength()}`);
}
break;
}
case 'ERROR': {
this.closeWithError(new error_1.CloudSDKError({
errCode: error_config_1.ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_SERVER_ERROR_MSG,
errMsg: `${msg.msgData.code} - ${msg.msgData.message}`
}));
break;
}
default: {
console.warn(`[realtime listener] virtual client receive unexpected msg ${msg.msgType}: `, msg);
break;
}
}
}
closeWithError(error) {
this.watchStatus = WATCH_STATUS.ERRORED;
this.clearACKSchedule();
this.listener.onError(error);
this.onWatchClose(this, (this.sessionInfo && this.sessionInfo.queryID) || '');
console.log(`[realtime] client closed (${this.collectionName} ${this.query}) (watchId ${this.watchId})`);
}
pause() {
this.watchStatus = WATCH_STATUS.PAUSED;
console.log(`[realtime] client paused (${this.collectionName} ${this.query}) (watchId ${this.watchId})`);
}
async resume() {
this.watchStatus = WATCH_STATUS.RESUMING;
console.log(`[realtime] client resuming with ${this.sessionInfo ? 'REBUILD_WATCH' : 'INIT_WATCH'} (${this.collectionName} ${this.query}) (${this.watchId})`);
try {
await (this.sessionInfo ? this.rebuildWatch() : this.initWatch());
console.log(`[realtime] client successfully resumed (${this.collectionName} ${this.query}) (${this.watchId})`);
}
catch (e) {
console.error(`[realtime] client resume failed (${this.collectionName} ${this.query}) (${this.watchId})`, e);
}
}
}
exports.VirtualWebSocketClient = VirtualWebSocketClient;
function getPublicEvent(event) {
const e = {
id: event.ID,
dataType: event.DataType,
queueType: event.QueueType,
docId: event.DocID,
doc: event.Doc && event.Doc !== '{}' ? JSON.parse(event.Doc) : undefined
};
if (event.DataType === 'update') {
if (event.UpdatedFields) {
e.updatedFields = JSON.parse(event.UpdatedFields);
}
if (event.removedFields || event.RemovedFields) {
e.removedFields = JSON.parse(event.removedFields);
}
}
return e;
}

@ -0,0 +1,78 @@
import { VirtualWebSocketClient } from './virtual-websocket-client';
import { IDatabaseServiceContext, IWatchOptions, DBRealtimeListener } from '../typings/index';
import { IRequestMessage } from '../typings/realtime';
import { CLOSE_EVENT_CODE } from './ws-event';
export interface IRealtimeWebSocketClientConstructorOptions {
maxReconnect?: number;
reconnectInterval?: number;
context: IDatabaseServiceContext;
}
export interface ISignature {
envId: string;
secretVersion: number;
signStr: string;
wsUrl: string;
expireTS: number;
}
export interface ILoginInfo {
loggedIn: boolean;
loggingInPromise?: Promise<ILoginResult>;
loginStartTS?: number;
loginResult?: ILoginResult;
}
export interface ILoginResult {
envId: string;
}
export interface IWSSendOptions {
msg: IRequestMessage;
waitResponse?: boolean;
skipOnMessage?: boolean;
timeout?: number;
}
export interface IWSWatchOptions extends IWatchOptions {
envId?: string;
collectionName: string;
query: string;
limit?: number;
orderBy?: Record<string, string>;
}
export declare class RealtimeWebSocketClient {
private _virtualWSClient;
private _queryIdClientMap;
private _watchIdClientMap;
private _maxReconnect;
private _reconnectInterval;
private _context;
private _ws?;
private _lastPingSendTS?;
private _pingFailed;
private _pongMissed;
private _pingTimeoutId?;
private _pongTimeoutId?;
private _logins;
private _wsInitPromise?;
private _wsReadySubsribers;
private _wsResponseWait;
private _rttObserved;
private _reconnectState;
private _wsSign;
constructor(options: IRealtimeWebSocketClientConstructorOptions);
private initWebSocketConnection;
private initWebSocketEvent;
private isWSConnected;
private onceWSConnected;
private webLogin;
private getWsSign;
private getWaitExpectedTimeoutLength;
private heartbeat;
clearHeartbeat(): void;
private ping;
send: <T = any>(opts: IWSSendOptions) => Promise<T>;
close(code: CLOSE_EVENT_CODE): void;
closeAllClients: (error: any) => void;
pauseClients: (clients?: Set<VirtualWebSocketClient>) => void;
resumeClients: (clients?: Set<VirtualWebSocketClient>) => void;
private onWatchStart;
private onWatchClose;
watch(options: IWSWatchOptions): DBRealtimeListener;
}

@ -0,0 +1,567 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const virtual_websocket_client_1 = require("./virtual-websocket-client");
const utils_1 = require("../utils/utils");
const message_1 = require("./message");
const ws_event_1 = require("./ws-event");
const error_1 = require("../utils/error");
const error_2 = require("./error");
const error_config_1 = require("../config/error.config");
const __1 = require("../");
const WS_READY_STATE = {
CONNECTING: 0,
OPEN: 1,
CLOSING: 2,
CLOSED: 3
};
const MAX_RTT_OBSERVED = 3;
const DEFAULT_EXPECTED_EVENT_WAIT_TIME = 5000;
const DEFAULT_UNTRUSTED_RTT_THRESHOLD = 10000;
const DEFAULT_MAX_RECONNECT = 5;
const DEFAULT_WS_RECONNECT_INTERVAL = 10000;
const DEFAULT_PING_FAIL_TOLERANCE = 2;
const DEFAULT_PONG_MISS_TOLERANCE = 2;
const DEFAULT_LOGIN_TIMEOUT = 5000;
class RealtimeWebSocketClient {
constructor(options) {
this._virtualWSClient = new Set();
this._queryIdClientMap = new Map();
this._watchIdClientMap = new Map();
this._pingFailed = 0;
this._pongMissed = 0;
this._logins = new Map();
this._wsReadySubsribers = [];
this._wsResponseWait = new Map();
this._rttObserved = [];
this.initWebSocketConnection = async (reconnect, availableRetries = this._maxReconnect) => {
if (reconnect && this._reconnectState) {
return;
}
if (reconnect) {
this._reconnectState = true;
}
if (this._wsInitPromise) {
return this._wsInitPromise;
}
if (reconnect) {
this.pauseClients();
}
this.close(ws_event_1.CLOSE_EVENT_CODE.ReconnectWebSocket);
this._wsInitPromise = new Promise(async (resolve, reject) => {
try {
const wsSign = await this.getWsSign();
await new Promise(success => {
const url = wsSign.wsUrl || 'wss://tcb-ws.tencentcloudapi.com';
this._ws = __1.Db.wsClass ? new __1.Db.wsClass(url) : new WebSocket(url);
success();
});
if (this._ws.connect) {
await this._ws.connect();
}
await this.initWebSocketEvent();
resolve();
if (reconnect) {
this.resumeClients();
this._reconnectState = false;
}
}
catch (e) {
console.error('[realtime] initWebSocketConnection connect fail', e);
if (availableRetries > 0) {
const isConnected = true;
this._wsInitPromise = undefined;
if (isConnected) {
await utils_1.sleep(this._reconnectInterval);
if (reconnect) {
this._reconnectState = false;
}
}
resolve(this.initWebSocketConnection(reconnect, availableRetries - 1));
}
else {
reject(e);
if (reconnect) {
this.closeAllClients(new error_1.CloudSDKError({
errCode: error_config_1.ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_RECONNECT_WATCH_FAIL,
errMsg: e
}));
}
}
}
});
try {
await this._wsInitPromise;
this._wsReadySubsribers.forEach(({ resolve }) => resolve());
}
catch (e) {
this._wsReadySubsribers.forEach(({ reject }) => reject());
}
finally {
this._wsInitPromise = undefined;
this._wsReadySubsribers = [];
}
};
this.initWebSocketEvent = () => new Promise((resolve, reject) => {
if (!this._ws) {
throw new Error('can not initWebSocketEvent, ws not exists');
}
let wsOpened = false;
this._ws.onopen = event => {
console.warn('[realtime] ws event: open', event);
wsOpened = true;
resolve();
};
this._ws.onerror = event => {
this._logins = new Map();
if (!wsOpened) {
console.error('[realtime] ws open failed with ws event: error', event);
reject(event);
}
else {
console.error('[realtime] ws event: error', event);
this.clearHeartbeat();
this._virtualWSClient.forEach(client => client.closeWithError(new error_1.CloudSDKError({
errCode: error_config_1.ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_WEBSOCKET_CONNECTION_ERROR,
errMsg: event
})));
}
};
this._ws.onclose = closeEvent => {
console.warn('[realtime] ws event: close', closeEvent);
this._logins = new Map();
this.clearHeartbeat();
switch (closeEvent.code) {
case ws_event_1.CLOSE_EVENT_CODE.ReconnectWebSocket: {
break;
}
case ws_event_1.CLOSE_EVENT_CODE.NoRealtimeListeners: {
break;
}
case ws_event_1.CLOSE_EVENT_CODE.HeartbeatPingError:
case ws_event_1.CLOSE_EVENT_CODE.HeartbeatPongTimeoutError:
case ws_event_1.CLOSE_EVENT_CODE.NormalClosure:
case ws_event_1.CLOSE_EVENT_CODE.AbnormalClosure: {
if (this._maxReconnect > 0) {
this.initWebSocketConnection(true, this._maxReconnect);
}
else {
this.closeAllClients(ws_event_1.getWSCloseError(closeEvent.code));
}
break;
}
case ws_event_1.CLOSE_EVENT_CODE.NoAuthentication: {
this.closeAllClients(ws_event_1.getWSCloseError(closeEvent.code, closeEvent.reason));
break;
}
default: {
if (this._maxReconnect > 0) {
this.initWebSocketConnection(true, this._maxReconnect);
}
else {
this.closeAllClients(ws_event_1.getWSCloseError(closeEvent.code));
}
}
}
};
this._ws.onmessage = res => {
const rawMsg = res.data;
this.heartbeat();
let msg;
try {
msg = JSON.parse(rawMsg);
}
catch (e) {
throw new Error(`[realtime] onMessage parse res.data error: ${e}`);
}
if (msg.msgType === 'ERROR') {
let virtualWatch = null;
this._virtualWSClient.forEach(item => {
if (item.watchId === msg.watchId) {
virtualWatch = item;
}
});
if (virtualWatch) {
virtualWatch.listener.onError(msg);
}
}
const responseWaitSpec = this._wsResponseWait.get(msg.requestId);
if (responseWaitSpec) {
try {
if (msg.msgType === 'ERROR') {
responseWaitSpec.reject(new error_2.RealtimeErrorMessageError(msg));
}
else {
responseWaitSpec.resolve(msg);
}
}
catch (e) {
console.error('ws onMessage responseWaitSpec.resolve(msg) errored:', e);
}
finally {
this._wsResponseWait.delete(msg.requestId);
}
if (responseWaitSpec.skipOnMessage) {
return;
}
}
if (msg.msgType === 'PONG') {
if (this._lastPingSendTS) {
const rtt = Date.now() - this._lastPingSendTS;
if (rtt > DEFAULT_UNTRUSTED_RTT_THRESHOLD) {
console.warn(`[realtime] untrusted rtt observed: ${rtt}`);
return;
}
if (this._rttObserved.length >= MAX_RTT_OBSERVED) {
this._rttObserved.splice(0, this._rttObserved.length - MAX_RTT_OBSERVED + 1);
}
this._rttObserved.push(rtt);
}
return;
}
let client = msg.watchId && this._watchIdClientMap.get(msg.watchId);
if (client) {
client.onMessage(msg);
}
else {
console.error(`[realtime] no realtime listener found responsible for watchId ${msg.watchId}: `, msg);
switch (msg.msgType) {
case 'INIT_EVENT':
case 'NEXT_EVENT':
case 'CHECK_EVENT': {
client = this._queryIdClientMap.get(msg.msgData.queryID);
if (client) {
client.onMessage(msg);
}
break;
}
default: {
for (const [, client] of this._watchIdClientMap) {
client.onMessage(msg);
break;
}
}
}
}
};
this.heartbeat();
});
this.isWSConnected = () => {
return Boolean(this._ws && this._ws.readyState === WS_READY_STATE.OPEN);
};
this.onceWSConnected = async () => {
if (this.isWSConnected()) {
return;
}
if (this._wsInitPromise) {
return this._wsInitPromise;
}
return new Promise((resolve, reject) => {
this._wsReadySubsribers.push({
resolve,
reject
});
});
};
this.webLogin = async (envId, refresh) => {
if (!refresh) {
if (envId) {
const loginInfo = this._logins.get(envId);
if (loginInfo) {
if (loginInfo.loggedIn && loginInfo.loginResult) {
return loginInfo.loginResult;
}
else if (loginInfo.loggingInPromise) {
return loginInfo.loggingInPromise;
}
}
}
else {
const emptyEnvLoginInfo = this._logins.get('');
if (emptyEnvLoginInfo && emptyEnvLoginInfo.loggingInPromise) {
return emptyEnvLoginInfo.loggingInPromise;
}
}
}
const promise = new Promise(async (resolve, reject) => {
try {
const wsSign = await this.getWsSign();
const msgData = {
envId: wsSign.envId || '',
accessToken: '',
referrer: 'web',
sdkVersion: '',
dataVersion: __1.Db.dataVersion || ''
};
const loginMsg = {
watchId: undefined,
requestId: message_1.genRequestId(),
msgType: 'LOGIN',
msgData,
exMsgData: {
runtime: __1.Db.runtime,
signStr: wsSign.signStr,
secretVersion: wsSign.secretVersion
}
};
const loginResMsg = await this.send({
msg: loginMsg,
waitResponse: true,
skipOnMessage: true,
timeout: DEFAULT_LOGIN_TIMEOUT
});
if (!loginResMsg.msgData.code) {
resolve({
envId: wsSign.envId
});
}
else {
reject(new Error(`${loginResMsg.msgData.code} ${loginResMsg.msgData.message}`));
}
}
catch (e) {
reject(e);
}
});
let loginInfo = envId && this._logins.get(envId);
const loginStartTS = Date.now();
if (loginInfo) {
loginInfo.loggedIn = false;
loginInfo.loggingInPromise = promise;
loginInfo.loginStartTS = loginStartTS;
}
else {
loginInfo = {
loggedIn: false,
loggingInPromise: promise,
loginStartTS
};
this._logins.set(envId || '', loginInfo);
}
try {
const loginResult = await promise;
const curLoginInfo = envId && this._logins.get(envId);
if (curLoginInfo &&
curLoginInfo === loginInfo &&
curLoginInfo.loginStartTS === loginStartTS) {
loginInfo.loggedIn = true;
loginInfo.loggingInPromise = undefined;
loginInfo.loginStartTS = undefined;
loginInfo.loginResult = loginResult;
return loginResult;
}
else if (curLoginInfo) {
if (curLoginInfo.loggedIn && curLoginInfo.loginResult) {
return curLoginInfo.loginResult;
}
else if (curLoginInfo.loggingInPromise) {
return curLoginInfo.loggingInPromise;
}
else {
throw new Error('ws unexpected login info');
}
}
else {
throw new Error('ws login info reset');
}
}
catch (e) {
loginInfo.loggedIn = false;
loginInfo.loggingInPromise = undefined;
loginInfo.loginStartTS = undefined;
loginInfo.loginResult = undefined;
throw e;
}
};
this.getWsSign = async () => {
if (this._wsSign && this._wsSign.expiredTs > Date.now()) {
return this._wsSign;
}
const expiredTs = Date.now() + 60000;
const res = await this._context.appConfig.request.send('auth.wsWebSign', { runtime: __1.Db.runtime });
if (res.code) {
throw new Error(`[tcb-js-sdk] 获取实时数据推送登录票据失败: ${res.code}`);
}
if (res.data) {
const { signStr, wsUrl, secretVersion, envId } = res.data;
return {
signStr,
wsUrl,
secretVersion,
envId,
expiredTs
};
}
else {
throw new Error('[tcb-js-sdk] 获取实时数据推送登录票据失败');
}
};
this.getWaitExpectedTimeoutLength = () => {
if (!this._rttObserved.length) {
return DEFAULT_EXPECTED_EVENT_WAIT_TIME;
}
return ((this._rttObserved.reduce((acc, cur) => acc + cur) /
this._rttObserved.length) *
1.5);
};
this.ping = async () => {
const msg = {
watchId: undefined,
requestId: message_1.genRequestId(),
msgType: 'PING',
msgData: null
};
await this.send({
msg
});
};
this.send = async (opts) => new Promise(async (_resolve, _reject) => {
let timeoutId;
let _hasResolved = false;
let _hasRejected = false;
const resolve = (value) => {
_hasResolved = true;
timeoutId && clearTimeout(timeoutId);
_resolve(value);
};
const reject = (error) => {
_hasRejected = true;
timeoutId && clearTimeout(timeoutId);
_reject(error);
};
if (opts.timeout) {
timeoutId = setTimeout(async () => {
if (!_hasResolved || !_hasRejected) {
await utils_1.sleep(0);
if (!_hasResolved || !_hasRejected) {
reject(new error_1.TimeoutError('wsclient.send timedout'));
}
}
}, opts.timeout);
}
try {
if (this._wsInitPromise) {
await this._wsInitPromise;
}
if (!this._ws) {
reject(new Error('invalid state: ws connection not exists, can not send message'));
return;
}
if (this._ws.readyState !== WS_READY_STATE.OPEN) {
reject(new Error(`ws readyState invalid: ${this._ws.readyState}, can not send message`));
return;
}
if (opts.waitResponse) {
this._wsResponseWait.set(opts.msg.requestId, {
resolve,
reject,
skipOnMessage: opts.skipOnMessage
});
}
try {
await this._ws.send(JSON.stringify(opts.msg));
if (!opts.waitResponse) {
resolve();
}
}
catch (err) {
if (err) {
reject(err);
if (opts.waitResponse) {
this._wsResponseWait.delete(opts.msg.requestId);
}
}
}
}
catch (e) {
reject(e);
}
});
this.closeAllClients = (error) => {
this._virtualWSClient.forEach(client => {
client.closeWithError(error);
});
};
this.pauseClients = (clients) => {
;
(clients || this._virtualWSClient).forEach(client => {
client.pause();
});
};
this.resumeClients = (clients) => {
;
(clients || this._virtualWSClient).forEach(client => {
client.resume();
});
};
this.onWatchStart = (client, queryID) => {
this._queryIdClientMap.set(queryID, client);
};
this.onWatchClose = (client, queryID) => {
if (queryID) {
this._queryIdClientMap.delete(queryID);
}
this._watchIdClientMap.delete(client.watchId);
this._virtualWSClient.delete(client);
if (!this._virtualWSClient.size) {
this.close(ws_event_1.CLOSE_EVENT_CODE.NoRealtimeListeners);
}
};
this._maxReconnect = options.maxReconnect || DEFAULT_MAX_RECONNECT;
this._reconnectInterval =
options.reconnectInterval || DEFAULT_WS_RECONNECT_INTERVAL;
this._context = options.context;
}
heartbeat(immediate) {
this.clearHeartbeat();
this._pingTimeoutId = setTimeout(async () => {
try {
if (!this._ws || this._ws.readyState !== WS_READY_STATE.OPEN) {
return;
}
this._lastPingSendTS = Date.now();
await this.ping();
this._pingFailed = 0;
this._pongTimeoutId = setTimeout(() => {
console.error('pong timed out');
if (this._pongMissed < DEFAULT_PONG_MISS_TOLERANCE) {
this._pongMissed++;
this.heartbeat(true);
}
else {
this.initWebSocketConnection(true);
}
}, this._context.appConfig.realtimePongWaitTimeout);
}
catch (e) {
if (this._pingFailed < DEFAULT_PING_FAIL_TOLERANCE) {
this._pingFailed++;
this.heartbeat();
}
else {
this.close(ws_event_1.CLOSE_EVENT_CODE.HeartbeatPingError);
}
}
}, immediate ? 0 : this._context.appConfig.realtimePingInterval);
}
clearHeartbeat() {
this._pingTimeoutId && clearTimeout(this._pingTimeoutId);
this._pongTimeoutId && clearTimeout(this._pongTimeoutId);
}
close(code) {
this.clearHeartbeat();
if (this._ws) {
this._ws.close(code, ws_event_1.CLOSE_EVENT_CODE_INFO[code].name);
this._ws = undefined;
}
}
watch(options) {
if (!this._ws && !this._wsInitPromise) {
this.initWebSocketConnection(false);
}
const virtualClient = new virtual_websocket_client_1.VirtualWebSocketClient(Object.assign(Object.assign({}, options), { send: this.send, login: this.webLogin, isWSConnected: this.isWSConnected, onceWSConnected: this.onceWSConnected, getWaitExpectedTimeoutLength: this.getWaitExpectedTimeoutLength, onWatchStart: this.onWatchStart, onWatchClose: this.onWatchClose, debug: true }));
this._virtualWSClient.add(virtualClient);
this._watchIdClientMap.set(virtualClient.watchId, virtualClient);
return virtualClient.listener;
}
}
exports.RealtimeWebSocketClient = RealtimeWebSocketClient;

@ -0,0 +1,126 @@
import { CloudSDKError } from '../utils/error';
export declare const CLOSE_EVENT_CODE_INFO: {
1000: {
code: number;
name: string;
description: string;
};
1001: {
code: number;
name: string;
description: string;
};
1002: {
code: number;
name: string;
description: string;
};
1003: {
code: number;
name: string;
description: string;
};
1005: {
code: number;
name: string;
description: string;
};
1006: {
code: number;
name: string;
description: string;
};
1007: {
code: number;
name: string;
description: string;
};
1008: {
code: number;
name: string;
description: string;
};
1009: {
code: number;
name: string;
description: string;
};
1010: {
code: number;
name: string;
description: string;
};
1011: {
code: number;
name: string;
description: string;
};
1012: {
code: number;
name: string;
description: string;
};
1013: {
code: number;
name: string;
description: string;
};
1014: {
code: number;
name: string;
description: string;
};
1015: {
code: number;
name: string;
description: string;
};
3000: {
code: number;
name: string;
description: string;
};
3001: {
code: number;
name: string;
description: string;
};
3002: {
code: number;
name: string;
description: string;
};
3003: {
code: number;
name: string;
description: string;
};
3050: {
code: number;
name: string;
description: string;
};
};
export declare enum CLOSE_EVENT_CODE {
NormalClosure = 1000,
GoingAway = 1001,
ProtocolError = 1002,
UnsupportedData = 1003,
NoStatusReceived = 1005,
AbnormalClosure = 1006,
InvalidFramePayloadData = 1007,
PolicyViolation = 1008,
MessageTooBig = 1009,
MissingExtension = 1010,
InternalError = 1011,
ServiceRestart = 1012,
TryAgainLater = 1013,
BadGateway = 1014,
TLSHandshake = 1015,
ReconnectWebSocket = 3000,
NoRealtimeListeners = 3001,
HeartbeatPingError = 3002,
HeartbeatPongTimeoutError = 3003,
NoAuthentication = 3050
}
export declare const getWSCloseError: (code: CLOSE_EVENT_CODE, reason?: string) => CloudSDKError;

@ -0,0 +1,139 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const error_1 = require("../utils/error");
const error_config_1 = require("../config/error.config");
exports.CLOSE_EVENT_CODE_INFO = {
1000: {
code: 1000,
name: 'Normal Closure',
description: 'Normal closure; the connection successfully completed whatever purpose for which it was created.'
},
1001: {
code: 1001,
name: 'Going Away',
description: 'The endpoint is going away, either because of a server failure or because the browser is navigating away from the page that opened the connection.'
},
1002: {
code: 1002,
name: 'Protocol Error',
description: 'The endpoint is terminating the connection due to a protocol error.'
},
1003: {
code: 1003,
name: 'Unsupported Data',
description: 'The connection is being terminated because the endpoint received data of a type it cannot accept (for example, a text-only endpoint received binary data).'
},
1005: {
code: 1005,
name: 'No Status Received',
description: 'Indicates that no status code was provided even though one was expected.'
},
1006: {
code: 1006,
name: 'Abnormal Closure',
description: 'Used to indicate that a connection was closed abnormally (that is, with no close frame being sent) when a status code is expected.'
},
1007: {
code: 1007,
name: 'Invalid frame payload data',
description: 'The endpoint is terminating the connection because a message was received that contained inconsistent data (e.g., non-UTF-8 data within a text message).'
},
1008: {
code: 1008,
name: 'Policy Violation',
description: 'The endpoint is terminating the connection because it received a message that violates its policy. This is a generic status code, used when codes 1003 and 1009 are not suitable.'
},
1009: {
code: 1009,
name: 'Message too big',
description: 'The endpoint is terminating the connection because a data frame was received that is too large.'
},
1010: {
code: 1010,
name: 'Missing Extension',
description: "The client is terminating the connection because it expected the server to negotiate one or more extension, but the server didn't."
},
1011: {
code: 1011,
name: 'Internal Error',
description: 'The server is terminating the connection because it encountered an unexpected condition that prevented it from fulfilling the request.'
},
1012: {
code: 1012,
name: 'Service Restart',
description: 'The server is terminating the connection because it is restarting.'
},
1013: {
code: 1013,
name: 'Try Again Later',
description: 'The server is terminating the connection due to a temporary condition, e.g. it is overloaded and is casting off some of its clients.'
},
1014: {
code: 1014,
name: 'Bad Gateway',
description: 'The server was acting as a gateway or proxy and received an invalid response from the upstream server. This is similar to 502 HTTP Status Code.'
},
1015: {
code: 1015,
name: 'TLS Handshake',
description: "Indicates that the connection was closed due to a failure to perform a TLS handshake (e.g., the server certificate can't be verified)."
},
3000: {
code: 3000,
name: 'Reconnect WebSocket',
description: 'The client is terminating the connection because it wants to reconnect'
},
3001: {
code: 3001,
name: 'No Realtime Listeners',
description: 'The client is terminating the connection because no more realtime listeners exist'
},
3002: {
code: 3002,
name: 'Heartbeat Ping Error',
description: 'The client is terminating the connection due to its failure in sending heartbeat messages'
},
3003: {
code: 3003,
name: 'Heartbeat Pong Timeout Error',
description: 'The client is terminating the connection because no heartbeat response is received from the server'
},
3050: {
code: 3050,
name: 'Server Close',
description: 'The client is terminating the connection because no heartbeat response is received from the server'
}
};
var CLOSE_EVENT_CODE;
(function (CLOSE_EVENT_CODE) {
CLOSE_EVENT_CODE[CLOSE_EVENT_CODE["NormalClosure"] = 1000] = "NormalClosure";
CLOSE_EVENT_CODE[CLOSE_EVENT_CODE["GoingAway"] = 1001] = "GoingAway";
CLOSE_EVENT_CODE[CLOSE_EVENT_CODE["ProtocolError"] = 1002] = "ProtocolError";
CLOSE_EVENT_CODE[CLOSE_EVENT_CODE["UnsupportedData"] = 1003] = "UnsupportedData";
CLOSE_EVENT_CODE[CLOSE_EVENT_CODE["NoStatusReceived"] = 1005] = "NoStatusReceived";
CLOSE_EVENT_CODE[CLOSE_EVENT_CODE["AbnormalClosure"] = 1006] = "AbnormalClosure";
CLOSE_EVENT_CODE[CLOSE_EVENT_CODE["InvalidFramePayloadData"] = 1007] = "InvalidFramePayloadData";
CLOSE_EVENT_CODE[CLOSE_EVENT_CODE["PolicyViolation"] = 1008] = "PolicyViolation";
CLOSE_EVENT_CODE[CLOSE_EVENT_CODE["MessageTooBig"] = 1009] = "MessageTooBig";
CLOSE_EVENT_CODE[CLOSE_EVENT_CODE["MissingExtension"] = 1010] = "MissingExtension";
CLOSE_EVENT_CODE[CLOSE_EVENT_CODE["InternalError"] = 1011] = "InternalError";
CLOSE_EVENT_CODE[CLOSE_EVENT_CODE["ServiceRestart"] = 1012] = "ServiceRestart";
CLOSE_EVENT_CODE[CLOSE_EVENT_CODE["TryAgainLater"] = 1013] = "TryAgainLater";
CLOSE_EVENT_CODE[CLOSE_EVENT_CODE["BadGateway"] = 1014] = "BadGateway";
CLOSE_EVENT_CODE[CLOSE_EVENT_CODE["TLSHandshake"] = 1015] = "TLSHandshake";
CLOSE_EVENT_CODE[CLOSE_EVENT_CODE["ReconnectWebSocket"] = 3000] = "ReconnectWebSocket";
CLOSE_EVENT_CODE[CLOSE_EVENT_CODE["NoRealtimeListeners"] = 3001] = "NoRealtimeListeners";
CLOSE_EVENT_CODE[CLOSE_EVENT_CODE["HeartbeatPingError"] = 3002] = "HeartbeatPingError";
CLOSE_EVENT_CODE[CLOSE_EVENT_CODE["HeartbeatPongTimeoutError"] = 3003] = "HeartbeatPongTimeoutError";
CLOSE_EVENT_CODE[CLOSE_EVENT_CODE["NoAuthentication"] = 3050] = "NoAuthentication";
})(CLOSE_EVENT_CODE = exports.CLOSE_EVENT_CODE || (exports.CLOSE_EVENT_CODE = {}));
exports.getWSCloseError = (code, reason) => {
const info = exports.CLOSE_EVENT_CODE_INFO[code];
const errMsg = !info
? `code ${code}`
: `${info.name}, code ${code}, reason ${reason || info.description}`;
return new error_1.CloudSDKError({
errCode: error_config_1.ERR_CODE.SDK_DATABASE_REALTIME_LISTENER_WEBSOCKET_CONNECTION_CLOSED,
errMsg
});
};

@ -0,0 +1,18 @@
export declare class RegExp {
$regularExpression?: {
pattern?: string;
options?: string;
};
constructor({ regexp, options }: {
regexp: any;
options: any;
});
parse(): {
$regularExpression: {
pattern: string;
options: string;
};
};
readonly _internalType: import("../utils/symbol").InternalSymbol;
}
export declare function RegExpConstructor(param: any): RegExp;

@ -0,0 +1,30 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const symbol_1 = require("../helper/symbol");
class RegExp {
constructor({ regexp, options }) {
if (!regexp) {
throw new TypeError('regexp must be a string');
}
this.$regularExpression = {
pattern: regexp || '',
options: options || ''
};
}
parse() {
return {
$regularExpression: {
pattern: this.$regularExpression.pattern,
options: this.$regularExpression.options
}
};
}
get _internalType() {
return symbol_1.SYMBOL_REGEXP;
}
}
exports.RegExp = RegExp;
function RegExpConstructor(param) {
return new RegExp(param);
}
exports.RegExpConstructor = RegExpConstructor;

@ -0,0 +1,11 @@
import { LogicCommand } from '../commands/logic';
export declare type IQueryCondition = Record<string, any> | LogicCommand;
export declare type AnyObject = {
[x: string]: any;
};
export declare function flattenQueryObject(query: Record<string, any>): Record<string, any>;
export declare function flattenObject(object: AnyObject): AnyObject;
export declare function mergeConditionAfterEncode(query: Record<string, any>, condition: Record<string, any>, key: string): void;
export declare function isConversionRequired(val: any): boolean;
export declare function encodeInternalDataType(val: any): IQueryCondition;
export declare function decodeInternalDataType(object: AnyObject): any;

@ -0,0 +1,90 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const type_1 = require("../utils/type");
const datatype_1 = require("./datatype");
function flatten(query, shouldPreserverObject, parents, visited) {
const cloned = Object.assign({}, query);
for (const key in query) {
if (/^\$/.test(key))
continue;
const value = query[key];
if (value === undefined) {
delete cloned[key];
continue;
}
if (!value)
continue;
if (type_1.isObject(value) && !shouldPreserverObject(value)) {
if (visited.indexOf(value) > -1) {
throw new Error('Cannot convert circular structure to JSON');
}
const newParents = [...parents, key];
const newVisited = [...visited, value];
const flattenedChild = flatten(value, shouldPreserverObject, newParents, newVisited);
cloned[key] = flattenedChild;
let hasKeyNotCombined = false;
for (const childKey in flattenedChild) {
if (!/^\$/.test(childKey)) {
cloned[`${key}.${childKey}`] = flattenedChild[childKey];
delete cloned[key][childKey];
}
else {
hasKeyNotCombined = true;
}
}
if (!hasKeyNotCombined) {
delete cloned[key];
}
}
}
return cloned;
}
function flattenQueryObject(query) {
return flatten(query, isConversionRequired, [], [query]);
}
exports.flattenQueryObject = flattenQueryObject;
function flattenObject(object) {
return flatten(object, (_) => false, [], [object]);
}
exports.flattenObject = flattenObject;
function mergeConditionAfterEncode(query, condition, key) {
if (!condition[key]) {
delete query[key];
}
for (const conditionKey in condition) {
if (query[conditionKey]) {
if (type_1.isArray(query[conditionKey])) {
query[conditionKey].push(condition[conditionKey]);
}
else if (type_1.isObject(query[conditionKey])) {
if (type_1.isObject(condition[conditionKey])) {
Object.assign(query[conditionKey], condition[conditionKey]);
}
else {
console.warn(`unmergable condition, query is object but condition is ${type_1.getType(condition)}, can only overwrite`, condition, key);
query[conditionKey] = condition[conditionKey];
}
}
else {
console.warn(`to-merge query is of type ${type_1.getType(query)}, can only overwrite`, query, condition, key);
query[conditionKey] = condition[conditionKey];
}
}
else {
query[conditionKey] = condition[conditionKey];
}
}
}
exports.mergeConditionAfterEncode = mergeConditionAfterEncode;
function isConversionRequired(val) {
return type_1.isInternalObject(val) || type_1.isDate(val) || type_1.isRegExp(val);
}
exports.isConversionRequired = isConversionRequired;
function encodeInternalDataType(val) {
return datatype_1.serialize(val);
}
exports.encodeInternalDataType = encodeInternalDataType;
function decodeInternalDataType(object) {
return datatype_1.deserialize(object);
}
exports.decodeInternalDataType = decodeInternalDataType;

@ -0,0 +1,7 @@
import { LogicCommand } from '../commands/logic';
export declare type IQueryCondition = Record<string, any> | LogicCommand;
export declare type AnyObject = {
[x: string]: any;
};
export declare function serialize(val: any): IQueryCondition;
export declare function deserialize(object: AnyObject): any;

@ -0,0 +1,99 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const symbol_1 = require("../helper/symbol");
const type_1 = require("../utils/type");
const index_1 = require("../geo/index");
const index_2 = require("../serverDate/index");
function serialize(val) {
return serializeHelper(val, [val]);
}
exports.serialize = serialize;
function serializeHelper(val, visited) {
if (type_1.isInternalObject(val)) {
switch (val._internalType) {
case symbol_1.SYMBOL_GEO_POINT: {
return val.toJSON();
}
case symbol_1.SYMBOL_SERVER_DATE: {
return val.parse();
}
case symbol_1.SYMBOL_REGEXP: {
return val.parse();
}
case symbol_1.SYMBOL_OBJECTID: {
return val.parse();
}
default: {
return val.toJSON ? val.toJSON() : val;
}
}
}
else if (type_1.isDate(val)) {
return val;
}
else if (type_1.isRegExp(val)) {
return {
$regularExpression: {
pattern: val.source,
options: val.flags
}
};
}
else if (type_1.isArray(val)) {
return val.map(item => {
if (visited.indexOf(item) > -1) {
throw new Error('Cannot convert circular structure to JSON');
}
return serializeHelper(item, [...visited, item]);
});
}
else if (type_1.isObject(val)) {
const rawRet = Object.assign({}, val);
const finalRet = {};
for (const key in rawRet) {
if (visited.indexOf(rawRet[key]) > -1) {
throw new Error('Cannot convert circular structure to JSON');
}
if (rawRet[key] !== undefined) {
finalRet[key] = serializeHelper(rawRet[key], [...visited, rawRet[key]]);
}
}
return finalRet;
}
else {
return val;
}
}
function deserialize(object) {
const ret = Object.assign({}, object);
for (const key in ret) {
switch (key) {
case '$date': {
switch (type_1.getType(ret[key])) {
case 'number': {
return new Date(ret[key]);
}
case 'object': {
return new index_2.ServerDate(ret[key]);
}
}
break;
}
case 'type': {
switch (ret.type) {
case 'Point': {
if (type_1.isArray(ret.coordinates) &&
type_1.isNumber(ret.coordinates[0]) &&
type_1.isNumber(ret.coordinates[1])) {
return new index_1.Point(ret.coordinates[0], ret.coordinates[1]);
}
break;
}
}
break;
}
}
}
return object;
}
exports.deserialize = deserialize;

@ -0,0 +1,8 @@
import { QueryCommand } from '../commands/query';
import { LogicCommand } from '../commands/logic';
export declare type IQueryCondition = Record<string, any> | LogicCommand;
export declare class QuerySerializer {
constructor();
static encode(query: IQueryCondition | QueryCommand | LogicCommand): IQueryCondition;
static encodeEJSON(query: IQueryCondition | QueryCommand | LogicCommand, raw: boolean): string;
}

@ -0,0 +1,254 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const query_1 = require("../commands/query");
const logic_1 = require("../commands/logic");
const symbol_1 = require("../helper/symbol");
const type_1 = require("../utils/type");
const operator_map_1 = require("../operator-map");
const common_1 = require("./common");
const utils_1 = require("../utils/utils");
const validate_1 = require("../validate");
class QuerySerializer {
constructor() { }
static encode(query) {
const encoder = new QueryEncoder();
return encoder.encodeQuery(query);
}
static encodeEJSON(query, raw) {
const encoder = new QueryEncoder();
return utils_1.stringifyByEJSON(raw ? query : encoder.encodeQuery(query));
}
}
exports.QuerySerializer = QuerySerializer;
class QueryEncoder {
encodeQuery(query, key) {
if (common_1.isConversionRequired(query)) {
if (logic_1.isLogicCommand(query)) {
return this.encodeLogicCommand(query);
}
else if (query_1.isQueryCommand(query)) {
return this.encodeQueryCommand(query);
}
else if (type_1.isRegExp(query)) {
return { [key]: this.encodeRegExp(query) };
}
else if (type_1.isDate(query)) {
return { [key]: query };
}
else {
return { [key]: this.encodeQueryObject(query) };
}
}
else {
if (type_1.isObject(query)) {
return this.encodeQueryObject(query);
}
else {
return query;
}
}
}
encodeRegExp(query) {
return {
$regularExpression: {
pattern: query.source,
options: query.flags
}
};
}
encodeLogicCommand(query) {
switch (query.operator) {
case logic_1.LOGIC_COMMANDS_LITERAL.NOR:
case logic_1.LOGIC_COMMANDS_LITERAL.AND:
case logic_1.LOGIC_COMMANDS_LITERAL.OR: {
const $op = operator_map_1.operatorToString(query.operator);
const subqueries = query.operands.map((oprand) => this.encodeQuery(oprand, query.fieldName));
return {
[$op]: subqueries
};
}
case logic_1.LOGIC_COMMANDS_LITERAL.NOT: {
const $op = operator_map_1.operatorToString(query.operator);
const operatorExpression = query.operands[0];
if (type_1.isRegExp(operatorExpression)) {
return {
[query.fieldName]: {
[$op]: this.encodeRegExp(operatorExpression)
}
};
}
else {
const subqueries = this.encodeQuery(operatorExpression)[query.fieldName];
return {
[query.fieldName]: {
[$op]: subqueries
}
};
}
}
default: {
const $op = operator_map_1.operatorToString(query.operator);
if (query.operands.length === 1) {
const subquery = this.encodeQuery(query.operands[0]);
return {
[$op]: subquery
};
}
else {
const subqueries = query.operands.map(this.encodeQuery.bind(this));
return {
[$op]: subqueries
};
}
}
}
}
encodeQueryCommand(query) {
if (query_1.isComparisonCommand(query)) {
return this.encodeComparisonCommand(query);
}
else {
return this.encodeComparisonCommand(query);
}
}
encodeComparisonCommand(query) {
if (query.fieldName === symbol_1.SYMBOL_UNSET_FIELD_NAME) {
throw new Error('Cannot encode a comparison command with unset field name');
}
const $op = operator_map_1.operatorToString(query.operator);
switch (query.operator) {
case query_1.QUERY_COMMANDS_LITERAL.EQ:
case query_1.QUERY_COMMANDS_LITERAL.NEQ:
case query_1.QUERY_COMMANDS_LITERAL.LT:
case query_1.QUERY_COMMANDS_LITERAL.LTE:
case query_1.QUERY_COMMANDS_LITERAL.GT:
case query_1.QUERY_COMMANDS_LITERAL.GTE:
case query_1.QUERY_COMMANDS_LITERAL.ELEM_MATCH:
case query_1.QUERY_COMMANDS_LITERAL.EXISTS:
case query_1.QUERY_COMMANDS_LITERAL.SIZE:
case query_1.QUERY_COMMANDS_LITERAL.MOD: {
return {
[query.fieldName]: {
[$op]: common_1.encodeInternalDataType(query.operands[0])
}
};
}
case query_1.QUERY_COMMANDS_LITERAL.IN:
case query_1.QUERY_COMMANDS_LITERAL.NIN:
case query_1.QUERY_COMMANDS_LITERAL.ALL: {
return {
[query.fieldName]: {
[$op]: common_1.encodeInternalDataType(query.operands)
}
};
}
case query_1.QUERY_COMMANDS_LITERAL.GEO_NEAR: {
const options = query.operands[0];
return {
[query.fieldName]: {
$nearSphere: {
$geometry: options.geometry.toJSON(),
$maxDistance: options.maxDistance,
$minDistance: options.minDistance
}
}
};
}
case query_1.QUERY_COMMANDS_LITERAL.GEO_WITHIN: {
const options = query.operands[0];
if (options.centerSphere) {
validate_1.Validate.isCentersPhere(options.centerSphere);
const centerSphere = options.centerSphere;
if (centerSphere[0]._internalType === symbol_1.SYMBOL_GEO_POINT) {
return {
[query.fieldName]: {
$geoWithin: {
$centerSphere: [centerSphere[0].toJSON().coordinates, centerSphere[1]]
}
}
};
}
return {
[query.fieldName]: {
$geoWithin: {
$centerSphere: options.centerSphere
}
}
};
}
return {
[query.fieldName]: {
$geoWithin: {
$geometry: options.geometry.toJSON()
}
}
};
}
case query_1.QUERY_COMMANDS_LITERAL.GEO_INTERSECTS: {
const options = query.operands[0];
return {
[query.fieldName]: {
$geoIntersects: {
$geometry: options.geometry.toJSON()
}
}
};
}
default: {
return {
[query.fieldName]: {
[$op]: common_1.encodeInternalDataType(query.operands[0])
}
};
}
}
}
encodeQueryObject(query) {
const flattened = common_1.flattenQueryObject(query);
for (const key in flattened) {
const val = flattened[key];
if (logic_1.isLogicCommand(val)) {
flattened[key] = val._setFieldName(key);
const condition = this.encodeLogicCommand(flattened[key]);
this.mergeConditionAfterEncode(flattened, condition, key);
}
else if (query_1.isComparisonCommand(val)) {
flattened[key] = val._setFieldName(key);
const condition = this.encodeComparisonCommand(flattened[key]);
this.mergeConditionAfterEncode(flattened, condition, key);
}
else if (common_1.isConversionRequired(val)) {
flattened[key] = common_1.encodeInternalDataType(val);
}
}
return flattened;
}
mergeConditionAfterEncode(query, condition, key) {
if (!condition[key]) {
delete query[key];
}
for (const conditionKey in condition) {
if (query[conditionKey]) {
if (type_1.isArray(query[conditionKey])) {
query[conditionKey] = query[conditionKey].concat(condition[conditionKey]);
}
else if (type_1.isObject(query[conditionKey])) {
if (type_1.isObject(condition[conditionKey])) {
Object.assign(query, condition);
}
else {
console.warn(`unmergable condition, query is object but condition is ${type_1.getType(condition)}, can only overwrite`, condition, key);
query[conditionKey] = condition[conditionKey];
}
}
else {
console.warn(`to-merge query is of type ${type_1.getType(query)}, can only overwrite`, query, condition, key);
query[conditionKey] = condition[conditionKey];
}
}
else {
query[conditionKey] = condition[conditionKey];
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save