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/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;
@@ -331,8 +343,7 @@ export class DdbObj {
331
343
  col_labels.length += 2;
332
344
  offset += col_labels.length;
333
345
  }
334
- if (buf_data[offset] !== type)
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
- 8,
473
- {
474
- unit: dv.getUint32(4, le),
475
- data: dv.getInt32(0, le)
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, buf.byteLength);
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, buf.byteLength);
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, }, extras, data } = this.value;
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
- binStart: bin_start,
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 && 0x01) | (cols && 0x02)),
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
- if (this.form !== DdbForm.table)
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
- if (this.form !== DdbForm.dict)
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
- if (key_type !== DdbType.string || value_type !== DdbType.any)
1177
- throw new Error('Not dict<string, any>, automatic conversion to js object is not supported for the time being');
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 inspect((value === null || value === nulls.int16) ?
1211
- null
1247
+ return value === null || value === nulls.int16 ?
1248
+ inspect(null, options)
1212
1249
  :
1213
- value, options);
1250
+ options.colors ?
1251
+ default_formatter.format(value).green
1252
+ :
1253
+ default_formatter.format(value);
1214
1254
  case DdbType.int:
1215
- return inspect((value === null || value === nulls.int32) ?
1216
- null
1255
+ return value === null || value === nulls.int32 ?
1256
+ inspect(null, options)
1217
1257
  :
1218
- value, options);
1258
+ options.colors ?
1259
+ default_formatter.format(value).green
1260
+ :
1261
+ default_formatter.format(value);
1219
1262
  case DdbType.long:
1220
- return (value === null || value === nulls.int64) ?
1221
- 'null'
1263
+ return value === null || value === nulls.int64 ?
1264
+ inspect(null, options)
1222
1265
  :
1223
1266
  options.colors ?
1224
- String(value).green
1267
+ default_formatter.format(value).green
1225
1268
  :
1226
- String(value);
1269
+ default_formatter.format(value);
1227
1270
  case DdbType.date:
1228
1271
  return (value === null || value === nulls.int32) ?
1229
- 'null'
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
- 'null'
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
- 'null'
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
- 'null'
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
- 'null'
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
- 'null'
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
- 'null'
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
- 'null'
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
- 'null'
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
- 'null'
1344
+ inspect(null, options)
1302
1345
  :
1303
- inspect(value, options);
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
- 'null'
1352
+ inspect(null, options)
1307
1353
  :
1308
- inspect(value, options);
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
- let [x, y] = value;
1348
- if (x === nulls.double)
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
- let [x, y] = value;
1359
- if (x === nulls.double)
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
- if (str.length !== format.length)
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
- if (i_second_start === -1)
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
- if (i_nanosecond_start === -1)
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
- if (i_second_start === -1)
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
- if (i_nanosecond_start === -1)
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
- if (str.length !== format.length)
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
- if (i_second_start === -1)
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
- if (i_nanosecond_start === -1)
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
- // 使用 HTTPS 加密
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(options = {}) {
2036
- if (options.url !== undefined)
2037
- this.url = options.url;
2038
- if (options.autologin !== undefined)
2039
- this.autologin = options.autologin;
2040
- if (options.username !== undefined)
2041
- this.username = options.username;
2042
- if (options.password !== undefined)
2043
- this.password = options.password;
2044
- if (options.python !== undefined)
2045
- 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;
2046
2107
  this.disconnect();
2047
2108
  await connect_websocket(this.url, {
2048
- protocols: this.python ? ['python'] : [],
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
- await this.rpc('connect', {});
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
- if (this.python)
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
- if (typeof func_type === 'undefined')
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
- if (!args.length || args.length !== vars.length)
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 i_lf_0 = buf.indexOf(0x0a); // '\n'
2335
- 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(' ');
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 n_obj = Number(parts[1]);
2420
+ const nobj = Number(parts[1]);
2344
2421
  /** 大小端: 协议中大端为 0, 小端为 1 */
2345
2422
  this.le = Number(parts[2]) !== 0;
2346
- const i_ls_1 = i_lf_0 + 1;
2347
- const i_lf_1 = buf.indexOf(0x0a, i_ls_1); // '\n'
2423
+ const ils1 = ilf0 + 1;
2424
+ const ilf1 = buf.indexOf(0x0a, ils1); // '\n'
2348
2425
  /** 'OK' 表示成功,其它文本表示失败 */
2349
- const message = this.dec.decode(buf.subarray(i_ls_1, i_lf_1));
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 buf_obj = buf.subarray(i_lf_1 + 1);
2432
+ const bufobj = buf.subarray(ilf1 + 1);
2356
2433
  if (this.print_object_buffer)
2357
- console.log(typed_array_to_buffer(buf_obj));
2434
+ console.log(typed_array_to_buffer(bufobj));
2358
2435
  return {
2359
2436
  type: 'object',
2360
2437
  data: parse_object ?
2361
- DdbObj.parse(buf_obj, this.le)
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: buf_obj,
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