aes70 2.0.14 → 2.0.15
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/AES70.es5.js +124 -82
- package/package.json +1 -1
- package/src/controller/abstract_udp_connection.js +25 -43
- package/src/controller/client_connection.js +17 -16
- package/src/controller/node_udp.js +71 -36
- package/src/controller/property.js +13 -21
- package/src/events.js +10 -5
- package/src/utils/subscribeEvent.js +32 -0
- package/src/utils/subscriptions.js +27 -0
package/dist/AES70.es5.js
CHANGED
|
@@ -31,12 +31,13 @@
|
|
|
31
31
|
class Events {
|
|
32
32
|
constructor() {
|
|
33
33
|
this.event_handlers = new Map();
|
|
34
|
+
this.event_handlers_cleared = false;
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
/**
|
|
37
38
|
* Emit an event.
|
|
38
39
|
*
|
|
39
|
-
* @param {
|
|
40
|
+
* @param {string} name - Name of the event.
|
|
40
41
|
* @param {...*} args - Extra arguments.
|
|
41
42
|
*/
|
|
42
43
|
emit(name) {
|
|
@@ -57,7 +58,7 @@
|
|
|
57
58
|
/**
|
|
58
59
|
* Subscribe to an event.
|
|
59
60
|
*
|
|
60
|
-
* @param {
|
|
61
|
+
* @param {string} name - Name of the event.
|
|
61
62
|
* @param {Function} cb - Callback function.
|
|
62
63
|
*/
|
|
63
64
|
on(name, cb) {
|
|
@@ -81,14 +82,17 @@
|
|
|
81
82
|
/**
|
|
82
83
|
* Removes an event handler.
|
|
83
84
|
*
|
|
84
|
-
* @param {
|
|
85
|
+
* @param {string} name - Name of the event.
|
|
85
86
|
* @param {Function} cb - Callback function.
|
|
86
87
|
*/
|
|
87
88
|
removeEventListener(name, cb) {
|
|
88
89
|
const handlers = this.event_handlers.get(name);
|
|
89
90
|
|
|
90
91
|
if (!handlers || !handlers.has(cb)) {
|
|
91
|
-
|
|
92
|
+
if (!this.event_handlers_cleared) {
|
|
93
|
+
console.warn('removeEventListeners(): not installed:', name, cb);
|
|
94
|
+
}
|
|
95
|
+
return;
|
|
92
96
|
}
|
|
93
97
|
|
|
94
98
|
handlers.delete(cb);
|
|
@@ -97,7 +101,7 @@
|
|
|
97
101
|
/**
|
|
98
102
|
* Removes an event handler.
|
|
99
103
|
*
|
|
100
|
-
* @param {
|
|
104
|
+
* @param {string} name
|
|
101
105
|
* @param {Function} cb
|
|
102
106
|
*/
|
|
103
107
|
off(name, cb) {
|
|
@@ -109,6 +113,7 @@
|
|
|
109
113
|
*/
|
|
110
114
|
removeAllEventListeners() {
|
|
111
115
|
this.event_handlers.clear();
|
|
116
|
+
this.event_handlers_cleared = true;
|
|
112
117
|
}
|
|
113
118
|
|
|
114
119
|
/**
|
|
@@ -1585,6 +1590,67 @@
|
|
|
1585
1590
|
}
|
|
1586
1591
|
}
|
|
1587
1592
|
|
|
1593
|
+
class Subscriptions {
|
|
1594
|
+
constructor() {
|
|
1595
|
+
this._callbacks = [];
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
/**
|
|
1599
|
+
* Add a subscription.
|
|
1600
|
+
*
|
|
1601
|
+
* @param {Function[]} cbs
|
|
1602
|
+
*/
|
|
1603
|
+
add(...cbs) {
|
|
1604
|
+
cbs.forEach((cb) => {
|
|
1605
|
+
this._callbacks.push(cb);
|
|
1606
|
+
});
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
unsubscribe() {
|
|
1610
|
+
this._callbacks.forEach((cb) => {
|
|
1611
|
+
try {
|
|
1612
|
+
cb();
|
|
1613
|
+
} catch (err) {
|
|
1614
|
+
console.error(err);
|
|
1615
|
+
}
|
|
1616
|
+
});
|
|
1617
|
+
this._callbacks.length = 0;
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
function addEvent(target, name, callback) {
|
|
1622
|
+
if (target.addEventListener) {
|
|
1623
|
+
target.addEventListener(name, callback);
|
|
1624
|
+
} else if (target.on) {
|
|
1625
|
+
target.on(name, callback);
|
|
1626
|
+
} else {
|
|
1627
|
+
throw new TypeError('Unsupported event target ', target);
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
function removeEvent(target, name, callback) {
|
|
1632
|
+
if (target.removeEventListener) {
|
|
1633
|
+
target.removeEventListener(name, callback);
|
|
1634
|
+
} else if (target.off) {
|
|
1635
|
+
target.off(name, callback);
|
|
1636
|
+
} else {
|
|
1637
|
+
throw new TypeError('Unsupported event target ', target);
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
/**
|
|
1642
|
+
*
|
|
1643
|
+
* @param {EventTarget|Events} target
|
|
1644
|
+
* @param {string} name
|
|
1645
|
+
* @param {callback} callback
|
|
1646
|
+
*/
|
|
1647
|
+
function subscribeEvent(target, name, callback) {
|
|
1648
|
+
addEvent(target, name, callback);
|
|
1649
|
+
return () => {
|
|
1650
|
+
removeEvent(target, name, callback);
|
|
1651
|
+
};
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1588
1654
|
class PendingCommand {
|
|
1589
1655
|
get handle() {
|
|
1590
1656
|
return this.command.handle;
|
|
@@ -1829,35 +1895,34 @@
|
|
|
1829
1895
|
* the connection will be closed and the returned promise will reject.
|
|
1830
1896
|
* @param {number} interval
|
|
1831
1897
|
* Keepalive interval in seconds.
|
|
1898
|
+
* @param {AbortSignal} [signal]
|
|
1899
|
+
* Optional abort signal which can be used to abort
|
|
1900
|
+
* the process.
|
|
1832
1901
|
*/
|
|
1833
|
-
wait_for_keepalive(interval) {
|
|
1834
|
-
|
|
1835
|
-
const subscriptions = [];
|
|
1902
|
+
wait_for_keepalive(interval, signal) {
|
|
1903
|
+
const subscriptions = new Subscriptions();
|
|
1836
1904
|
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
}
|
|
1845
|
-
subscriptions.
|
|
1905
|
+
return new Promise((resolve, reject) => {
|
|
1906
|
+
if (signal) {
|
|
1907
|
+
subscriptions.add(
|
|
1908
|
+
subscribeEvent(signal, 'abort', () => {
|
|
1909
|
+
reject(signal.reason);
|
|
1910
|
+
})
|
|
1911
|
+
);
|
|
1912
|
+
}
|
|
1913
|
+
subscriptions.add(
|
|
1846
1914
|
this.subscribe('error', (error) => {
|
|
1847
1915
|
reject(error);
|
|
1848
|
-
cleanup();
|
|
1849
1916
|
}),
|
|
1850
1917
|
this.subscribe('close', () => {
|
|
1851
1918
|
reject(new CloseError());
|
|
1852
|
-
cleanup();
|
|
1853
1919
|
}),
|
|
1854
1920
|
this.subscribe('keepalive', () => {
|
|
1855
1921
|
resolve();
|
|
1856
|
-
cleanup();
|
|
1857
1922
|
})
|
|
1858
1923
|
);
|
|
1859
1924
|
this.set_keepalive_interval(interval);
|
|
1860
|
-
});
|
|
1925
|
+
}).finally(() => subscriptions.unsubscribe());
|
|
1861
1926
|
}
|
|
1862
1927
|
}
|
|
1863
1928
|
|
|
@@ -3486,8 +3551,7 @@
|
|
|
3486
3551
|
* @returns {Function} The getter. If none could be found, null is returned.
|
|
3487
3552
|
*/
|
|
3488
3553
|
getter(o, no_bind) {
|
|
3489
|
-
|
|
3490
|
-
let i = 0;
|
|
3554
|
+
const name = this.name;
|
|
3491
3555
|
const aliases = this.aliases;
|
|
3492
3556
|
const accessors = this.accessors;
|
|
3493
3557
|
|
|
@@ -3529,10 +3593,13 @@
|
|
|
3529
3593
|
return no_bind ? fun : fun.bind(o);
|
|
3530
3594
|
}
|
|
3531
3595
|
|
|
3532
|
-
|
|
3596
|
+
// iterate all possible names
|
|
3597
|
+
const possibleNames = aliases ? [name, ...aliases] : [name];
|
|
3598
|
+
|
|
3599
|
+
for (const possibleName of possibleNames) {
|
|
3533
3600
|
if (this.static) {
|
|
3534
3601
|
const c = o.constructor;
|
|
3535
|
-
const v = c[
|
|
3602
|
+
const v = c[possibleName];
|
|
3536
3603
|
|
|
3537
3604
|
if (v !== void 0) {
|
|
3538
3605
|
return function () {
|
|
@@ -3540,16 +3607,11 @@
|
|
|
3540
3607
|
};
|
|
3541
3608
|
}
|
|
3542
3609
|
} else {
|
|
3543
|
-
const fun = o['Get' +
|
|
3610
|
+
const fun = o['Get' + possibleName];
|
|
3544
3611
|
|
|
3545
3612
|
if (fun) return no_bind ? fun : fun.bind(o);
|
|
3546
3613
|
}
|
|
3547
|
-
|
|
3548
|
-
if (aliases && i < aliases.length) {
|
|
3549
|
-
name = aliases[i++];
|
|
3550
|
-
continue;
|
|
3551
|
-
}
|
|
3552
|
-
} while (false);
|
|
3614
|
+
}
|
|
3553
3615
|
|
|
3554
3616
|
return null;
|
|
3555
3617
|
}
|
|
@@ -3565,20 +3627,15 @@
|
|
|
3565
3627
|
setter(o, no_bind) {
|
|
3566
3628
|
if (this.readonly || this.static) return null;
|
|
3567
3629
|
|
|
3568
|
-
|
|
3569
|
-
i = 0;
|
|
3630
|
+
const name = this.name;
|
|
3570
3631
|
const aliases = this.aliases;
|
|
3632
|
+
const possibleNames = aliases ? [name, ...aliases] : [name];
|
|
3571
3633
|
|
|
3572
|
-
|
|
3573
|
-
const fun = o['Set' +
|
|
3634
|
+
for (const possibleName of possibleNames) {
|
|
3635
|
+
const fun = o['Set' + possibleName];
|
|
3574
3636
|
|
|
3575
3637
|
if (fun) return no_bind ? fun : fun.bind(o);
|
|
3576
|
-
|
|
3577
|
-
if (aliases && i < aliases.length) {
|
|
3578
|
-
name = aliases[i++];
|
|
3579
|
-
continue;
|
|
3580
|
-
}
|
|
3581
|
-
} while (false);
|
|
3638
|
+
}
|
|
3582
3639
|
|
|
3583
3640
|
return null;
|
|
3584
3641
|
}
|
|
@@ -28733,35 +28790,6 @@
|
|
|
28733
28790
|
|
|
28734
28791
|
/* eslint-env node */
|
|
28735
28792
|
|
|
28736
|
-
|
|
28737
|
-
function delay(n) {
|
|
28738
|
-
return new Promise((resolve) => setTimeout(resolve, n));
|
|
28739
|
-
}
|
|
28740
|
-
|
|
28741
|
-
async function waitForKeepalive(socket, options) {
|
|
28742
|
-
const waiter = socket.receiveMessage().then((buffer) => {
|
|
28743
|
-
const pdus = [];
|
|
28744
|
-
const pos = decodeMessage(new DataView(buffer), 0, pdus);
|
|
28745
|
-
|
|
28746
|
-
if (pdus.length !== 1) throw new Error('Expected keepalive response.');
|
|
28747
|
-
if (pos !== buffer.byteLength)
|
|
28748
|
-
throw new Error('Trailing data in initial keepalive pdu.');
|
|
28749
|
-
|
|
28750
|
-
return true;
|
|
28751
|
-
});
|
|
28752
|
-
|
|
28753
|
-
const msg = encodeMessage(new KeepAlive(1000));
|
|
28754
|
-
const t = 5 * (options.retry_interval || 250);
|
|
28755
|
-
|
|
28756
|
-
for (let i = 0; i < 3; i++) {
|
|
28757
|
-
socket.send(msg);
|
|
28758
|
-
|
|
28759
|
-
if (await Promise.race([waiter, delay(t)])) return;
|
|
28760
|
-
}
|
|
28761
|
-
|
|
28762
|
-
throw new Error('Failed to connect.');
|
|
28763
|
-
}
|
|
28764
|
-
|
|
28765
28793
|
/**
|
|
28766
28794
|
* :class:`ClientConnection` subclass which implements OCP.1 with UDP
|
|
28767
28795
|
* transport.
|
|
@@ -28807,8 +28835,12 @@
|
|
|
28807
28835
|
/**
|
|
28808
28836
|
* Connect to the given endpoint.
|
|
28809
28837
|
*
|
|
28810
|
-
* @param {String} options.host
|
|
28811
|
-
*
|
|
28838
|
+
* @param {String} options.host
|
|
28839
|
+
* Hostname or ip address.
|
|
28840
|
+
* @param {number} options.port
|
|
28841
|
+
* Port number.
|
|
28842
|
+
* @param {'udp4' | 'udp6'} [options.type]
|
|
28843
|
+
* Optional ip protocol type.
|
|
28812
28844
|
* @param {number} [options.delay=10] - Delay in ms between individual packets.
|
|
28813
28845
|
* This can be a useful strategy when communicating with devices which
|
|
28814
28846
|
* cannot handle high packet rates.
|
|
@@ -28822,23 +28854,33 @@
|
|
|
28822
28854
|
* in an individual UDP packet. Note that AES70 messages which are larger
|
|
28823
28855
|
* than this limit are sent anyway. This only limits how many seperate
|
|
28824
28856
|
* messages are batched into a single packet.
|
|
28825
|
-
* @
|
|
28857
|
+
* @param {AbortSignal} [options.connectSignal]
|
|
28858
|
+
* An optional AbortSignal which can be used to abort the connect attempt.
|
|
28859
|
+
* Note that this is different from the `signal` option which will destroy
|
|
28860
|
+
* the socket also after the connect attempt has been successful.
|
|
28861
|
+
* @returns {Promise<AbstractUDPConnection>}
|
|
28826
28862
|
* The connection.
|
|
28827
28863
|
*/
|
|
28828
28864
|
static async connect(udpApi, options) {
|
|
28829
|
-
const
|
|
28830
|
-
|
|
28831
|
-
|
|
28832
|
-
|
|
28833
|
-
|
|
28865
|
+
const { host, port, type, connectSignal } = options;
|
|
28866
|
+
const socket = await udpApi.connect({
|
|
28867
|
+
host,
|
|
28868
|
+
port,
|
|
28869
|
+
type,
|
|
28870
|
+
signal: connectSignal,
|
|
28871
|
+
});
|
|
28872
|
+
|
|
28873
|
+
const connection = new this(socket, options);
|
|
28834
28874
|
|
|
28835
28875
|
try {
|
|
28836
|
-
await
|
|
28837
|
-
|
|
28876
|
+
await connection.wait_for_keepalive(1, connectSignal);
|
|
28877
|
+
connectSignal?.throwIfAborted();
|
|
28838
28878
|
} catch (err) {
|
|
28839
|
-
|
|
28879
|
+
connection.close();
|
|
28840
28880
|
throw err;
|
|
28841
28881
|
}
|
|
28882
|
+
|
|
28883
|
+
return connection;
|
|
28842
28884
|
}
|
|
28843
28885
|
|
|
28844
28886
|
write(buf) {
|
package/package.json
CHANGED
|
@@ -1,38 +1,6 @@
|
|
|
1
1
|
/* eslint-env node */
|
|
2
|
-
|
|
3
|
-
import { encodeMessage } from '../OCP1/encode_message.js';
|
|
4
|
-
import { decodeMessage } from '../OCP1/decode_message.js';
|
|
5
|
-
import { KeepAlive } from '../OCP1/keepalive.js';
|
|
6
2
|
import { ClientConnection } from './client_connection.js';
|
|
7
3
|
|
|
8
|
-
function delay(n) {
|
|
9
|
-
return new Promise((resolve) => setTimeout(resolve, n));
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
async function waitForKeepalive(socket, options) {
|
|
13
|
-
const waiter = socket.receiveMessage().then((buffer) => {
|
|
14
|
-
const pdus = [];
|
|
15
|
-
const pos = decodeMessage(new DataView(buffer), 0, pdus);
|
|
16
|
-
|
|
17
|
-
if (pdus.length !== 1) throw new Error('Expected keepalive response.');
|
|
18
|
-
if (pos !== buffer.byteLength)
|
|
19
|
-
throw new Error('Trailing data in initial keepalive pdu.');
|
|
20
|
-
|
|
21
|
-
return true;
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
const msg = encodeMessage(new KeepAlive(1000));
|
|
25
|
-
const t = 5 * (options.retry_interval || 250);
|
|
26
|
-
|
|
27
|
-
for (let i = 0; i < 3; i++) {
|
|
28
|
-
socket.send(msg);
|
|
29
|
-
|
|
30
|
-
if (await Promise.race([waiter, delay(t)])) return;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
throw new Error('Failed to connect.');
|
|
34
|
-
}
|
|
35
|
-
|
|
36
4
|
/**
|
|
37
5
|
* :class:`ClientConnection` subclass which implements OCP.1 with UDP
|
|
38
6
|
* transport.
|
|
@@ -78,8 +46,12 @@ export class AbstractUDPConnection extends ClientConnection {
|
|
|
78
46
|
/**
|
|
79
47
|
* Connect to the given endpoint.
|
|
80
48
|
*
|
|
81
|
-
* @param {String} options.host
|
|
82
|
-
*
|
|
49
|
+
* @param {String} options.host
|
|
50
|
+
* Hostname or ip address.
|
|
51
|
+
* @param {number} options.port
|
|
52
|
+
* Port number.
|
|
53
|
+
* @param {'udp4' | 'udp6'} [options.type]
|
|
54
|
+
* Optional ip protocol type.
|
|
83
55
|
* @param {number} [options.delay=10] - Delay in ms between individual packets.
|
|
84
56
|
* This can be a useful strategy when communicating with devices which
|
|
85
57
|
* cannot handle high packet rates.
|
|
@@ -93,23 +65,33 @@ export class AbstractUDPConnection extends ClientConnection {
|
|
|
93
65
|
* in an individual UDP packet. Note that AES70 messages which are larger
|
|
94
66
|
* than this limit are sent anyway. This only limits how many seperate
|
|
95
67
|
* messages are batched into a single packet.
|
|
96
|
-
* @
|
|
68
|
+
* @param {AbortSignal} [options.connectSignal]
|
|
69
|
+
* An optional AbortSignal which can be used to abort the connect attempt.
|
|
70
|
+
* Note that this is different from the `signal` option which will destroy
|
|
71
|
+
* the socket also after the connect attempt has been successful.
|
|
72
|
+
* @returns {Promise<AbstractUDPConnection>}
|
|
97
73
|
* The connection.
|
|
98
74
|
*/
|
|
99
75
|
static async connect(udpApi, options) {
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
76
|
+
const { host, port, type, connectSignal } = options;
|
|
77
|
+
const socket = await udpApi.connect({
|
|
78
|
+
host,
|
|
79
|
+
port,
|
|
80
|
+
type,
|
|
81
|
+
signal: connectSignal,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const connection = new this(socket, options);
|
|
105
85
|
|
|
106
86
|
try {
|
|
107
|
-
await
|
|
108
|
-
|
|
87
|
+
await connection.wait_for_keepalive(1, connectSignal);
|
|
88
|
+
connectSignal?.throwIfAborted();
|
|
109
89
|
} catch (err) {
|
|
110
|
-
|
|
90
|
+
connection.close();
|
|
111
91
|
throw err;
|
|
112
92
|
}
|
|
93
|
+
|
|
94
|
+
return connection;
|
|
113
95
|
}
|
|
114
96
|
|
|
115
97
|
write(buf) {
|
|
@@ -9,6 +9,8 @@ import { Arguments } from './arguments.js';
|
|
|
9
9
|
import { OcaStatus } from '../types/OcaStatus.js';
|
|
10
10
|
import { EncodedArguments } from '../OCP1/encoded_arguments.js';
|
|
11
11
|
import { CloseError } from '../close_error.js';
|
|
12
|
+
import { Subscriptions } from '../utils/subscriptions.js';
|
|
13
|
+
import { subscribeEvent } from '../utils/subscribeEvent.js';
|
|
12
14
|
|
|
13
15
|
class PendingCommand {
|
|
14
16
|
get handle() {
|
|
@@ -254,34 +256,33 @@ export class ClientConnection extends Connection {
|
|
|
254
256
|
* the connection will be closed and the returned promise will reject.
|
|
255
257
|
* @param {number} interval
|
|
256
258
|
* Keepalive interval in seconds.
|
|
259
|
+
* @param {AbortSignal} [signal]
|
|
260
|
+
* Optional abort signal which can be used to abort
|
|
261
|
+
* the process.
|
|
257
262
|
*/
|
|
258
|
-
wait_for_keepalive(interval) {
|
|
263
|
+
wait_for_keepalive(interval, signal) {
|
|
264
|
+
const subscriptions = new Subscriptions();
|
|
265
|
+
|
|
259
266
|
return new Promise((resolve, reject) => {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
subscriptions.length = 0;
|
|
269
|
-
};
|
|
270
|
-
subscriptions.push(
|
|
267
|
+
if (signal) {
|
|
268
|
+
subscriptions.add(
|
|
269
|
+
subscribeEvent(signal, 'abort', () => {
|
|
270
|
+
reject(signal.reason);
|
|
271
|
+
})
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
subscriptions.add(
|
|
271
275
|
this.subscribe('error', (error) => {
|
|
272
276
|
reject(error);
|
|
273
|
-
cleanup();
|
|
274
277
|
}),
|
|
275
278
|
this.subscribe('close', () => {
|
|
276
279
|
reject(new CloseError());
|
|
277
|
-
cleanup();
|
|
278
280
|
}),
|
|
279
281
|
this.subscribe('keepalive', () => {
|
|
280
282
|
resolve();
|
|
281
|
-
cleanup();
|
|
282
283
|
})
|
|
283
284
|
);
|
|
284
285
|
this.set_keepalive_interval(interval);
|
|
285
|
-
});
|
|
286
|
+
}).finally(() => subscriptions.unsubscribe());
|
|
286
287
|
}
|
|
287
288
|
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
/* eslint-env node */
|
|
2
2
|
|
|
3
3
|
import { createSocket } from 'dgram';
|
|
4
|
-
import { isIP } from 'net';
|
|
4
|
+
import { isIP, isIPv4 } from 'net';
|
|
5
5
|
import { lookup } from 'dns';
|
|
6
|
+
import { Subscriptions } from '../utils/subscriptions.js';
|
|
7
|
+
import { subscribeEvent } from '../utils/subscribeEvent.js';
|
|
6
8
|
|
|
7
|
-
function lookup_address(host) {
|
|
9
|
+
function lookup_address(host, family) {
|
|
8
10
|
if (isIP(host)) return Promise.resolve(host);
|
|
9
11
|
|
|
10
12
|
return new Promise((resolve, reject) => {
|
|
11
|
-
lookup(host, { family
|
|
13
|
+
lookup(host, { family }, (err, address) => {
|
|
12
14
|
if (err) reject(err);
|
|
13
15
|
else resolve(address);
|
|
14
16
|
});
|
|
@@ -52,7 +54,11 @@ export class NodeUDP {
|
|
|
52
54
|
this._inbuf = [];
|
|
53
55
|
|
|
54
56
|
for (let i = 0; i < inbuf.length; i++) {
|
|
55
|
-
|
|
57
|
+
try {
|
|
58
|
+
onmessage(inbuf[i].buffer);
|
|
59
|
+
} catch (err) {
|
|
60
|
+
console.error(err);
|
|
61
|
+
}
|
|
56
62
|
}
|
|
57
63
|
}
|
|
58
64
|
|
|
@@ -73,30 +79,11 @@ export class NodeUDP {
|
|
|
73
79
|
this.socket.send(Buffer.from(buf));
|
|
74
80
|
}
|
|
75
81
|
|
|
76
|
-
receiveMessage(timeout) {
|
|
77
|
-
return new Promise((resolve, reject) => {
|
|
78
|
-
if (this._error) {
|
|
79
|
-
reject(this._error);
|
|
80
|
-
} else if (this._inbuf.length) {
|
|
81
|
-
resolve(this._inbuf.shift());
|
|
82
|
-
} else {
|
|
83
|
-
this.onmessage = (msg) => {
|
|
84
|
-
this.onmessage = null;
|
|
85
|
-
this.onerror = null;
|
|
86
|
-
resolve(msg);
|
|
87
|
-
};
|
|
88
|
-
this.onerror = (err) => {
|
|
89
|
-
this.onmessage = null;
|
|
90
|
-
this.onerror = null;
|
|
91
|
-
reject(err);
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
|
|
97
82
|
close() {
|
|
98
|
-
this.socket
|
|
99
|
-
|
|
83
|
+
if (this.socket) {
|
|
84
|
+
this.socket.close();
|
|
85
|
+
this.socket = null;
|
|
86
|
+
}
|
|
100
87
|
}
|
|
101
88
|
|
|
102
89
|
constructor(socket) {
|
|
@@ -114,14 +101,63 @@ export class NodeUDP {
|
|
|
114
101
|
});
|
|
115
102
|
}
|
|
116
103
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
104
|
+
/**
|
|
105
|
+
* Creates a new udp socket and connects it to the target address.
|
|
106
|
+
* @param {string} options.host
|
|
107
|
+
* The hostname or ip address to connect to.
|
|
108
|
+
* @param {number} options.port
|
|
109
|
+
* The port.
|
|
110
|
+
* @param {'udp4' | 'udp6'} [options.type]
|
|
111
|
+
* The ip protocol to use. This is only relevant if host is
|
|
112
|
+
* not an ip address and hostname lookup is used.
|
|
113
|
+
* @param {Abortsignal} [options.signal]
|
|
114
|
+
* An optional AbortSignal to abort the connect operation.
|
|
115
|
+
* @returns
|
|
116
|
+
*/
|
|
117
|
+
static connect(options) {
|
|
118
|
+
const { host, port, type, signal } = options;
|
|
119
|
+
|
|
120
|
+
signal?.throwIfAborted();
|
|
121
|
+
|
|
122
|
+
const subscriptions = new Subscriptions();
|
|
123
|
+
|
|
124
|
+
return new Promise((resolve, reject) => {
|
|
125
|
+
let socket;
|
|
126
|
+
|
|
127
|
+
if (signal) {
|
|
128
|
+
subscriptions.add(
|
|
129
|
+
subscribeEvent(signal, 'abort', (reason) => {
|
|
130
|
+
const err = signal.reason;
|
|
131
|
+
reject(err);
|
|
132
|
+
})
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const lookupFamily =
|
|
137
|
+
type === 'udp4' ? 4 : type === 'udp6' ? 6 : undefined;
|
|
138
|
+
lookup_address(host, lookupFamily).then((ip) => {
|
|
139
|
+
if (signal?.aborted) {
|
|
140
|
+
// IF the abort signal was triggered during the ip lookup,
|
|
141
|
+
// we have to simply ignore the resolve result.
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const type = isIPv4(ip) ? 'udp4' : 'udp6';
|
|
145
|
+
socket = createSocket(type);
|
|
146
|
+
|
|
147
|
+
if (signal) {
|
|
148
|
+
subscriptions.add(
|
|
149
|
+
subscribeEvent(signal, 'abort', () => {
|
|
150
|
+
socket.close();
|
|
151
|
+
})
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
121
155
|
const onerror = function (ev) {
|
|
122
156
|
reject(ev);
|
|
157
|
+
socket.close();
|
|
123
158
|
};
|
|
124
|
-
|
|
159
|
+
subscriptions.add(subscribeEvent(socket, 'error', onerror));
|
|
160
|
+
|
|
125
161
|
socket.bind(
|
|
126
162
|
{
|
|
127
163
|
exclusive: true,
|
|
@@ -129,12 +165,11 @@ export class NodeUDP {
|
|
|
129
165
|
() => {
|
|
130
166
|
socket.on('connect', () => {
|
|
131
167
|
resolve(new this(socket));
|
|
132
|
-
socket.removeListener('error', onerror);
|
|
133
168
|
});
|
|
134
|
-
socket.connect(port,
|
|
169
|
+
socket.connect(port, ip);
|
|
135
170
|
}
|
|
136
171
|
);
|
|
137
|
-
});
|
|
138
|
-
});
|
|
172
|
+
}, reject);
|
|
173
|
+
}).finally(() => subscriptions.unsubscribe());
|
|
139
174
|
}
|
|
140
175
|
}
|
|
@@ -56,8 +56,7 @@ export class Property {
|
|
|
56
56
|
* @returns {Function} The getter. If none could be found, null is returned.
|
|
57
57
|
*/
|
|
58
58
|
getter(o, no_bind) {
|
|
59
|
-
|
|
60
|
-
let i = 0;
|
|
59
|
+
const name = this.name;
|
|
61
60
|
const aliases = this.aliases;
|
|
62
61
|
const accessors = this.accessors;
|
|
63
62
|
|
|
@@ -99,10 +98,13 @@ export class Property {
|
|
|
99
98
|
return no_bind ? fun : fun.bind(o);
|
|
100
99
|
}
|
|
101
100
|
|
|
102
|
-
|
|
101
|
+
// iterate all possible names
|
|
102
|
+
const possibleNames = aliases ? [name, ...aliases] : [name];
|
|
103
|
+
|
|
104
|
+
for (const possibleName of possibleNames) {
|
|
103
105
|
if (this.static) {
|
|
104
106
|
const c = o.constructor;
|
|
105
|
-
const v = c[
|
|
107
|
+
const v = c[possibleName];
|
|
106
108
|
|
|
107
109
|
if (v !== void 0) {
|
|
108
110
|
return function () {
|
|
@@ -110,16 +112,11 @@ export class Property {
|
|
|
110
112
|
};
|
|
111
113
|
}
|
|
112
114
|
} else {
|
|
113
|
-
const fun = o['Get' +
|
|
115
|
+
const fun = o['Get' + possibleName];
|
|
114
116
|
|
|
115
117
|
if (fun) return no_bind ? fun : fun.bind(o);
|
|
116
118
|
}
|
|
117
|
-
|
|
118
|
-
if (aliases && i < aliases.length) {
|
|
119
|
-
name = aliases[i++];
|
|
120
|
-
continue;
|
|
121
|
-
}
|
|
122
|
-
} while (false);
|
|
119
|
+
}
|
|
123
120
|
|
|
124
121
|
return null;
|
|
125
122
|
}
|
|
@@ -135,20 +132,15 @@ export class Property {
|
|
|
135
132
|
setter(o, no_bind) {
|
|
136
133
|
if (this.readonly || this.static) return null;
|
|
137
134
|
|
|
138
|
-
|
|
139
|
-
i = 0;
|
|
135
|
+
const name = this.name;
|
|
140
136
|
const aliases = this.aliases;
|
|
137
|
+
const possibleNames = aliases ? [name, ...aliases] : [name];
|
|
141
138
|
|
|
142
|
-
|
|
143
|
-
const fun = o['Set' +
|
|
139
|
+
for (const possibleName of possibleNames) {
|
|
140
|
+
const fun = o['Set' + possibleName];
|
|
144
141
|
|
|
145
142
|
if (fun) return no_bind ? fun : fun.bind(o);
|
|
146
|
-
|
|
147
|
-
if (aliases && i < aliases.length) {
|
|
148
|
-
name = aliases[i++];
|
|
149
|
-
continue;
|
|
150
|
-
}
|
|
151
|
-
} while (false);
|
|
143
|
+
}
|
|
152
144
|
|
|
153
145
|
return null;
|
|
154
146
|
}
|
package/src/events.js
CHANGED
|
@@ -4,12 +4,13 @@
|
|
|
4
4
|
export class Events {
|
|
5
5
|
constructor() {
|
|
6
6
|
this.event_handlers = new Map();
|
|
7
|
+
this.event_handlers_cleared = false;
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Emit an event.
|
|
11
12
|
*
|
|
12
|
-
* @param {
|
|
13
|
+
* @param {string} name - Name of the event.
|
|
13
14
|
* @param {...*} args - Extra arguments.
|
|
14
15
|
*/
|
|
15
16
|
emit(name) {
|
|
@@ -30,7 +31,7 @@ export class Events {
|
|
|
30
31
|
/**
|
|
31
32
|
* Subscribe to an event.
|
|
32
33
|
*
|
|
33
|
-
* @param {
|
|
34
|
+
* @param {string} name - Name of the event.
|
|
34
35
|
* @param {Function} cb - Callback function.
|
|
35
36
|
*/
|
|
36
37
|
on(name, cb) {
|
|
@@ -54,14 +55,17 @@ export class Events {
|
|
|
54
55
|
/**
|
|
55
56
|
* Removes an event handler.
|
|
56
57
|
*
|
|
57
|
-
* @param {
|
|
58
|
+
* @param {string} name - Name of the event.
|
|
58
59
|
* @param {Function} cb - Callback function.
|
|
59
60
|
*/
|
|
60
61
|
removeEventListener(name, cb) {
|
|
61
62
|
const handlers = this.event_handlers.get(name);
|
|
62
63
|
|
|
63
64
|
if (!handlers || !handlers.has(cb)) {
|
|
64
|
-
|
|
65
|
+
if (!this.event_handlers_cleared) {
|
|
66
|
+
console.warn('removeEventListeners(): not installed:', name, cb);
|
|
67
|
+
}
|
|
68
|
+
return;
|
|
65
69
|
}
|
|
66
70
|
|
|
67
71
|
handlers.delete(cb);
|
|
@@ -70,7 +74,7 @@ export class Events {
|
|
|
70
74
|
/**
|
|
71
75
|
* Removes an event handler.
|
|
72
76
|
*
|
|
73
|
-
* @param {
|
|
77
|
+
* @param {string} name
|
|
74
78
|
* @param {Function} cb
|
|
75
79
|
*/
|
|
76
80
|
off(name, cb) {
|
|
@@ -82,6 +86,7 @@ export class Events {
|
|
|
82
86
|
*/
|
|
83
87
|
removeAllEventListeners() {
|
|
84
88
|
this.event_handlers.clear();
|
|
89
|
+
this.event_handlers_cleared = true;
|
|
85
90
|
}
|
|
86
91
|
|
|
87
92
|
/**
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
function addEvent(target, name, callback) {
|
|
2
|
+
if (target.addEventListener) {
|
|
3
|
+
target.addEventListener(name, callback);
|
|
4
|
+
} else if (target.on) {
|
|
5
|
+
target.on(name, callback);
|
|
6
|
+
} else {
|
|
7
|
+
throw new TypeError('Unsupported event target ', target);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function removeEvent(target, name, callback) {
|
|
12
|
+
if (target.removeEventListener) {
|
|
13
|
+
target.removeEventListener(name, callback);
|
|
14
|
+
} else if (target.off) {
|
|
15
|
+
target.off(name, callback);
|
|
16
|
+
} else {
|
|
17
|
+
throw new TypeError('Unsupported event target ', target);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
*
|
|
23
|
+
* @param {EventTarget|Events} target
|
|
24
|
+
* @param {string} name
|
|
25
|
+
* @param {callback} callback
|
|
26
|
+
*/
|
|
27
|
+
export function subscribeEvent(target, name, callback) {
|
|
28
|
+
addEvent(target, name, callback);
|
|
29
|
+
return () => {
|
|
30
|
+
removeEvent(target, name, callback);
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export class Subscriptions {
|
|
2
|
+
constructor() {
|
|
3
|
+
this._callbacks = [];
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Add a subscription.
|
|
8
|
+
*
|
|
9
|
+
* @param {Function[]} cbs
|
|
10
|
+
*/
|
|
11
|
+
add(...cbs) {
|
|
12
|
+
cbs.forEach((cb) => {
|
|
13
|
+
this._callbacks.push(cb);
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
unsubscribe() {
|
|
18
|
+
this._callbacks.forEach((cb) => {
|
|
19
|
+
try {
|
|
20
|
+
cb();
|
|
21
|
+
} catch (err) {
|
|
22
|
+
console.error(err);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
this._callbacks.length = 0;
|
|
26
|
+
}
|
|
27
|
+
}
|