dolphindb 0.0.6 → 0.0.7

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/README.md CHANGED
@@ -320,3 +320,19 @@ async upload (
320
320
  ): Promise<void>
321
321
  ```
322
322
 
323
+
324
+ ### Notes
325
+ ```ts
326
+ import { nulls, DdbInt, timestamp2str } from 'dolphindb'
327
+
328
+ // create DolphinDB null integer value (the smallest 32-bit integer)
329
+ new DdbInt(nulls.int32)
330
+
331
+ // convert DolphinDB timestamp value to string
332
+ timestamp2str(
333
+ (
334
+ await ddb.call<DdbObj<bigint>>('now', [false])
335
+ ).value
336
+ ) === '2022.02.23 17:23:13.494'
337
+ ```
338
+
package/README.zh.md CHANGED
@@ -320,3 +320,18 @@ async upload (
320
320
  ): Promise<void>
321
321
  ```
322
322
 
323
+
324
+ ### Notes
325
+ ```ts
326
+ import { nulls, DdbInt, timestamp2str } from 'dolphindb'
327
+
328
+ // 创建 DolphinDB 空值 (最小的 32-bit 整数)
329
+ new DdbInt(nulls.int32)
330
+
331
+ // 将 DolphinDB 中的 timestamp 格式化为 string
332
+ timestamp2str(
333
+ (
334
+ await ddb.call<DdbObj<bigint>>('now', [false])
335
+ ).value
336
+ ) === '2022.02.23 17:23:13.494'
337
+ ```
package/browser.d.ts CHANGED
@@ -188,30 +188,68 @@ export declare class DDB {
188
188
  /** utf-8 text decoder */
189
189
  dec: TextDecoder;
190
190
  enc: TextEncoder;
191
- ws_url: string;
192
- ws: WebSocket;
191
+ /** DolphinDB WebSocket URL
192
+ e.g. `ws://127.0.0.1:8848/`, `wss://dolphindb.com`
193
+ */
194
+ url: string;
195
+ websocket: WebSocket;
193
196
  /** little endian (server) */
194
197
  le: boolean;
195
198
  /** little endian (client) */
196
199
  static le_client: boolean;
200
+ /** python session flag (2048) */
201
+ python: boolean;
202
+ message_hook: (message: Uint8Array) => any;
197
203
  /** print message handler */
198
204
  printer(message: string): void;
199
205
  /** resolver, rejector, promise of last rpc */
200
206
  presolver(buf: Uint8Array): void;
201
207
  prejector(error: Error): void;
202
208
  presult: Promise<Uint8Array>;
203
- constructor(ws_url?: string);
204
- /** 连接到 DolphinDB Server */
205
- connect({ ws_url, login, username, password, }?: {
206
- /** 默认使用实例初始化时传入的 WebSocket URL */
207
- ws_url?: string;
208
- /** 是否在建立连接后自动登录,默认 true */
209
+ /**
210
+ Initialize an instance of DolphinDB Client using the WebSocket URL
211
+ (without establishing an actual network connection)
212
+ @example
213
+ let ddb = new DDB('ws://127.0.0.1:8848')
214
+ let ddb_secure = new DDB('wss://dolphindb.com')
215
+ */
216
+ constructor(url?: string);
217
+ /** Establish the actual WebSocket connection to the DolphinDB corresponding to the URL */
218
+ connect({ url, login, username, password, python, }?: {
219
+ /** By default, the WebSocket URL passed in when the instance is initialized is used */
220
+ url?: string;
221
+ /** Whether to automatically log in after the connection is established, the default is true */
209
222
  login?: boolean;
210
- /** DolphinDB 登录用户名 */
223
+ /** DolphinDB username */
211
224
  username?: string;
212
- /** DolphinDB 登录密码 */
225
+ /** DolphinDB password */
213
226
  password?: string;
227
+ /** set python session flag */
228
+ python?: boolean;
214
229
  }): Promise<void>;
230
+ get_rpc_options({ urgent, secondary, async: _async, pickle, clear, api, compress, cancellable, priority, parallelism, root_id, limit, }?: {
231
+ urgent?: boolean;
232
+ /** API 提交的任务, secondary 必须为 false */
233
+ secondary?: boolean;
234
+ /** 是否异步任务(不返回结果) */
235
+ async?: boolean;
236
+ /** 让服务端以 pickle 协议返回数据 */
237
+ pickle?: boolean;
238
+ /** 本次任务完成后 clear session memory */
239
+ clear?: boolean;
240
+ /** 是否为 api client */
241
+ api?: boolean;
242
+ compress?: boolean;
243
+ /** 任务是否可以取消 */
244
+ cancellable?: boolean;
245
+ priority?: number;
246
+ /** `8` 0 ~ 64, 指定本任务并行度 */
247
+ parallelism?: number;
248
+ /** 根任务编号,内部使用,API中固定为空 */
249
+ root_id?: string;
250
+ /** 指定分块返回的块大小 */
251
+ limit?: boolean;
252
+ }): string;
215
253
  disconnect(): void;
216
254
  /** rpc through websocket (function/script/variable command)
217
255
  - type: API 类型: 'script' | 'function' | 'variable'
@@ -219,7 +257,7 @@ export declare class DDB {
219
257
  - urgent?: 决定 `行为标识` 那一行字符串的取值(只适用于 script 和 function)
220
258
  - vars?: type === 'variable' 时必传,variable 指令中待上传的变量名
221
259
  */
222
- rpc<T extends DdbObj = DdbObj>(type: 'script' | 'function' | 'variable', { script, func, args, vars, urgent, }: {
260
+ rpc<T extends DdbObj = DdbObj>(type: 'script' | 'function' | 'variable' | 'connect', { script, func, args, vars, urgent, }: {
223
261
  script?: string;
224
262
  func?: string;
225
263
  args?: (DdbObj | string | boolean)[];
@@ -254,9 +292,9 @@ export declare class DDB {
254
292
  }): Promise<T>;
255
293
  /** upload variable through websocket (variable command) */
256
294
  upload(
257
- /** 上传的变量名 */
295
+ /** Uploaded variables' name */
258
296
  vars: string[],
259
- /** 上传的变量值 */
297
+ /** Uploaded variables' value */
260
298
  args: (DdbObj | string | boolean)[]): Promise<DdbObj<DdbValue>>;
261
299
  /** 解析服务端响应报文,返回去掉 header 的 data buf */
262
300
  parse_message(buf: Uint8Array): {
package/browser.js CHANGED
@@ -394,8 +394,8 @@ class DdbObj {
394
394
  throw new Error(`array vector 存在非法 unit = ${unit}`);
395
395
  }
396
396
  let total_length = 0;
397
- for (let i = 0; i < lengths.length; i++)
398
- total_length += lengths[i];
397
+ for (const x of lengths)
398
+ total_length += x;
399
399
  const [len_items, data] = this.parse_vector_items(buf.subarray(i_data_start), le, type - 64, total_length);
400
400
  blocks.push({
401
401
  unit,
@@ -1014,23 +1014,33 @@ function timestamp2str(timestamp) {
1014
1014
  }
1015
1015
  exports.timestamp2str = timestamp2str;
1016
1016
  class DDB {
1017
- constructor(ws_url) {
1017
+ /**
1018
+ Initialize an instance of DolphinDB Client using the WebSocket URL
1019
+ (without establishing an actual network connection)
1020
+ @example
1021
+ let ddb = new DDB('ws://127.0.0.1:8848')
1022
+ let ddb_secure = new DDB('wss://dolphindb.com')
1023
+ */
1024
+ constructor(url) {
1018
1025
  /** 当前的 session id (http 或 tcp) */
1019
1026
  this.sid = '0';
1020
1027
  /** utf-8 text decoder */
1021
1028
  this.dec = new TextDecoder('utf-8');
1022
1029
  this.enc = new TextEncoder();
1023
- this.ws = null;
1030
+ this.websocket = null;
1024
1031
  /** little endian (server) */
1025
1032
  this.le = true;
1033
+ /** python session flag (2048) */
1034
+ this.python = false;
1035
+ this.message_hook = null;
1026
1036
  this.presult = Promise.resolve(null);
1027
- if (!ws_url) {
1028
- const url = new URL(location.href);
1029
- const hostname = url.searchParams.get('hostname') || location.hostname;
1030
- const port = url.searchParams.get('port') || location.port;
1031
- ws_url = `${location.protocol === 'https:' ? 'wss' : 'ws'}://${hostname}${port ? `:${port}` : ''}/`;
1037
+ if (!url) {
1038
+ const _url = new URL(location.href);
1039
+ const hostname = _url.searchParams.get('hostname') || location.hostname;
1040
+ const port = _url.searchParams.get('port') || location.port;
1041
+ url = `${location.protocol === 'https:' ? 'wss' : 'ws'}://${hostname}${port ? `:${port}` : ''}/`;
1032
1042
  }
1033
- this.ws_url = ws_url;
1043
+ this.url = url;
1034
1044
  }
1035
1045
  /** print message handler */
1036
1046
  printer(message) {
@@ -1039,66 +1049,100 @@ class DDB {
1039
1049
  /** resolver, rejector, promise of last rpc */
1040
1050
  presolver(buf) { }
1041
1051
  prejector(error) { }
1042
- /** 连接到 DolphinDB Server */
1043
- async connect({ ws_url = this.ws_url, login = true, username = 'admin', password = '123456', } = {}) {
1044
- this.ws_url = ws_url;
1045
- if (this.ws?.readyState === WebSocket.OPEN)
1052
+ /** Establish the actual WebSocket connection to the DolphinDB corresponding to the URL */
1053
+ async connect({ url = this.url, login = true, username = 'admin', password = '123456', python = false, } = {}) {
1054
+ this.url = url;
1055
+ this.python = python;
1056
+ if (this.websocket?.readyState === WebSocket.OPEN)
1046
1057
  this.disconnect();
1047
- let ws = new WebSocket(ws_url);
1058
+ let websocket = new WebSocket(url, python ? ['python'] : []);
1048
1059
  // https://stackoverflow.com/questions/11821096/what-is-the-difference-between-an-arraybuffer-and-a-blob/39951543
1049
- ws.binaryType = 'arraybuffer';
1050
- this.ws = ws;
1051
- ws.addEventListener('open', ev => {
1052
- console.log(`${this.ws_url} opened`);
1053
- ws.send(`API ${this.sid} 8\n` +
1054
- 'connect\n');
1055
- });
1056
- ws.addEventListener('close', ev => {
1057
- console.log(`${this.ws_url} closed with code = ${ev.code}, reason = '${ev.reason}'`);
1058
- });
1059
- ws.addEventListener('error', ev => {
1060
- console.log(`${this.ws_url} errored`, ev);
1061
- });
1062
- // 为首个 connect 响应报文初始化 presult 链表头结点
1063
- this.presult = new Promise((resolve, reject) => {
1064
- this.presolver = resolve;
1065
- this.prejector = reject;
1066
- });
1067
- ws.addEventListener('message', ({ data: buf }) => {
1068
- try {
1069
- const { type, data } = this.parse_message(new Uint8Array(buf));
1070
- if (type === 'message') {
1071
- this.printer(data);
1072
- return;
1060
+ websocket.binaryType = 'arraybuffer';
1061
+ this.websocket = websocket;
1062
+ await new Promise((resolve, reject) => {
1063
+ websocket.addEventListener('open', async (event) => {
1064
+ console.log(`${websocket.url} opened`);
1065
+ await this.rpc('connect', {});
1066
+ resolve();
1067
+ });
1068
+ websocket.addEventListener('close', event => {
1069
+ console.log(`${websocket.url} closed with code = ${event.code}, reason = '${event.reason}'`);
1070
+ });
1071
+ websocket.addEventListener('error', event => {
1072
+ const message = `${websocket.url} errored`;
1073
+ console.error(message, event);
1074
+ reject(Object.assign(new Error(message), { event }));
1075
+ });
1076
+ websocket.addEventListener('message', event => {
1077
+ const buf = new Uint8Array(event.data);
1078
+ if (this.message_hook)
1079
+ this.message_hook(buf);
1080
+ try {
1081
+ const { type, data } = this.parse_message(buf);
1082
+ if (type === 'message') {
1083
+ this.printer(data);
1084
+ return;
1085
+ }
1086
+ this.presolver(data);
1073
1087
  }
1074
- this.presolver(data);
1075
- }
1076
- catch (error) {
1077
- this.prejector(error);
1078
- }
1088
+ catch (error) {
1089
+ this.prejector(error);
1090
+ }
1091
+ });
1079
1092
  });
1080
- await this.presult;
1081
- await this.eval('def pnode_run (nodes, func_name, args, add_node_alias = true) {\n' +
1082
- ' nargs = size(args)\n' +
1083
- ' func = funcByName(func_name)\n' +
1084
- ' \n' +
1085
- ' if (!nargs)\n' +
1086
- ' return pnodeRun(func, nodes, add_node_alias)\n' +
1087
- ' \n' +
1088
- ' args_partial = array(any, 1 + nargs, 1 + nargs)\n' +
1089
- ' args_partial[0] = func\n' +
1090
- ' args_partial[1:] = args\n' +
1091
- ' return pnodeRun(\n' +
1092
- ' unifiedCall(partial, args_partial),\n' +
1093
- ' nodes,\n' +
1094
- ' add_node_alias\n' +
1095
- ' )\n' +
1096
- '}\n', { urgent: true });
1093
+ if (!this.python)
1094
+ await this.eval('def pnode_run (nodes, func_name, args, add_node_alias = true) {\n' +
1095
+ ' nargs = size(args)\n' +
1096
+ ' func = funcByName(func_name)\n' +
1097
+ ' \n' +
1098
+ ' if (!nargs)\n' +
1099
+ ' return pnodeRun(func, nodes, add_node_alias)\n' +
1100
+ ' \n' +
1101
+ ' args_partial = array(any, 1 + nargs, 1 + nargs)\n' +
1102
+ ' args_partial[0] = func\n' +
1103
+ ' args_partial[1:] = args\n' +
1104
+ ' return pnodeRun(\n' +
1105
+ ' unifiedCall(partial, args_partial),\n' +
1106
+ ' nodes,\n' +
1107
+ ' add_node_alias\n' +
1108
+ ' )\n' +
1109
+ '}\n', { urgent: true });
1097
1110
  if (login)
1098
1111
  await this.call('login', [username, password], { urgent: true });
1099
1112
  }
1113
+ 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, } = {}) {
1114
+ let flag = 0;
1115
+ if (urgent)
1116
+ flag += 1;
1117
+ if (secondary)
1118
+ flag += 2;
1119
+ if (_async)
1120
+ flag += 4;
1121
+ if (pickle)
1122
+ flag += 8;
1123
+ if (clear)
1124
+ flag += 16;
1125
+ if (api)
1126
+ flag += 32;
1127
+ if (compress)
1128
+ flag += 64;
1129
+ // python session
1130
+ if (this.python)
1131
+ flag += 2048;
1132
+ const options = [
1133
+ flag,
1134
+ cancellable ? 1 : 0,
1135
+ priority,
1136
+ parallelism,
1137
+ ...limit ? [
1138
+ root_id,
1139
+ limit,
1140
+ ] : [],
1141
+ ];
1142
+ return `/ ${options.join('_')}`;
1143
+ }
1100
1144
  disconnect() {
1101
- this.ws?.close(1000);
1145
+ this.websocket?.close(1000);
1102
1146
  }
1103
1147
  /** rpc through websocket (function/script/variable command)
1104
1148
  - type: API 类型: 'script' | 'function' | 'variable'
@@ -1107,10 +1151,10 @@ class DDB {
1107
1151
  - vars?: type === 'variable' 时必传,variable 指令中待上传的变量名
1108
1152
  */
1109
1153
  async rpc(type, { script, func, args = [], vars = [], urgent, }) {
1110
- if (!this.ws)
1154
+ if (!this.websocket)
1111
1155
  await this.connect();
1112
- if (this.ws.readyState !== WebSocket.OPEN)
1113
- throw new Error('ws 连接已断开');
1156
+ if (this.websocket.readyState !== WebSocket.OPEN)
1157
+ throw new Error(`${this.url} is already disconnected`);
1114
1158
  // 临界区:保证多个 rpc 并发时形成 promise 链
1115
1159
  // ddb 世界观:需要等待上一个 rpc 结果从 server 返回之后才能发起下一个调用
1116
1160
  // 违反世界观可能造成:
@@ -1148,14 +1192,16 @@ class DDB {
1148
1192
  `${vars.join(',')}\n` +
1149
1193
  `${vars.length}\n` +
1150
1194
  `${Number(DDB.le_client)}\n`;
1195
+ case 'connect':
1196
+ return 'connect\n';
1151
1197
  }
1152
1198
  })());
1153
1199
  const message = (0, utils_browser_1.concat)([
1154
- this.enc.encode(`API2 ${this.sid} ${command.length}${urgent ? ` / 1_1_8_8` : ''}\n`),
1200
+ this.enc.encode(`API2 ${this.sid} ${command.length}${this.get_rpc_options({ urgent })}\n`),
1155
1201
  command,
1156
1202
  ...args.map((arg) => arg.pack())
1157
1203
  ]);
1158
- this.ws.send(message);
1204
+ this.websocket.send(message);
1159
1205
  return DdbObj.parse(await presult, // data_buf
1160
1206
  this.le);
1161
1207
  }
@@ -1178,6 +1224,8 @@ class DDB {
1178
1224
  - add_node_alias?: 设置 nodes 参数时选传,其它情况不传
1179
1225
  */
1180
1226
  async call(func, args = [], { urgent, node, nodes, func_type, add_node_alias } = {}) {
1227
+ if (this.python)
1228
+ throw new Error('call doesn\'t support python session yet');
1181
1229
  if (node) {
1182
1230
  if (typeof func_type === 'undefined')
1183
1231
  throw new Error('指定 node 时必须设置 func_type');
@@ -1193,7 +1241,13 @@ class DDB {
1193
1241
  new DdbVectorString(nodes),
1194
1242
  func,
1195
1243
  new DdbVectorAny(this.to_ddbobjs(args)),
1196
- ...typeof add_node_alias === 'undefined' ? [] : [add_node_alias]
1244
+ ...(() => {
1245
+ if (typeof add_node_alias !== 'undefined')
1246
+ return [add_node_alias];
1247
+ if (this.python)
1248
+ return [true];
1249
+ return [];
1250
+ })()
1197
1251
  ];
1198
1252
  func = 'pnode_run';
1199
1253
  }
@@ -1205,9 +1259,9 @@ class DDB {
1205
1259
  }
1206
1260
  /** upload variable through websocket (variable command) */
1207
1261
  async upload(
1208
- /** 上传的变量名 */
1262
+ /** Uploaded variables' name */
1209
1263
  vars,
1210
- /** 上传的变量值 */
1264
+ /** Uploaded variables' value */
1211
1265
  args) {
1212
1266
  if (!args.length || args.length !== vars.length)
1213
1267
  throw new Error('variable 指令参数为空或参数名为空,或数量不匹配');