@synapticon/motion-master-client 2.3.0-beta.0 → 6.0.0-alpha.22

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.
Files changed (66) hide show
  1. package/LICENSE +21 -0
  2. package/dist/client.d.ts +20 -0
  3. package/dist/client.js +37 -0
  4. package/dist/constants.d.ts +2 -0
  5. package/dist/constants.js +8 -0
  6. package/dist/generated/Api.d.ts +991 -0
  7. package/dist/generated/Api.js +614 -0
  8. package/dist/generated/data-contracts.d.ts +644 -0
  9. package/dist/generated/data-contracts.js +12 -0
  10. package/dist/generated/http-client.d.ts +60 -0
  11. package/dist/generated/http-client.js +171 -0
  12. package/dist/hex.d.ts +2 -0
  13. package/dist/hex.js +20 -0
  14. package/dist/index.d.ts +10 -7
  15. package/dist/index.js +16 -22
  16. package/dist/mmpd.d.ts +54 -0
  17. package/dist/mmpd.js +323 -0
  18. package/dist/sdo.d.ts +15 -0
  19. package/dist/sdo.js +212 -0
  20. package/dist/somanet.d.ts +5 -0
  21. package/dist/somanet.js +15 -0
  22. package/dist/web-socket-connection.d.ts +69 -0
  23. package/dist/web-socket-connection.js +206 -0
  24. package/package.json +29 -35
  25. package/.vscode/launch.json +0 -23
  26. package/README.md +0 -118
  27. package/box.config.json +0 -7
  28. package/dist/index.d.ts.map +0 -1
  29. package/dist/index.js.map +0 -1
  30. package/dist/motion-master-client-web-socket-connection.d.ts +0 -43
  31. package/dist/motion-master-client-web-socket-connection.d.ts.map +0 -1
  32. package/dist/motion-master-client-web-socket-connection.js +0 -91
  33. package/dist/motion-master-client-web-socket-connection.js.map +0 -1
  34. package/dist/motion-master-client.d.ts +0 -105
  35. package/dist/motion-master-client.d.ts.map +0 -1
  36. package/dist/motion-master-client.js +0 -348
  37. package/dist/motion-master-client.js.map +0 -1
  38. package/dist/motion-master-notification-subscribe-data.d.ts +0 -8
  39. package/dist/motion-master-notification-subscribe-data.d.ts.map +0 -1
  40. package/dist/motion-master-notification-subscribe-data.js +0 -3
  41. package/dist/motion-master-notification-subscribe-data.js.map +0 -1
  42. package/dist/motion-master-notification-web-socket-connection.d.ts +0 -56
  43. package/dist/motion-master-notification-web-socket-connection.d.ts.map +0 -1
  44. package/dist/motion-master-notification-web-socket-connection.js +0 -115
  45. package/dist/motion-master-notification-web-socket-connection.js.map +0 -1
  46. package/dist/motion-master-notification.d.ts +0 -31
  47. package/dist/motion-master-notification.d.ts.map +0 -1
  48. package/dist/motion-master-notification.js +0 -44
  49. package/dist/motion-master-notification.js.map +0 -1
  50. package/dist/util.d.ts +0 -16
  51. package/dist/util.d.ts.map +0 -1
  52. package/dist/util.js +0 -24
  53. package/dist/util.js.map +0 -1
  54. package/licenses.format.json +0 -4
  55. package/motion-master-client.config.json +0 -7
  56. package/smm_data_dic.json +0 -1213
  57. package/src/index.ts +0 -7
  58. package/src/motion-master-client-web-socket-connection.ts +0 -104
  59. package/src/motion-master-client.ts +0 -468
  60. package/src/motion-master-notification-subscribe-data.ts +0 -7
  61. package/src/motion-master-notification-web-socket-connection.ts +0 -143
  62. package/src/motion-master-notification.ts +0 -51
  63. package/src/util.ts +0 -22
  64. package/test.sh +0 -122
  65. package/tsconfig.json +0 -63
  66. package/tslint.json +0 -118
package/dist/sdo.js ADDED
@@ -0,0 +1,212 @@
1
+ // Encoding and decoding helpers for CoE SDO values: turning the raw little-endian
2
+ // byte arrays exchanged over the wire into human-readable values and back.
3
+ import { parseHexBytes } from './hex.js';
4
+ // CoE data types the SDO download card can encode a value into. `raw` is an
5
+ // escape hatch for entering bytes directly.
6
+ export const SDO_TYPES = [
7
+ 'uint8', 'int8', 'uint16', 'int16', 'uint32', 'int32',
8
+ 'uint64', 'int64', 'float32', 'float64', 'string', 'raw',
9
+ ];
10
+ // Placeholder text shown in the value input for each type.
11
+ export const SDO_TYPE_HINT = {
12
+ uint8: 'e.g. 200 or 0xC8',
13
+ int8: 'e.g. -42',
14
+ uint16: 'e.g. 1000 or 0x03E8',
15
+ int16: 'e.g. -1000',
16
+ uint32: 'e.g. 100000',
17
+ int32: 'e.g. -100000',
18
+ uint64: 'e.g. 4294967296',
19
+ int64: 'e.g. -4294967296',
20
+ float32: 'e.g. 3.14',
21
+ float64: 'e.g. 3.141592653589793',
22
+ string: 'e.g. SOMANET',
23
+ raw: 'hex bytes, e.g. 01 02 0A FF',
24
+ };
25
+ const INT_ENC = {
26
+ uint8: { size: 1, signed: false }, int8: { size: 1, signed: true },
27
+ uint16: { size: 2, signed: false }, int16: { size: 2, signed: true },
28
+ uint32: { size: 4, signed: false }, int32: { size: 4, signed: true },
29
+ uint64: { size: 8, signed: false }, int64: { size: 8, signed: true },
30
+ };
31
+ // Accepts decimal (with optional leading '-') or 0x-prefixed hex; returns null on anything else.
32
+ function parseBigIntFlexible(s) {
33
+ if (/^-?[0-9]+$/.test(s))
34
+ return BigInt(s);
35
+ if (/^0[xX][0-9a-fA-F]+$/.test(s))
36
+ return BigInt(s);
37
+ if (/^-0[xX][0-9a-fA-F]+$/.test(s))
38
+ return -BigInt(s.slice(1));
39
+ return null;
40
+ }
41
+ // Encodes a human-entered value into the little-endian byte sequence for an SDO download.
42
+ export function encodeSdoValue(type, raw) {
43
+ const s = raw.trim();
44
+ const meta = INT_ENC[type];
45
+ if (meta) {
46
+ const n = parseBigIntFlexible(s);
47
+ if (n === null)
48
+ return { error: 'Not a valid integer' };
49
+ const bits = BigInt(meta.size * 8);
50
+ const min = meta.signed ? -(1n << (bits - 1n)) : 0n;
51
+ const max = meta.signed ? (1n << (bits - 1n)) - 1n : (1n << bits) - 1n;
52
+ if (n < min || n > max)
53
+ return { error: `Out of range for ${type} [${min}, ${max}]` };
54
+ const view = new DataView(new ArrayBuffer(meta.size));
55
+ if (meta.size === 1)
56
+ meta.signed ? view.setInt8(0, Number(n)) : view.setUint8(0, Number(n));
57
+ else if (meta.size === 2)
58
+ meta.signed ? view.setInt16(0, Number(n), true) : view.setUint16(0, Number(n), true);
59
+ else if (meta.size === 4)
60
+ meta.signed ? view.setInt32(0, Number(n), true) : view.setUint32(0, Number(n), true);
61
+ else
62
+ meta.signed ? view.setBigInt64(0, n, true) : view.setBigUint64(0, n, true);
63
+ return { bytes: [...new Uint8Array(view.buffer)] };
64
+ }
65
+ if (type === 'float32' || type === 'float64') {
66
+ if (s === '')
67
+ return { error: 'Value is empty' };
68
+ const f = Number(s);
69
+ if (Number.isNaN(f))
70
+ return { error: 'Not a valid number' };
71
+ const size = type === 'float32' ? 4 : 8;
72
+ const view = new DataView(new ArrayBuffer(size));
73
+ if (type === 'float32')
74
+ view.setFloat32(0, f, true);
75
+ else
76
+ view.setFloat64(0, f, true);
77
+ return { bytes: [...new Uint8Array(view.buffer)] };
78
+ }
79
+ if (type === 'string') {
80
+ return { bytes: [...new TextEncoder().encode(raw)] };
81
+ }
82
+ // raw: whitespace- or comma-separated hex bytes
83
+ const bytes = parseHexBytes(s);
84
+ if (bytes === null)
85
+ return { error: 'Not a valid even-length hex byte string' };
86
+ return { bytes };
87
+ }
88
+ // Maps a CoE object's data-type name (as reported by SDO Info) to the SdoType the
89
+ // encoder understands, so a value typed inline against an object can be encoded
90
+ // without the user picking a type. Falls back to `raw` for unknown/complex types.
91
+ export function sdoTypeForDataTypeName(dataTypeName) {
92
+ switch (dataTypeName) {
93
+ case 'BOOLEAN':
94
+ case 'UNSIGNED8':
95
+ case 'BYTE':
96
+ return 'uint8';
97
+ case 'INTEGER8':
98
+ return 'int8';
99
+ case 'UNSIGNED16':
100
+ case 'WORD':
101
+ return 'uint16';
102
+ case 'INTEGER16':
103
+ return 'int16';
104
+ case 'UNSIGNED32':
105
+ case 'DWORD':
106
+ return 'uint32';
107
+ case 'INTEGER32':
108
+ return 'int32';
109
+ case 'UNSIGNED64':
110
+ return 'uint64';
111
+ case 'INTEGER64':
112
+ return 'int64';
113
+ case 'REAL32':
114
+ return 'float32';
115
+ case 'REAL64':
116
+ return 'float64';
117
+ case 'VISIBLE_STRING':
118
+ case 'UNICODE_STRING':
119
+ return 'string';
120
+ default:
121
+ return 'raw';
122
+ }
123
+ }
124
+ // Best-effort interpretation of raw SDO bytes as every fixed-width type that fits,
125
+ // plus a printable-string reading. Used to show what an uploaded value could mean.
126
+ export function interpretSdoBytes(bytes) {
127
+ const view = new DataView(new Uint8Array(bytes).buffer);
128
+ const out = [];
129
+ if (bytes.length === 1) {
130
+ out.push({ label: 'int8', value: String(view.getInt8(0)) });
131
+ out.push({ label: 'uint8', value: String(view.getUint8(0)) });
132
+ }
133
+ else if (bytes.length === 2) {
134
+ out.push({ label: 'int16 LE', value: String(view.getInt16(0, true)) });
135
+ out.push({ label: 'uint16 LE', value: String(view.getUint16(0, true)) });
136
+ }
137
+ else if (bytes.length === 4) {
138
+ out.push({ label: 'int32 LE', value: String(view.getInt32(0, true)) });
139
+ out.push({ label: 'uint32 LE', value: String(view.getUint32(0, true)) });
140
+ out.push({ label: 'float32 LE', value: String(view.getFloat32(0, true)) });
141
+ }
142
+ else if (bytes.length === 8) {
143
+ out.push({ label: 'int64 LE', value: view.getBigInt64(0, true).toString() });
144
+ out.push({ label: 'uint64 LE', value: view.getBigUint64(0, true).toString() });
145
+ out.push({ label: 'float64 LE', value: String(view.getFloat64(0, true)) });
146
+ }
147
+ if (bytes.length > 0 && bytes.every(b => (b >= 0x20 && b <= 0x7e) || b === 0)) {
148
+ // VISIBLE_STRING is space-padded to a fixed width and may carry a terminating
149
+ // NUL — neither is significant, so strip trailing 0x20/0x00 before display.
150
+ let len = bytes.length;
151
+ while (len > 0 && (bytes[len - 1] === 0x00 || bytes[len - 1] === 0x20))
152
+ len--;
153
+ const str = new TextDecoder().decode(new Uint8Array(bytes.slice(0, len).filter(b => b !== 0)));
154
+ out.push({ label: 'string', value: `"${str}"` });
155
+ }
156
+ return out;
157
+ }
158
+ // Decodes raw SDO bytes into a typed value, given the object's CoE data-type name.
159
+ // Falls back to the raw byte array for unknown types or short reads.
160
+ export function decodeSdoBytes(dataTypeName, bytes) {
161
+ const view = new DataView(new Uint8Array(bytes).buffer);
162
+ switch (dataTypeName) {
163
+ case 'BOOLEAN':
164
+ case 'UNSIGNED8':
165
+ case 'BYTE':
166
+ if (bytes.length >= 1)
167
+ return view.getUint8(0);
168
+ break;
169
+ case 'INTEGER8':
170
+ if (bytes.length >= 1)
171
+ return view.getInt8(0);
172
+ break;
173
+ case 'INTEGER16':
174
+ if (bytes.length >= 2)
175
+ return view.getInt16(0, true);
176
+ break;
177
+ case 'INTEGER32':
178
+ if (bytes.length >= 4)
179
+ return view.getInt32(0, true);
180
+ break;
181
+ case 'UNSIGNED16':
182
+ case 'WORD':
183
+ if (bytes.length >= 2)
184
+ return view.getUint16(0, true);
185
+ break;
186
+ case 'UNSIGNED32':
187
+ case 'DWORD':
188
+ if (bytes.length >= 4)
189
+ return view.getUint32(0, true);
190
+ break;
191
+ case 'REAL32':
192
+ if (bytes.length >= 4)
193
+ return view.getFloat32(0, true);
194
+ break;
195
+ case 'REAL64':
196
+ if (bytes.length >= 8)
197
+ return view.getFloat64(0, true);
198
+ break;
199
+ case 'INTEGER64':
200
+ if (bytes.length >= 8)
201
+ return Number(view.getBigInt64(0, true));
202
+ break;
203
+ case 'UNSIGNED64':
204
+ if (bytes.length >= 8)
205
+ return Number(view.getBigUint64(0, true));
206
+ break;
207
+ case 'VISIBLE_STRING':
208
+ case 'UNICODE_STRING':
209
+ return new TextDecoder().decode(new Uint8Array(bytes.filter(b => b !== 0)));
210
+ }
211
+ return bytes;
212
+ }
@@ -0,0 +1,5 @@
1
+ export interface SomanetFile {
2
+ name: string;
3
+ size: number | null;
4
+ }
5
+ export declare function parseSomanetFileList(text: string): SomanetFile[];
@@ -0,0 +1,15 @@
1
+ // Helpers for SOMANET (Synapticon) drive specifics.
2
+ // Parse the body of a SOMANET `fs-getlist` FoE read. Each entry is one line in
3
+ // the form `<filename>, size: <bytes>`; size is null when a line doesn't match.
4
+ export function parseSomanetFileList(text) {
5
+ return text
6
+ .split(/\r?\n/)
7
+ .map(line => line.trim())
8
+ .filter(Boolean)
9
+ .map(line => {
10
+ const match = line.match(/^(.*?),\s*size:\s*(\d+)\s*$/);
11
+ if (match)
12
+ return { name: match[1].trim(), size: Number(match[2]) };
13
+ return { name: line, size: null };
14
+ });
15
+ }
@@ -0,0 +1,69 @@
1
+ export type ReadyState = 'connecting' | 'open' | 'closed';
2
+ export type MonitoringRow = (number | null)[];
3
+ export type MonitoringBatch = MonitoringRow[];
4
+ export interface MonitoringMessage {
5
+ type: 'monitoring';
6
+ topic: string;
7
+ data: MonitoringBatch;
8
+ }
9
+ export interface NotificationMessage {
10
+ type: 'notification';
11
+ data: unknown;
12
+ }
13
+ export interface ProgressMessage {
14
+ type: 'progress';
15
+ data: unknown;
16
+ }
17
+ export type ServerMessage = MonitoringMessage | NotificationMessage | ProgressMessage;
18
+ type MonitoringListener = (rows: MonitoringBatch) => void;
19
+ type NotificationListener = (data: unknown) => void;
20
+ type ProgressListener = (data: unknown) => void;
21
+ type StateListener = (state: ReadyState) => void;
22
+ export interface WebSocketLike {
23
+ readyState: number;
24
+ send(data: string): void;
25
+ close(): void;
26
+ onopen: ((event: unknown) => void) | null;
27
+ onclose: ((event: unknown) => void) | null;
28
+ onerror: ((event: unknown) => void) | null;
29
+ onmessage: ((event: {
30
+ data: unknown;
31
+ }) => void) | null;
32
+ }
33
+ export type WebSocketConstructor = new (url: string, protocolsOrOptions?: unknown) => WebSocketLike;
34
+ export interface WebSocketConnectionOptions {
35
+ url: string;
36
+ WebSocket?: WebSocketConstructor;
37
+ protocolsOrOptions?: unknown;
38
+ reconnectDelayMs?: number;
39
+ maxReconnectDelayMs?: number;
40
+ }
41
+ export declare class WebSocketConnection {
42
+ private readonly url;
43
+ private readonly WebSocketImpl?;
44
+ private readonly protocolsOrOptions?;
45
+ private readonly reconnectDelayMs;
46
+ private readonly maxReconnectDelayMs;
47
+ private socket;
48
+ private state;
49
+ private closedByUser;
50
+ private reconnectAttempts;
51
+ private reconnectTimer;
52
+ private readonly topicListeners;
53
+ private readonly notificationListeners;
54
+ private readonly progressListeners;
55
+ private readonly stateListeners;
56
+ constructor(options: WebSocketConnectionOptions);
57
+ get readyState(): ReadyState;
58
+ connect(): void;
59
+ close(): void;
60
+ subscribe(topic: string, listener: MonitoringListener): () => void;
61
+ onNotification(listener: NotificationListener): () => void;
62
+ onProgress(listener: ProgressListener): () => void;
63
+ onStateChange(listener: StateListener): () => void;
64
+ private setState;
65
+ private send;
66
+ private handleDisconnect;
67
+ private route;
68
+ }
69
+ export {};
@@ -0,0 +1,206 @@
1
+ // Framework-agnostic connection to Motion Master's single WebSocket — the one bidirectional
2
+ // socket that carries server->client monitoring batches, notifications, and (planned) procedure
3
+ // progress, and client->server topic subscribe/unsubscribe. It owns reconnection, multiplexes
4
+ // per-topic subscriptions over the one socket (ref-counted: subscribe on the first listener,
5
+ // unsubscribe on the last, re-subscribe everything on reconnect), and routes inbound frames by
6
+ // type. It is isomorphic: the browser global `WebSocket` is used by default, and any compatible
7
+ // implementation (e.g. the Node `ws` package) can be injected — there is no hard dependency on
8
+ // either, so the package stays usable in both environments.
9
+ /// The numeric `readyState` value a WebSocket reports when it is open. Hard-coded (rather than read
10
+ /// off a global) so the client never depends on a particular `WebSocket` implementation's statics.
11
+ const WS_OPEN = 1;
12
+ export class WebSocketConnection {
13
+ url;
14
+ WebSocketImpl;
15
+ protocolsOrOptions;
16
+ reconnectDelayMs;
17
+ maxReconnectDelayMs;
18
+ socket = null;
19
+ state = 'closed';
20
+ closedByUser = false;
21
+ reconnectAttempts = 0;
22
+ reconnectTimer = null;
23
+ topicListeners = new Map();
24
+ notificationListeners = new Set();
25
+ progressListeners = new Set();
26
+ stateListeners = new Set();
27
+ constructor(options) {
28
+ this.url = options.url;
29
+ this.WebSocketImpl = options.WebSocket;
30
+ this.protocolsOrOptions = options.protocolsOrOptions;
31
+ this.reconnectDelayMs = options.reconnectDelayMs ?? 1000;
32
+ this.maxReconnectDelayMs = options.maxReconnectDelayMs ?? 15000;
33
+ }
34
+ get readyState() {
35
+ return this.state;
36
+ }
37
+ /// Opens the socket if it is not already open or connecting. Idempotent — safe to call from every
38
+ /// subscribe path. Connection is lazy: an HTTP-only consumer that never subscribes never opens a
39
+ /// socket.
40
+ connect() {
41
+ if (this.socket || this.state === 'connecting') {
42
+ return;
43
+ }
44
+ const Impl = this.WebSocketImpl ?? globalThis.WebSocket;
45
+ if (!Impl) {
46
+ throw new Error('No WebSocket implementation available. Inject one via the `WebSocket` option (e.g. the ' +
47
+ '`ws` package on Node < 22).');
48
+ }
49
+ this.closedByUser = false;
50
+ this.setState('connecting');
51
+ const socket = new Impl(this.url, this.protocolsOrOptions);
52
+ this.socket = socket;
53
+ socket.onopen = () => {
54
+ this.reconnectAttempts = 0;
55
+ this.setState('open');
56
+ // (Re)subscribe to every topic that currently has listeners — also covers reconnects.
57
+ for (const topic of this.topicListeners.keys()) {
58
+ this.send({ subscribe: topic });
59
+ }
60
+ };
61
+ socket.onmessage = (event) => this.route(event.data);
62
+ socket.onclose = () => this.handleDisconnect();
63
+ socket.onerror = () => this.handleDisconnect();
64
+ }
65
+ /// Permanently closes the socket and cancels any pending reconnect. Registered listeners are
66
+ /// kept, so a later `connect()` resumes them.
67
+ close() {
68
+ this.closedByUser = true;
69
+ if (this.reconnectTimer !== null) {
70
+ clearTimeout(this.reconnectTimer);
71
+ this.reconnectTimer = null;
72
+ }
73
+ const socket = this.socket;
74
+ this.socket = null;
75
+ if (socket) {
76
+ socket.onopen = null;
77
+ socket.onmessage = null;
78
+ socket.onclose = null;
79
+ socket.onerror = null;
80
+ socket.close();
81
+ }
82
+ this.setState('closed');
83
+ }
84
+ /// Subscribes `listener` to a topic's monitoring batches. The first subscriber to a topic sends a
85
+ /// `subscribe` frame; the last to leave sends `unsubscribe`. Connects lazily. Returns an
86
+ /// unsubscribe function.
87
+ subscribe(topic, listener) {
88
+ let listeners = this.topicListeners.get(topic);
89
+ if (!listeners) {
90
+ listeners = new Set();
91
+ this.topicListeners.set(topic, listeners);
92
+ this.send({ subscribe: topic });
93
+ }
94
+ listeners.add(listener);
95
+ this.connect();
96
+ return () => {
97
+ const current = this.topicListeners.get(topic);
98
+ if (!current) {
99
+ return;
100
+ }
101
+ current.delete(listener);
102
+ if (current.size === 0) {
103
+ this.topicListeners.delete(topic);
104
+ this.send({ unsubscribe: topic });
105
+ }
106
+ };
107
+ }
108
+ /// Registers a listener for server notifications (e.g. `{ event: 'slaves_changed' }`). Connects
109
+ /// lazily. Returns a removal function.
110
+ onNotification(listener) {
111
+ this.notificationListeners.add(listener);
112
+ this.connect();
113
+ return () => {
114
+ this.notificationListeners.delete(listener);
115
+ };
116
+ }
117
+ /// Registers a listener for procedure progress messages (reserved; fires once the backend ships
118
+ /// them). Connects lazily. Returns a removal function.
119
+ onProgress(listener) {
120
+ this.progressListeners.add(listener);
121
+ this.connect();
122
+ return () => {
123
+ this.progressListeners.delete(listener);
124
+ };
125
+ }
126
+ /// Observes connection-state changes. Returns a removal function.
127
+ onStateChange(listener) {
128
+ this.stateListeners.add(listener);
129
+ return () => {
130
+ this.stateListeners.delete(listener);
131
+ };
132
+ }
133
+ setState(state) {
134
+ if (this.state === state) {
135
+ return;
136
+ }
137
+ this.state = state;
138
+ for (const listener of this.stateListeners) {
139
+ listener(state);
140
+ }
141
+ }
142
+ send(payload) {
143
+ const socket = this.socket;
144
+ if (socket && socket.readyState === WS_OPEN) {
145
+ socket.send(JSON.stringify(payload));
146
+ }
147
+ }
148
+ handleDisconnect() {
149
+ if (this.socket) {
150
+ this.socket.onopen = null;
151
+ this.socket.onmessage = null;
152
+ this.socket.onclose = null;
153
+ this.socket.onerror = null;
154
+ this.socket = null;
155
+ }
156
+ this.setState('closed');
157
+ if (this.closedByUser || this.reconnectDelayMs <= 0) {
158
+ return;
159
+ }
160
+ const delay = Math.min(this.reconnectDelayMs * 2 ** this.reconnectAttempts, this.maxReconnectDelayMs);
161
+ this.reconnectAttempts += 1;
162
+ this.reconnectTimer = setTimeout(() => {
163
+ this.reconnectTimer = null;
164
+ this.connect();
165
+ }, delay);
166
+ }
167
+ route(raw) {
168
+ if (typeof raw !== 'string') {
169
+ return;
170
+ }
171
+ let msg;
172
+ try {
173
+ msg = JSON.parse(raw);
174
+ }
175
+ catch {
176
+ return;
177
+ }
178
+ if (!msg || typeof msg !== 'object') {
179
+ return;
180
+ }
181
+ const { type } = msg;
182
+ if (type === 'monitoring') {
183
+ const { topic, data } = msg;
184
+ if (typeof topic === 'string' && Array.isArray(data)) {
185
+ const listeners = this.topicListeners.get(topic);
186
+ if (listeners) {
187
+ for (const listener of listeners) {
188
+ listener(data);
189
+ }
190
+ }
191
+ }
192
+ }
193
+ else if (type === 'notification') {
194
+ const { data } = msg;
195
+ for (const listener of this.notificationListeners) {
196
+ listener(data);
197
+ }
198
+ }
199
+ else if (type === 'progress') {
200
+ const { data } = msg;
201
+ for (const listener of this.progressListeners) {
202
+ listener(data);
203
+ }
204
+ }
205
+ }
206
+ }
package/package.json CHANGED
@@ -1,44 +1,38 @@
1
1
  {
2
2
  "name": "@synapticon/motion-master-client",
3
- "version": "2.3.0-beta.0",
4
- "private": false,
5
- "description": "",
6
- "main": "./dist/index.js",
7
- "types": "./dist/index.d.ts",
3
+ "version": "6.0.0-alpha.22",
4
+ "description": "Isomorphic TypeScript client for Motion Master: the generated HTTP API client, the WebSocket connection, and helpers.",
5
+ "license": "MIT",
8
6
  "repository": {
9
7
  "type": "git",
10
- "url": "https://github.com/synapticon/motion-master-client.git"
8
+ "url": "git+https://github.com/synapticon/motion-master.git",
9
+ "directory": "web/packages/motion-master-client"
11
10
  },
12
- "scripts": {
13
- "build": "npx tsc --build tsconfig.json",
14
- "watch": "npx tsc --watch",
15
- "motion-master-client": "node dist/cli.js",
16
- "test": "echo \"Error: no test specified\" && exit 1",
17
- "licenses": "npx license-checker --csv --out tmp/licenses.csv --customPath licenses.format.json"
11
+ "homepage": "https://github.com/synapticon/motion-master#readme",
12
+ "type": "module",
13
+ "main": "./dist/index.js",
14
+ "types": "./dist/index.d.ts",
15
+ "files": [
16
+ "dist",
17
+ "LICENSE"
18
+ ],
19
+ "sideEffects": false,
20
+ "publishConfig": {
21
+ "access": "public"
18
22
  },
19
- "bin": {
20
- "motion-master-client": "dist/cli.js"
23
+ "devDependencies": {
24
+ "swagger-typescript-api": "^13.7.2",
25
+ "typescript": "^5.6.0"
21
26
  },
22
- "author": "Marko Sanković <msankovic@synapticon.com> (https://github.com/markosankovic)",
23
- "license": "MIT",
24
- "dependencies": {
25
- "@synapticon/motion-master-proto": "^5.2.2",
26
- "commander": "^6.1.0",
27
- "debug": "^4.1.1",
28
- "lodash": "^4.17.20",
29
- "long": "^4.0.0",
30
- "rxjs": "^6.6.3",
31
- "uuid": "^8.3.0",
32
- "yaml": "^1.10.0"
27
+ "scripts": {
28
+ "generate": "swagger-typescript-api generate -p ../../../apps/motion_master/swagger.yml -o ./src/generated -n api.ts --modular",
29
+ "build": "tsc -p tsconfig.build.json && node scripts/fix-esm-imports.mjs"
33
30
  },
34
- "devDependencies": {
35
- "@types/commander": "^2.12.2",
36
- "@types/lodash": "^4.14.161",
37
- "@types/node": "^14.6.4",
38
- "@types/uuid": "^8.3.0",
39
- "@types/yaml": "^1.9.7",
40
- "license-checker": "^25.0.1",
41
- "tslint": "^6.1.3",
42
- "typescript": "^3.9.7"
31
+ "module": "./dist/index.js",
32
+ "exports": {
33
+ ".": {
34
+ "types": "./dist/index.d.ts",
35
+ "import": "./dist/index.js"
36
+ }
43
37
  }
44
- }
38
+ }
@@ -1,23 +0,0 @@
1
- {
2
- // Use IntelliSense to learn about possible attributes.
3
- // Hover to view descriptions of existing attributes.
4
- // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5
- "version": "0.2.0",
6
- "configurations": [
7
- {
8
- "type": "node",
9
- "request": "launch",
10
- "name": "Upload 0x1000:0 for device at position 0",
11
- "program": "${workspaceFolder}/dist/cli.js",
12
- "args": [
13
- "-p",
14
- "0",
15
- "upload",
16
- "0x1000:0",
17
- ],
18
- "outFiles": [
19
- "${workspaceFolder}/**/*.js"
20
- ]
21
- }
22
- ]
23
- }