dolphindb 2.0.602 → 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.js CHANGED
@@ -5,7 +5,7 @@ import ipaddrjs from 'ipaddr.js';
5
5
  const { fromByteArray: buf2ipaddr } = ipaddrjs;
6
6
  import 'xshell/prototype.browser.js';
7
7
  import { blue } from 'xshell/chalk.browser.js';
8
- import { concat } from 'xshell/utils.browser.js';
8
+ import { concat, assert } from 'xshell/utils.browser.js';
9
9
  import { connect_websocket } from 'xshell/net.browser.js';
10
10
  export var DdbForm;
11
11
  (function (DdbForm) {
@@ -63,7 +63,11 @@ export var DdbType;
63
63
  DdbType[DdbType["complex"] = 34] = "complex";
64
64
  DdbType[DdbType["point"] = 35] = "point";
65
65
  DdbType[DdbType["duration"] = 36] = "duration";
66
- DdbType[DdbType["object"] = 37] = "object";
66
+ DdbType[DdbType["decimal32"] = 37] = "decimal32";
67
+ DdbType[DdbType["decimal64"] = 38] = "decimal64";
68
+ DdbType[DdbType["decimal128"] = 39] = "decimal128";
69
+ DdbType[DdbType["object"] = 40] = "object";
70
+ DdbType[DdbType["pynone"] = 41] = "pynone";
67
71
  DdbType[DdbType["symbol_extended"] = 145] = "symbol_extended";
68
72
  })(DdbType = DdbType || (DdbType = {}));
69
73
  export var DdbFunctionType;
@@ -115,6 +119,14 @@ export const nulls = {
115
119
  bytes16: Uint8Array.from(new Array(16).fill(0))
116
120
  };
117
121
  export const timezone_offset = 1000 * 60 * new Date().getTimezoneOffset();
122
+ export var StreamingStatusCode;
123
+ (function (StreamingStatusCode) {
124
+ StreamingStatusCode[StreamingStatusCode["ok"] = 0] = "ok";
125
+ StreamingStatusCode[StreamingStatusCode["connection_existed"] = 1] = "connection_existed";
126
+ StreamingStatusCode[StreamingStatusCode["not_leader"] = 2] = "not_leader";
127
+ StreamingStatusCode[StreamingStatusCode["other"] = 3] = "other";
128
+ StreamingStatusCode[StreamingStatusCode["error"] = 4] = "error";
129
+ })(StreamingStatusCode = StreamingStatusCode || (StreamingStatusCode = {}));
118
130
  /** 可以表示所有 DolphinDB 数据库中的数据类型 */
119
131
  export class DdbObj {
120
132
  static dec = new TextDecoder('utf-8');
@@ -268,7 +280,7 @@ export class DdbObj {
268
280
  if (form === DdbForm.dict)
269
281
  return dict;
270
282
  else {
271
- const { chartType: type, stacking, title: titles, extras, data, ...others } = dict.to_dict();
283
+ const { chartType: type, stacking, binStart: bin_start, binEnd: bin_end, binCount: bin_count, title: titles, extras, data, ...others } = dict.to_dict();
272
284
  const [chart, x_axis, y_axis] = titles.value;
273
285
  dict.form = DdbForm.chart;
274
286
  dict.value = {
@@ -279,6 +291,8 @@ export class DdbObj {
279
291
  x_axis,
280
292
  y_axis,
281
293
  },
294
+ ...bin_start ? { bin_start, bin_end, } : {},
295
+ ...bin_count ? { bin_count } : {},
282
296
  ...extras ? (() => {
283
297
  const { multiYAxes: multi_y_axes = false, ...extras_others } = extras.to_dict({ strip: true });
284
298
  return {
@@ -330,8 +344,7 @@ export class DdbObj {
330
344
  col_labels.length += 2;
331
345
  offset += col_labels.length;
332
346
  }
333
- if (buf_data[offset] !== type)
334
- throw new Error('matrix.datatype !== matrix.type');
347
+ assert(buf_data[offset] === type, 'matrix.datatype === matrix.type');
335
348
  const rows = dv.getUint32(offset + 2, le);
336
349
  const cols = dv.getUint32(offset + 6, le);
337
350
  const [len_items, data] = this.parse_vector_items(buf_data.subarray(offset + 10), le, type, rows * cols // 假设小于 2**32
@@ -442,38 +455,30 @@ export class DdbObj {
442
455
  case DdbType.uuid:
443
456
  case DdbType.ipaddr:
444
457
  case DdbType.int128:
445
- return [
446
- 16,
447
- buf.slice(0, 16)
448
- ];
458
+ return [16, buf.slice(0, 16)];
449
459
  case DdbType.blob: {
450
460
  const dv = new DataView(buf.buffer, buf.byteOffset);
451
461
  const length = dv.getUint32(0, le);
452
- return [
453
- 4 + length,
454
- buf.slice(4, 4 + length)
455
- ];
462
+ return [4 + length, buf.slice(4, 4 + length)];
456
463
  }
457
464
  case DdbType.complex:
458
465
  case DdbType.point: {
459
466
  const dv = new DataView(buf.buffer, buf.byteOffset);
460
- return [
461
- 16,
462
- [
463
- dv.getFloat64(0, le),
464
- dv.getFloat64(8, le)
465
- ]
466
- ];
467
+ return [16, [dv.getFloat64(0, le), dv.getFloat64(8, le)]];
467
468
  }
468
469
  case DdbType.duration: {
469
470
  const dv = new DataView(buf.buffer, buf.byteOffset);
470
- return [
471
- 8,
472
- {
473
- unit: dv.getUint32(4, le),
474
- data: dv.getInt32(0, le)
475
- }
476
- ];
471
+ return [8, { unit: dv.getUint32(4, le), data: dv.getInt32(0, le) }];
472
+ }
473
+ case DdbType.decimal32: {
474
+ const dv = new DataView(buf.buffer, buf.byteOffset);
475
+ const data = dv.getInt32(4, le);
476
+ return [8, { scale: dv.getInt32(0, le), data: data === nulls.int32 ? null : data }];
477
+ }
478
+ case DdbType.decimal64: {
479
+ const dv = new DataView(buf.buffer, buf.byteOffset);
480
+ const data = dv.getBigInt64(4, le);
481
+ return [12, { scale: dv.getInt32(0, le), data: data === nulls.int64 ? null : data }];
477
482
  }
478
483
  default:
479
484
  throw new Error(`${DdbType[type] || type} 暂时不支持解析`);
@@ -634,7 +639,7 @@ export class DdbObj {
634
639
  // 02 00 00 00 symbol base size = 2
635
640
  // 00 61 61 00 以 \0 分割的字符串
636
641
  // 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00>
637
- const dv = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
642
+ const dv = new DataView(buf.buffer, buf.byteOffset);
638
643
  const base_id = dv.getUint32(0, le);
639
644
  const base_size = dv.getUint32(4, le);
640
645
  let base_length = 0;
@@ -669,7 +674,7 @@ export class DdbObj {
669
674
  // 04 00 00 00 61 62 63 64
670
675
  // 04 00 00 00 61 62 63 64>
671
676
  let value = new Array(length);
672
- const dv = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
677
+ const dv = new DataView(buf.buffer, buf.byteOffset);
673
678
  let i_head = 0;
674
679
  for (let i = 0; i < length; i++) {
675
680
  const sublen = dv.getUint32(i_head, le);
@@ -709,6 +714,31 @@ export class DdbObj {
709
714
  values
710
715
  ];
711
716
  }
717
+ // 25 01 type = decimal32, form = vector
718
+ // 02 00 00 00 01 00 00 00
719
+ // 00 00 00 00 scale = 0
720
+ // 01 00 00 00 data[0] = 1
721
+ // 3a 01 00 00 data[1] = 0x013a = 314
722
+ case DdbType.decimal32: {
723
+ const dv = new DataView(buf.buffer, buf.byteOffset);
724
+ return [
725
+ 4 + 4 * length,
726
+ {
727
+ scale: dv.getInt32(0, le),
728
+ data: new Int32Array(buf.buffer.slice(buf.byteOffset + 4, buf.byteOffset + 4 + 4 * length))
729
+ }
730
+ ];
731
+ }
732
+ case DdbType.decimal64: {
733
+ const dv = new DataView(buf.buffer, buf.byteOffset);
734
+ return [
735
+ 4 + 8 * length,
736
+ {
737
+ scale: dv.getInt32(0, le),
738
+ data: new BigInt64Array(buf.buffer.slice(buf.byteOffset + 4, buf.byteOffset + 4 + 8 * length))
739
+ }
740
+ ];
741
+ }
712
742
  // case DdbType.duration: -> 实际会返回一个 any vector
713
743
  // [2y, 1M, 3d, 7H, 11m, 12s, 15ms, 16us, 17ns]
714
744
  // <Buffer 19 01 type = any, form = vector
@@ -812,10 +842,7 @@ export class DdbObj {
812
842
  case DdbType.int128:
813
843
  return [this.value];
814
844
  case DdbType.blob:
815
- return [
816
- Uint32Array.of(this.value.byteLength),
817
- this.value
818
- ];
845
+ return [Uint32Array.of(this.value.byteLength), this.value];
819
846
  case DdbType.complex:
820
847
  case DdbType.point:
821
848
  return [Float64Array.from(this.value)];
@@ -823,6 +850,14 @@ export class DdbObj {
823
850
  const { unit, data } = this.value;
824
851
  return [Int32Array.of(data, unit)];
825
852
  }
853
+ case DdbType.decimal32: {
854
+ const { scale, data } = this.value;
855
+ return [Int32Array.of(scale, data === null ? nulls.int32 : data)];
856
+ }
857
+ case DdbType.decimal64: {
858
+ const { scale, data } = this.value;
859
+ return [Int32Array.of(scale), BigInt64Array.of(data === null ? nulls.int64 : data)];
860
+ }
826
861
  default:
827
862
  throw new Error(`${DdbType[type] || type} 暂时不支持序列化`);
828
863
  }
@@ -844,12 +879,7 @@ export class DdbObj {
844
879
  ...DdbObj.pack_vector_body(value, type, this.rows)
845
880
  ];
846
881
  case DdbForm.set:
847
- return [
848
- new DdbObj({
849
- ...this,
850
- form: DdbForm.vector,
851
- }).pack()
852
- ];
882
+ return [new DdbObj({ ...this, form: DdbForm.vector }).pack()];
853
883
  case DdbForm.table:
854
884
  return [
855
885
  Uint32Array.of(this.rows, this.cols),
@@ -861,36 +891,27 @@ export class DdbObj {
861
891
  ...this.value.map(col => col.pack())
862
892
  ];
863
893
  case DdbForm.dict:
864
- return [
865
- value[0].pack(),
866
- value[1].pack(),
867
- ];
894
+ return [value[0].pack(), value[1].pack()];
868
895
  case DdbForm.chart: {
869
- const { type, stacking, titles: { chart, x_axis, y_axis, }, extras, data } = this.value;
896
+ const { type, stacking, bin_start, bin_end, bin_count, titles: { chart, x_axis, y_axis }, extras, data } = this.value;
870
897
  const { value: [keys, values] } = new DdbDict({
871
898
  chartType: new DdbInt(type),
872
899
  stacking,
900
+ ...bin_start ? { binStart: bin_start, binEnd: bin_end } : {},
901
+ ...bin_count ? { binCount: bin_count } : {},
873
902
  title: new DdbVectorString([chart, x_axis, y_axis]),
874
903
  ...extras ? (() => {
875
904
  const { multi_y_axes, ...extras_other } = extras;
876
- return {
877
- extras: new DdbDict({
878
- multiYAxes: multi_y_axes,
879
- ...extras_other
880
- })
881
- };
905
+ return { extras: new DdbDict({ multiYAxes: multi_y_axes, ...extras_other }) };
882
906
  })() : {},
883
907
  data,
884
908
  });
885
- return [
886
- keys.pack(),
887
- values.pack(),
888
- ];
909
+ return [keys.pack(), values.pack()];
889
910
  }
890
911
  case DdbForm.matrix: {
891
912
  const { rows, cols, data } = value;
892
913
  return [
893
- Uint8Array.of((rows && 0x01) | (cols && 0x02)),
914
+ Uint8Array.of((rows ? 0x01 : 0x00) | (cols ? 0x02 : 0x00)),
894
915
  ...rows ? [rows.pack()] : [],
895
916
  ...cols ? [cols.pack()] : [],
896
917
  Uint8Array.of(this.type, this.form),
@@ -899,7 +920,7 @@ export class DdbObj {
899
920
  ];
900
921
  }
901
922
  default:
902
- throw new Error(`${DdbForm[form]} Serialization is not currently supported`);
923
+ throw new Error(`${DdbForm[form]} serialization is not currently supported`);
903
924
  }
904
925
  })();
905
926
  if (!body)
@@ -988,16 +1009,21 @@ export class DdbObj {
988
1009
  data
989
1010
  ];
990
1011
  }
1012
+ case DdbType.decimal32:
1013
+ case DdbType.decimal64: {
1014
+ const { scale, data } = value;
1015
+ return [Int32Array.of(scale), data];
1016
+ }
991
1017
  default:
992
1018
  throw new Error(`vector ${DdbType[type]} 暂不支持序列化`);
993
1019
  }
994
1020
  }
995
- toString(colors = false) {
1021
+ toString(options) {
996
1022
  const type = this.inspect_type();
997
1023
  const data = (() => {
998
1024
  switch (this.form) {
999
1025
  case DdbForm.scalar:
1000
- return format(this.type, this.value, this.le);
1026
+ return format(this.type, this.value, this.le, options);
1001
1027
  case DdbForm.vector:
1002
1028
  case DdbForm.pair:
1003
1029
  case DdbForm.set: {
@@ -1028,7 +1054,7 @@ export class DdbObj {
1028
1054
  for (const length of lengths) {
1029
1055
  let _items = new Array(Math.min(limit, length));
1030
1056
  for (let i = 0; i < _items.length; i++)
1031
- _items[i] = format(_type, data[acc_len + i], this.le);
1057
+ _items[i] = format(_type, data[acc_len + i], this.le, options);
1032
1058
  items[i_items++] = format_array(_items, length > limit);
1033
1059
  acc_len += length;
1034
1060
  }
@@ -1054,7 +1080,7 @@ export class DdbObj {
1054
1080
  const len_data = value.length / 16;
1055
1081
  let items = new Array(Math.min(limit, len_data));
1056
1082
  for (let i = 0; i < items.length; i++)
1057
- items[i] = format(this.type, value.subarray(16 * i, 16 * (i + 1)), this.le);
1083
+ items[i] = format(this.type, value.subarray(16 * i, 16 * (i + 1)), this.le, options);
1058
1084
  return format_array(items, len_data > limit);
1059
1085
  }
1060
1086
  case DdbType.complex:
@@ -1064,24 +1090,33 @@ export class DdbObj {
1064
1090
  const len_data = value.length / 2;
1065
1091
  let items = new Array(Math.min(limit, len_data));
1066
1092
  for (let i = 0; i < items.length; i++)
1067
- items[i] = format(this.type, value.subarray(2 * i, 2 * (i + 1)), this.le);
1093
+ items[i] = format(this.type, value.subarray(2 * i, 2 * (i + 1)), this.le, options);
1068
1094
  return format_array(items, len_data > limit);
1069
1095
  }
1096
+ case DdbType.decimal32:
1097
+ case DdbType.decimal64: {
1098
+ const limit = 50;
1099
+ const { data } = this.value;
1100
+ let items = new Array(Math.min(limit, data.length));
1101
+ for (let i = 0; i < items.length; i++)
1102
+ items[i] = formati(this, i, options);
1103
+ return format_array(items, data.length > limit);
1104
+ }
1070
1105
  default: {
1071
1106
  const limit = 50;
1072
1107
  let items = new Array(Math.min(limit, this.value.length));
1073
1108
  for (let i = 0; i < items.length; i++)
1074
- items[i] = format(this.type, this.value[i], this.le);
1109
+ items[i] = format(this.type, this.value[i], this.le, options);
1075
1110
  return format_array(items, this.value.length > limit);
1076
1111
  }
1077
1112
  }
1078
1113
  }
1079
1114
  case DdbForm.chart:
1080
- return JSON.stringify(this.value, (key, value) => key === 'data' ? value.toString() : value, 4);
1115
+ return JSON.stringify(this.value, (key, value) => key === 'data' ? value.toString(options) : value, 4);
1081
1116
  }
1082
1117
  return String(this.value);
1083
1118
  })();
1084
- return `${colors ? blue(type) : type}(${this.name ? `'${this.name}', ` : ''}${data})\n`;
1119
+ return `${options?.colors ? blue(type) : type}(${this.name ? `'${this.name}', ` : ''}${data})\n`;
1085
1120
  }
1086
1121
  inspect_type() {
1087
1122
  const tname = DdbType[this.type];
@@ -1101,7 +1136,7 @@ export class DdbObj {
1101
1136
  case DdbForm.table:
1102
1137
  return `table[${this.rows}r][${this.cols}c]`;
1103
1138
  case DdbForm.dict:
1104
- return `dict<${DdbType[this.value[0].type]}, ${DdbType[this.value[1].type]}>`;
1139
+ return `dict<${DdbType[this.value[0].type]}, ${DdbType[this.value[1].type]}>[${this.rows}]`;
1105
1140
  case DdbForm.chart:
1106
1141
  return `chart<${DdbChartType[this.value.type]}>`;
1107
1142
  case DdbForm.matrix:
@@ -1129,28 +1164,31 @@ export class DdbObj {
1129
1164
  return values.map(value => this.to_ddbobj(value));
1130
1165
  }
1131
1166
  to_cols() {
1167
+ assert(this.form === DdbForm.table, 'form must be DdbForm.table, otherwise it cannot to_cols');
1132
1168
  return this.value.map(col => {
1133
1169
  let col_ = {
1134
1170
  title: col.name,
1135
1171
  dataIndex: col.name,
1136
1172
  };
1137
1173
  switch (col.type) {
1174
+ case DdbType.char:
1175
+ col_.render = (value) => format(DdbType.char, value, this.le);
1176
+ break;
1138
1177
  case DdbType.timestamp:
1139
- col_.render = value => timestamp2str(value);
1178
+ col_.render = (value) => timestamp2str(value);
1140
1179
  break;
1141
1180
  case DdbType.date:
1142
- col_.render = value => date2str(value);
1181
+ col_.render = (value) => date2str(value);
1143
1182
  break;
1144
1183
  case DdbType.ipaddr:
1145
- col_.render = value => ipaddr2str(value, this.le);
1184
+ col_.render = (value) => ipaddr2str(value, this.le);
1146
1185
  break;
1147
1186
  }
1148
1187
  return col_;
1149
1188
  });
1150
1189
  }
1151
1190
  to_rows() {
1152
- if (this.form !== DdbForm.table)
1153
- throw new Error('this.form is not DdbForm.table, cannot call to_rows');
1191
+ assert(this.form === DdbForm.table, 'form must be DdbForm.table, otherwise it cannot to_rows');
1154
1192
  let rows = new Array(this.rows);
1155
1193
  for (let i = 0; i < this.rows; i++) {
1156
1194
  let row = {};
@@ -1182,13 +1220,10 @@ export class DdbObj {
1182
1220
  return rows;
1183
1221
  }
1184
1222
  to_dict({ strip, deep, } = {}) {
1185
- if (this.form !== DdbForm.dict)
1186
- throw new Error('this.form is not DdbForm.dict and cannot be converted to js object');
1223
+ assert(this.form === DdbForm.dict, 'this.form must be DdbForm.dict, otherwise to_dict cannot be called to convert to js object');
1187
1224
  const [{ value: keys, type: key_type }, { value: values, type: value_type }] = this.value;
1188
- if (key_type !== DdbType.string || value_type !== DdbType.any)
1189
- throw new Error('Not dict<string, any>, automatic conversion to js object is not supported for the time being');
1190
- if (deep && !strip)
1191
- throw new Error('strip = true must be set when deep = true');
1225
+ assert(key_type === DdbType.string && value_type === DdbType.any, 'Currently only supports automatic conversion of dict<string, any> to js object');
1226
+ assert(!(deep && !strip), 'strip = true must be set when deep = true');
1192
1227
  let obj = {};
1193
1228
  for (let i = 0; i < this.rows; i++) {
1194
1229
  let value = values[i];
@@ -1200,8 +1235,23 @@ export class DdbObj {
1200
1235
  return obj;
1201
1236
  }
1202
1237
  }
1238
+ /** Integer must use this number formatter, InspectOptions.decimals also use this if not passed */
1239
+ let default_formatter = Intl.NumberFormat('en-US', { maximumFractionDigits: 20 });
1240
+ let _decimals = 20;
1241
+ /** Cache, in order to optimize performance, usually options.decimals are unchanged */
1242
+ let _formatter = Intl.NumberFormat('en-US', { maximumFractionDigits: 20 });
1203
1243
  /** Formats a single element (value) as a string according to DdbType, null returns a 'null' string */
1204
1244
  export function format(type, value, le, options = {}) {
1245
+ const formatter = (() => {
1246
+ const { decimals } = options;
1247
+ if (decimals === undefined || decimals === null)
1248
+ return default_formatter;
1249
+ if (decimals !== _decimals) {
1250
+ _decimals = decimals;
1251
+ _formatter = Intl.NumberFormat('en-US', { maximumFractionDigits: decimals, minimumFractionDigits: decimals });
1252
+ }
1253
+ return _formatter;
1254
+ })();
1205
1255
  switch (type) {
1206
1256
  case DdbType.bool:
1207
1257
  return String((value === null || value === nulls.int8) ?
@@ -1219,20 +1269,20 @@ export function format(type, value, le, options = {}) {
1219
1269
  :
1220
1270
  value);
1221
1271
  case DdbType.short:
1222
- return String((value === null || value === nulls.int16) ?
1223
- null
1272
+ return (value === null || value === nulls.int16) ?
1273
+ 'null'
1224
1274
  :
1225
- value);
1275
+ default_formatter.format(value);
1226
1276
  case DdbType.int:
1227
- return String((value === null || value === nulls.int32) ?
1228
- null
1277
+ return (value === null || value === nulls.int32) ?
1278
+ 'null'
1229
1279
  :
1230
- value);
1280
+ default_formatter.format(value);
1231
1281
  case DdbType.long:
1232
1282
  return (value === null || value === nulls.int64) ?
1233
1283
  'null'
1234
1284
  :
1235
- String(value);
1285
+ default_formatter.format(value);
1236
1286
  case DdbType.date:
1237
1287
  return (value === null || value === nulls.int32) ?
1238
1288
  'null'
@@ -1282,12 +1332,12 @@ export function format(type, value, le, options = {}) {
1282
1332
  return (value === null || value === nulls.float32) ?
1283
1333
  'null'
1284
1334
  :
1285
- String(value);
1335
+ formatter.format(value);
1286
1336
  case DdbType.double:
1287
1337
  return (value === null || value === nulls.double) ?
1288
1338
  'null'
1289
1339
  :
1290
- String(value);
1340
+ formatter.format(value);
1291
1341
  case DdbType.symbol:
1292
1342
  case DdbType.string:
1293
1343
  return value.quote('single');
@@ -1314,30 +1364,32 @@ export function format(type, value, le, options = {}) {
1314
1364
  :
1315
1365
  DdbObj.dec.decode(value)).quote('single');
1316
1366
  case DdbType.point: {
1317
- let [x, y] = value;
1318
- if (x === nulls.double)
1319
- x = null;
1320
- if (y === nulls.double)
1321
- y = null;
1322
- return `(${String(x)}, ${String(y)})`;
1367
+ const [x, y] = value;
1368
+ return `(${format(DdbType.double, x, le, options)}, ${format(DdbType.double, y, le, options)})`;
1323
1369
  }
1324
1370
  case DdbType.complex: {
1325
- let [x, y] = value;
1326
- if (x === nulls.double)
1327
- x = null;
1328
- if (y === nulls.double)
1329
- y = null;
1330
- return `${String(x)}+${String(y)}i`;
1371
+ const [x, y] = value;
1372
+ return `${format(DdbType.double, x, le, options)}+${format(DdbType.double, y, le, options)}i`;
1331
1373
  }
1332
1374
  case DdbType.duration: {
1333
1375
  const { data, unit } = value;
1334
1376
  return `${data}${DdbDurationUnit[unit]}`;
1335
1377
  }
1378
+ case DdbType.decimal32:
1379
+ case DdbType.decimal64: {
1380
+ const { scale, data } = value;
1381
+ if (data === null ||
1382
+ (data === nulls.int64 && type === DdbType.decimal64) ||
1383
+ (data === nulls.int32 && type === DdbType.decimal32))
1384
+ return 'null';
1385
+ const s = String(data < 0 ? -data : data).padStart(scale, '0');
1386
+ return (data < 0 ? '-' : '') + (scale ? `${s.slice(0, -scale) || '0'}.${s.slice(-scale)}` : s);
1387
+ }
1336
1388
  default:
1337
1389
  return String(value);
1338
1390
  }
1339
1391
  }
1340
- /** Format vector, the index-th item in the collection is a string, a null value returns a 'null' string */
1392
+ /** formatted vector, the index-th item in the collection is a string, a null value returns a 'null' string */
1341
1393
  export function formati(obj, index, options = {}) {
1342
1394
  if (64 <= obj.type && obj.type < 128) { // array vector
1343
1395
  // 因为 array vector 目前只支持:Logical, Integral(不包括 INT128, COMPRESS 类型), Floating, Temporal
@@ -1393,6 +1445,16 @@ export function formati(obj, index, options = {}) {
1393
1445
  case DdbType.complex:
1394
1446
  case DdbType.point:
1395
1447
  return format(obj.type, obj.value.subarray(2 * index, 2 * (index + 1)), obj.le, options);
1448
+ case DdbType.decimal32:
1449
+ case DdbType.decimal64: {
1450
+ const { scale, data } = obj.value;
1451
+ const x = data[index];
1452
+ if (x === nulls.int64 && obj.type === DdbType.decimal64 ||
1453
+ x === nulls.int32 && obj.type === DdbType.decimal32)
1454
+ return '';
1455
+ const s = String(x < 0 ? -x : x).padStart(scale, '0');
1456
+ return (x < 0 ? '-' : '') + (scale ? `${s.slice(0, -scale) || '0'}.${s.slice(-scale)}` : s);
1457
+ }
1396
1458
  default:
1397
1459
  return format(obj.type, obj.value[index], obj.le, options);
1398
1460
  }
@@ -1807,8 +1869,7 @@ export function datehour2str(datehour, format = 'YYYY.MM.DDTHH') {
1807
1869
  export function str2timestamp(str, format = 'YYYY.MM.DD HH:mm:ss.SSS') {
1808
1870
  if (!str || str === 'null')
1809
1871
  return nulls.int64;
1810
- if (str.length !== format.length)
1811
- throw new Error('The length of the timestamp string is not equal to the length of the format string');
1872
+ assert('The length of the timestamp string must be equal to the length of the format string');
1812
1873
  return BigInt(-timezone_offset +
1813
1874
  dayjs(str, format).valueOf());
1814
1875
  }
@@ -1821,12 +1882,10 @@ export function nanotime2str(nanotime, format = 'HH:mm:ss.SSSSSSSSS') {
1821
1882
  if (nanotime < 0n)
1822
1883
  return String(nanotime);
1823
1884
  const i_second_start = format.indexOf('ss');
1824
- if (i_second_start === -1)
1825
- throw new Error('格式串必须包含秒的格式 (ss)');
1885
+ assert(i_second_start !== -1, 'The format string must contain the format for seconds (ss)');
1826
1886
  const i_second_end = i_second_start + 2;
1827
1887
  const i_nanosecond_start = format.indexOf('SSSSSSSSS', i_second_end);
1828
- if (i_nanosecond_start === -1)
1829
- throw new Error('格式串必须包含纳秒的格式 (SSSSSSSSS)');
1888
+ assert(i_nanosecond_start !== -1, 'Format string must contain nanosecond format (SSSSSSSSS)');
1830
1889
  return (dayjs(timezone_offset + (Number(nanotime) / 1000000)).format(format.slice(0, i_second_end)) +
1831
1890
  format.slice(i_second_end, i_nanosecond_start) +
1832
1891
  String(nanotime % 1000000000n).padStart(9, '0'));
@@ -1854,12 +1913,10 @@ export function nanotimestamp2str(nanotimestamp, format = 'YYYY.MM.DD HH:mm:ss.S
1854
1913
  if (nanotimestamp === null || nanotimestamp === nulls.int64)
1855
1914
  return 'null';
1856
1915
  const i_second_start = format.indexOf('ss');
1857
- if (i_second_start === -1)
1858
- throw new Error('The format string must contain the format for seconds (ss)');
1916
+ assert(i_second_start !== -1, 'The format string must contain the format for seconds (ss)');
1859
1917
  const i_second_end = i_second_start + 2;
1860
1918
  const i_nanosecond_start = format.indexOf('SSSSSSSSS', i_second_end);
1861
- if (i_nanosecond_start === -1)
1862
- throw new Error('The format string must contain the format for nanoseconds (SSSSSSSSS)');
1919
+ assert(i_nanosecond_start !== -1, 'Format string must contain nanosecond format (SSSSSSSSS)');
1863
1920
  const remainder = nanotimestamp % 1000000000n;
1864
1921
  const borrow = remainder < 0n;
1865
1922
  return (dayjs(timezone_offset +
@@ -1881,15 +1938,12 @@ export function nanotimestamp2str(nanotimestamp, format = 'YYYY.MM.DD HH:mm:ss.S
1881
1938
  export function str2nanotimestamp(str, format = 'YYYY.MM.DD HH:mm:ss.SSSSSSSSS') {
1882
1939
  if (!str || str === 'null')
1883
1940
  return nulls.int64;
1884
- if (str.length !== format.length)
1885
- throw new Error('nanotimestamp string length is not equal to format string length');
1941
+ assert(str.length === format.length, 'nanotimestamp string length must be equal to format string length');
1886
1942
  const i_second_start = format.indexOf('ss');
1887
- if (i_second_start === -1)
1888
- throw new Error('The format string must contain the format for seconds (ss)');
1943
+ assert(i_second_start !== -1, 'The format string must contain the format for seconds (ss)');
1889
1944
  const i_second_end = i_second_start + 2;
1890
1945
  const i_nanosecond_start = format.indexOf('SSSSSSSSS', i_second_end);
1891
- if (i_nanosecond_start === -1)
1892
- throw new Error('Format string must contain nanosecond format (SSSSSSSSS)');
1946
+ assert(i_nanosecond_start !== -1, 'Format string must contain nanosecond format (SSSSSSSSS)');
1893
1947
  return (BigInt(-timezone_offset +
1894
1948
  dayjs(str.slice(0, i_second_end), format.slice(0, i_second_end)).valueOf()) * 1000000n
1895
1949
  +
@@ -1917,6 +1971,7 @@ export function int1282str(buffer, le = true) {
1917
1971
  return [...buf].map(x => x.toString(16)
1918
1972
  .padStart(2, '0')).join('');
1919
1973
  }
1974
+ export const winsize = 40;
1920
1975
  export class DDB {
1921
1976
  /** 当前的 session id (http 或 tcp) */
1922
1977
  sid = '0';
@@ -1940,13 +1995,18 @@ export class DDB {
1940
1995
  password = '123456';
1941
1996
  /** python session flag (2048) */
1942
1997
  python = false;
1998
+ /** Whether it is a streaming data connection, this field is always null for non-streaming data */
1999
+ streaming = null;
2000
+ // --- 内部选项, 状态
1943
2001
  print_message_buffer = false;
1944
2002
  print_object_buffer = false;
1945
2003
  print_message = true;
1946
2004
  parse_object = true;
2005
+ pnode_run_defined = false;
1947
2006
  /** DdbMessage listeners */
1948
2007
  listeners = [];
1949
2008
  pconnect = Promise.resolve();
2009
+ ppnoderun = Promise.resolve();
1950
2010
  presult = Promise.resolve(null);
1951
2011
  get connected() {
1952
2012
  return this.websocket?.readyState === WebSocket.OPEN;
@@ -1960,6 +2020,7 @@ export class DDB {
1960
2020
  - username?: DolphinDB username, default `'admin'`
1961
2021
  - password?: DolphinDB password, default `'123456'`
1962
2022
  - python?: set python session flag, default `false`
2023
+ - streaming?: When this option is set, the WebSocket connection is only used for streaming data
1963
2024
 
1964
2025
  @example
1965
2026
  let ddb = new DDB('ws://127.0.0.1:8848')
@@ -1977,7 +2038,7 @@ export class DDB {
1977
2038
  const _url = new URL(location.href);
1978
2039
  const hostname = _url.searchParams.get('hostname') || location.hostname;
1979
2040
  const port = _url.searchParams.get('port') || location.port;
1980
- url = `${location.protocol === 'https:' ? 'wss' : 'ws'}://${hostname}${port ? `:${port}` : ''}/`;
2041
+ url = `${location.protocol === 'https:' ? 'wss' : 'ws'}://${hostname}:${port}/`;
1981
2042
  }
1982
2043
  this.url = url;
1983
2044
  if (options.autologin !== undefined)
@@ -1988,8 +2049,13 @@ export class DDB {
1988
2049
  this.password = options.password;
1989
2050
  if (options.python !== undefined)
1990
2051
  this.python = options.python;
2052
+ if (options.streaming !== undefined)
2053
+ this.streaming = options.streaming;
2054
+ }
2055
+ on_message(event) {
2056
+ 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');
2057
+ console.log(event.data);
1991
2058
  }
1992
- on_message(event) { }
1993
2059
  /** Establish the actual WebSocket connection to the DolphinDB corresponding to the URL
1994
2060
  - options?:
1995
2061
  - url?: DolphinDB WebSocket URL. By default, the WebSocket URL passed in when the instance is initialized is used
@@ -1997,69 +2063,42 @@ export class DDB {
1997
2063
  - username?: DolphinDB username, default `'admin'`
1998
2064
  - password?: DolphinDB password, default `'123456'`
1999
2065
  - python?: set python session flag, default `false`
2066
+ - streaming?: When this option is set, the WebSocket connection is only used for streaming data
2000
2067
  */
2001
- async connect(options = {}) {
2002
- if (options.url !== undefined)
2003
- this.url = options.url;
2004
- if (options.autologin !== undefined)
2005
- this.autologin = options.autologin;
2006
- if (options.username !== undefined)
2007
- this.username = options.username;
2008
- if (options.password !== undefined)
2009
- this.password = options.password;
2010
- if (options.python !== undefined)
2011
- this.python = options.python;
2068
+ async connect({ url, autologin, username, password, python, streaming } = {}) {
2069
+ if (url !== undefined)
2070
+ this.url = url;
2071
+ if (autologin !== undefined)
2072
+ this.autologin = autologin;
2073
+ if (username !== undefined)
2074
+ this.username = username;
2075
+ if (password !== undefined)
2076
+ this.password = password;
2077
+ if (python !== undefined)
2078
+ this.python = python;
2079
+ if (streaming !== undefined)
2080
+ this.streaming = streaming;
2012
2081
  this.disconnect();
2013
2082
  await connect_websocket(this.url, {
2014
- protocols: this.python ? ['python'] : [],
2083
+ protocols: (() => {
2084
+ if (this.streaming)
2085
+ return 'streaming';
2086
+ if (this.python)
2087
+ return 'python';
2088
+ })(),
2015
2089
  on_open: async (event, websocket) => {
2016
2090
  this.websocket = websocket;
2017
- await this.rpc('connect', {});
2091
+ if (this.streaming)
2092
+ await this.subscribe();
2093
+ else
2094
+ await this.rpc('connect', {});
2018
2095
  },
2019
2096
  on_message: (event) => {
2020
2097
  this.on_message(event);
2021
2098
  }
2022
2099
  });
2023
- await this.eval(this.python ?
2024
- 'def pnode_run (nodes, func_name, args, add_node_alias):\n' +
2025
- ' nargs = size(args)\n' +
2026
- ' func = funcByName(func_name)\n' +
2027
- ' \n' +
2028
- ' if not nargs:\n' +
2029
- ' return pnodeRun(func, nodes, add_node_alias)\n' +
2030
- ' \n' +
2031
- ' args_partial = [ ]\n' +
2032
- ' args_partial.append(func)\n' +
2033
- ' for a in args:\n' +
2034
- ' args_partial.append(a)\n' +
2035
- ' \n' +
2036
- ' return pnodeRun(\n' +
2037
- ' unifiedCall(partial, args_partial),\n' +
2038
- ' nodes,\n' +
2039
- ' add_node_alias\n' +
2040
- ' )\n'
2041
- :
2042
- 'def pnode_run (nodes, func_name, args, add_node_alias = true) {\n' +
2043
- ' nargs = size(args)\n' +
2044
- ' func = funcByName(func_name)\n' +
2045
- ' \n' +
2046
- ' if (!nargs)\n' +
2047
- ' return pnodeRun(func, nodes, add_node_alias)\n' +
2048
- ' \n' +
2049
- ' args_partial = array(any, 1 + nargs, 1 + nargs)\n' +
2050
- ' args_partial[0] = func\n' +
2051
- ' args_partial[1:] = args\n' +
2052
- ' return pnodeRun(\n' +
2053
- ' unifiedCall(partial, args_partial),\n' +
2054
- ' nodes,\n' +
2055
- ' add_node_alias\n' +
2056
- ' )\n' +
2057
- '}\n', { urgent: true });
2058
- if (this.autologin)
2059
- if (this.python)
2060
- await this.eval(`login(${this.username.quote('double')}, ${this.password.quote('double')})`, { urgent: true });
2061
- else
2062
- await this.call('login', [this.username, this.password], { urgent: true });
2100
+ if (!this.streaming && this.autologin)
2101
+ await this.call('login', [this.username, this.password], { urgent: true });
2063
2102
  }
2064
2103
  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, } = {}) {
2065
2104
  let flag = 0;
@@ -2098,6 +2137,7 @@ export class DDB {
2098
2137
  this.on_message = () => { };
2099
2138
  this.presult = Promise.resolve(null);
2100
2139
  this.pconnect = Promise.resolve();
2140
+ this.pnode_run_defined = false;
2101
2141
  }
2102
2142
  /** rpc through websocket (function/script/variable command)
2103
2143
  - type: API 类型: 'script' | 'function' | 'variable'
@@ -2126,6 +2166,55 @@ export class DDB {
2126
2166
  }
2127
2167
  if (!this.connected)
2128
2168
  throw new Error(`${this.url} is already disconnected`);
2169
+ if (func === 'pnode_run' && !this.pnode_run_defined) {
2170
+ const ptail = this.ppnoderun;
2171
+ let resolve;
2172
+ this.ppnoderun = new Promise((_resolve, _reject) => {
2173
+ resolve = _resolve;
2174
+ });
2175
+ await ptail;
2176
+ try {
2177
+ if (!this.pnode_run_defined)
2178
+ await this.eval(this.python ?
2179
+ 'def pnode_run (nodes, func_name, args, add_node_alias):\n' +
2180
+ ' nargs = size(args)\n' +
2181
+ ' func = funcByName(func_name)\n' +
2182
+ ' \n' +
2183
+ ' if not nargs:\n' +
2184
+ ' return pnodeRun(func, nodes, add_node_alias)\n' +
2185
+ ' \n' +
2186
+ ' args_partial = [ ]\n' +
2187
+ ' args_partial.append(func)\n' +
2188
+ ' for a in args:\n' +
2189
+ ' args_partial.append(a)\n' +
2190
+ ' \n' +
2191
+ ' return pnodeRun(\n' +
2192
+ ' unifiedCall(partial, args_partial),\n' +
2193
+ ' nodes,\n' +
2194
+ ' add_node_alias\n' +
2195
+ ' )\n'
2196
+ :
2197
+ 'def pnode_run (nodes, func_name, args, add_node_alias = true) {\n' +
2198
+ ' nargs = size(args)\n' +
2199
+ ' func = funcByName(func_name)\n' +
2200
+ ' \n' +
2201
+ ' if (!nargs)\n' +
2202
+ ' return pnodeRun(func, nodes, add_node_alias)\n' +
2203
+ ' \n' +
2204
+ ' args_partial = array(any, 1 + nargs, 1 + nargs)\n' +
2205
+ ' args_partial[0] = func\n' +
2206
+ ' args_partial[1:] = args\n' +
2207
+ ' return pnodeRun(\n' +
2208
+ ' unifiedCall(partial, args_partial),\n' +
2209
+ ' nodes,\n' +
2210
+ ' add_node_alias\n' +
2211
+ ' )\n' +
2212
+ '}\n', { urgent: true });
2213
+ }
2214
+ finally {
2215
+ resolve();
2216
+ }
2217
+ }
2129
2218
  // this 上的当前配置需要在 message 到达后使用,先保存起来
2130
2219
  const _handlers = [...this.listeners].reverse();
2131
2220
  // 临界区:保证多个 rpc 并发时形成 promise 链
@@ -2227,8 +2316,7 @@ export class DDB {
2227
2316
  */
2228
2317
  async call(func, args = [], { urgent, node, nodes, func_type, add_node_alias, listener, parse_object, } = {}) {
2229
2318
  if (node) {
2230
- if (typeof func_type === 'undefined')
2231
- throw new Error('指定 node 时必须设置 func_type');
2319
+ assert(func_type in DdbFunctionType, 'func_type must be set when specifying node');
2232
2320
  args = [
2233
2321
  node,
2234
2322
  new DdbFunction(func, func_type),
@@ -2265,10 +2353,21 @@ export class DDB {
2265
2353
  vars,
2266
2354
  /** Uploaded variables' value */
2267
2355
  args, { listener, parse_object, } = {}) {
2268
- if (!args.length || args.length !== vars.length)
2269
- throw new Error('variable 指令参数为空或参数名为空,或数量不匹配');
2356
+ assert(args.length && args.length === vars.length, 'variable command parameter cannot be empty and parameter name cannot be empty, and the number should match');
2270
2357
  return this.rpc('variable', { vars, args, listener, parse_object });
2271
2358
  }
2359
+ /** Cancel all console jobs corresponding to the current session id */
2360
+ async cancel() {
2361
+ let ddb = new DDB(this.url, this);
2362
+ try {
2363
+ await ddb.eval(`jobs = exec rootJobId from getConsoleJobs() where sessionId = ${this.sid}\n` +
2364
+ 'if (size(jobs))\n' +
2365
+ ' cancelConsoleJob(jobs)\n', { urgent: true });
2366
+ }
2367
+ finally {
2368
+ ddb.disconnect();
2369
+ }
2370
+ }
2272
2371
  /** 解析服务端响应报文,返回去掉 header 的 data buf */
2273
2372
  parse_message(buf, parse_object = this.parse_object) {
2274
2373
  // MSG\n
@@ -2283,8 +2382,8 @@ export class DDB {
2283
2382
  // 'OK\n'
2284
2383
  // '\x04\x00\x02\x00\x00\x00'
2285
2384
  /** index of line feed 0 */
2286
- const i_lf_0 = buf.indexOf(0x0a); // '\n'
2287
- const parts = this.dec.decode(buf.subarray(0, i_lf_0)).split(' ');
2385
+ const ilf0 = buf.indexOf(0x0a); // '\n'
2386
+ const parts = this.dec.decode(buf.subarray(0, ilf0)).split(' ');
2288
2387
  /** session id */
2289
2388
  const sid = parts[0];
2290
2389
  if (sid !== this.sid) {
@@ -2292,35 +2391,142 @@ export class DDB {
2292
2391
  this.sid = sid;
2293
2392
  }
2294
2393
  /** 返回对象的数量 */
2295
- const n_obj = Number(parts[1]);
2394
+ const nobj = Number(parts[1]);
2296
2395
  /** 大小端: 协议中大端为 0, 小端为 1 */
2297
2396
  this.le = Number(parts[2]) !== 0;
2298
- const i_ls_1 = i_lf_0 + 1;
2299
- const i_lf_1 = buf.indexOf(0x0a, i_ls_1); // '\n'
2397
+ const ils1 = ilf0 + 1;
2398
+ const ilf1 = buf.indexOf(0x0a, ils1); // '\n'
2300
2399
  /** 'OK' 表示成功,其它文本表示失败 */
2301
- const message = this.dec.decode(buf.subarray(i_ls_1, i_lf_1));
2400
+ const message = this.dec.decode(buf.subarray(ils1, ilf1));
2302
2401
  if (message !== 'OK')
2303
2402
  return {
2304
2403
  type: 'error',
2305
2404
  data: new Error(message)
2306
2405
  };
2307
- const buf_obj = buf.subarray(i_lf_1 + 1);
2406
+ const bufobj = buf.subarray(ilf1 + 1);
2308
2407
  if (this.print_object_buffer)
2309
- console.log(buf_obj);
2408
+ console.log(bufobj);
2310
2409
  return {
2311
2410
  type: 'object',
2312
2411
  data: parse_object ?
2313
- DdbObj.parse(buf_obj, this.le)
2412
+ DdbObj.parse(bufobj, this.le)
2314
2413
  :
2315
2414
  new DdbObj({
2316
2415
  form: DdbForm.scalar,
2317
2416
  type: DdbType.void,
2318
2417
  length: 0,
2319
2418
  le: this.le,
2320
- buffer: buf_obj,
2419
+ buffer: bufobj,
2321
2420
  })
2322
2421
  };
2323
2422
  }
2423
+ /** Internal stream subscription method */
2424
+ async subscribe() {
2425
+ const args = DdbObj.to_ddbobjs([
2426
+ new DdbVectorString(this.autologin ?
2427
+ [this.username, this.password]
2428
+ :
2429
+ // 不自动登录也要传空的 string vector, 否则 server 无法解析
2430
+ []),
2431
+ // todo: 还有用吗?需要获取客户端 IP 吗
2432
+ 'localhost',
2433
+ // todo: 端口还有用吗?
2434
+ new DdbInt(0),
2435
+ this.streaming.table,
2436
+ (this.streaming.action ||= `api_js_${new Date().getTime()}`),
2437
+ // new DdbInt(-1), // offset
2438
+ // filter
2439
+ // allow exists
2440
+ ]);
2441
+ return new Promise((resolve, reject) => {
2442
+ let first = true;
2443
+ let second = true;
2444
+ // 先准备好收到 websocket message 的 callback
2445
+ this.on_message = ({ data: buffer }) => {
2446
+ if (first) {
2447
+ first = false;
2448
+ try {
2449
+ const dv = new DataView(buffer);
2450
+ const status = dv.getUint8(1);
2451
+ this.le = Boolean(dv.getUint8(2));
2452
+ const nobjs = dv.getUint32(3, this.le);
2453
+ // 解析返回数据,从 server 实现看其实只有一个 DdbObj
2454
+ let offset = 7;
2455
+ let objs = new Array(nobjs);
2456
+ const buf = new Uint8Array(buffer);
2457
+ for (let i = 0; i < nobjs; i++) {
2458
+ const obj = DdbObj.parse(buf.subarray(offset), this.le);
2459
+ offset += obj.length;
2460
+ objs[i] = obj;
2461
+ console.log(`订阅流表的响应数据: objs[${i}]: ${obj.toString()}`);
2462
+ }
2463
+ this.streaming.colnames = objs[0].value;
2464
+ if (status === StreamingStatusCode.ok)
2465
+ console.log('Subscribed to streaming table:', { status: StreamingStatusCode[status], result: objs[0] });
2466
+ else
2467
+ reject(new Error(`Failed to subscribe to streaming table: { status: ${StreamingStatusCode[status]}, error: ${objs[0]?.value} }`));
2468
+ }
2469
+ catch (error) {
2470
+ reject(error);
2471
+ }
2472
+ }
2473
+ else {
2474
+ try {
2475
+ const dv = new DataView(buffer);
2476
+ const buf = new Uint8Array(buffer);
2477
+ let { streaming } = this;
2478
+ streaming.time = dv.getBigInt64(1, this.le);
2479
+ streaming.id = dv.getBigInt64(9, this.le);
2480
+ const i_topic_end = buf.indexOf(0, 17);
2481
+ streaming.topic = this.dec.decode(buf.subarray(17, i_topic_end));
2482
+ if (second) {
2483
+ second = false;
2484
+ // 是 table
2485
+ streaming.schema = DdbObj.parse(buf.subarray(i_topic_end + 1), this.le);
2486
+ console.log('Received streaming table schema:', streaming.schema);
2487
+ streaming.window = {
2488
+ offset: 0,
2489
+ rows: 0,
2490
+ segments: [],
2491
+ };
2492
+ assert(streaming.schema.rows === 0, 'schema.rows === 0');
2493
+ resolve(streaming.schema);
2494
+ }
2495
+ else {
2496
+ // 是 column 片段组成的 any vector
2497
+ const data = streaming.data = DdbObj.parse(buf.subarray(i_topic_end + 1), this.le);
2498
+ let { window: win } = streaming;
2499
+ win.rows += data.rows;
2500
+ win.segments.push(data);
2501
+ if (win.rows >= winsize * 2 && win.segments.length >= 2) {
2502
+ let winsize_ = 0;
2503
+ let i = win.segments.length - 1;
2504
+ // 往前移动至首个累计 winsize_ 超过 winsize 的位置
2505
+ for (; winsize_ < winsize; i--)
2506
+ winsize_ += win.segments[i].rows;
2507
+ win.segments = win.segments.slice(i);
2508
+ win.offset += win.rows - winsize_;
2509
+ win.rows = winsize_;
2510
+ console.log('exceeds winsize, shrink window.segments', win);
2511
+ }
2512
+ streaming.handler(streaming);
2513
+ }
2514
+ }
2515
+ catch (error) {
2516
+ reject(error);
2517
+ }
2518
+ }
2519
+ };
2520
+ // 发送订阅 websocket message
2521
+ this.websocket.send(concat([
2522
+ Uint8Array.of(1, // from = SubscriberFromType::FROM_API
2523
+ 1, // type = SubscriberRPCType::RPC_START
2524
+ Number(DDB.le_client)),
2525
+ Uint32Array.of(args.length),
2526
+ ...args.map(obj => obj.pack())
2527
+ ]));
2528
+ });
2529
+ }
2324
2530
  }
2325
2531
  export let ddb = window.ddb = new DDB();
2326
2532
  //# sourceMappingURL=browser.js.map