@tdengine/websocket 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -0
- package/dist/browser/index.js +778 -0
- package/dist/main/index.js +711 -0
- package/dist/main/index.js.map +1 -0
- package/dist/module/index.mjs +704 -0
- package/dist/module/index.mjs.map +1 -0
- package/dist/types.d.ts +103 -0
- package/dist/types.d.ts.map +1 -0
- package/example/basicUsageAsync.ts +48 -0
- package/example/basicUsagePrimse.ts +43 -0
- package/example/cloudUsage.ts +55 -0
- package/example/continousConnectAndVersion.ts +16 -0
- package/example/test.mjs +51 -0
- package/index.ts +7 -0
- package/jest.config.js +8 -0
- package/package.json +55 -0
- package/src/constant.ts +74 -0
- package/src/taosResult.ts +269 -0
- package/src/tdengineWebsocket.ts +39 -0
- package/src/ut8Helper.ts +42 -0
- package/src/wsClient.ts +196 -0
- package/src/wsError.ts +5 -0
- package/src/wsOptions.ts +14 -0
- package/src/wsQuery.ts +30 -0
- package/src/wsQueryInterface.ts +212 -0
- package/src/wsQueryResponse.ts +112 -0
- package/tdengine-websocket-3.0.0.tgz +0 -0
- package/test/bulkPulling/connect.test.ts +27 -0
- package/test/bulkPulling/queryTables.test.ts +274 -0
- package/test/bulkPulling/version.test.ts +19 -0
- package/test/utils.ts +235 -0
- package/tsconfig.json +101 -0
package/src/constant.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
interface IndexableString {
|
|
2
|
+
[index: number]: string
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
interface StringIndexable {
|
|
6
|
+
[index: string]: number
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const TDengineTypeName: IndexableString = {
|
|
10
|
+
0: 'NULL',
|
|
11
|
+
1: 'BOOL',
|
|
12
|
+
2: 'TINYINT',
|
|
13
|
+
3: 'SMALLINT',
|
|
14
|
+
4: 'INT',
|
|
15
|
+
5: 'BIGINT',
|
|
16
|
+
6: 'FLOAT',
|
|
17
|
+
7: 'DOUBLE',
|
|
18
|
+
8: 'VARCHAR',
|
|
19
|
+
9: 'TIMESTAMP',
|
|
20
|
+
10: 'NCHAR',
|
|
21
|
+
11: 'TINYINT UNSIGNED',
|
|
22
|
+
12: 'SMALLINT UNSIGNED',
|
|
23
|
+
13: 'INT UNSIGNED',
|
|
24
|
+
14: 'BIGINT UNSIGNED',
|
|
25
|
+
15: 'JSON',
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const ColumnsBlockType: StringIndexable = {
|
|
29
|
+
'SOLID': 0,
|
|
30
|
+
'VARCHAR': 1,
|
|
31
|
+
'NCHAR': 2
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
export const TDengineTypeCode: StringIndexable = {
|
|
36
|
+
'NULL': 0,
|
|
37
|
+
'BOOL': 1,
|
|
38
|
+
'TINYINT': 2,
|
|
39
|
+
'SMALLINT': 3,
|
|
40
|
+
'INT': 4,
|
|
41
|
+
'BIGINT': 5,
|
|
42
|
+
'FLOAT': 6,
|
|
43
|
+
'DOUBLE': 7,
|
|
44
|
+
'BINARY': 8,
|
|
45
|
+
'VARCHAR': 8,
|
|
46
|
+
'TIMESTAMP': 9,
|
|
47
|
+
'NCHAR': 10,
|
|
48
|
+
'TINYINT UNSIGNED': 11,
|
|
49
|
+
'SMALLINT UNSIGNED': 12,
|
|
50
|
+
'INT UNSIGNED': 13,
|
|
51
|
+
'BIGINT UNSIGNED': 14,
|
|
52
|
+
'JSON': 15,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const TDenginePrecision: IndexableString = {
|
|
56
|
+
0: 'MILLISECOND',
|
|
57
|
+
1: "MICROSECOND",
|
|
58
|
+
2: "NANOSECOND",
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const TDengineTypeLength: StringIndexable = {
|
|
62
|
+
'BOOL': 1,
|
|
63
|
+
'TINYINT': 1,
|
|
64
|
+
'SMALLINT': 2,
|
|
65
|
+
'INT': 4,
|
|
66
|
+
'BIGINT': 8,
|
|
67
|
+
'FLOAT': 4,
|
|
68
|
+
'DOUBLE': 8,
|
|
69
|
+
'TIMESTAMP': 8,
|
|
70
|
+
'TINYINT UNSIGNED': 1,
|
|
71
|
+
'SMALLINT UNSIGNED': 2,
|
|
72
|
+
'INT UNSIGNED': 4,
|
|
73
|
+
'BIGINT UNSIGNED': 8,
|
|
74
|
+
}
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { WSFetchBlockResponse, WSFetchResponse, WSQueryResponse } from "./wsQueryResponse";
|
|
2
|
+
import { ColumnsBlockType, TDengineTypeCode, TDengineTypeName } from './constant'
|
|
3
|
+
import { TaosResultError, WebSocketQueryInterFaceError } from "./wsError";
|
|
4
|
+
import { AppendRune } from "./ut8Helper"
|
|
5
|
+
|
|
6
|
+
export class TaosResult {
|
|
7
|
+
meta: Array<ResponseMeta> | null;
|
|
8
|
+
data: Array<Array<any>> | null;
|
|
9
|
+
precision: number = 0;
|
|
10
|
+
affectRows: number = 0;
|
|
11
|
+
/** unit nano seconds */
|
|
12
|
+
timing: bigint;
|
|
13
|
+
constructor(queryResponse: WSQueryResponse) {
|
|
14
|
+
if (queryResponse.is_update == true) {
|
|
15
|
+
this.meta = null
|
|
16
|
+
this.data = null
|
|
17
|
+
} else {
|
|
18
|
+
if (queryResponse.fields_count && queryResponse.fields_names && queryResponse.fields_types && queryResponse.fields_lengths) {
|
|
19
|
+
let _meta = [];
|
|
20
|
+
for (let i = 0; i < queryResponse.fields_count; i++) {
|
|
21
|
+
_meta.push({
|
|
22
|
+
name: queryResponse.fields_names[i],
|
|
23
|
+
type: queryResponse.fields_types[i],
|
|
24
|
+
length: queryResponse.fields_lengths[i]
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
this.meta = _meta;
|
|
28
|
+
} else {
|
|
29
|
+
throw new TaosResultError(`fields_count,fields_names,fields_types,fields_lengths of the update query response should be null`)
|
|
30
|
+
}
|
|
31
|
+
this.data = [];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
this.affectRows = queryResponse.affected_rows
|
|
35
|
+
this.timing = queryResponse.timing
|
|
36
|
+
this.precision = queryResponse.precision
|
|
37
|
+
// console.log(`typeof this.timing:${typeof this.timing}, typeof fetchResponse.timing:${typeof queryResponse.timing}`)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
setRows(fetchResponse: WSFetchResponse) {
|
|
41
|
+
this.affectRows += fetchResponse.rows;
|
|
42
|
+
// console.log(`typeof this.timing:${typeof this.timing}, typeof fetchResponse.timing:${typeof fetchResponse.timing}`)
|
|
43
|
+
this.timing = this.timing + fetchResponse.timing
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
setData(fetchBlockResponse: WSFetchBlockResponse) {
|
|
47
|
+
if (this.data) {
|
|
48
|
+
this.data.push([])
|
|
49
|
+
} else {
|
|
50
|
+
throw new TaosResultError(`update query response cannot set data`)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Mapping the WebSocket response type code to TDengine's type name.
|
|
56
|
+
*/
|
|
57
|
+
getTDengineMeta(): Array<TDengineMeta> | null {
|
|
58
|
+
if (this.meta) {
|
|
59
|
+
let _ = new Array<TDengineMeta>()
|
|
60
|
+
this.meta.forEach(m => {
|
|
61
|
+
_.push({
|
|
62
|
+
name: m.name,
|
|
63
|
+
type: TDengineTypeName[m.type],
|
|
64
|
+
length: m.length
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
return _;
|
|
68
|
+
} else {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
interface TDengineMeta {
|
|
76
|
+
name: string,
|
|
77
|
+
type: string,
|
|
78
|
+
length: number,
|
|
79
|
+
}
|
|
80
|
+
interface ResponseMeta {
|
|
81
|
+
name: string,
|
|
82
|
+
type: number,
|
|
83
|
+
length: number,
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function parseBlock(fetchResponse: WSFetchResponse, blocks: WSFetchBlockResponse, taosResult: TaosResult): TaosResult {
|
|
87
|
+
|
|
88
|
+
if (taosResult.meta && taosResult.data) {
|
|
89
|
+
let metaList = taosResult.meta;
|
|
90
|
+
|
|
91
|
+
// console.log(typeof taosResult.timing)
|
|
92
|
+
// console.log(typeof blocks.timing)
|
|
93
|
+
// console.log(blocks.id)
|
|
94
|
+
|
|
95
|
+
taosResult.timing = BigInt(taosResult.timing) + blocks.timing
|
|
96
|
+
|
|
97
|
+
const INT_32_SIZE = 4;
|
|
98
|
+
|
|
99
|
+
// Offset num of bytes from rawBlockBuffer.
|
|
100
|
+
let bufferOffset = (4 * 5) + 8 + (4 + 1) * metaList.length
|
|
101
|
+
let colLengthBlockSize = INT_32_SIZE * metaList.length
|
|
102
|
+
// console.log("===colLengthBlockSize:" + colLengthBlockSize)
|
|
103
|
+
|
|
104
|
+
let bitMapSize = (fetchResponse.rows + (1 << 3) - 1) >> 3
|
|
105
|
+
|
|
106
|
+
// whole raw block ArrayBuffer
|
|
107
|
+
let dataBuffer = blocks.data.slice(bufferOffset);
|
|
108
|
+
|
|
109
|
+
// record the head of column in block
|
|
110
|
+
let colBlockHead = 0;
|
|
111
|
+
for (let i = 0; i < fetchResponse.rows; i++) {
|
|
112
|
+
let row = [];
|
|
113
|
+
// point to the head of the column in the block
|
|
114
|
+
colBlockHead = 0 + colLengthBlockSize;
|
|
115
|
+
// point to the head of columns's data in the block (include bitMap and offsetArray)
|
|
116
|
+
let colDataHead = colBlockHead;
|
|
117
|
+
// traverse row after row.
|
|
118
|
+
for (let j = 0; j < metaList.length; j++) {
|
|
119
|
+
|
|
120
|
+
let isVarType = _isVarTye(metaList[j])
|
|
121
|
+
// console.log("== dataBuffer Length:" + dataBuffer.byteLength)
|
|
122
|
+
// console.log("== loop i:" + i + "J=" + j + "col:" + metaList[j].name + "type:" + metaList[j].type)
|
|
123
|
+
// console.log("== loop isVarType:" + isVarType);
|
|
124
|
+
if (isVarType == ColumnsBlockType.SOLID) {
|
|
125
|
+
|
|
126
|
+
colDataHead = colBlockHead + bitMapSize + metaList[j].length * i
|
|
127
|
+
|
|
128
|
+
let byteArrayIndex = i >> 3;
|
|
129
|
+
let bitwiseOffset = 7 - (i & 7)
|
|
130
|
+
let bitMapArr = dataBuffer.slice(colBlockHead, colBlockHead + bitMapSize)
|
|
131
|
+
// console.log("==i:" + i + "byteArrayIndex=" + byteArrayIndex)
|
|
132
|
+
// console.log("== loop colblockhead:" + colBlockHead)
|
|
133
|
+
// console.log("== loop bitmap:" + bitMapSize)
|
|
134
|
+
// console.log("== loop bitMap length=" + bitMapArr.byteLength)
|
|
135
|
+
// console.log("==loop bitMap bitwiseoffset:" + bitwiseOffset + "byteArrayIndex:" + byteArrayIndex)
|
|
136
|
+
let bitFlag = ((new DataView(bitMapArr).getUint8(byteArrayIndex)) & (1 << bitwiseOffset)) >> bitwiseOffset
|
|
137
|
+
|
|
138
|
+
if (bitFlag == 1) {
|
|
139
|
+
row.push("NULL")
|
|
140
|
+
} else {
|
|
141
|
+
row.push(readSolidData(dataBuffer, colDataHead, metaList[j]))
|
|
142
|
+
}
|
|
143
|
+
// console.log("=====(new DataView(dataBuffer, INT_32_SIZE * j, INT_32_SIZE).getInt32(0))=" + (new DataView(dataBuffer, INT_32_SIZE * j, INT_32_SIZE).getInt32(0, true)));
|
|
144
|
+
colBlockHead = colBlockHead + bitMapSize + (new DataView(dataBuffer, INT_32_SIZE * j, INT_32_SIZE).getInt32(0, true))
|
|
145
|
+
|
|
146
|
+
} else {
|
|
147
|
+
// if null check
|
|
148
|
+
let varOffset = new DataView(dataBuffer, colBlockHead + (INT_32_SIZE * i), INT_32_SIZE).getInt32(0, true)
|
|
149
|
+
// console.log("== var type offset:" + varOffset)
|
|
150
|
+
if (varOffset == -1) {
|
|
151
|
+
row.push("NULL")
|
|
152
|
+
colBlockHead = colBlockHead + INT_32_SIZE * fetchResponse.rows + (new DataView(dataBuffer, j * INT_32_SIZE, INT_32_SIZE).getInt32(0, true))
|
|
153
|
+
} else {
|
|
154
|
+
colDataHead = colBlockHead + INT_32_SIZE * fetchResponse.rows + varOffset
|
|
155
|
+
let dataLength = (new DataView(dataBuffer, colDataHead, 2).getInt16(0, true))
|
|
156
|
+
// console.log("== loop var type length:" + dataLength)
|
|
157
|
+
if (isVarType == ColumnsBlockType.VARCHAR) {
|
|
158
|
+
|
|
159
|
+
row.push(readVarchar(dataBuffer, colDataHead + 2, dataLength))
|
|
160
|
+
|
|
161
|
+
} else {
|
|
162
|
+
row.push(readNchar(dataBuffer, colDataHead + 2, dataLength))
|
|
163
|
+
}
|
|
164
|
+
colBlockHead = colBlockHead + INT_32_SIZE * fetchResponse.rows + (new DataView(dataBuffer, j * INT_32_SIZE, INT_32_SIZE).getInt32(0, true))
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
taosResult.data.push(row);
|
|
169
|
+
}
|
|
170
|
+
return taosResult;
|
|
171
|
+
} else {
|
|
172
|
+
throw new TaosResultError("cannot fetch block for an update query.")
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function _isVarTye(meta: ResponseMeta): Number {
|
|
178
|
+
switch (meta.type) {
|
|
179
|
+
case TDengineTypeCode['NCHAR']: {
|
|
180
|
+
return ColumnsBlockType['NCHAR']
|
|
181
|
+
}
|
|
182
|
+
case TDengineTypeCode['VARCHAR']: {
|
|
183
|
+
return ColumnsBlockType['VARCHAR']
|
|
184
|
+
}
|
|
185
|
+
case TDengineTypeCode['BINARY']: {
|
|
186
|
+
return ColumnsBlockType['VARCHAR']
|
|
187
|
+
}
|
|
188
|
+
case TDengineTypeCode['JSON']: {
|
|
189
|
+
return ColumnsBlockType['VARCHAR']
|
|
190
|
+
}
|
|
191
|
+
default: {
|
|
192
|
+
return ColumnsBlockType['SOLID']
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function readSolidData(dataBuffer: ArrayBuffer, colDataHead: number, meta: ResponseMeta): Number | Boolean | BigInt {
|
|
198
|
+
|
|
199
|
+
switch (meta.type) {
|
|
200
|
+
case TDengineTypeCode['BOOL']: {
|
|
201
|
+
return (Boolean)(new DataView(dataBuffer, colDataHead, 1).getInt8(0))
|
|
202
|
+
}
|
|
203
|
+
case TDengineTypeCode['TINYINT']: {
|
|
204
|
+
return (new DataView(dataBuffer, colDataHead, 1).getInt8(0))
|
|
205
|
+
}
|
|
206
|
+
case TDengineTypeCode['SMALLINT']: {
|
|
207
|
+
return (new DataView(dataBuffer, colDataHead, 2).getInt16(0, true))
|
|
208
|
+
}
|
|
209
|
+
case TDengineTypeCode['INT']: {
|
|
210
|
+
return (new DataView(dataBuffer, colDataHead, 4).getInt32(0, true))
|
|
211
|
+
}
|
|
212
|
+
case TDengineTypeCode['BIGINT']: {
|
|
213
|
+
return (new DataView(dataBuffer, colDataHead, 8).getBigInt64(0, true))
|
|
214
|
+
}
|
|
215
|
+
case TDengineTypeCode['TINYINT UNSIGNED']: {
|
|
216
|
+
return (new DataView(dataBuffer, colDataHead, 1).getUint8(0))
|
|
217
|
+
}
|
|
218
|
+
case TDengineTypeCode['SMALLINT UNSIGNED']: {
|
|
219
|
+
return (new DataView(dataBuffer, colDataHead, 2).getUint16(0, true))
|
|
220
|
+
}
|
|
221
|
+
case TDengineTypeCode['INT UNSIGNED']: {
|
|
222
|
+
return (new DataView(dataBuffer, colDataHead, 4).getUint32(0, true))
|
|
223
|
+
}
|
|
224
|
+
case TDengineTypeCode['BIGINT UNSIGNED']: {
|
|
225
|
+
return (new DataView(dataBuffer, colDataHead, 8).getBigUint64(0, true))
|
|
226
|
+
}
|
|
227
|
+
case TDengineTypeCode['FLOAT']: {
|
|
228
|
+
return (parseFloat(new DataView(dataBuffer, colDataHead, 4).getFloat32(0, true).toFixed(5)) )
|
|
229
|
+
}
|
|
230
|
+
case TDengineTypeCode['DOUBLE']: {
|
|
231
|
+
return (parseFloat(new DataView(dataBuffer, colDataHead, 8).getFloat64(0, true).toFixed(15)))
|
|
232
|
+
}
|
|
233
|
+
case TDengineTypeCode['TIMESTAMP']: {
|
|
234
|
+
return (new DataView(dataBuffer, colDataHead, 8).getBigInt64(0, true))
|
|
235
|
+
// could change
|
|
236
|
+
}
|
|
237
|
+
default: {
|
|
238
|
+
throw new WebSocketQueryInterFaceError(`unspported type ${meta.type} for column ${meta.name}`)
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
function readVarchar(dataBuffer: ArrayBuffer, colDataHead: number, length: number): string {
|
|
245
|
+
let data = "";
|
|
246
|
+
data += new TextDecoder().decode(dataBuffer.slice(colDataHead, colDataHead + length))
|
|
247
|
+
return data;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function readNchar(dataBuffer: ArrayBuffer, colDataHead: number, length: number): string {
|
|
251
|
+
let decoder = new TextDecoder();
|
|
252
|
+
let data = "";
|
|
253
|
+
let buff: ArrayBuffer = dataBuffer.slice(colDataHead, colDataHead + length);
|
|
254
|
+
for (let i = 0; i < length / 4; i++) {
|
|
255
|
+
// console.log("== readNchar data:" + new DataView(buff, i * 4, 4).getUint32(0, true))
|
|
256
|
+
data += AppendRune(new DataView(buff, i * 4, 4).getUint32(0, true))
|
|
257
|
+
|
|
258
|
+
}
|
|
259
|
+
return data;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
function iteratorBuff(arr: ArrayBuffer) {
|
|
264
|
+
let buf = Buffer.from(arr);
|
|
265
|
+
for (const value of buf) {
|
|
266
|
+
console.log(value.toString())
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
|
|
2
|
+
import { execute } from './wsQuery'
|
|
3
|
+
import { TaosResult } from './taosResult'
|
|
4
|
+
import { WSInterface } from './wsQueryInterface'
|
|
5
|
+
import { WSConnResponse } from './wsQueryResponse'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
export class TDengineWebSocket {
|
|
9
|
+
_wsInterface: WSInterface
|
|
10
|
+
_data: Array<any> = []
|
|
11
|
+
_meta: Array<any> = []
|
|
12
|
+
|
|
13
|
+
constructor(url: string) {
|
|
14
|
+
this._wsInterface = new WSInterface(new URL(url))
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
connect(database?:string):Promise<WSConnResponse> {
|
|
18
|
+
return this._wsInterface.connect(database)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
state(){
|
|
22
|
+
return this._wsInterface.getState();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* return client version.
|
|
27
|
+
*/
|
|
28
|
+
version(): Promise<string> {
|
|
29
|
+
return this._wsInterface.version()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
query(sql:string):Promise<TaosResult>{
|
|
33
|
+
return execute(sql,this._wsInterface)
|
|
34
|
+
}
|
|
35
|
+
close() {
|
|
36
|
+
this._wsInterface.close();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
}
|
package/src/ut8Helper.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Numbers fundamental to the encoding.
|
|
2
|
+
// the "error" Rune or "Unicode replacement character"
|
|
3
|
+
const RuneError = '\uFFFD'
|
|
4
|
+
// Maximum valid Unicode code point.
|
|
5
|
+
const MaxRune = '\U0010FFFF'
|
|
6
|
+
// Code points in the surrogate range are not valid for UTF-8.
|
|
7
|
+
const surrogateMin = 0xD800
|
|
8
|
+
const surrogateMax = 0xDFFF
|
|
9
|
+
const tx = 128;
|
|
10
|
+
const t2 = 192;
|
|
11
|
+
const t3 = 224;
|
|
12
|
+
const t4 = 240;
|
|
13
|
+
const maskx = 63;
|
|
14
|
+
const rune1Max = (1 << 7) - 1;
|
|
15
|
+
|
|
16
|
+
const rune2Max = (1 << 11) - 1;
|
|
17
|
+
const rune3Max = (1 << 16) - 1;
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
// AppendRune appends the UTF-8 encoding of r to the end of p and
|
|
21
|
+
// returns the extended buffer. If the rune is out of range,
|
|
22
|
+
// it appends the encoding of RuneError.
|
|
23
|
+
export function AppendRune(r:any) {
|
|
24
|
+
let p:Array<any> = [];
|
|
25
|
+
// console.log("== AppendRun r:");
|
|
26
|
+
// console.log(r)
|
|
27
|
+
if (r <= rune1Max) {
|
|
28
|
+
p.push(r & 0xff);
|
|
29
|
+
return Buffer.from(p).toString();
|
|
30
|
+
}
|
|
31
|
+
if (r <= rune2Max) {
|
|
32
|
+
p.push(t2 | ((r >> 6) & 0xff), tx | (r & 0xff) & maskx)
|
|
33
|
+
} else if ((r > MaxRune) || (surrogateMax <= r && r <= surrogateMax)) {
|
|
34
|
+
p.push(RuneError)
|
|
35
|
+
} else if (r <= rune3Max) {
|
|
36
|
+
p.push(t3 | ((r >> 12) & 0xff), tx | ((r >> 6) & 0xff) & maskx, tx | (r & 0xff) & maskx)
|
|
37
|
+
} else {
|
|
38
|
+
p.push(t4 | ((r >> 18) & 0xff), tx | ((r >> 12) & 0xff) & maskx, tx | ((r >> 6) & 0xff) & maskx, tx | (r & 0xff) & maskx)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return Buffer.from(p).toString();
|
|
42
|
+
}
|
package/src/wsClient.ts
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { ICloseEvent, IMessageEvent, w3cwebsocket } from "websocket";
|
|
2
|
+
import { TDWebSocketClientError, WebSocketQueryError } from './wsError'
|
|
3
|
+
|
|
4
|
+
interface MessageId {
|
|
5
|
+
action: string,
|
|
6
|
+
req_id: bigint,
|
|
7
|
+
id?: bigint
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface MessageAction {
|
|
11
|
+
reject: Function,
|
|
12
|
+
resolve: Function,
|
|
13
|
+
timer: ReturnType<typeof setTimeout>,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
var _msgActionRegister: Map<MessageId, MessageAction> = new Map();
|
|
17
|
+
|
|
18
|
+
export class TDWebSocketClient {
|
|
19
|
+
private _wsConn: w3cwebsocket;
|
|
20
|
+
_wsURL: URL;
|
|
21
|
+
_timeout = 5000;
|
|
22
|
+
|
|
23
|
+
// create ws
|
|
24
|
+
constructor(url: URL) {
|
|
25
|
+
|
|
26
|
+
// return w3bsocket3
|
|
27
|
+
if (url) {
|
|
28
|
+
this._wsURL = url;
|
|
29
|
+
let origin = url.origin;
|
|
30
|
+
let pathname = url.pathname;
|
|
31
|
+
let search = url.search;
|
|
32
|
+
|
|
33
|
+
this._wsConn = new w3cwebsocket(origin.concat(pathname).concat(search));
|
|
34
|
+
|
|
35
|
+
// this._wsConn.onopen = this._onopen
|
|
36
|
+
|
|
37
|
+
this._wsConn.onerror = function (err: Error) { throw err }
|
|
38
|
+
|
|
39
|
+
this._wsConn.onclose = this._onclose
|
|
40
|
+
|
|
41
|
+
this._wsConn.onmessage = this._onmessage
|
|
42
|
+
this._wsConn._binaryType = "arraybuffer"
|
|
43
|
+
} else {
|
|
44
|
+
throw new WebSocketQueryError("websocket URL must be defined")
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
Ready(): Promise<TDWebSocketClient> {
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
this._wsConn.onopen = () => {
|
|
53
|
+
// console.log("websocket connection opened")
|
|
54
|
+
resolve(this);
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private _onclose(e: ICloseEvent) {
|
|
60
|
+
return new Promise((resolve, reject) => {
|
|
61
|
+
resolve("websocket connection closed")
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
private _onmessage(event: any) {
|
|
67
|
+
let data = event.data;
|
|
68
|
+
// console.log("[wsClient._onMessage()._msgActionRegister]\n")
|
|
69
|
+
// console.log(_msgActionRegister)
|
|
70
|
+
|
|
71
|
+
// console.log("===="+ (Object.prototype.toString.call(data)))
|
|
72
|
+
|
|
73
|
+
if (Object.prototype.toString.call(data) === '[object ArrayBuffer]') {
|
|
74
|
+
let id = new DataView(data, 8, 8).getBigUint64(0, true)
|
|
75
|
+
// console.log("fetch block response id:" + id)
|
|
76
|
+
|
|
77
|
+
let action: MessageAction | any = undefined;
|
|
78
|
+
|
|
79
|
+
_msgActionRegister.forEach((v: MessageAction, k: MessageId) => {
|
|
80
|
+
if (k.id == id) {
|
|
81
|
+
action = v
|
|
82
|
+
_msgActionRegister.delete(k)
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
if (action) {
|
|
86
|
+
action.resolve(data);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
_msgActionRegister.clear()
|
|
90
|
+
throw new TDWebSocketClientError(`no callback registered for fetch_block with id=${id}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
} else if (Object.prototype.toString.call(data) === '[object Blob]') {
|
|
94
|
+
data.arrayBuffer().then((d: ArrayBuffer) => {
|
|
95
|
+
let id = new DataView(d, 8, 8).getBigUint64(0, true)
|
|
96
|
+
// console.log("fetch block response id:" + id)
|
|
97
|
+
|
|
98
|
+
let action: MessageAction | any = undefined;
|
|
99
|
+
|
|
100
|
+
_msgActionRegister.forEach((v: MessageAction, k: MessageId) => {
|
|
101
|
+
if (k.id == id) {
|
|
102
|
+
action = v
|
|
103
|
+
_msgActionRegister.delete(k)
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
if (action) {
|
|
107
|
+
action.resolve(d);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
_msgActionRegister.clear()
|
|
111
|
+
throw new TDWebSocketClientError(`no callback registered for fetch_block with id=${id}`);
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
} else if (Object.prototype.toString.call(data) === '[object String]') {
|
|
116
|
+
let msg = JSON.parse(data)
|
|
117
|
+
// console.log("[_onmessage.stringType]==>:" + data);
|
|
118
|
+
let action: MessageAction | any = undefined;
|
|
119
|
+
|
|
120
|
+
_msgActionRegister.forEach((v: MessageAction, k: MessageId) => {
|
|
121
|
+
if (k.action == 'version') {
|
|
122
|
+
action = v
|
|
123
|
+
_msgActionRegister.delete(k)
|
|
124
|
+
}
|
|
125
|
+
if (k.req_id == msg.req_id && k.action == msg.action) {
|
|
126
|
+
action = v
|
|
127
|
+
_msgActionRegister.delete(k)
|
|
128
|
+
}
|
|
129
|
+
})
|
|
130
|
+
if (action) {
|
|
131
|
+
action.resolve(msg);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
_msgActionRegister.clear()
|
|
135
|
+
throw new TDWebSocketClientError(`no callback registered for ${msg.action} with req_id=${msg.req_id}`);
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
_msgActionRegister.clear()
|
|
139
|
+
throw new TDWebSocketClientError(`invalid message type ${Object.prototype.toString.call(data)}`)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
close() {
|
|
144
|
+
if (this._wsConn) {
|
|
145
|
+
_msgActionRegister.clear();
|
|
146
|
+
this._wsConn.close();
|
|
147
|
+
} else {
|
|
148
|
+
throw new TDWebSocketClientError("WebSocket connection is undefined.")
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
readyState(): number {
|
|
153
|
+
return this._wsConn.readyState;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
sendMsg(message: string, register: Boolean = true) {
|
|
157
|
+
// console.log("[wsClient.sendMessage()]===>" + message)
|
|
158
|
+
let msg = JSON.parse(message);
|
|
159
|
+
// console.log(typeof msg.args.id)
|
|
160
|
+
if (msg.args.id) {
|
|
161
|
+
msg.args.id = BigInt(msg.args.id)
|
|
162
|
+
}
|
|
163
|
+
// console.log("[wsClient.sendMessage.msg]===>\n")
|
|
164
|
+
// console.log(msg)
|
|
165
|
+
|
|
166
|
+
return new Promise((resolve, reject) => {
|
|
167
|
+
if (this._wsConn && this._wsConn.readyState > 0) {
|
|
168
|
+
if (register) {
|
|
169
|
+
|
|
170
|
+
this._registerCallback({ action: msg.action, req_id: msg.args.req_id, id: msg.args.id === undefined ? msg.args.id : BigInt(msg.args.id) }, resolve, reject)
|
|
171
|
+
// console.log("[wsClient.sendMessage._msgActionRegister]===>\n")
|
|
172
|
+
// console.log(_msgActionRegister)
|
|
173
|
+
}
|
|
174
|
+
this._wsConn.send(message)
|
|
175
|
+
} else {
|
|
176
|
+
reject(new WebSocketQueryError(`WebSocket connection is not ready,status :${this._wsConn?.readyState}`))
|
|
177
|
+
}
|
|
178
|
+
})
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private _registerCallback(id: MessageId, res: (args: unknown) => void, rej: (reason: any) => void) {
|
|
182
|
+
// console.log("register messageId:"+ JSON.stringify(id))
|
|
183
|
+
_msgActionRegister.set(id,
|
|
184
|
+
{
|
|
185
|
+
reject: rej,
|
|
186
|
+
resolve: res,
|
|
187
|
+
timer: setTimeout(() => rej(new WebSocketQueryError(`action:${id.action},req_id:${id.req_id} timeout with ${this._timeout} milliseconds`)), this._timeout)
|
|
188
|
+
})
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
configTimeout(ms: number) {
|
|
192
|
+
this._timeout = ms;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
}
|
|
196
|
+
|
package/src/wsError.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export class TDWebSocketClientError extends Error { }
|
|
2
|
+
export class WebSocketQueryError extends TDWebSocketClientError { }
|
|
3
|
+
export class WebSocketInterfaceError extends TDWebSocketClientError {}
|
|
4
|
+
export class WebSocketQueryInterFaceError extends WebSocketInterfaceError{}
|
|
5
|
+
export class TaosResultError extends Error{};
|
package/src/wsOptions.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface User {
|
|
2
|
+
user?: string;
|
|
3
|
+
passwd?: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface Uri {
|
|
7
|
+
scheme: string;
|
|
8
|
+
url?: string;
|
|
9
|
+
host?: string | undefined | null;
|
|
10
|
+
path: '/rest/sql/' | string;
|
|
11
|
+
port?: number | undefined | null;
|
|
12
|
+
query?: { [key: string]: string };
|
|
13
|
+
fragment?: string | undefined | null;
|
|
14
|
+
}
|
package/src/wsQuery.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { TaosResult } from './taosResult';
|
|
2
|
+
import { WSInterface } from './wsQueryInterface'
|
|
3
|
+
export async function execute(sql: string, wsInterface: WSInterface): Promise<TaosResult> {
|
|
4
|
+
let taosResult;
|
|
5
|
+
let wsQueryResponse = await wsInterface.query(sql);
|
|
6
|
+
try {
|
|
7
|
+
taosResult = new TaosResult(wsQueryResponse);
|
|
8
|
+
if (wsQueryResponse.is_update == true) {
|
|
9
|
+
return taosResult;
|
|
10
|
+
} else {
|
|
11
|
+
while (true) {
|
|
12
|
+
let wsFetchResponse = await wsInterface.fetch(wsQueryResponse)
|
|
13
|
+
// console.log("[wsQuery.execute.wsFetchResponse]==>\n")
|
|
14
|
+
// console.log(wsFetchResponse)
|
|
15
|
+
// console.log(typeof BigInt(8))
|
|
16
|
+
// console.log(typeof wsFetchResponse.timing)
|
|
17
|
+
if (wsFetchResponse.completed == true) {
|
|
18
|
+
break;
|
|
19
|
+
} else {
|
|
20
|
+
taosResult.setRows(wsFetchResponse)
|
|
21
|
+
let tmp: TaosResult = await wsInterface.fetchBlock(wsFetchResponse, taosResult)
|
|
22
|
+
taosResult = tmp;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return taosResult;
|
|
26
|
+
}
|
|
27
|
+
} finally {
|
|
28
|
+
wsInterface.freeResult(wsQueryResponse)
|
|
29
|
+
}
|
|
30
|
+
}
|