@hautechai/sdk 0.3.5 → 0.3.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/dist/sdk/index.js CHANGED
@@ -14,6 +14,11 @@ import { OperationsListener } from './listeners';
14
14
  import { getAPI, getBaseUrl } from './api';
15
15
  import { OperationsApi } from '../autogenerated';
16
16
  import poses from './poses';
17
+ const replaceProtocol = (url, protocol) => {
18
+ const parsed = new URL(url);
19
+ parsed.protocol = protocol;
20
+ return parsed.toString();
21
+ };
17
22
  export const createSDK = (options) => {
18
23
  let token = undefined;
19
24
  const authToken = async () => {
@@ -30,12 +35,13 @@ export const createSDK = (options) => {
30
35
  const operationsListener = new OperationsListener({
31
36
  ws: (options.useWebsocket ?? true)
32
37
  ? {
33
- endpoint: getBaseUrl(options),
38
+ endpoint: replaceProtocol(getBaseUrl(options), 'wss'),
34
39
  token: authToken,
35
40
  }
36
41
  : null,
37
42
  // TODO: Refactor the API initialization
38
43
  operations: () => getAPI(OperationsApi, optionsWithTokenRefresher),
44
+ allowPollingFallback: options.allowPollingFallback ?? true,
39
45
  });
40
46
  operationsListener.subscribe();
41
47
  return {
@@ -1,19 +1,20 @@
1
1
  import { OperationEntity, OperationsApi } from '../../autogenerated';
2
- import { client as WebSocketClient, connection as Connection } from 'websocket';
2
+ import { w3cwebsocket as WebSocketClient } from 'websocket';
3
3
  export declare class OperationsListener {
4
4
  useWebsocket: {
5
5
  endpoint: string;
6
6
  token: () => string | Promise<string>;
7
7
  } | null;
8
8
  ws: WebSocketClient | null;
9
- connection: Connection | null;
10
9
  operations: () => Promise<OperationsApi>;
11
- constructor({ ws, operations, }: {
10
+ allowPollingFallback: boolean;
11
+ constructor({ ws, operations, allowPollingFallback, }: {
12
12
  ws: {
13
13
  endpoint: string;
14
14
  token: () => string | Promise<string>;
15
15
  } | null;
16
16
  operations: () => Promise<OperationsApi>;
17
+ allowPollingFallback: boolean;
17
18
  });
18
19
  operationsStore: Record<string, OperationEntity>;
19
20
  getOperation(id: string): Promise<OperationEntity | null>;
@@ -1,12 +1,13 @@
1
- import { client as WebSocketClient } from 'websocket';
1
+ import { w3cwebsocket as WebSocketClient } from 'websocket';
2
2
  import { HautechError } from '../errors';
3
3
  // This is pretty much a dirty solution until we need to rework this part.
4
4
  export class OperationsListener {
5
- constructor({ ws, operations, }) {
5
+ constructor({ ws, operations, allowPollingFallback = false, }) {
6
6
  this.useWebsocket = null;
7
7
  this.ws = null;
8
- this.connection = null;
9
8
  this.operationsStore = {};
9
+ if (!ws)
10
+ allowPollingFallback = true;
10
11
  if (ws) {
11
12
  this.useWebsocket = {
12
13
  endpoint: ws?.endpoint,
@@ -14,10 +15,11 @@ export class OperationsListener {
14
15
  };
15
16
  }
16
17
  this.operations = operations;
18
+ this.allowPollingFallback = allowPollingFallback;
17
19
  }
18
20
  async getOperation(id) {
19
- const isWsReady = this.connection?.connected;
20
- if (!this.operationsStore[id] || !isWsReady) {
21
+ const fallbackToPolling = this.allowPollingFallback && !(this.ws?.readyState === WebSocket.OPEN);
22
+ if (!this.operationsStore[id] || fallbackToPolling) {
21
23
  const api = await this.operations();
22
24
  const operation = await api.operationsControllerGetOperationV1(id);
23
25
  if (operation.status == 200)
@@ -35,28 +37,37 @@ export class OperationsListener {
35
37
  if (!this.useWebsocket)
36
38
  return;
37
39
  try {
38
- this.ws = new WebSocketClient();
39
- this.ws.connect(this.useWebsocket.endpoint, ['1', await this.useWebsocket.token()]);
40
- this.ws.on('connect', (connection) => {
41
- this.connection = connection;
40
+ const token = await this.useWebsocket.token();
41
+ this.ws = new WebSocketClient(this.useWebsocket.endpoint, ['1', token]);
42
+ this.ws.onopen = () => {
42
43
  this.onOpen();
43
- connection.on('message', (msg) => {
44
- if (msg.type === 'utf8')
45
- this.onMessage(msg.utf8Data);
46
- else if (msg.type === 'binary')
47
- this.onMessage(msg.binaryData.toString('utf8'));
48
- });
49
- connection.on('close', (number, reason) => this.onClose(number, reason));
50
- });
44
+ };
45
+ this.ws.onerror = (err) => {
46
+ console.error('HautechAI SDK encountered a WebSocket error: ', err);
47
+ };
48
+ this.ws.onmessage = (msg) => {
49
+ if (typeof msg.data === 'string') {
50
+ this.onMessage(msg.data);
51
+ }
52
+ else if (msg.data instanceof Buffer) {
53
+ this.onMessage(msg.data.toString('utf8'));
54
+ }
55
+ else {
56
+ this.onMessage(Buffer.from(msg.data).toString('utf8'));
57
+ }
58
+ };
59
+ this.ws.onclose = (event) => {
60
+ this.onClose(event.code, event.reason);
61
+ };
51
62
  }
52
63
  catch (err) {
53
64
  throw new HautechError(`SDK failed to open websocket: ${err}`);
54
65
  }
55
66
  }
56
67
  onOpen() {
57
- if (!this.connection)
68
+ if (!this.ws)
58
69
  throw new HautechError('Semantics error: this is a bug.');
59
- this.connection.send(JSON.stringify({
70
+ this.ws.send(JSON.stringify({
60
71
  event: 'subscribe',
61
72
  data: {
62
73
  channel: 'own_resources',
@@ -64,7 +75,7 @@ export class OperationsListener {
64
75
  }));
65
76
  }
66
77
  onMessage(msg) {
67
- if (!this.connection)
78
+ if (!this.ws)
68
79
  throw new HautechError('Semantics error: this is a bug.');
69
80
  const { event, data } = JSON.parse(msg);
70
81
  switch (event) {
@@ -77,14 +88,13 @@ export class OperationsListener {
77
88
  }
78
89
  }
79
90
  onClose(number, reason) {
80
- if (!this.connection)
91
+ if (!this.ws)
81
92
  throw new HautechError('Semantics error: this is a bug.');
82
93
  // Reset dirty state.
83
94
  this.operationsStore = {};
84
95
  this.ws = null;
85
96
  }
86
97
  close() {
87
- this.ws?.abort();
88
- this.connection?.close();
98
+ this.ws?.close();
89
99
  }
90
100
  }
@@ -14,7 +14,7 @@ type Waited<T extends OperationEntityWithMetadata> = T & ({
14
14
  status: typeof OperationEntityStatusEnum.Finished;
15
15
  output: NonNullable<T['output']>;
16
16
  });
17
- declare const operations: (options: SDKOptions, relevantOperations: OperationsListener) => {
17
+ declare const operations: (options: SDKOptions, operationsListener: OperationsListener) => {
18
18
  run: {
19
19
  haute: {
20
20
  linda: {
@@ -2,7 +2,7 @@ import { OperationsApi, } from '../../autogenerated';
2
2
  import { useAutogeneratedAPI } from '../api';
3
3
  import { transformToListResponse } from '../transformers';
4
4
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
5
- const operations = (options, relevantOperations) => {
5
+ const operations = (options, operationsListener) => {
6
6
  const api = useAutogeneratedAPI({ API: OperationsApi, options });
7
7
  const createOperation = (callMethod) => (props) => api.call({ run: (methods) => callMethod(methods, props) });
8
8
  return {
@@ -77,7 +77,7 @@ const operations = (options, relevantOperations) => {
77
77
  const deadline = timeoutMs ? Date.now() + timeoutMs : undefined;
78
78
  const delay = 1000;
79
79
  const poll = async (id) => {
80
- const operation = await relevantOperations.getOperation(id);
80
+ const operation = await operationsListener.getOperation(id);
81
81
  if (operation?.status !== 'pending')
82
82
  return operation;
83
83
  return null;
package/dist/types.d.ts CHANGED
@@ -2,6 +2,7 @@ export interface SDKOptions {
2
2
  authToken: () => string | Promise<string>;
3
3
  endpoint?: string;
4
4
  useWebsocket?: boolean;
5
+ allowPollingFallback?: boolean;
5
6
  }
6
7
  export type ListProps = {
7
8
  cursor?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hautechai/sdk",
3
- "version": "0.3.5",
3
+ "version": "0.3.7",
4
4
  "license": "MIT",
5
5
  "keywords": [],
6
6
  "repository": {