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/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["object"] = 37] = "object";
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
- /** 可以表示所有 DolphinDB 数据库中的数据类型 */
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
- /** 实际数据。不同的 DdbForm, DdbType 使用 DdbValue 中不同的类型来表示实际数据 */
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;
@@ -267,7 +279,7 @@ export class DdbObj {
267
279
  if (form === DdbForm.dict)
268
280
  return dict;
269
281
  else {
270
- const { chartType: type, stacking, title: titles, extras, data, ...others } = dict.to_dict();
282
+ const { chartType: type, stacking, binStart: bin_start, binEnd: bin_end, binCount: bin_count, title: titles, extras, data, ...others } = dict.to_dict();
271
283
  const [chart, x_axis, y_axis] = titles.value;
272
284
  dict.form = DdbForm.chart;
273
285
  dict.value = {
@@ -278,6 +290,8 @@ export class DdbObj {
278
290
  x_axis,
279
291
  y_axis,
280
292
  },
293
+ ...bin_start ? { bin_start, bin_end, } : {},
294
+ ...bin_count ? { bin_count } : {},
281
295
  ...extras ? (() => {
282
296
  const { multiYAxes: multi_y_axes = false, ...extras_others } = extras.to_dict({ strip: true });
283
297
  return {
@@ -329,8 +343,7 @@ export class DdbObj {
329
343
  col_labels.length += 2;
330
344
  offset += col_labels.length;
331
345
  }
332
- if (buf_data[offset] !== type)
333
- throw new Error('matrix.datatype !== matrix.type');
346
+ assert(buf_data[offset] === type, 'matrix.datatype === matrix.type');
334
347
  const rows = dv.getUint32(offset + 2, le);
335
348
  const cols = dv.getUint32(offset + 6, le);
336
349
  const [len_items, data] = this.parse_vector_items(buf_data.subarray(offset + 10), le, type, rows * cols // 假设小于 2**32
@@ -441,38 +454,30 @@ export class DdbObj {
441
454
  case DdbType.uuid:
442
455
  case DdbType.ipaddr:
443
456
  case DdbType.int128:
444
- return [
445
- 16,
446
- buf.slice(0, 16)
447
- ];
457
+ return [16, buf.slice(0, 16)];
448
458
  case DdbType.blob: {
449
459
  const dv = new DataView(buf.buffer, buf.byteOffset);
450
460
  const length = dv.getUint32(0, le);
451
- return [
452
- 4 + length,
453
- buf.slice(4, 4 + length)
454
- ];
461
+ return [4 + length, buf.slice(4, 4 + length)];
455
462
  }
456
463
  case DdbType.complex:
457
464
  case DdbType.point: {
458
465
  const dv = new DataView(buf.buffer, buf.byteOffset);
459
- return [
460
- 16,
461
- [
462
- dv.getFloat64(0, le),
463
- dv.getFloat64(8, le)
464
- ]
465
- ];
466
+ return [16, [dv.getFloat64(0, le), dv.getFloat64(8, le)]];
466
467
  }
467
468
  case DdbType.duration: {
468
469
  const dv = new DataView(buf.buffer, buf.byteOffset);
469
- return [
470
- 8,
471
- {
472
- unit: dv.getUint32(4, le),
473
- data: dv.getInt32(0, le)
474
- }
475
- ];
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 }];
476
481
  }
477
482
  default:
478
483
  throw new Error(`${DdbType[type] || type} 暂时不支持解析`);
@@ -633,7 +638,7 @@ export class DdbObj {
633
638
  // 02 00 00 00 symbol base size = 2
634
639
  // 00 61 61 00 以 \0 分割的字符串
635
640
  // 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00>
636
- const dv = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
641
+ const dv = new DataView(buf.buffer, buf.byteOffset);
637
642
  const base_id = dv.getUint32(0, le);
638
643
  const base_size = dv.getUint32(4, le);
639
644
  let base_length = 0;
@@ -668,7 +673,7 @@ export class DdbObj {
668
673
  // 04 00 00 00 61 62 63 64
669
674
  // 04 00 00 00 61 62 63 64>
670
675
  let value = new Array(length);
671
- const dv = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
676
+ const dv = new DataView(buf.buffer, buf.byteOffset);
672
677
  let i_head = 0;
673
678
  for (let i = 0; i < length; i++) {
674
679
  const sublen = dv.getUint32(i_head, le);
@@ -708,6 +713,31 @@ export class DdbObj {
708
713
  values
709
714
  ];
710
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
+ }
711
741
  // case DdbType.duration: -> 实际会返回一个 any vector
712
742
  // [2y, 1M, 3d, 7H, 11m, 12s, 15ms, 16us, 17ns]
713
743
  // <Buffer 19 01 type = any, form = vector
@@ -811,10 +841,7 @@ export class DdbObj {
811
841
  case DdbType.int128:
812
842
  return [this.value];
813
843
  case DdbType.blob:
814
- return [
815
- Uint32Array.of(this.value.byteLength),
816
- this.value
817
- ];
844
+ return [Uint32Array.of(this.value.byteLength), this.value];
818
845
  case DdbType.complex:
819
846
  case DdbType.point:
820
847
  return [Float64Array.from(this.value)];
@@ -822,8 +849,16 @@ export class DdbObj {
822
849
  const { unit, data } = this.value;
823
850
  return [Int32Array.of(data, unit)];
824
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
+ }
825
860
  default:
826
- throw new Error(`${DdbType[type] || type} 暂时不支持序列化`);
861
+ throw new Error(`${DdbType[type] || type} serialization is not currently supported`);
827
862
  }
828
863
  case DdbForm.vector:
829
864
  case DdbForm.pair:
@@ -843,12 +878,7 @@ export class DdbObj {
843
878
  ...DdbObj.pack_vector_body(value, type, this.rows)
844
879
  ];
845
880
  case DdbForm.set:
846
- return [
847
- new DdbObj({
848
- ...this,
849
- form: DdbForm.vector,
850
- }).pack()
851
- ];
881
+ return [new DdbObj({ ...this, form: DdbForm.vector }).pack()];
852
882
  case DdbForm.table:
853
883
  return [
854
884
  Uint32Array.of(this.rows, this.cols),
@@ -860,36 +890,27 @@ export class DdbObj {
860
890
  ...this.value.map(col => col.pack())
861
891
  ];
862
892
  case DdbForm.dict:
863
- return [
864
- value[0].pack(),
865
- value[1].pack(),
866
- ];
893
+ return [value[0].pack(), value[1].pack()];
867
894
  case DdbForm.chart: {
868
- const { type, stacking, titles: { chart, x_axis, y_axis, }, extras, data } = this.value;
895
+ const { type, stacking, bin_start, bin_end, bin_count, titles: { chart, x_axis, y_axis }, extras, data } = this.value;
869
896
  const { value: [keys, values] } = new DdbDict({
870
897
  chartType: new DdbInt(type),
871
898
  stacking,
899
+ ...bin_start ? { binStart: bin_start, binEnd: bin_end } : {},
900
+ ...bin_count ? { binCount: bin_count } : {},
872
901
  title: new DdbVectorString([chart, x_axis, y_axis]),
873
902
  ...extras ? (() => {
874
903
  const { multi_y_axes, ...extras_other } = extras;
875
- return {
876
- extras: new DdbDict({
877
- multiYAxes: multi_y_axes,
878
- ...extras_other
879
- })
880
- };
904
+ return { extras: new DdbDict({ multiYAxes: multi_y_axes, ...extras_other }) };
881
905
  })() : {},
882
906
  data,
883
907
  });
884
- return [
885
- keys.pack(),
886
- values.pack(),
887
- ];
908
+ return [keys.pack(), values.pack()];
888
909
  }
889
910
  case DdbForm.matrix: {
890
911
  const { rows, cols, data } = value;
891
912
  return [
892
- Uint8Array.of((rows && 0x01) | (cols && 0x02)),
913
+ Uint8Array.of((rows ? 0x01 : 0x00) | (cols ? 0x02 : 0x00)),
893
914
  ...rows ? [rows.pack()] : [],
894
915
  ...cols ? [cols.pack()] : [],
895
916
  Uint8Array.of(this.type, this.form),
@@ -898,7 +919,7 @@ export class DdbObj {
898
919
  ];
899
920
  }
900
921
  default:
901
- throw new Error(`${DdbForm[form]} 暂不支持序列化`);
922
+ throw new Error(`${DdbForm[form]} serialization is not currently supported`);
902
923
  }
903
924
  })();
904
925
  if (!body)
@@ -987,6 +1008,11 @@ export class DdbObj {
987
1008
  data
988
1009
  ];
989
1010
  }
1011
+ case DdbType.decimal32:
1012
+ case DdbType.decimal64: {
1013
+ const { scale, data } = value;
1014
+ return [Int32Array.of(scale), data];
1015
+ }
990
1016
  default:
991
1017
  throw new Error(`vector ${DdbType[type]} 暂不支持序列化`);
992
1018
  }
@@ -1066,6 +1092,15 @@ export class DdbObj {
1066
1092
  items[i] = format(this.type, value.subarray(2 * i, 2 * (i + 1)), this.le, options);
1067
1093
  return format_array(items, len_data > limit);
1068
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
+ }
1069
1104
  default: {
1070
1105
  const limit = 50;
1071
1106
  let items = new Array(Math.min(limit, this.value.length));
@@ -1100,7 +1135,7 @@ export class DdbObj {
1100
1135
  case DdbForm.table:
1101
1136
  return `table[${this.rows}r][${this.cols}c]`;
1102
1137
  case DdbForm.dict:
1103
- return `dict<${DdbType[this.value[0].type]}, ${DdbType[this.value[1].type]}>`;
1138
+ return `dict<${DdbType[this.value[0].type]}, ${DdbType[this.value[1].type]}>[${this.rows}]`;
1104
1139
  case DdbForm.chart:
1105
1140
  return `chart<${DdbChartType[this.value.type]}>`;
1106
1141
  case DdbForm.matrix:
@@ -1128,8 +1163,7 @@ export class DdbObj {
1128
1163
  return values.map(value => this.to_ddbobj(value));
1129
1164
  }
1130
1165
  to_rows() {
1131
- if (this.form !== DdbForm.table)
1132
- 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');
1133
1167
  let rows = new Array(this.rows);
1134
1168
  for (let i = 0; i < this.rows; i++) {
1135
1169
  let row = {};
@@ -1161,13 +1195,10 @@ export class DdbObj {
1161
1195
  return rows;
1162
1196
  }
1163
1197
  to_dict({ strip, deep, } = {}) {
1164
- if (this.form !== DdbForm.dict)
1165
- 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');
1166
1199
  const [{ value: keys, type: key_type }, { value: values, type: value_type }] = this.value;
1167
- if (key_type !== DdbType.string || value_type !== DdbType.any)
1168
- throw new Error('Not dict<string, any>, automatic conversion to js object is not supported for the time being');
1169
- if (deep && !strip)
1170
- 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');
1171
1202
  let obj = {};
1172
1203
  for (let i = 0; i < this.rows; i++) {
1173
1204
  let value = values[i];
@@ -1179,8 +1210,23 @@ export class DdbObj {
1179
1210
  return obj;
1180
1211
  }
1181
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 });
1182
1218
  /** Formats a single element (value) as a string according to DdbType, null returns a 'null' string */
1183
- 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
+ })();
1184
1230
  switch (type) {
1185
1231
  case DdbType.bool:
1186
1232
  return inspect((value === null || value === nulls.int8) ?
@@ -1198,26 +1244,32 @@ export function format(type, value, le, options) {
1198
1244
  :
1199
1245
  value, options);
1200
1246
  case DdbType.short:
1201
- return inspect((value === null || value === nulls.int16) ?
1202
- null
1247
+ return value === null || value === nulls.int16 ?
1248
+ inspect(null, options)
1203
1249
  :
1204
- value, options);
1250
+ options.colors ?
1251
+ default_formatter.format(value).green
1252
+ :
1253
+ default_formatter.format(value);
1205
1254
  case DdbType.int:
1206
- return inspect((value === null || value === nulls.int32) ?
1207
- null
1255
+ return value === null || value === nulls.int32 ?
1256
+ inspect(null, options)
1208
1257
  :
1209
- value, options);
1258
+ options.colors ?
1259
+ default_formatter.format(value).green
1260
+ :
1261
+ default_formatter.format(value);
1210
1262
  case DdbType.long:
1211
- return (value === null || value === nulls.int64) ?
1212
- 'null'
1263
+ return value === null || value === nulls.int64 ?
1264
+ inspect(null, options)
1213
1265
  :
1214
1266
  options.colors ?
1215
- String(value).green
1267
+ default_formatter.format(value).green
1216
1268
  :
1217
- String(value);
1269
+ default_formatter.format(value);
1218
1270
  case DdbType.date:
1219
1271
  return (value === null || value === nulls.int32) ?
1220
- 'null'
1272
+ inspect(null, options)
1221
1273
  :
1222
1274
  options.colors ?
1223
1275
  date2str(value).green
@@ -1225,7 +1277,7 @@ export function format(type, value, le, options) {
1225
1277
  date2str(value);
1226
1278
  case DdbType.month:
1227
1279
  return (value === null || value === nulls.int32) ?
1228
- 'null'
1280
+ inspect(null, options)
1229
1281
  :
1230
1282
  options.colors ?
1231
1283
  month2str(value).green
@@ -1233,7 +1285,7 @@ export function format(type, value, le, options) {
1233
1285
  month2str(value);
1234
1286
  case DdbType.time:
1235
1287
  return (value === null || value === nulls.int32) ?
1236
- 'null'
1288
+ inspect(null, options)
1237
1289
  :
1238
1290
  options.colors ?
1239
1291
  time2str(value).green
@@ -1241,7 +1293,7 @@ export function format(type, value, le, options) {
1241
1293
  time2str(value);
1242
1294
  case DdbType.minute:
1243
1295
  return (value === null || value === nulls.int32) ?
1244
- 'null'
1296
+ inspect(null, options)
1245
1297
  :
1246
1298
  options.colors ?
1247
1299
  minute2str(value).green
@@ -1249,7 +1301,7 @@ export function format(type, value, le, options) {
1249
1301
  minute2str(value);
1250
1302
  case DdbType.second:
1251
1303
  return (value === null || value === nulls.int32) ?
1252
- 'null'
1304
+ inspect(null, options)
1253
1305
  :
1254
1306
  options.colors ?
1255
1307
  second2str(value).green
@@ -1257,7 +1309,7 @@ export function format(type, value, le, options) {
1257
1309
  second2str(value);
1258
1310
  case DdbType.datetime:
1259
1311
  return (value === null || value === nulls.int32) ?
1260
- 'null'
1312
+ inspect(null, options)
1261
1313
  :
1262
1314
  options.colors ?
1263
1315
  datetime2str(value).green
@@ -1265,7 +1317,7 @@ export function format(type, value, le, options) {
1265
1317
  datetime2str(value);
1266
1318
  case DdbType.timestamp:
1267
1319
  return (value === null || value === nulls.int64) ?
1268
- 'null'
1320
+ inspect(null, options)
1269
1321
  :
1270
1322
  options.colors ?
1271
1323
  timestamp2str(value).green
@@ -1273,7 +1325,7 @@ export function format(type, value, le, options) {
1273
1325
  timestamp2str(value);
1274
1326
  case DdbType.nanotime:
1275
1327
  return (value === null || value === nulls.int64) ?
1276
- 'null'
1328
+ inspect(null, options)
1277
1329
  :
1278
1330
  options.colors ?
1279
1331
  nanotime2str(value).green
@@ -1281,7 +1333,7 @@ export function format(type, value, le, options) {
1281
1333
  nanotime2str(value);
1282
1334
  case DdbType.nanotimestamp:
1283
1335
  return (value === null || value === nulls.int64) ?
1284
- 'null'
1336
+ inspect(null, options)
1285
1337
  :
1286
1338
  options.colors ?
1287
1339
  nanotimestamp2str(value).green
@@ -1289,14 +1341,20 @@ export function format(type, value, le, options) {
1289
1341
  nanotimestamp2str(value);
1290
1342
  case DdbType.float:
1291
1343
  return (value === null || value === nulls.float32) ?
1292
- 'null'
1344
+ inspect(null, options)
1293
1345
  :
1294
- inspect(value, options);
1346
+ options.colors ?
1347
+ formatter.format(value).green
1348
+ :
1349
+ formatter.format(value);
1295
1350
  case DdbType.double:
1296
1351
  return (value === null || value === nulls.double) ?
1297
- 'null'
1352
+ inspect(null, options)
1298
1353
  :
1299
- inspect(value, options);
1354
+ options.colors ?
1355
+ formatter.format(value).green
1356
+ :
1357
+ formatter.format(value);
1300
1358
  case DdbType.symbol:
1301
1359
  case DdbType.string:
1302
1360
  return inspect(value, options);
@@ -1335,38 +1393,35 @@ export function format(type, value, le, options) {
1335
1393
  :
1336
1394
  DdbObj.dec.decode(value), options);
1337
1395
  case DdbType.point: {
1338
- let [x, y] = value;
1339
- if (x === nulls.double)
1340
- x = null;
1341
- if (y === nulls.double)
1342
- y = null;
1343
- return options.colors ?
1344
- `(${String(x).green}, ${String(y).green})`
1345
- :
1346
- `(${String(x)}, ${String(y)})`;
1396
+ const [x, y] = value;
1397
+ return `(${format(DdbType.double, x, le, options)}, ${format(DdbType.double, y, le, options)})`;
1347
1398
  }
1348
1399
  case DdbType.complex: {
1349
- let [x, y] = value;
1350
- if (x === nulls.double)
1351
- x = null;
1352
- if (y === nulls.double)
1353
- y = null;
1354
- return options.colors ?
1355
- `${String(x).green}+${`${String(y)}i`.green}`
1356
- :
1357
- `${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`;
1358
1402
  }
1359
1403
  case DdbType.duration: {
1360
1404
  const { data, unit } = value;
1361
1405
  const str = `${data}${DdbDurationUnit[unit]}`;
1362
1406
  return options.colors ? str.green : str;
1363
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
+ }
1364
1419
  default:
1365
1420
  return inspect(value, options);
1366
1421
  }
1367
1422
  }
1368
1423
  /** formatted vector, the index-th item in the collection is a string, a null value returns a 'null' string */
1369
- export function formati(obj, index, options) {
1424
+ export function formati(obj, index, options = {}) {
1370
1425
  if (64 <= obj.type && obj.type < 128) { // array vector
1371
1426
  // 因为 array vector 目前只支持:Logical, Integral(不包括 INT128, COMPRESS 类型), Floating, Temporal
1372
1427
  // 都对应 TypedArray 中的一格,所以 lengths.length 等于 block 中的 row 的个数
@@ -1421,6 +1476,17 @@ export function formati(obj, index, options) {
1421
1476
  case DdbType.complex:
1422
1477
  case DdbType.point:
1423
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
+ }
1424
1490
  default:
1425
1491
  return format(obj.type, obj.value[index], obj.le, options);
1426
1492
  }
@@ -1835,8 +1901,7 @@ export function datehour2str(datehour, format = 'YYYY.MM.DDTHH') {
1835
1901
  export function str2timestamp(str, format = 'YYYY.MM.DD HH:mm:ss.SSS') {
1836
1902
  if (!str || str === 'null')
1837
1903
  return nulls.int64;
1838
- if (str.length !== format.length)
1839
- 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');
1840
1905
  return BigInt(-timezone_offset +
1841
1906
  dayjs(str, format).valueOf());
1842
1907
  }
@@ -1849,12 +1914,10 @@ export function nanotime2str(nanotime, format = 'HH:mm:ss.SSSSSSSSS') {
1849
1914
  if (nanotime < 0n)
1850
1915
  return String(nanotime);
1851
1916
  const i_second_start = format.indexOf('ss');
1852
- if (i_second_start === -1)
1853
- 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)');
1854
1918
  const i_second_end = i_second_start + 2;
1855
1919
  const i_nanosecond_start = format.indexOf('SSSSSSSSS', i_second_end);
1856
- if (i_nanosecond_start === -1)
1857
- 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)');
1858
1921
  return (dayjs(timezone_offset + (Number(nanotime) / 1000000)).format(format.slice(0, i_second_end)) +
1859
1922
  format.slice(i_second_end, i_nanosecond_start) +
1860
1923
  String(nanotime % 1000000000n).padStart(9, '0'));
@@ -1882,12 +1945,10 @@ export function nanotimestamp2str(nanotimestamp, format = 'YYYY.MM.DD HH:mm:ss.S
1882
1945
  if (nanotimestamp === null || nanotimestamp === nulls.int64)
1883
1946
  return 'null';
1884
1947
  const i_second_start = format.indexOf('ss');
1885
- if (i_second_start === -1)
1886
- 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)');
1887
1949
  const i_second_end = i_second_start + 2;
1888
1950
  const i_nanosecond_start = format.indexOf('SSSSSSSSS', i_second_end);
1889
- if (i_nanosecond_start === -1)
1890
- 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)');
1891
1952
  const remainder = nanotimestamp % 1000000000n;
1892
1953
  const borrow = remainder < 0n;
1893
1954
  return (dayjs(timezone_offset +
@@ -1909,15 +1970,12 @@ export function nanotimestamp2str(nanotimestamp, format = 'YYYY.MM.DD HH:mm:ss.S
1909
1970
  export function str2nanotimestamp(str, format = 'YYYY.MM.DD HH:mm:ss.SSSSSSSSS') {
1910
1971
  if (!str || str === 'null')
1911
1972
  return nulls.int64;
1912
- if (str.length !== format.length)
1913
- 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');
1914
1974
  const i_second_start = format.indexOf('ss');
1915
- if (i_second_start === -1)
1916
- 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)');
1917
1976
  const i_second_end = i_second_start + 2;
1918
1977
  const i_nanosecond_start = format.indexOf('SSSSSSSSS', i_second_end);
1919
- if (i_nanosecond_start === -1)
1920
- throw new Error('Format string must contain nanosecond format (SSSSSSSSS)');
1978
+ assert(i_nanosecond_start !== -1, 'Format string must contain nanosecond format (SSSSSSSSS)');
1921
1979
  return (BigInt(-timezone_offset +
1922
1980
  dayjs(str.slice(0, i_second_end), format.slice(0, i_second_end)).valueOf()) * 1000000n
1923
1981
  +
@@ -1945,6 +2003,7 @@ export function int1282str(buffer, le = true) {
1945
2003
  return [...buf].map(x => x.toString(16)
1946
2004
  .padStart(2, '0')).join('');
1947
2005
  }
2006
+ export const winsize = 40;
1948
2007
  export class DDB {
1949
2008
  /** 当前的 session id (http 或 tcp) */
1950
2009
  sid = '0';
@@ -1968,13 +2027,18 @@ export class DDB {
1968
2027
  password = '123456';
1969
2028
  /** python session flag (2048) */
1970
2029
  python = false;
2030
+ /** Whether it is a streaming data connection, this field is always null for non-streaming data */
2031
+ streaming = null;
2032
+ // --- 内部选项, 状态
1971
2033
  print_message_buffer = false;
1972
2034
  print_object_buffer = false;
1973
2035
  print_message = true;
1974
2036
  parse_object = true;
2037
+ pnode_run_defined = false;
1975
2038
  /** DdbMessage listeners */
1976
2039
  listeners = [];
1977
2040
  pconnect = Promise.resolve();
2041
+ ppnoderun = Promise.resolve();
1978
2042
  presult = Promise.resolve(null);
1979
2043
  get connected() {
1980
2044
  return this.websocket?.readyState === WebSocket.OPEN;
@@ -1988,11 +2052,12 @@ export class DDB {
1988
2052
  - username?: DolphinDB username, default `'admin'`
1989
2053
  - password?: DolphinDB password, default `'123456'`
1990
2054
  - python?: set python session flag, default `false`
2055
+ - streaming?: When this option is set, the WebSocket connection is only used for streaming data
1991
2056
 
1992
2057
  @example
1993
2058
  let ddb = new DDB('ws://127.0.0.1:8848')
1994
2059
 
1995
- // 使用 HTTPS 加密
2060
+ // Encrypt with HTTPS
1996
2061
  let ddbsecure = new DDB('wss://dolphindb.com', {
1997
2062
  autologin: true,
1998
2063
  username: 'admin',
@@ -2010,8 +2075,13 @@ export class DDB {
2010
2075
  this.password = options.password;
2011
2076
  if (options.python !== undefined)
2012
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);
2013
2084
  }
2014
- on_message(event) { }
2015
2085
  /** Establish the actual WebSocket connection to the DolphinDB corresponding to the URL
2016
2086
  - options?:
2017
2087
  - url?: DolphinDB WebSocket URL. By default, the WebSocket URL passed in when the instance is initialized is used
@@ -2019,69 +2089,42 @@ export class DDB {
2019
2089
  - username?: DolphinDB username, default `'admin'`
2020
2090
  - password?: DolphinDB password, default `'123456'`
2021
2091
  - python?: set python session flag, default `false`
2092
+ - streaming?: When this option is set, the WebSocket connection is only used for streaming data
2022
2093
  */
2023
- async connect(options = {}) {
2024
- if (options.url !== undefined)
2025
- this.url = options.url;
2026
- if (options.autologin !== undefined)
2027
- this.autologin = options.autologin;
2028
- if (options.username !== undefined)
2029
- this.username = options.username;
2030
- if (options.password !== undefined)
2031
- this.password = options.password;
2032
- if (options.python !== undefined)
2033
- this.python = options.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;
2034
2107
  this.disconnect();
2035
2108
  await connect_websocket(this.url, {
2036
- protocols: this.python ? ['python'] : [],
2109
+ protocols: (() => {
2110
+ if (this.streaming)
2111
+ return 'streaming';
2112
+ if (this.python)
2113
+ return 'python';
2114
+ })(),
2037
2115
  on_open: async (event, websocket) => {
2038
2116
  this.websocket = websocket;
2039
- await this.rpc('connect', {});
2117
+ if (this.streaming)
2118
+ await this.subscribe();
2119
+ else
2120
+ await this.rpc('connect', {});
2040
2121
  },
2041
2122
  on_message: (event) => {
2042
2123
  this.on_message(event);
2043
2124
  }
2044
2125
  });
2045
- await this.eval(this.python ?
2046
- 'def pnode_run (nodes, func_name, args, add_node_alias):\n' +
2047
- ' nargs = size(args)\n' +
2048
- ' func = funcByName(func_name)\n' +
2049
- ' \n' +
2050
- ' if not nargs:\n' +
2051
- ' return pnodeRun(func, nodes, add_node_alias)\n' +
2052
- ' \n' +
2053
- ' args_partial = [ ]\n' +
2054
- ' args_partial.append(func)\n' +
2055
- ' for a in args:\n' +
2056
- ' args_partial.append(a)\n' +
2057
- ' \n' +
2058
- ' return pnodeRun(\n' +
2059
- ' unifiedCall(partial, args_partial),\n' +
2060
- ' nodes,\n' +
2061
- ' add_node_alias\n' +
2062
- ' )\n'
2063
- :
2064
- 'def pnode_run (nodes, func_name, args, add_node_alias = true) {\n' +
2065
- ' nargs = size(args)\n' +
2066
- ' func = funcByName(func_name)\n' +
2067
- ' \n' +
2068
- ' if (!nargs)\n' +
2069
- ' return pnodeRun(func, nodes, add_node_alias)\n' +
2070
- ' \n' +
2071
- ' args_partial = array(any, 1 + nargs, 1 + nargs)\n' +
2072
- ' args_partial[0] = func\n' +
2073
- ' args_partial[1:] = args\n' +
2074
- ' return pnodeRun(\n' +
2075
- ' unifiedCall(partial, args_partial),\n' +
2076
- ' nodes,\n' +
2077
- ' add_node_alias\n' +
2078
- ' )\n' +
2079
- '}\n', { urgent: true });
2080
- if (this.autologin)
2081
- if (this.python)
2082
- await this.eval(`login(${this.username.quote('double')}, ${this.password.quote('double')})`, { urgent: true });
2083
- else
2084
- 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 });
2085
2128
  }
2086
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, } = {}) {
2087
2130
  let flag = 0;
@@ -2120,6 +2163,7 @@ export class DDB {
2120
2163
  this.on_message = () => { };
2121
2164
  this.presult = Promise.resolve(null);
2122
2165
  this.pconnect = Promise.resolve();
2166
+ this.pnode_run_defined = false;
2123
2167
  }
2124
2168
  /** rpc through websocket (function/script/variable command)
2125
2169
  - type: API 类型: 'script' | 'function' | 'variable'
@@ -2148,6 +2192,55 @@ export class DDB {
2148
2192
  }
2149
2193
  if (!this.connected)
2150
2194
  throw new Error(`${this.url} is already disconnected`);
2195
+ if (func === 'pnode_run' && !this.pnode_run_defined) {
2196
+ const ptail = this.ppnoderun;
2197
+ let resolve;
2198
+ this.ppnoderun = new Promise((_resolve, _reject) => {
2199
+ resolve = _resolve;
2200
+ });
2201
+ await ptail;
2202
+ try {
2203
+ if (!this.pnode_run_defined)
2204
+ await this.eval(this.python ?
2205
+ 'def pnode_run (nodes, func_name, args, add_node_alias):\n' +
2206
+ ' nargs = size(args)\n' +
2207
+ ' func = funcByName(func_name)\n' +
2208
+ ' \n' +
2209
+ ' if not nargs:\n' +
2210
+ ' return pnodeRun(func, nodes, add_node_alias)\n' +
2211
+ ' \n' +
2212
+ ' args_partial = [ ]\n' +
2213
+ ' args_partial.append(func)\n' +
2214
+ ' for a in args:\n' +
2215
+ ' args_partial.append(a)\n' +
2216
+ ' \n' +
2217
+ ' return pnodeRun(\n' +
2218
+ ' unifiedCall(partial, args_partial),\n' +
2219
+ ' nodes,\n' +
2220
+ ' add_node_alias\n' +
2221
+ ' )\n'
2222
+ :
2223
+ 'def pnode_run (nodes, func_name, args, add_node_alias = true) {\n' +
2224
+ ' nargs = size(args)\n' +
2225
+ ' func = funcByName(func_name)\n' +
2226
+ ' \n' +
2227
+ ' if (!nargs)\n' +
2228
+ ' return pnodeRun(func, nodes, add_node_alias)\n' +
2229
+ ' \n' +
2230
+ ' args_partial = array(any, 1 + nargs, 1 + nargs)\n' +
2231
+ ' args_partial[0] = func\n' +
2232
+ ' args_partial[1:] = args\n' +
2233
+ ' return pnodeRun(\n' +
2234
+ ' unifiedCall(partial, args_partial),\n' +
2235
+ ' nodes,\n' +
2236
+ ' add_node_alias\n' +
2237
+ ' )\n' +
2238
+ '}\n', { urgent: true });
2239
+ }
2240
+ finally {
2241
+ resolve();
2242
+ }
2243
+ }
2151
2244
  // this 上的当前配置需要在 message 到达后使用,先保存起来
2152
2245
  const _handlers = [...this.listeners].reverse();
2153
2246
  // 临界区:保证多个 rpc 并发时形成 promise 链
@@ -2249,8 +2342,7 @@ export class DDB {
2249
2342
  */
2250
2343
  async call(func, args = [], { urgent, node, nodes, func_type, add_node_alias, listener, parse_object, } = {}) {
2251
2344
  if (node) {
2252
- if (typeof func_type === 'undefined')
2253
- throw new Error('指定 node 时必须设置 func_type');
2345
+ assert(func_type in DdbFunctionType, 'func_type must be set when specifying node');
2254
2346
  args = [
2255
2347
  node,
2256
2348
  new DdbFunction(func, func_type),
@@ -2287,10 +2379,21 @@ export class DDB {
2287
2379
  vars,
2288
2380
  /** Uploaded variables' value */
2289
2381
  args, { listener, parse_object, } = {}) {
2290
- if (!args.length || args.length !== vars.length)
2291
- 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');
2292
2383
  return this.rpc('variable', { vars, args, listener, parse_object });
2293
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
+ }
2294
2397
  /** 解析服务端响应报文,返回去掉 header 的 data buf */
2295
2398
  parse_message(buf, parse_object = this.parse_object) {
2296
2399
  // MSG\n
@@ -2305,8 +2408,8 @@ export class DDB {
2305
2408
  // 'OK\n'
2306
2409
  // '\x04\x00\x02\x00\x00\x00'
2307
2410
  /** index of line feed 0 */
2308
- const i_lf_0 = buf.indexOf(0x0a); // '\n'
2309
- const parts = this.dec.decode(buf.subarray(0, i_lf_0)).split(' ');
2411
+ const ilf0 = buf.indexOf(0x0a); // '\n'
2412
+ const parts = this.dec.decode(buf.subarray(0, ilf0)).split(' ');
2310
2413
  /** session id */
2311
2414
  const sid = parts[0];
2312
2415
  if (sid !== this.sid) {
@@ -2314,34 +2417,141 @@ export class DDB {
2314
2417
  this.sid = sid;
2315
2418
  }
2316
2419
  /** 返回对象的数量 */
2317
- const n_obj = Number(parts[1]);
2420
+ const nobj = Number(parts[1]);
2318
2421
  /** 大小端: 协议中大端为 0, 小端为 1 */
2319
2422
  this.le = Number(parts[2]) !== 0;
2320
- const i_ls_1 = i_lf_0 + 1;
2321
- const i_lf_1 = buf.indexOf(0x0a, i_ls_1); // '\n'
2423
+ const ils1 = ilf0 + 1;
2424
+ const ilf1 = buf.indexOf(0x0a, ils1); // '\n'
2322
2425
  /** 'OK' 表示成功,其它文本表示失败 */
2323
- const message = this.dec.decode(buf.subarray(i_ls_1, i_lf_1));
2426
+ const message = this.dec.decode(buf.subarray(ils1, ilf1));
2324
2427
  if (message !== 'OK')
2325
2428
  return {
2326
2429
  type: 'error',
2327
2430
  data: new Error(message)
2328
2431
  };
2329
- const buf_obj = buf.subarray(i_lf_1 + 1);
2432
+ const bufobj = buf.subarray(ilf1 + 1);
2330
2433
  if (this.print_object_buffer)
2331
- console.log(typed_array_to_buffer(buf_obj));
2434
+ console.log(typed_array_to_buffer(bufobj));
2332
2435
  return {
2333
2436
  type: 'object',
2334
2437
  data: parse_object ?
2335
- DdbObj.parse(buf_obj, this.le)
2438
+ DdbObj.parse(bufobj, this.le)
2336
2439
  :
2337
2440
  new DdbObj({
2338
2441
  form: DdbForm.scalar,
2339
2442
  type: DdbType.void,
2340
2443
  length: 0,
2341
2444
  le: this.le,
2342
- buffer: buf_obj,
2445
+ buffer: bufobj,
2343
2446
  })
2344
2447
  };
2345
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
+ }
2346
2556
  }
2347
2557
  //# sourceMappingURL=index.js.map