dolphindb 2.0.603 → 2.0.800

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/browser.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');
@@ -332,8 +344,7 @@ export class DdbObj {
332
344
  col_labels.length += 2;
333
345
  offset += col_labels.length;
334
346
  }
335
- if (buf_data[offset] !== type)
336
- throw new Error('matrix.datatype !== matrix.type');
347
+ assert(buf_data[offset] === type, 'matrix.datatype === matrix.type');
337
348
  const rows = dv.getUint32(offset + 2, le);
338
349
  const cols = dv.getUint32(offset + 6, le);
339
350
  const [len_items, data] = this.parse_vector_items(buf_data.subarray(offset + 10), le, type, rows * cols // 假设小于 2**32
@@ -444,38 +455,30 @@ export class DdbObj {
444
455
  case DdbType.uuid:
445
456
  case DdbType.ipaddr:
446
457
  case DdbType.int128:
447
- return [
448
- 16,
449
- buf.slice(0, 16)
450
- ];
458
+ return [16, buf.slice(0, 16)];
451
459
  case DdbType.blob: {
452
460
  const dv = new DataView(buf.buffer, buf.byteOffset);
453
461
  const length = dv.getUint32(0, le);
454
- return [
455
- 4 + length,
456
- buf.slice(4, 4 + length)
457
- ];
462
+ return [4 + length, buf.slice(4, 4 + length)];
458
463
  }
459
464
  case DdbType.complex:
460
465
  case DdbType.point: {
461
466
  const dv = new DataView(buf.buffer, buf.byteOffset);
462
- return [
463
- 16,
464
- [
465
- dv.getFloat64(0, le),
466
- dv.getFloat64(8, le)
467
- ]
468
- ];
467
+ return [16, [dv.getFloat64(0, le), dv.getFloat64(8, le)]];
469
468
  }
470
469
  case DdbType.duration: {
471
470
  const dv = new DataView(buf.buffer, buf.byteOffset);
472
- return [
473
- 8,
474
- {
475
- unit: dv.getUint32(4, le),
476
- data: dv.getInt32(0, le)
477
- }
478
- ];
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 }];
479
482
  }
480
483
  default:
481
484
  throw new Error(`${DdbType[type] || type} 暂时不支持解析`);
@@ -636,7 +639,7 @@ export class DdbObj {
636
639
  // 02 00 00 00 symbol base size = 2
637
640
  // 00 61 61 00 以 \0 分割的字符串
638
641
  // 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00>
639
- const dv = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
642
+ const dv = new DataView(buf.buffer, buf.byteOffset);
640
643
  const base_id = dv.getUint32(0, le);
641
644
  const base_size = dv.getUint32(4, le);
642
645
  let base_length = 0;
@@ -671,7 +674,7 @@ export class DdbObj {
671
674
  // 04 00 00 00 61 62 63 64
672
675
  // 04 00 00 00 61 62 63 64>
673
676
  let value = new Array(length);
674
- const dv = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
677
+ const dv = new DataView(buf.buffer, buf.byteOffset);
675
678
  let i_head = 0;
676
679
  for (let i = 0; i < length; i++) {
677
680
  const sublen = dv.getUint32(i_head, le);
@@ -711,6 +714,31 @@ export class DdbObj {
711
714
  values
712
715
  ];
713
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
+ }
714
742
  // case DdbType.duration: -> 实际会返回一个 any vector
715
743
  // [2y, 1M, 3d, 7H, 11m, 12s, 15ms, 16us, 17ns]
716
744
  // <Buffer 19 01 type = any, form = vector
@@ -814,10 +842,7 @@ export class DdbObj {
814
842
  case DdbType.int128:
815
843
  return [this.value];
816
844
  case DdbType.blob:
817
- return [
818
- Uint32Array.of(this.value.byteLength),
819
- this.value
820
- ];
845
+ return [Uint32Array.of(this.value.byteLength), this.value];
821
846
  case DdbType.complex:
822
847
  case DdbType.point:
823
848
  return [Float64Array.from(this.value)];
@@ -825,6 +850,14 @@ export class DdbObj {
825
850
  const { unit, data } = this.value;
826
851
  return [Int32Array.of(data, unit)];
827
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
+ }
828
861
  default:
829
862
  throw new Error(`${DdbType[type] || type} 暂时不支持序列化`);
830
863
  }
@@ -846,12 +879,7 @@ export class DdbObj {
846
879
  ...DdbObj.pack_vector_body(value, type, this.rows)
847
880
  ];
848
881
  case DdbForm.set:
849
- return [
850
- new DdbObj({
851
- ...this,
852
- form: DdbForm.vector,
853
- }).pack()
854
- ];
882
+ return [new DdbObj({ ...this, form: DdbForm.vector }).pack()];
855
883
  case DdbForm.table:
856
884
  return [
857
885
  Uint32Array.of(this.rows, this.cols),
@@ -863,43 +891,27 @@ export class DdbObj {
863
891
  ...this.value.map(col => col.pack())
864
892
  ];
865
893
  case DdbForm.dict:
866
- return [
867
- value[0].pack(),
868
- value[1].pack(),
869
- ];
894
+ return [value[0].pack(), value[1].pack()];
870
895
  case DdbForm.chart: {
871
- const { type, stacking, bin_start, bin_end, bin_count, 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;
872
897
  const { value: [keys, values] } = new DdbDict({
873
898
  chartType: new DdbInt(type),
874
899
  stacking,
875
- ...bin_start ? {
876
- binStart: bin_start,
877
- binEnd: bin_end
878
- } : {},
879
- ...bin_count ? {
880
- binCount: bin_count
881
- } : {},
900
+ ...bin_start ? { binStart: bin_start, binEnd: bin_end } : {},
901
+ ...bin_count ? { binCount: bin_count } : {},
882
902
  title: new DdbVectorString([chart, x_axis, y_axis]),
883
903
  ...extras ? (() => {
884
904
  const { multi_y_axes, ...extras_other } = extras;
885
- return {
886
- extras: new DdbDict({
887
- multiYAxes: multi_y_axes,
888
- ...extras_other
889
- })
890
- };
905
+ return { extras: new DdbDict({ multiYAxes: multi_y_axes, ...extras_other }) };
891
906
  })() : {},
892
907
  data,
893
908
  });
894
- return [
895
- keys.pack(),
896
- values.pack(),
897
- ];
909
+ return [keys.pack(), values.pack()];
898
910
  }
899
911
  case DdbForm.matrix: {
900
912
  const { rows, cols, data } = value;
901
913
  return [
902
- Uint8Array.of((rows && 0x01) | (cols && 0x02)),
914
+ Uint8Array.of((rows ? 0x01 : 0x00) | (cols ? 0x02 : 0x00)),
903
915
  ...rows ? [rows.pack()] : [],
904
916
  ...cols ? [cols.pack()] : [],
905
917
  Uint8Array.of(this.type, this.form),
@@ -908,7 +920,7 @@ export class DdbObj {
908
920
  ];
909
921
  }
910
922
  default:
911
- throw new Error(`${DdbForm[form]} Serialization is not currently supported`);
923
+ throw new Error(`${DdbForm[form]} serialization is not currently supported`);
912
924
  }
913
925
  })();
914
926
  if (!body)
@@ -997,16 +1009,21 @@ export class DdbObj {
997
1009
  data
998
1010
  ];
999
1011
  }
1012
+ case DdbType.decimal32:
1013
+ case DdbType.decimal64: {
1014
+ const { scale, data } = value;
1015
+ return [Int32Array.of(scale), data];
1016
+ }
1000
1017
  default:
1001
1018
  throw new Error(`vector ${DdbType[type]} 暂不支持序列化`);
1002
1019
  }
1003
1020
  }
1004
- toString(colors = false) {
1021
+ toString(options) {
1005
1022
  const type = this.inspect_type();
1006
1023
  const data = (() => {
1007
1024
  switch (this.form) {
1008
1025
  case DdbForm.scalar:
1009
- return format(this.type, this.value, this.le);
1026
+ return format(this.type, this.value, this.le, options);
1010
1027
  case DdbForm.vector:
1011
1028
  case DdbForm.pair:
1012
1029
  case DdbForm.set: {
@@ -1037,7 +1054,7 @@ export class DdbObj {
1037
1054
  for (const length of lengths) {
1038
1055
  let _items = new Array(Math.min(limit, length));
1039
1056
  for (let i = 0; i < _items.length; i++)
1040
- _items[i] = format(_type, data[acc_len + i], this.le);
1057
+ _items[i] = format(_type, data[acc_len + i], this.le, options);
1041
1058
  items[i_items++] = format_array(_items, length > limit);
1042
1059
  acc_len += length;
1043
1060
  }
@@ -1063,7 +1080,7 @@ export class DdbObj {
1063
1080
  const len_data = value.length / 16;
1064
1081
  let items = new Array(Math.min(limit, len_data));
1065
1082
  for (let i = 0; i < items.length; i++)
1066
- 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);
1067
1084
  return format_array(items, len_data > limit);
1068
1085
  }
1069
1086
  case DdbType.complex:
@@ -1073,24 +1090,33 @@ export class DdbObj {
1073
1090
  const len_data = value.length / 2;
1074
1091
  let items = new Array(Math.min(limit, len_data));
1075
1092
  for (let i = 0; i < items.length; i++)
1076
- 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);
1077
1094
  return format_array(items, len_data > limit);
1078
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
+ }
1079
1105
  default: {
1080
1106
  const limit = 50;
1081
1107
  let items = new Array(Math.min(limit, this.value.length));
1082
1108
  for (let i = 0; i < items.length; i++)
1083
- items[i] = format(this.type, this.value[i], this.le);
1109
+ items[i] = format(this.type, this.value[i], this.le, options);
1084
1110
  return format_array(items, this.value.length > limit);
1085
1111
  }
1086
1112
  }
1087
1113
  }
1088
1114
  case DdbForm.chart:
1089
- 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);
1090
1116
  }
1091
1117
  return String(this.value);
1092
1118
  })();
1093
- return `${colors ? blue(type) : type}(${this.name ? `'${this.name}', ` : ''}${data})\n`;
1119
+ return `${options?.colors ? blue(type) : type}(${this.name ? `'${this.name}', ` : ''}${data})\n`;
1094
1120
  }
1095
1121
  inspect_type() {
1096
1122
  const tname = DdbType[this.type];
@@ -1138,28 +1164,31 @@ export class DdbObj {
1138
1164
  return values.map(value => this.to_ddbobj(value));
1139
1165
  }
1140
1166
  to_cols() {
1167
+ assert(this.form === DdbForm.table, 'form must be DdbForm.table, otherwise it cannot to_cols');
1141
1168
  return this.value.map(col => {
1142
1169
  let col_ = {
1143
1170
  title: col.name,
1144
1171
  dataIndex: col.name,
1145
1172
  };
1146
1173
  switch (col.type) {
1174
+ case DdbType.char:
1175
+ col_.render = (value) => format(DdbType.char, value, this.le);
1176
+ break;
1147
1177
  case DdbType.timestamp:
1148
- col_.render = value => timestamp2str(value);
1178
+ col_.render = (value) => timestamp2str(value);
1149
1179
  break;
1150
1180
  case DdbType.date:
1151
- col_.render = value => date2str(value);
1181
+ col_.render = (value) => date2str(value);
1152
1182
  break;
1153
1183
  case DdbType.ipaddr:
1154
- col_.render = value => ipaddr2str(value, this.le);
1184
+ col_.render = (value) => ipaddr2str(value, this.le);
1155
1185
  break;
1156
1186
  }
1157
1187
  return col_;
1158
1188
  });
1159
1189
  }
1160
1190
  to_rows() {
1161
- if (this.form !== DdbForm.table)
1162
- 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');
1163
1192
  let rows = new Array(this.rows);
1164
1193
  for (let i = 0; i < this.rows; i++) {
1165
1194
  let row = {};
@@ -1191,13 +1220,10 @@ export class DdbObj {
1191
1220
  return rows;
1192
1221
  }
1193
1222
  to_dict({ strip, deep, } = {}) {
1194
- if (this.form !== DdbForm.dict)
1195
- 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');
1196
1224
  const [{ value: keys, type: key_type }, { value: values, type: value_type }] = this.value;
1197
- if (key_type !== DdbType.string || value_type !== DdbType.any)
1198
- throw new Error('Not dict<string, any>, automatic conversion to js object is not supported for the time being');
1199
- if (deep && !strip)
1200
- 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');
1201
1227
  let obj = {};
1202
1228
  for (let i = 0; i < this.rows; i++) {
1203
1229
  let value = values[i];
@@ -1209,8 +1235,23 @@ export class DdbObj {
1209
1235
  return obj;
1210
1236
  }
1211
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 });
1212
1243
  /** Formats a single element (value) as a string according to DdbType, null returns a 'null' string */
1213
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
+ })();
1214
1255
  switch (type) {
1215
1256
  case DdbType.bool:
1216
1257
  return String((value === null || value === nulls.int8) ?
@@ -1228,20 +1269,20 @@ export function format(type, value, le, options = {}) {
1228
1269
  :
1229
1270
  value);
1230
1271
  case DdbType.short:
1231
- return String((value === null || value === nulls.int16) ?
1232
- null
1272
+ return (value === null || value === nulls.int16) ?
1273
+ 'null'
1233
1274
  :
1234
- value);
1275
+ default_formatter.format(value);
1235
1276
  case DdbType.int:
1236
- return String((value === null || value === nulls.int32) ?
1237
- null
1277
+ return (value === null || value === nulls.int32) ?
1278
+ 'null'
1238
1279
  :
1239
- value);
1280
+ default_formatter.format(value);
1240
1281
  case DdbType.long:
1241
1282
  return (value === null || value === nulls.int64) ?
1242
1283
  'null'
1243
1284
  :
1244
- String(value);
1285
+ default_formatter.format(value);
1245
1286
  case DdbType.date:
1246
1287
  return (value === null || value === nulls.int32) ?
1247
1288
  'null'
@@ -1291,12 +1332,12 @@ export function format(type, value, le, options = {}) {
1291
1332
  return (value === null || value === nulls.float32) ?
1292
1333
  'null'
1293
1334
  :
1294
- String(value);
1335
+ formatter.format(value);
1295
1336
  case DdbType.double:
1296
1337
  return (value === null || value === nulls.double) ?
1297
1338
  'null'
1298
1339
  :
1299
- String(value);
1340
+ formatter.format(value);
1300
1341
  case DdbType.symbol:
1301
1342
  case DdbType.string:
1302
1343
  return value.quote('single');
@@ -1323,30 +1364,32 @@ export function format(type, value, le, options = {}) {
1323
1364
  :
1324
1365
  DdbObj.dec.decode(value)).quote('single');
1325
1366
  case DdbType.point: {
1326
- let [x, y] = value;
1327
- if (x === nulls.double)
1328
- x = null;
1329
- if (y === nulls.double)
1330
- y = null;
1331
- return `(${String(x)}, ${String(y)})`;
1367
+ const [x, y] = value;
1368
+ return `(${format(DdbType.double, x, le, options)}, ${format(DdbType.double, y, le, options)})`;
1332
1369
  }
1333
1370
  case DdbType.complex: {
1334
- let [x, y] = value;
1335
- if (x === nulls.double)
1336
- x = null;
1337
- if (y === nulls.double)
1338
- y = null;
1339
- 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`;
1340
1373
  }
1341
1374
  case DdbType.duration: {
1342
1375
  const { data, unit } = value;
1343
1376
  return `${data}${DdbDurationUnit[unit]}`;
1344
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
+ }
1345
1388
  default:
1346
1389
  return String(value);
1347
1390
  }
1348
1391
  }
1349
- /** 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 */
1350
1393
  export function formati(obj, index, options = {}) {
1351
1394
  if (64 <= obj.type && obj.type < 128) { // array vector
1352
1395
  // 因为 array vector 目前只支持:Logical, Integral(不包括 INT128, COMPRESS 类型), Floating, Temporal
@@ -1402,6 +1445,16 @@ export function formati(obj, index, options = {}) {
1402
1445
  case DdbType.complex:
1403
1446
  case DdbType.point:
1404
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
+ }
1405
1458
  default:
1406
1459
  return format(obj.type, obj.value[index], obj.le, options);
1407
1460
  }
@@ -1816,8 +1869,7 @@ export function datehour2str(datehour, format = 'YYYY.MM.DDTHH') {
1816
1869
  export function str2timestamp(str, format = 'YYYY.MM.DD HH:mm:ss.SSS') {
1817
1870
  if (!str || str === 'null')
1818
1871
  return nulls.int64;
1819
- if (str.length !== format.length)
1820
- 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');
1821
1873
  return BigInt(-timezone_offset +
1822
1874
  dayjs(str, format).valueOf());
1823
1875
  }
@@ -1830,12 +1882,10 @@ export function nanotime2str(nanotime, format = 'HH:mm:ss.SSSSSSSSS') {
1830
1882
  if (nanotime < 0n)
1831
1883
  return String(nanotime);
1832
1884
  const i_second_start = format.indexOf('ss');
1833
- if (i_second_start === -1)
1834
- throw new Error('格式串必须包含秒的格式 (ss)');
1885
+ assert(i_second_start !== -1, 'The format string must contain the format for seconds (ss)');
1835
1886
  const i_second_end = i_second_start + 2;
1836
1887
  const i_nanosecond_start = format.indexOf('SSSSSSSSS', i_second_end);
1837
- if (i_nanosecond_start === -1)
1838
- throw new Error('格式串必须包含纳秒的格式 (SSSSSSSSS)');
1888
+ assert(i_nanosecond_start !== -1, 'Format string must contain nanosecond format (SSSSSSSSS)');
1839
1889
  return (dayjs(timezone_offset + (Number(nanotime) / 1000000)).format(format.slice(0, i_second_end)) +
1840
1890
  format.slice(i_second_end, i_nanosecond_start) +
1841
1891
  String(nanotime % 1000000000n).padStart(9, '0'));
@@ -1863,12 +1913,10 @@ export function nanotimestamp2str(nanotimestamp, format = 'YYYY.MM.DD HH:mm:ss.S
1863
1913
  if (nanotimestamp === null || nanotimestamp === nulls.int64)
1864
1914
  return 'null';
1865
1915
  const i_second_start = format.indexOf('ss');
1866
- if (i_second_start === -1)
1867
- 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)');
1868
1917
  const i_second_end = i_second_start + 2;
1869
1918
  const i_nanosecond_start = format.indexOf('SSSSSSSSS', i_second_end);
1870
- if (i_nanosecond_start === -1)
1871
- 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)');
1872
1920
  const remainder = nanotimestamp % 1000000000n;
1873
1921
  const borrow = remainder < 0n;
1874
1922
  return (dayjs(timezone_offset +
@@ -1890,15 +1938,12 @@ export function nanotimestamp2str(nanotimestamp, format = 'YYYY.MM.DD HH:mm:ss.S
1890
1938
  export function str2nanotimestamp(str, format = 'YYYY.MM.DD HH:mm:ss.SSSSSSSSS') {
1891
1939
  if (!str || str === 'null')
1892
1940
  return nulls.int64;
1893
- if (str.length !== format.length)
1894
- 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');
1895
1942
  const i_second_start = format.indexOf('ss');
1896
- if (i_second_start === -1)
1897
- 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)');
1898
1944
  const i_second_end = i_second_start + 2;
1899
1945
  const i_nanosecond_start = format.indexOf('SSSSSSSSS', i_second_end);
1900
- if (i_nanosecond_start === -1)
1901
- throw new Error('Format string must contain nanosecond format (SSSSSSSSS)');
1946
+ assert(i_nanosecond_start !== -1, 'Format string must contain nanosecond format (SSSSSSSSS)');
1902
1947
  return (BigInt(-timezone_offset +
1903
1948
  dayjs(str.slice(0, i_second_end), format.slice(0, i_second_end)).valueOf()) * 1000000n
1904
1949
  +
@@ -1926,6 +1971,7 @@ export function int1282str(buffer, le = true) {
1926
1971
  return [...buf].map(x => x.toString(16)
1927
1972
  .padStart(2, '0')).join('');
1928
1973
  }
1974
+ export const winsize = 40;
1929
1975
  export class DDB {
1930
1976
  /** 当前的 session id (http 或 tcp) */
1931
1977
  sid = '0';
@@ -1949,7 +1995,9 @@ export class DDB {
1949
1995
  password = '123456';
1950
1996
  /** python session flag (2048) */
1951
1997
  python = false;
1952
- // 内部选项, 状态
1998
+ /** Whether it is a streaming data connection, this field is always null for non-streaming data */
1999
+ streaming = null;
2000
+ // --- 内部选项, 状态
1953
2001
  print_message_buffer = false;
1954
2002
  print_object_buffer = false;
1955
2003
  print_message = true;
@@ -1972,6 +2020,7 @@ export class DDB {
1972
2020
  - username?: DolphinDB username, default `'admin'`
1973
2021
  - password?: DolphinDB password, default `'123456'`
1974
2022
  - python?: set python session flag, default `false`
2023
+ - streaming?: When this option is set, the WebSocket connection is only used for streaming data
1975
2024
 
1976
2025
  @example
1977
2026
  let ddb = new DDB('ws://127.0.0.1:8848')
@@ -1989,7 +2038,7 @@ export class DDB {
1989
2038
  const _url = new URL(location.href);
1990
2039
  const hostname = _url.searchParams.get('hostname') || location.hostname;
1991
2040
  const port = _url.searchParams.get('port') || location.port;
1992
- url = `${location.protocol === 'https:' ? 'wss' : 'ws'}://${hostname}${port ? `:${port}` : ''}/`;
2041
+ url = `${location.protocol === 'https:' ? 'wss' : 'ws'}://${hostname}:${port}/`;
1993
2042
  }
1994
2043
  this.url = url;
1995
2044
  if (options.autologin !== undefined)
@@ -2000,8 +2049,13 @@ export class DDB {
2000
2049
  this.password = options.password;
2001
2050
  if (options.python !== undefined)
2002
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);
2003
2058
  }
2004
- on_message(event) { }
2005
2059
  /** Establish the actual WebSocket connection to the DolphinDB corresponding to the URL
2006
2060
  - options?:
2007
2061
  - url?: DolphinDB WebSocket URL. By default, the WebSocket URL passed in when the instance is initialized is used
@@ -2009,34 +2063,42 @@ export class DDB {
2009
2063
  - username?: DolphinDB username, default `'admin'`
2010
2064
  - password?: DolphinDB password, default `'123456'`
2011
2065
  - python?: set python session flag, default `false`
2066
+ - streaming?: When this option is set, the WebSocket connection is only used for streaming data
2012
2067
  */
2013
- async connect(options = {}) {
2014
- if (options.url !== undefined)
2015
- this.url = options.url;
2016
- if (options.autologin !== undefined)
2017
- this.autologin = options.autologin;
2018
- if (options.username !== undefined)
2019
- this.username = options.username;
2020
- if (options.password !== undefined)
2021
- this.password = options.password;
2022
- if (options.python !== undefined)
2023
- 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;
2024
2081
  this.disconnect();
2025
2082
  await connect_websocket(this.url, {
2026
- protocols: this.python ? ['python'] : [],
2083
+ protocols: (() => {
2084
+ if (this.streaming)
2085
+ return 'streaming';
2086
+ if (this.python)
2087
+ return 'python';
2088
+ })(),
2027
2089
  on_open: async (event, websocket) => {
2028
2090
  this.websocket = websocket;
2029
- await this.rpc('connect', {});
2091
+ if (this.streaming)
2092
+ await this.subscribe();
2093
+ else
2094
+ await this.rpc('connect', {});
2030
2095
  },
2031
2096
  on_message: (event) => {
2032
2097
  this.on_message(event);
2033
2098
  }
2034
2099
  });
2035
- if (this.autologin)
2036
- if (this.python)
2037
- await this.eval(`login(${this.username.quote('double')}, ${this.password.quote('double')})`, { urgent: true });
2038
- else
2039
- 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 });
2040
2102
  }
2041
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, } = {}) {
2042
2104
  let flag = 0;
@@ -2254,8 +2316,7 @@ export class DDB {
2254
2316
  */
2255
2317
  async call(func, args = [], { urgent, node, nodes, func_type, add_node_alias, listener, parse_object, } = {}) {
2256
2318
  if (node) {
2257
- if (typeof func_type === 'undefined')
2258
- throw new Error('指定 node 时必须设置 func_type');
2319
+ assert(func_type in DdbFunctionType, 'func_type must be set when specifying node');
2259
2320
  args = [
2260
2321
  node,
2261
2322
  new DdbFunction(func, func_type),
@@ -2292,10 +2353,21 @@ export class DDB {
2292
2353
  vars,
2293
2354
  /** Uploaded variables' value */
2294
2355
  args, { listener, parse_object, } = {}) {
2295
- if (!args.length || args.length !== vars.length)
2296
- 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');
2297
2357
  return this.rpc('variable', { vars, args, listener, parse_object });
2298
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
+ }
2299
2371
  /** 解析服务端响应报文,返回去掉 header 的 data buf */
2300
2372
  parse_message(buf, parse_object = this.parse_object) {
2301
2373
  // MSG\n
@@ -2310,8 +2382,8 @@ export class DDB {
2310
2382
  // 'OK\n'
2311
2383
  // '\x04\x00\x02\x00\x00\x00'
2312
2384
  /** index of line feed 0 */
2313
- const i_lf_0 = buf.indexOf(0x0a); // '\n'
2314
- 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(' ');
2315
2387
  /** session id */
2316
2388
  const sid = parts[0];
2317
2389
  if (sid !== this.sid) {
@@ -2319,35 +2391,142 @@ export class DDB {
2319
2391
  this.sid = sid;
2320
2392
  }
2321
2393
  /** 返回对象的数量 */
2322
- const n_obj = Number(parts[1]);
2394
+ const nobj = Number(parts[1]);
2323
2395
  /** 大小端: 协议中大端为 0, 小端为 1 */
2324
2396
  this.le = Number(parts[2]) !== 0;
2325
- const i_ls_1 = i_lf_0 + 1;
2326
- const i_lf_1 = buf.indexOf(0x0a, i_ls_1); // '\n'
2397
+ const ils1 = ilf0 + 1;
2398
+ const ilf1 = buf.indexOf(0x0a, ils1); // '\n'
2327
2399
  /** 'OK' 表示成功,其它文本表示失败 */
2328
- const message = this.dec.decode(buf.subarray(i_ls_1, i_lf_1));
2400
+ const message = this.dec.decode(buf.subarray(ils1, ilf1));
2329
2401
  if (message !== 'OK')
2330
2402
  return {
2331
2403
  type: 'error',
2332
2404
  data: new Error(message)
2333
2405
  };
2334
- const buf_obj = buf.subarray(i_lf_1 + 1);
2406
+ const bufobj = buf.subarray(ilf1 + 1);
2335
2407
  if (this.print_object_buffer)
2336
- console.log(buf_obj);
2408
+ console.log(bufobj);
2337
2409
  return {
2338
2410
  type: 'object',
2339
2411
  data: parse_object ?
2340
- DdbObj.parse(buf_obj, this.le)
2412
+ DdbObj.parse(bufobj, this.le)
2341
2413
  :
2342
2414
  new DdbObj({
2343
2415
  form: DdbForm.scalar,
2344
2416
  type: DdbType.void,
2345
2417
  length: 0,
2346
2418
  le: this.le,
2347
- buffer: buf_obj,
2419
+ buffer: bufobj,
2348
2420
  })
2349
2421
  };
2350
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
+ }
2351
2530
  }
2352
2531
  export let ddb = window.ddb = new DDB();
2353
2532
  //# sourceMappingURL=browser.js.map