dolphindb 2.0.603 → 2.0.800
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/browser.d.ts +98 -17
- package/browser.js +333 -154
- package/browser.js.map +1 -1
- package/docs.en.json +2096 -646
- package/docs.zh.json +1290 -729
- package/index.d.ts +103 -18
- package/index.js +350 -166
- package/index.js.map +1 -1
- package/language.d.ts +2 -2
- package/language.js +5 -3
- package/language.js.map +1 -1
- package/package.json +7 -7
package/index.js
CHANGED
|
@@ -4,8 +4,8 @@ import DayjsCustomParseFormat from 'dayjs/plugin/customParseFormat.js';
|
|
|
4
4
|
dayjs.extend(DayjsCustomParseFormat);
|
|
5
5
|
import ipaddrjs from 'ipaddr.js';
|
|
6
6
|
const { fromByteArray: buf2ipaddr } = ipaddrjs;
|
|
7
|
+
import { concat, assert, inspect, typed_array_to_buffer } from 'xshell/utils.js';
|
|
7
8
|
import { connect_websocket } from 'xshell/net.js';
|
|
8
|
-
import { concat, inspect, typed_array_to_buffer } from 'xshell/utils.js';
|
|
9
9
|
export var DdbForm;
|
|
10
10
|
(function (DdbForm) {
|
|
11
11
|
DdbForm[DdbForm["scalar"] = 0] = "scalar";
|
|
@@ -62,7 +62,11 @@ export var DdbType;
|
|
|
62
62
|
DdbType[DdbType["complex"] = 34] = "complex";
|
|
63
63
|
DdbType[DdbType["point"] = 35] = "point";
|
|
64
64
|
DdbType[DdbType["duration"] = 36] = "duration";
|
|
65
|
-
DdbType[DdbType["
|
|
65
|
+
DdbType[DdbType["decimal32"] = 37] = "decimal32";
|
|
66
|
+
DdbType[DdbType["decimal64"] = 38] = "decimal64";
|
|
67
|
+
DdbType[DdbType["decimal128"] = 39] = "decimal128";
|
|
68
|
+
DdbType[DdbType["object"] = 40] = "object";
|
|
69
|
+
DdbType[DdbType["pynone"] = 41] = "pynone";
|
|
66
70
|
DdbType[DdbType["symbol_extended"] = 145] = "symbol_extended";
|
|
67
71
|
})(DdbType = DdbType || (DdbType = {}));
|
|
68
72
|
export var DdbFunctionType;
|
|
@@ -114,7 +118,15 @@ export const nulls = {
|
|
|
114
118
|
bytes16: Uint8Array.from(new Array(16).fill(0))
|
|
115
119
|
};
|
|
116
120
|
export const timezone_offset = 1000 * 60 * new Date().getTimezoneOffset();
|
|
117
|
-
|
|
121
|
+
export var StreamingStatusCode;
|
|
122
|
+
(function (StreamingStatusCode) {
|
|
123
|
+
StreamingStatusCode[StreamingStatusCode["ok"] = 0] = "ok";
|
|
124
|
+
StreamingStatusCode[StreamingStatusCode["connection_existed"] = 1] = "connection_existed";
|
|
125
|
+
StreamingStatusCode[StreamingStatusCode["not_leader"] = 2] = "not_leader";
|
|
126
|
+
StreamingStatusCode[StreamingStatusCode["other"] = 3] = "other";
|
|
127
|
+
StreamingStatusCode[StreamingStatusCode["error"] = 4] = "error";
|
|
128
|
+
})(StreamingStatusCode = StreamingStatusCode || (StreamingStatusCode = {}));
|
|
129
|
+
/** Can represent data types in all DolphinDB databases */
|
|
118
130
|
export class DdbObj {
|
|
119
131
|
static dec = new TextDecoder('utf-8');
|
|
120
132
|
static enc = new TextEncoder();
|
|
@@ -146,7 +158,7 @@ export class DdbObj {
|
|
|
146
158
|
rows;
|
|
147
159
|
/** 第 2 维 */
|
|
148
160
|
cols;
|
|
149
|
-
/**
|
|
161
|
+
/** The actual data. Different DdbForm, DdbType use different types in DdbValue to represent actual data */
|
|
150
162
|
value;
|
|
151
163
|
/** 原始二进制数据,仅在 parse_object 为 false 时通过 parse_message 生成的顶层对象有这个属性 */
|
|
152
164
|
buffer;
|
|
@@ -331,8 +343,7 @@ export class DdbObj {
|
|
|
331
343
|
col_labels.length += 2;
|
|
332
344
|
offset += col_labels.length;
|
|
333
345
|
}
|
|
334
|
-
|
|
335
|
-
throw new Error('matrix.datatype !== matrix.type');
|
|
346
|
+
assert(buf_data[offset] === type, 'matrix.datatype === matrix.type');
|
|
336
347
|
const rows = dv.getUint32(offset + 2, le);
|
|
337
348
|
const cols = dv.getUint32(offset + 6, le);
|
|
338
349
|
const [len_items, data] = this.parse_vector_items(buf_data.subarray(offset + 10), le, type, rows * cols // 假设小于 2**32
|
|
@@ -443,38 +454,30 @@ export class DdbObj {
|
|
|
443
454
|
case DdbType.uuid:
|
|
444
455
|
case DdbType.ipaddr:
|
|
445
456
|
case DdbType.int128:
|
|
446
|
-
return [
|
|
447
|
-
16,
|
|
448
|
-
buf.slice(0, 16)
|
|
449
|
-
];
|
|
457
|
+
return [16, buf.slice(0, 16)];
|
|
450
458
|
case DdbType.blob: {
|
|
451
459
|
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
452
460
|
const length = dv.getUint32(0, le);
|
|
453
|
-
return [
|
|
454
|
-
4 + length,
|
|
455
|
-
buf.slice(4, 4 + length)
|
|
456
|
-
];
|
|
461
|
+
return [4 + length, buf.slice(4, 4 + length)];
|
|
457
462
|
}
|
|
458
463
|
case DdbType.complex:
|
|
459
464
|
case DdbType.point: {
|
|
460
465
|
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
461
|
-
return [
|
|
462
|
-
16,
|
|
463
|
-
[
|
|
464
|
-
dv.getFloat64(0, le),
|
|
465
|
-
dv.getFloat64(8, le)
|
|
466
|
-
]
|
|
467
|
-
];
|
|
466
|
+
return [16, [dv.getFloat64(0, le), dv.getFloat64(8, le)]];
|
|
468
467
|
}
|
|
469
468
|
case DdbType.duration: {
|
|
470
469
|
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
471
|
-
return [
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
470
|
+
return [8, { unit: dv.getUint32(4, le), data: dv.getInt32(0, le) }];
|
|
471
|
+
}
|
|
472
|
+
case DdbType.decimal32: {
|
|
473
|
+
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
474
|
+
const data = dv.getInt32(4, le);
|
|
475
|
+
return [8, { scale: dv.getInt32(0, le), data: data === nulls.int32 ? null : data }];
|
|
476
|
+
}
|
|
477
|
+
case DdbType.decimal64: {
|
|
478
|
+
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
479
|
+
const data = dv.getBigInt64(4, le);
|
|
480
|
+
return [12, { scale: dv.getInt32(0, le), data: data === nulls.int64 ? null : data }];
|
|
478
481
|
}
|
|
479
482
|
default:
|
|
480
483
|
throw new Error(`${DdbType[type] || type} 暂时不支持解析`);
|
|
@@ -635,7 +638,7 @@ export class DdbObj {
|
|
|
635
638
|
// 02 00 00 00 symbol base size = 2
|
|
636
639
|
// 00 61 61 00 以 \0 分割的字符串
|
|
637
640
|
// 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00>
|
|
638
|
-
const dv = new DataView(buf.buffer, buf.byteOffset
|
|
641
|
+
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
639
642
|
const base_id = dv.getUint32(0, le);
|
|
640
643
|
const base_size = dv.getUint32(4, le);
|
|
641
644
|
let base_length = 0;
|
|
@@ -670,7 +673,7 @@ export class DdbObj {
|
|
|
670
673
|
// 04 00 00 00 61 62 63 64
|
|
671
674
|
// 04 00 00 00 61 62 63 64>
|
|
672
675
|
let value = new Array(length);
|
|
673
|
-
const dv = new DataView(buf.buffer, buf.byteOffset
|
|
676
|
+
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
674
677
|
let i_head = 0;
|
|
675
678
|
for (let i = 0; i < length; i++) {
|
|
676
679
|
const sublen = dv.getUint32(i_head, le);
|
|
@@ -710,6 +713,31 @@ export class DdbObj {
|
|
|
710
713
|
values
|
|
711
714
|
];
|
|
712
715
|
}
|
|
716
|
+
// 25 01 type = decimal32, form = vector
|
|
717
|
+
// 02 00 00 00 01 00 00 00
|
|
718
|
+
// 00 00 00 00 scale = 0
|
|
719
|
+
// 01 00 00 00 data[0] = 1
|
|
720
|
+
// 3a 01 00 00 data[1] = 0x013a = 314
|
|
721
|
+
case DdbType.decimal32: {
|
|
722
|
+
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
723
|
+
return [
|
|
724
|
+
4 + 4 * length,
|
|
725
|
+
{
|
|
726
|
+
scale: dv.getInt32(0, le),
|
|
727
|
+
data: new Int32Array(buf.buffer.slice(buf.byteOffset + 4, buf.byteOffset + 4 + 4 * length))
|
|
728
|
+
}
|
|
729
|
+
];
|
|
730
|
+
}
|
|
731
|
+
case DdbType.decimal64: {
|
|
732
|
+
const dv = new DataView(buf.buffer, buf.byteOffset);
|
|
733
|
+
return [
|
|
734
|
+
4 + 8 * length,
|
|
735
|
+
{
|
|
736
|
+
scale: dv.getInt32(0, le),
|
|
737
|
+
data: new BigInt64Array(buf.buffer.slice(buf.byteOffset + 4, buf.byteOffset + 4 + 8 * length))
|
|
738
|
+
}
|
|
739
|
+
];
|
|
740
|
+
}
|
|
713
741
|
// case DdbType.duration: -> 实际会返回一个 any vector
|
|
714
742
|
// [2y, 1M, 3d, 7H, 11m, 12s, 15ms, 16us, 17ns]
|
|
715
743
|
// <Buffer 19 01 type = any, form = vector
|
|
@@ -813,10 +841,7 @@ export class DdbObj {
|
|
|
813
841
|
case DdbType.int128:
|
|
814
842
|
return [this.value];
|
|
815
843
|
case DdbType.blob:
|
|
816
|
-
return [
|
|
817
|
-
Uint32Array.of(this.value.byteLength),
|
|
818
|
-
this.value
|
|
819
|
-
];
|
|
844
|
+
return [Uint32Array.of(this.value.byteLength), this.value];
|
|
820
845
|
case DdbType.complex:
|
|
821
846
|
case DdbType.point:
|
|
822
847
|
return [Float64Array.from(this.value)];
|
|
@@ -824,8 +849,16 @@ export class DdbObj {
|
|
|
824
849
|
const { unit, data } = this.value;
|
|
825
850
|
return [Int32Array.of(data, unit)];
|
|
826
851
|
}
|
|
852
|
+
case DdbType.decimal32: {
|
|
853
|
+
const { scale, data } = this.value;
|
|
854
|
+
return [Int32Array.of(scale, data === null ? nulls.int32 : data)];
|
|
855
|
+
}
|
|
856
|
+
case DdbType.decimal64: {
|
|
857
|
+
const { scale, data } = this.value;
|
|
858
|
+
return [Int32Array.of(scale), BigInt64Array.of(data === null ? nulls.int64 : data)];
|
|
859
|
+
}
|
|
827
860
|
default:
|
|
828
|
-
throw new Error(`${DdbType[type] || type}
|
|
861
|
+
throw new Error(`${DdbType[type] || type} serialization is not currently supported`);
|
|
829
862
|
}
|
|
830
863
|
case DdbForm.vector:
|
|
831
864
|
case DdbForm.pair:
|
|
@@ -845,12 +878,7 @@ export class DdbObj {
|
|
|
845
878
|
...DdbObj.pack_vector_body(value, type, this.rows)
|
|
846
879
|
];
|
|
847
880
|
case DdbForm.set:
|
|
848
|
-
return [
|
|
849
|
-
new DdbObj({
|
|
850
|
-
...this,
|
|
851
|
-
form: DdbForm.vector,
|
|
852
|
-
}).pack()
|
|
853
|
-
];
|
|
881
|
+
return [new DdbObj({ ...this, form: DdbForm.vector }).pack()];
|
|
854
882
|
case DdbForm.table:
|
|
855
883
|
return [
|
|
856
884
|
Uint32Array.of(this.rows, this.cols),
|
|
@@ -862,43 +890,27 @@ export class DdbObj {
|
|
|
862
890
|
...this.value.map(col => col.pack())
|
|
863
891
|
];
|
|
864
892
|
case DdbForm.dict:
|
|
865
|
-
return [
|
|
866
|
-
value[0].pack(),
|
|
867
|
-
value[1].pack(),
|
|
868
|
-
];
|
|
893
|
+
return [value[0].pack(), value[1].pack()];
|
|
869
894
|
case DdbForm.chart: {
|
|
870
|
-
const { type, stacking, bin_start, bin_end, bin_count, titles: { chart, x_axis, y_axis
|
|
895
|
+
const { type, stacking, bin_start, bin_end, bin_count, titles: { chart, x_axis, y_axis }, extras, data } = this.value;
|
|
871
896
|
const { value: [keys, values] } = new DdbDict({
|
|
872
897
|
chartType: new DdbInt(type),
|
|
873
898
|
stacking,
|
|
874
|
-
...bin_start ? {
|
|
875
|
-
|
|
876
|
-
binEnd: bin_end
|
|
877
|
-
} : {},
|
|
878
|
-
...bin_count ? {
|
|
879
|
-
binCount: bin_count
|
|
880
|
-
} : {},
|
|
899
|
+
...bin_start ? { binStart: bin_start, binEnd: bin_end } : {},
|
|
900
|
+
...bin_count ? { binCount: bin_count } : {},
|
|
881
901
|
title: new DdbVectorString([chart, x_axis, y_axis]),
|
|
882
902
|
...extras ? (() => {
|
|
883
903
|
const { multi_y_axes, ...extras_other } = extras;
|
|
884
|
-
return {
|
|
885
|
-
extras: new DdbDict({
|
|
886
|
-
multiYAxes: multi_y_axes,
|
|
887
|
-
...extras_other
|
|
888
|
-
})
|
|
889
|
-
};
|
|
904
|
+
return { extras: new DdbDict({ multiYAxes: multi_y_axes, ...extras_other }) };
|
|
890
905
|
})() : {},
|
|
891
906
|
data,
|
|
892
907
|
});
|
|
893
|
-
return [
|
|
894
|
-
keys.pack(),
|
|
895
|
-
values.pack(),
|
|
896
|
-
];
|
|
908
|
+
return [keys.pack(), values.pack()];
|
|
897
909
|
}
|
|
898
910
|
case DdbForm.matrix: {
|
|
899
911
|
const { rows, cols, data } = value;
|
|
900
912
|
return [
|
|
901
|
-
Uint8Array.of((rows
|
|
913
|
+
Uint8Array.of((rows ? 0x01 : 0x00) | (cols ? 0x02 : 0x00)),
|
|
902
914
|
...rows ? [rows.pack()] : [],
|
|
903
915
|
...cols ? [cols.pack()] : [],
|
|
904
916
|
Uint8Array.of(this.type, this.form),
|
|
@@ -907,7 +919,7 @@ export class DdbObj {
|
|
|
907
919
|
];
|
|
908
920
|
}
|
|
909
921
|
default:
|
|
910
|
-
throw new Error(`${DdbForm[form]}
|
|
922
|
+
throw new Error(`${DdbForm[form]} serialization is not currently supported`);
|
|
911
923
|
}
|
|
912
924
|
})();
|
|
913
925
|
if (!body)
|
|
@@ -996,6 +1008,11 @@ export class DdbObj {
|
|
|
996
1008
|
data
|
|
997
1009
|
];
|
|
998
1010
|
}
|
|
1011
|
+
case DdbType.decimal32:
|
|
1012
|
+
case DdbType.decimal64: {
|
|
1013
|
+
const { scale, data } = value;
|
|
1014
|
+
return [Int32Array.of(scale), data];
|
|
1015
|
+
}
|
|
999
1016
|
default:
|
|
1000
1017
|
throw new Error(`vector ${DdbType[type]} 暂不支持序列化`);
|
|
1001
1018
|
}
|
|
@@ -1075,6 +1092,15 @@ export class DdbObj {
|
|
|
1075
1092
|
items[i] = format(this.type, value.subarray(2 * i, 2 * (i + 1)), this.le, options);
|
|
1076
1093
|
return format_array(items, len_data > limit);
|
|
1077
1094
|
}
|
|
1095
|
+
case DdbType.decimal32:
|
|
1096
|
+
case DdbType.decimal64: {
|
|
1097
|
+
const limit = 50;
|
|
1098
|
+
const { data } = this.value;
|
|
1099
|
+
let items = new Array(Math.min(limit, data.length));
|
|
1100
|
+
for (let i = 0; i < items.length; i++)
|
|
1101
|
+
items[i] = formati(this, i, options);
|
|
1102
|
+
return format_array(items, data.length > limit);
|
|
1103
|
+
}
|
|
1078
1104
|
default: {
|
|
1079
1105
|
const limit = 50;
|
|
1080
1106
|
let items = new Array(Math.min(limit, this.value.length));
|
|
@@ -1137,8 +1163,7 @@ export class DdbObj {
|
|
|
1137
1163
|
return values.map(value => this.to_ddbobj(value));
|
|
1138
1164
|
}
|
|
1139
1165
|
to_rows() {
|
|
1140
|
-
|
|
1141
|
-
throw new Error('this.form is not DdbForm.table, cannot to_rows');
|
|
1166
|
+
assert(this.form === DdbForm.table, 'form must be DdbForm.table, otherwise it cannot to_rows');
|
|
1142
1167
|
let rows = new Array(this.rows);
|
|
1143
1168
|
for (let i = 0; i < this.rows; i++) {
|
|
1144
1169
|
let row = {};
|
|
@@ -1170,13 +1195,10 @@ export class DdbObj {
|
|
|
1170
1195
|
return rows;
|
|
1171
1196
|
}
|
|
1172
1197
|
to_dict({ strip, deep, } = {}) {
|
|
1173
|
-
|
|
1174
|
-
throw new Error('this.form is not DdbForm.dict and cannot be converted to js object');
|
|
1198
|
+
assert(this.form === DdbForm.dict, 'this.form must be DdbForm.dict, otherwise to_dict cannot be called to convert to js object');
|
|
1175
1199
|
const [{ value: keys, type: key_type }, { value: values, type: value_type }] = this.value;
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
if (deep && !strip)
|
|
1179
|
-
throw new Error('strip = true must be set when deep = true');
|
|
1200
|
+
assert(key_type === DdbType.string && value_type === DdbType.any, 'Currently only supports automatic conversion of dict<string, any> to js object');
|
|
1201
|
+
assert(!(deep && !strip), 'strip = true must be set when deep = true');
|
|
1180
1202
|
let obj = {};
|
|
1181
1203
|
for (let i = 0; i < this.rows; i++) {
|
|
1182
1204
|
let value = values[i];
|
|
@@ -1188,8 +1210,23 @@ export class DdbObj {
|
|
|
1188
1210
|
return obj;
|
|
1189
1211
|
}
|
|
1190
1212
|
}
|
|
1213
|
+
/** 整数一定用这个 number formatter, InspectOptions.decimals 不传也用这个 */
|
|
1214
|
+
let default_formatter = Intl.NumberFormat('en-US', { maximumFractionDigits: 20 });
|
|
1215
|
+
let _decimals = 20;
|
|
1216
|
+
/** 缓存,为了优化性能,通常 options.decimals 都是不变的 */
|
|
1217
|
+
let _formatter = Intl.NumberFormat('en-US', { maximumFractionDigits: 20 });
|
|
1191
1218
|
/** Formats a single element (value) as a string according to DdbType, null returns a 'null' string */
|
|
1192
|
-
export function format(type, value, le, options) {
|
|
1219
|
+
export function format(type, value, le, options = {}) {
|
|
1220
|
+
const formatter = (() => {
|
|
1221
|
+
const { decimals } = options;
|
|
1222
|
+
if (decimals === undefined || decimals === null)
|
|
1223
|
+
return default_formatter;
|
|
1224
|
+
if (decimals !== _decimals) {
|
|
1225
|
+
_decimals = decimals;
|
|
1226
|
+
_formatter = Intl.NumberFormat('en-US', { maximumFractionDigits: decimals, minimumFractionDigits: decimals });
|
|
1227
|
+
}
|
|
1228
|
+
return _formatter;
|
|
1229
|
+
})();
|
|
1193
1230
|
switch (type) {
|
|
1194
1231
|
case DdbType.bool:
|
|
1195
1232
|
return inspect((value === null || value === nulls.int8) ?
|
|
@@ -1207,26 +1244,32 @@ export function format(type, value, le, options) {
|
|
|
1207
1244
|
:
|
|
1208
1245
|
value, options);
|
|
1209
1246
|
case DdbType.short:
|
|
1210
|
-
return
|
|
1211
|
-
null
|
|
1247
|
+
return value === null || value === nulls.int16 ?
|
|
1248
|
+
inspect(null, options)
|
|
1212
1249
|
:
|
|
1213
|
-
|
|
1250
|
+
options.colors ?
|
|
1251
|
+
default_formatter.format(value).green
|
|
1252
|
+
:
|
|
1253
|
+
default_formatter.format(value);
|
|
1214
1254
|
case DdbType.int:
|
|
1215
|
-
return
|
|
1216
|
-
null
|
|
1255
|
+
return value === null || value === nulls.int32 ?
|
|
1256
|
+
inspect(null, options)
|
|
1217
1257
|
:
|
|
1218
|
-
|
|
1258
|
+
options.colors ?
|
|
1259
|
+
default_formatter.format(value).green
|
|
1260
|
+
:
|
|
1261
|
+
default_formatter.format(value);
|
|
1219
1262
|
case DdbType.long:
|
|
1220
|
-
return
|
|
1221
|
-
|
|
1263
|
+
return value === null || value === nulls.int64 ?
|
|
1264
|
+
inspect(null, options)
|
|
1222
1265
|
:
|
|
1223
1266
|
options.colors ?
|
|
1224
|
-
|
|
1267
|
+
default_formatter.format(value).green
|
|
1225
1268
|
:
|
|
1226
|
-
|
|
1269
|
+
default_formatter.format(value);
|
|
1227
1270
|
case DdbType.date:
|
|
1228
1271
|
return (value === null || value === nulls.int32) ?
|
|
1229
|
-
|
|
1272
|
+
inspect(null, options)
|
|
1230
1273
|
:
|
|
1231
1274
|
options.colors ?
|
|
1232
1275
|
date2str(value).green
|
|
@@ -1234,7 +1277,7 @@ export function format(type, value, le, options) {
|
|
|
1234
1277
|
date2str(value);
|
|
1235
1278
|
case DdbType.month:
|
|
1236
1279
|
return (value === null || value === nulls.int32) ?
|
|
1237
|
-
|
|
1280
|
+
inspect(null, options)
|
|
1238
1281
|
:
|
|
1239
1282
|
options.colors ?
|
|
1240
1283
|
month2str(value).green
|
|
@@ -1242,7 +1285,7 @@ export function format(type, value, le, options) {
|
|
|
1242
1285
|
month2str(value);
|
|
1243
1286
|
case DdbType.time:
|
|
1244
1287
|
return (value === null || value === nulls.int32) ?
|
|
1245
|
-
|
|
1288
|
+
inspect(null, options)
|
|
1246
1289
|
:
|
|
1247
1290
|
options.colors ?
|
|
1248
1291
|
time2str(value).green
|
|
@@ -1250,7 +1293,7 @@ export function format(type, value, le, options) {
|
|
|
1250
1293
|
time2str(value);
|
|
1251
1294
|
case DdbType.minute:
|
|
1252
1295
|
return (value === null || value === nulls.int32) ?
|
|
1253
|
-
|
|
1296
|
+
inspect(null, options)
|
|
1254
1297
|
:
|
|
1255
1298
|
options.colors ?
|
|
1256
1299
|
minute2str(value).green
|
|
@@ -1258,7 +1301,7 @@ export function format(type, value, le, options) {
|
|
|
1258
1301
|
minute2str(value);
|
|
1259
1302
|
case DdbType.second:
|
|
1260
1303
|
return (value === null || value === nulls.int32) ?
|
|
1261
|
-
|
|
1304
|
+
inspect(null, options)
|
|
1262
1305
|
:
|
|
1263
1306
|
options.colors ?
|
|
1264
1307
|
second2str(value).green
|
|
@@ -1266,7 +1309,7 @@ export function format(type, value, le, options) {
|
|
|
1266
1309
|
second2str(value);
|
|
1267
1310
|
case DdbType.datetime:
|
|
1268
1311
|
return (value === null || value === nulls.int32) ?
|
|
1269
|
-
|
|
1312
|
+
inspect(null, options)
|
|
1270
1313
|
:
|
|
1271
1314
|
options.colors ?
|
|
1272
1315
|
datetime2str(value).green
|
|
@@ -1274,7 +1317,7 @@ export function format(type, value, le, options) {
|
|
|
1274
1317
|
datetime2str(value);
|
|
1275
1318
|
case DdbType.timestamp:
|
|
1276
1319
|
return (value === null || value === nulls.int64) ?
|
|
1277
|
-
|
|
1320
|
+
inspect(null, options)
|
|
1278
1321
|
:
|
|
1279
1322
|
options.colors ?
|
|
1280
1323
|
timestamp2str(value).green
|
|
@@ -1282,7 +1325,7 @@ export function format(type, value, le, options) {
|
|
|
1282
1325
|
timestamp2str(value);
|
|
1283
1326
|
case DdbType.nanotime:
|
|
1284
1327
|
return (value === null || value === nulls.int64) ?
|
|
1285
|
-
|
|
1328
|
+
inspect(null, options)
|
|
1286
1329
|
:
|
|
1287
1330
|
options.colors ?
|
|
1288
1331
|
nanotime2str(value).green
|
|
@@ -1290,7 +1333,7 @@ export function format(type, value, le, options) {
|
|
|
1290
1333
|
nanotime2str(value);
|
|
1291
1334
|
case DdbType.nanotimestamp:
|
|
1292
1335
|
return (value === null || value === nulls.int64) ?
|
|
1293
|
-
|
|
1336
|
+
inspect(null, options)
|
|
1294
1337
|
:
|
|
1295
1338
|
options.colors ?
|
|
1296
1339
|
nanotimestamp2str(value).green
|
|
@@ -1298,14 +1341,20 @@ export function format(type, value, le, options) {
|
|
|
1298
1341
|
nanotimestamp2str(value);
|
|
1299
1342
|
case DdbType.float:
|
|
1300
1343
|
return (value === null || value === nulls.float32) ?
|
|
1301
|
-
|
|
1344
|
+
inspect(null, options)
|
|
1302
1345
|
:
|
|
1303
|
-
|
|
1346
|
+
options.colors ?
|
|
1347
|
+
formatter.format(value).green
|
|
1348
|
+
:
|
|
1349
|
+
formatter.format(value);
|
|
1304
1350
|
case DdbType.double:
|
|
1305
1351
|
return (value === null || value === nulls.double) ?
|
|
1306
|
-
|
|
1352
|
+
inspect(null, options)
|
|
1307
1353
|
:
|
|
1308
|
-
|
|
1354
|
+
options.colors ?
|
|
1355
|
+
formatter.format(value).green
|
|
1356
|
+
:
|
|
1357
|
+
formatter.format(value);
|
|
1309
1358
|
case DdbType.symbol:
|
|
1310
1359
|
case DdbType.string:
|
|
1311
1360
|
return inspect(value, options);
|
|
@@ -1344,38 +1393,35 @@ export function format(type, value, le, options) {
|
|
|
1344
1393
|
:
|
|
1345
1394
|
DdbObj.dec.decode(value), options);
|
|
1346
1395
|
case DdbType.point: {
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
x = null;
|
|
1350
|
-
if (y === nulls.double)
|
|
1351
|
-
y = null;
|
|
1352
|
-
return options.colors ?
|
|
1353
|
-
`(${String(x).green}, ${String(y).green})`
|
|
1354
|
-
:
|
|
1355
|
-
`(${String(x)}, ${String(y)})`;
|
|
1396
|
+
const [x, y] = value;
|
|
1397
|
+
return `(${format(DdbType.double, x, le, options)}, ${format(DdbType.double, y, le, options)})`;
|
|
1356
1398
|
}
|
|
1357
1399
|
case DdbType.complex: {
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
x = null;
|
|
1361
|
-
if (y === nulls.double)
|
|
1362
|
-
y = null;
|
|
1363
|
-
return options.colors ?
|
|
1364
|
-
`${String(x).green}+${`${String(y)}i`.green}`
|
|
1365
|
-
:
|
|
1366
|
-
`${String(x)}+${String(y)}i`;
|
|
1400
|
+
const [x, y] = value;
|
|
1401
|
+
return `${format(DdbType.double, x, le, options)}+${format(DdbType.double, y, le, options)}i`;
|
|
1367
1402
|
}
|
|
1368
1403
|
case DdbType.duration: {
|
|
1369
1404
|
const { data, unit } = value;
|
|
1370
1405
|
const str = `${data}${DdbDurationUnit[unit]}`;
|
|
1371
1406
|
return options.colors ? str.green : str;
|
|
1372
1407
|
}
|
|
1408
|
+
case DdbType.decimal32:
|
|
1409
|
+
case DdbType.decimal64: {
|
|
1410
|
+
const { scale, data } = value;
|
|
1411
|
+
if (data === null ||
|
|
1412
|
+
(data === nulls.int64 && type === DdbType.decimal64) ||
|
|
1413
|
+
(data === nulls.int32 && type === DdbType.decimal32))
|
|
1414
|
+
return 'null';
|
|
1415
|
+
const s = String(data < 0 ? -data : data).padStart(scale, '0');
|
|
1416
|
+
const str = (data < 0 ? '-' : '') + (scale ? `${s.slice(0, -scale) || '0'}.${s.slice(-scale)}` : s);
|
|
1417
|
+
return options.colors ? str.green : str;
|
|
1418
|
+
}
|
|
1373
1419
|
default:
|
|
1374
1420
|
return inspect(value, options);
|
|
1375
1421
|
}
|
|
1376
1422
|
}
|
|
1377
1423
|
/** formatted vector, the index-th item in the collection is a string, a null value returns a 'null' string */
|
|
1378
|
-
export function formati(obj, index, options) {
|
|
1424
|
+
export function formati(obj, index, options = {}) {
|
|
1379
1425
|
if (64 <= obj.type && obj.type < 128) { // array vector
|
|
1380
1426
|
// 因为 array vector 目前只支持:Logical, Integral(不包括 INT128, COMPRESS 类型), Floating, Temporal
|
|
1381
1427
|
// 都对应 TypedArray 中的一格,所以 lengths.length 等于 block 中的 row 的个数
|
|
@@ -1430,6 +1476,17 @@ export function formati(obj, index, options) {
|
|
|
1430
1476
|
case DdbType.complex:
|
|
1431
1477
|
case DdbType.point:
|
|
1432
1478
|
return format(obj.type, obj.value.subarray(2 * index, 2 * (index + 1)), obj.le, options);
|
|
1479
|
+
case DdbType.decimal32:
|
|
1480
|
+
case DdbType.decimal64: {
|
|
1481
|
+
const { scale, data } = obj.value;
|
|
1482
|
+
const x = data[index];
|
|
1483
|
+
if (x === nulls.int64 && obj.type === DdbType.decimal64 ||
|
|
1484
|
+
x === nulls.int32 && obj.type === DdbType.decimal32)
|
|
1485
|
+
return '';
|
|
1486
|
+
const s = String(x < 0 ? -x : x).padStart(scale, '0');
|
|
1487
|
+
const str = (x < 0 ? '-' : '') + (scale ? `${s.slice(0, -scale) || '0'}.${s.slice(-scale)}` : s);
|
|
1488
|
+
return options.colors ? str.green : str;
|
|
1489
|
+
}
|
|
1433
1490
|
default:
|
|
1434
1491
|
return format(obj.type, obj.value[index], obj.le, options);
|
|
1435
1492
|
}
|
|
@@ -1844,8 +1901,7 @@ export function datehour2str(datehour, format = 'YYYY.MM.DDTHH') {
|
|
|
1844
1901
|
export function str2timestamp(str, format = 'YYYY.MM.DD HH:mm:ss.SSS') {
|
|
1845
1902
|
if (!str || str === 'null')
|
|
1846
1903
|
return nulls.int64;
|
|
1847
|
-
|
|
1848
|
-
throw new Error('The length of the timestamp string is not equal to the length of the format string');
|
|
1904
|
+
assert('The length of the timestamp string must be equal to the length of the format string');
|
|
1849
1905
|
return BigInt(-timezone_offset +
|
|
1850
1906
|
dayjs(str, format).valueOf());
|
|
1851
1907
|
}
|
|
@@ -1858,12 +1914,10 @@ export function nanotime2str(nanotime, format = 'HH:mm:ss.SSSSSSSSS') {
|
|
|
1858
1914
|
if (nanotime < 0n)
|
|
1859
1915
|
return String(nanotime);
|
|
1860
1916
|
const i_second_start = format.indexOf('ss');
|
|
1861
|
-
|
|
1862
|
-
throw new Error('The format string must contain the format for seconds (ss)');
|
|
1917
|
+
assert(i_second_start !== -1, 'The format string must contain the format for seconds (ss)');
|
|
1863
1918
|
const i_second_end = i_second_start + 2;
|
|
1864
1919
|
const i_nanosecond_start = format.indexOf('SSSSSSSSS', i_second_end);
|
|
1865
|
-
|
|
1866
|
-
throw new Error('The format string must contain the format for nanoseconds (SSSSSSSSS)');
|
|
1920
|
+
assert(i_nanosecond_start !== -1, 'Format string must contain nanosecond format (SSSSSSSSS)');
|
|
1867
1921
|
return (dayjs(timezone_offset + (Number(nanotime) / 1000000)).format(format.slice(0, i_second_end)) +
|
|
1868
1922
|
format.slice(i_second_end, i_nanosecond_start) +
|
|
1869
1923
|
String(nanotime % 1000000000n).padStart(9, '0'));
|
|
@@ -1891,12 +1945,10 @@ export function nanotimestamp2str(nanotimestamp, format = 'YYYY.MM.DD HH:mm:ss.S
|
|
|
1891
1945
|
if (nanotimestamp === null || nanotimestamp === nulls.int64)
|
|
1892
1946
|
return 'null';
|
|
1893
1947
|
const i_second_start = format.indexOf('ss');
|
|
1894
|
-
|
|
1895
|
-
throw new Error('The format string must contain the format for seconds (ss)');
|
|
1948
|
+
assert(i_second_start !== -1, 'The format string must contain the format for seconds (ss)');
|
|
1896
1949
|
const i_second_end = i_second_start + 2;
|
|
1897
1950
|
const i_nanosecond_start = format.indexOf('SSSSSSSSS', i_second_end);
|
|
1898
|
-
|
|
1899
|
-
throw new Error('The format string must contain the format for nanoseconds (SSSSSSSSS)');
|
|
1951
|
+
assert(i_nanosecond_start !== -1, 'Format string must contain nanosecond format (SSSSSSSSS)');
|
|
1900
1952
|
const remainder = nanotimestamp % 1000000000n;
|
|
1901
1953
|
const borrow = remainder < 0n;
|
|
1902
1954
|
return (dayjs(timezone_offset +
|
|
@@ -1918,15 +1970,12 @@ export function nanotimestamp2str(nanotimestamp, format = 'YYYY.MM.DD HH:mm:ss.S
|
|
|
1918
1970
|
export function str2nanotimestamp(str, format = 'YYYY.MM.DD HH:mm:ss.SSSSSSSSS') {
|
|
1919
1971
|
if (!str || str === 'null')
|
|
1920
1972
|
return nulls.int64;
|
|
1921
|
-
|
|
1922
|
-
throw new Error('nanotimestamp string length is not equal to format string length');
|
|
1973
|
+
assert(str.length === format.length, 'nanotimestamp string length must be equal to format string length');
|
|
1923
1974
|
const i_second_start = format.indexOf('ss');
|
|
1924
|
-
|
|
1925
|
-
throw new Error('The format string must contain the format for seconds (ss)');
|
|
1975
|
+
assert(i_second_start !== -1, 'The format string must contain the format for seconds (ss)');
|
|
1926
1976
|
const i_second_end = i_second_start + 2;
|
|
1927
1977
|
const i_nanosecond_start = format.indexOf('SSSSSSSSS', i_second_end);
|
|
1928
|
-
|
|
1929
|
-
throw new Error('Format string must contain nanosecond format (SSSSSSSSS)');
|
|
1978
|
+
assert(i_nanosecond_start !== -1, 'Format string must contain nanosecond format (SSSSSSSSS)');
|
|
1930
1979
|
return (BigInt(-timezone_offset +
|
|
1931
1980
|
dayjs(str.slice(0, i_second_end), format.slice(0, i_second_end)).valueOf()) * 1000000n
|
|
1932
1981
|
+
|
|
@@ -1954,6 +2003,7 @@ export function int1282str(buffer, le = true) {
|
|
|
1954
2003
|
return [...buf].map(x => x.toString(16)
|
|
1955
2004
|
.padStart(2, '0')).join('');
|
|
1956
2005
|
}
|
|
2006
|
+
export const winsize = 40;
|
|
1957
2007
|
export class DDB {
|
|
1958
2008
|
/** 当前的 session id (http 或 tcp) */
|
|
1959
2009
|
sid = '0';
|
|
@@ -1977,6 +2027,8 @@ export class DDB {
|
|
|
1977
2027
|
password = '123456';
|
|
1978
2028
|
/** python session flag (2048) */
|
|
1979
2029
|
python = false;
|
|
2030
|
+
/** Whether it is a streaming data connection, this field is always null for non-streaming data */
|
|
2031
|
+
streaming = null;
|
|
1980
2032
|
// --- 内部选项, 状态
|
|
1981
2033
|
print_message_buffer = false;
|
|
1982
2034
|
print_object_buffer = false;
|
|
@@ -2000,11 +2052,12 @@ export class DDB {
|
|
|
2000
2052
|
- username?: DolphinDB username, default `'admin'`
|
|
2001
2053
|
- password?: DolphinDB password, default `'123456'`
|
|
2002
2054
|
- python?: set python session flag, default `false`
|
|
2055
|
+
- streaming?: When this option is set, the WebSocket connection is only used for streaming data
|
|
2003
2056
|
|
|
2004
2057
|
@example
|
|
2005
2058
|
let ddb = new DDB('ws://127.0.0.1:8848')
|
|
2006
2059
|
|
|
2007
|
-
//
|
|
2060
|
+
// Encrypt with HTTPS
|
|
2008
2061
|
let ddbsecure = new DDB('wss://dolphindb.com', {
|
|
2009
2062
|
autologin: true,
|
|
2010
2063
|
username: 'admin',
|
|
@@ -2022,8 +2075,13 @@ export class DDB {
|
|
|
2022
2075
|
this.password = options.password;
|
|
2023
2076
|
if (options.python !== undefined)
|
|
2024
2077
|
this.python = options.python;
|
|
2078
|
+
if (options.streaming !== undefined)
|
|
2079
|
+
this.streaming = options.streaming;
|
|
2080
|
+
}
|
|
2081
|
+
on_message(event) {
|
|
2082
|
+
console.log('This is the default on_message before calling this.rpc, it should not be called unless the server pushes the message first after the connection is established');
|
|
2083
|
+
console.log(event.data);
|
|
2025
2084
|
}
|
|
2026
|
-
on_message(event) { }
|
|
2027
2085
|
/** Establish the actual WebSocket connection to the DolphinDB corresponding to the URL
|
|
2028
2086
|
- options?:
|
|
2029
2087
|
- url?: DolphinDB WebSocket URL. By default, the WebSocket URL passed in when the instance is initialized is used
|
|
@@ -2031,34 +2089,42 @@ export class DDB {
|
|
|
2031
2089
|
- username?: DolphinDB username, default `'admin'`
|
|
2032
2090
|
- password?: DolphinDB password, default `'123456'`
|
|
2033
2091
|
- python?: set python session flag, default `false`
|
|
2092
|
+
- streaming?: When this option is set, the WebSocket connection is only used for streaming data
|
|
2034
2093
|
*/
|
|
2035
|
-
async connect(
|
|
2036
|
-
if (
|
|
2037
|
-
this.url =
|
|
2038
|
-
if (
|
|
2039
|
-
this.autologin =
|
|
2040
|
-
if (
|
|
2041
|
-
this.username =
|
|
2042
|
-
if (
|
|
2043
|
-
this.password =
|
|
2044
|
-
if (
|
|
2045
|
-
this.python =
|
|
2094
|
+
async connect({ url, autologin, username, password, python, streaming } = {}) {
|
|
2095
|
+
if (url !== undefined)
|
|
2096
|
+
this.url = url;
|
|
2097
|
+
if (autologin !== undefined)
|
|
2098
|
+
this.autologin = autologin;
|
|
2099
|
+
if (username !== undefined)
|
|
2100
|
+
this.username = username;
|
|
2101
|
+
if (password !== undefined)
|
|
2102
|
+
this.password = password;
|
|
2103
|
+
if (python !== undefined)
|
|
2104
|
+
this.python = python;
|
|
2105
|
+
if (streaming !== undefined)
|
|
2106
|
+
this.streaming = streaming;
|
|
2046
2107
|
this.disconnect();
|
|
2047
2108
|
await connect_websocket(this.url, {
|
|
2048
|
-
protocols:
|
|
2109
|
+
protocols: (() => {
|
|
2110
|
+
if (this.streaming)
|
|
2111
|
+
return 'streaming';
|
|
2112
|
+
if (this.python)
|
|
2113
|
+
return 'python';
|
|
2114
|
+
})(),
|
|
2049
2115
|
on_open: async (event, websocket) => {
|
|
2050
2116
|
this.websocket = websocket;
|
|
2051
|
-
|
|
2117
|
+
if (this.streaming)
|
|
2118
|
+
await this.subscribe();
|
|
2119
|
+
else
|
|
2120
|
+
await this.rpc('connect', {});
|
|
2052
2121
|
},
|
|
2053
2122
|
on_message: (event) => {
|
|
2054
2123
|
this.on_message(event);
|
|
2055
2124
|
}
|
|
2056
2125
|
});
|
|
2057
|
-
if (this.autologin)
|
|
2058
|
-
|
|
2059
|
-
await this.eval(`login(${this.username.quote('double')}, ${this.password.quote('double')})`, { urgent: true });
|
|
2060
|
-
else
|
|
2061
|
-
await this.call('login', [this.username, this.password], { urgent: true });
|
|
2126
|
+
if (!this.streaming && this.autologin)
|
|
2127
|
+
await this.call('login', [this.username, this.password], { urgent: true });
|
|
2062
2128
|
}
|
|
2063
2129
|
get_rpc_options({ urgent = false, secondary = false, async: _async = false, pickle = false, clear = false, api = false, compress = false, cancellable = true, priority = urgent ? 8 : 4, parallelism = 8, root_id = '', limit, } = {}) {
|
|
2064
2130
|
let flag = 0;
|
|
@@ -2097,6 +2163,7 @@ export class DDB {
|
|
|
2097
2163
|
this.on_message = () => { };
|
|
2098
2164
|
this.presult = Promise.resolve(null);
|
|
2099
2165
|
this.pconnect = Promise.resolve();
|
|
2166
|
+
this.pnode_run_defined = false;
|
|
2100
2167
|
}
|
|
2101
2168
|
/** rpc through websocket (function/script/variable command)
|
|
2102
2169
|
- type: API 类型: 'script' | 'function' | 'variable'
|
|
@@ -2275,8 +2342,7 @@ export class DDB {
|
|
|
2275
2342
|
*/
|
|
2276
2343
|
async call(func, args = [], { urgent, node, nodes, func_type, add_node_alias, listener, parse_object, } = {}) {
|
|
2277
2344
|
if (node) {
|
|
2278
|
-
|
|
2279
|
-
throw new Error('指定 node 时必须设置 func_type');
|
|
2345
|
+
assert(func_type in DdbFunctionType, 'func_type must be set when specifying node');
|
|
2280
2346
|
args = [
|
|
2281
2347
|
node,
|
|
2282
2348
|
new DdbFunction(func, func_type),
|
|
@@ -2313,10 +2379,21 @@ export class DDB {
|
|
|
2313
2379
|
vars,
|
|
2314
2380
|
/** Uploaded variables' value */
|
|
2315
2381
|
args, { listener, parse_object, } = {}) {
|
|
2316
|
-
|
|
2317
|
-
throw new Error('variable command parameter is empty or parameter name is empty, or the number does not match');
|
|
2382
|
+
assert(args.length && args.length === vars.length, 'variable command parameter cannot be empty and parameter name cannot be empty, and the number should match');
|
|
2318
2383
|
return this.rpc('variable', { vars, args, listener, parse_object });
|
|
2319
2384
|
}
|
|
2385
|
+
/** Cancel all console jobs corresponding to the current session id */
|
|
2386
|
+
async cancel() {
|
|
2387
|
+
let ddb = new DDB(this.url, this);
|
|
2388
|
+
try {
|
|
2389
|
+
await ddb.eval(`jobs = exec rootJobId from getConsoleJobs() where sessionId = ${this.sid}\n` +
|
|
2390
|
+
'if (size(jobs))\n' +
|
|
2391
|
+
' cancelConsoleJob(jobs)\n', { urgent: true });
|
|
2392
|
+
}
|
|
2393
|
+
finally {
|
|
2394
|
+
ddb.disconnect();
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
2320
2397
|
/** 解析服务端响应报文,返回去掉 header 的 data buf */
|
|
2321
2398
|
parse_message(buf, parse_object = this.parse_object) {
|
|
2322
2399
|
// MSG\n
|
|
@@ -2331,8 +2408,8 @@ export class DDB {
|
|
|
2331
2408
|
// 'OK\n'
|
|
2332
2409
|
// '\x04\x00\x02\x00\x00\x00'
|
|
2333
2410
|
/** index of line feed 0 */
|
|
2334
|
-
const
|
|
2335
|
-
const parts = this.dec.decode(buf.subarray(0,
|
|
2411
|
+
const ilf0 = buf.indexOf(0x0a); // '\n'
|
|
2412
|
+
const parts = this.dec.decode(buf.subarray(0, ilf0)).split(' ');
|
|
2336
2413
|
/** session id */
|
|
2337
2414
|
const sid = parts[0];
|
|
2338
2415
|
if (sid !== this.sid) {
|
|
@@ -2340,34 +2417,141 @@ export class DDB {
|
|
|
2340
2417
|
this.sid = sid;
|
|
2341
2418
|
}
|
|
2342
2419
|
/** 返回对象的数量 */
|
|
2343
|
-
const
|
|
2420
|
+
const nobj = Number(parts[1]);
|
|
2344
2421
|
/** 大小端: 协议中大端为 0, 小端为 1 */
|
|
2345
2422
|
this.le = Number(parts[2]) !== 0;
|
|
2346
|
-
const
|
|
2347
|
-
const
|
|
2423
|
+
const ils1 = ilf0 + 1;
|
|
2424
|
+
const ilf1 = buf.indexOf(0x0a, ils1); // '\n'
|
|
2348
2425
|
/** 'OK' 表示成功,其它文本表示失败 */
|
|
2349
|
-
const message = this.dec.decode(buf.subarray(
|
|
2426
|
+
const message = this.dec.decode(buf.subarray(ils1, ilf1));
|
|
2350
2427
|
if (message !== 'OK')
|
|
2351
2428
|
return {
|
|
2352
2429
|
type: 'error',
|
|
2353
2430
|
data: new Error(message)
|
|
2354
2431
|
};
|
|
2355
|
-
const
|
|
2432
|
+
const bufobj = buf.subarray(ilf1 + 1);
|
|
2356
2433
|
if (this.print_object_buffer)
|
|
2357
|
-
console.log(typed_array_to_buffer(
|
|
2434
|
+
console.log(typed_array_to_buffer(bufobj));
|
|
2358
2435
|
return {
|
|
2359
2436
|
type: 'object',
|
|
2360
2437
|
data: parse_object ?
|
|
2361
|
-
DdbObj.parse(
|
|
2438
|
+
DdbObj.parse(bufobj, this.le)
|
|
2362
2439
|
:
|
|
2363
2440
|
new DdbObj({
|
|
2364
2441
|
form: DdbForm.scalar,
|
|
2365
2442
|
type: DdbType.void,
|
|
2366
2443
|
length: 0,
|
|
2367
2444
|
le: this.le,
|
|
2368
|
-
buffer:
|
|
2445
|
+
buffer: bufobj,
|
|
2369
2446
|
})
|
|
2370
2447
|
};
|
|
2371
2448
|
}
|
|
2449
|
+
/** Internal stream subscription method */
|
|
2450
|
+
async subscribe() {
|
|
2451
|
+
const args = DdbObj.to_ddbobjs([
|
|
2452
|
+
new DdbVectorString(this.autologin ?
|
|
2453
|
+
[this.username, this.password]
|
|
2454
|
+
:
|
|
2455
|
+
// 不自动登录也要传空的 string vector, 否则 server 无法解析
|
|
2456
|
+
[]),
|
|
2457
|
+
// todo: 还有用吗?需要获取客户端 IP 吗
|
|
2458
|
+
'localhost',
|
|
2459
|
+
// todo: 端口还有用吗?
|
|
2460
|
+
new DdbInt(0),
|
|
2461
|
+
this.streaming.table,
|
|
2462
|
+
(this.streaming.action ||= `api_js_${new Date().getTime()}`),
|
|
2463
|
+
// new DdbInt(-1), // offset
|
|
2464
|
+
// filter
|
|
2465
|
+
// allow exists
|
|
2466
|
+
]);
|
|
2467
|
+
return new Promise((resolve, reject) => {
|
|
2468
|
+
let first = true;
|
|
2469
|
+
let second = true;
|
|
2470
|
+
// 先准备好收到 websocket message 的 callback
|
|
2471
|
+
this.on_message = ({ data: buffer }) => {
|
|
2472
|
+
if (first) {
|
|
2473
|
+
first = false;
|
|
2474
|
+
try {
|
|
2475
|
+
const dv = new DataView(buffer);
|
|
2476
|
+
const status = dv.getUint8(1);
|
|
2477
|
+
this.le = Boolean(dv.getUint8(2));
|
|
2478
|
+
const nobjs = dv.getUint32(3, this.le);
|
|
2479
|
+
// 解析返回数据,从 server 实现看其实只有一个 DdbObj
|
|
2480
|
+
let offset = 7;
|
|
2481
|
+
let objs = new Array(nobjs);
|
|
2482
|
+
const buf = new Uint8Array(buffer);
|
|
2483
|
+
for (let i = 0; i < nobjs; i++) {
|
|
2484
|
+
const obj = DdbObj.parse(buf.subarray(offset), this.le);
|
|
2485
|
+
offset += obj.length;
|
|
2486
|
+
objs[i] = obj;
|
|
2487
|
+
console.log(`订阅流表的响应数据: objs[${i}]:`, obj);
|
|
2488
|
+
}
|
|
2489
|
+
this.streaming.colnames = objs[0].value;
|
|
2490
|
+
if (status === StreamingStatusCode.ok)
|
|
2491
|
+
console.log('Subscribed to streaming table:', { status: StreamingStatusCode[status], result: objs[0] });
|
|
2492
|
+
else
|
|
2493
|
+
reject(new Error(`Failed to subscribe to streaming table: { status: ${StreamingStatusCode[status]}, error: ${objs[0]?.value} }`));
|
|
2494
|
+
}
|
|
2495
|
+
catch (error) {
|
|
2496
|
+
reject(error);
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2499
|
+
else {
|
|
2500
|
+
try {
|
|
2501
|
+
const dv = new DataView(buffer);
|
|
2502
|
+
const buf = new Uint8Array(buffer);
|
|
2503
|
+
let { streaming } = this;
|
|
2504
|
+
streaming.time = dv.getBigInt64(1, this.le);
|
|
2505
|
+
streaming.id = dv.getBigInt64(9, this.le);
|
|
2506
|
+
const i_topic_end = buf.indexOf(0, 17);
|
|
2507
|
+
streaming.topic = this.dec.decode(buf.subarray(17, i_topic_end));
|
|
2508
|
+
if (second) {
|
|
2509
|
+
second = false;
|
|
2510
|
+
// 是 table
|
|
2511
|
+
streaming.schema = DdbObj.parse(buf.subarray(i_topic_end + 1), this.le);
|
|
2512
|
+
console.log('Received streaming table schema:', streaming.schema);
|
|
2513
|
+
streaming.window = {
|
|
2514
|
+
offset: 0,
|
|
2515
|
+
rows: 0,
|
|
2516
|
+
segments: [],
|
|
2517
|
+
};
|
|
2518
|
+
assert(streaming.schema.rows === 0, 'schema.rows === 0');
|
|
2519
|
+
resolve(streaming.schema);
|
|
2520
|
+
}
|
|
2521
|
+
else {
|
|
2522
|
+
// 是 column 片段组成的 any vector
|
|
2523
|
+
const data = streaming.data = DdbObj.parse(buf.subarray(i_topic_end + 1), this.le);
|
|
2524
|
+
let { window: win } = streaming;
|
|
2525
|
+
win.rows += data.rows;
|
|
2526
|
+
win.segments.push(data);
|
|
2527
|
+
if (win.rows >= winsize * 2 && win.segments.length >= 2) {
|
|
2528
|
+
let winsize_ = 0;
|
|
2529
|
+
let i = win.segments.length - 1;
|
|
2530
|
+
// 往前移动至首个累计 winsize_ 超过 winsize 的位置
|
|
2531
|
+
for (; winsize_ < winsize; i--)
|
|
2532
|
+
winsize_ += win.segments[i].rows;
|
|
2533
|
+
win.segments = win.segments.slice(i);
|
|
2534
|
+
win.offset += win.rows - winsize_;
|
|
2535
|
+
win.rows = winsize_;
|
|
2536
|
+
console.log('exceeds winsize, shrink window.segments', win);
|
|
2537
|
+
}
|
|
2538
|
+
streaming.handler(streaming);
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
catch (error) {
|
|
2542
|
+
reject(error);
|
|
2543
|
+
}
|
|
2544
|
+
}
|
|
2545
|
+
};
|
|
2546
|
+
// 发送订阅 websocket message
|
|
2547
|
+
this.websocket.send(concat([
|
|
2548
|
+
Uint8Array.of(1, // from = SubscriberFromType::FROM_API
|
|
2549
|
+
1, // type = SubscriberRPCType::RPC_START
|
|
2550
|
+
Number(DDB.le_client)),
|
|
2551
|
+
Uint32Array.of(args.length),
|
|
2552
|
+
...args.map(obj => obj.pack())
|
|
2553
|
+
]));
|
|
2554
|
+
});
|
|
2555
|
+
}
|
|
2372
2556
|
}
|
|
2373
2557
|
//# sourceMappingURL=index.js.map
|