aes70 2.0.0 → 2.0.1

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 CHANGED
@@ -1041,7 +1041,7 @@
1041
1041
  if (this._closed) return;
1042
1042
  this._closed = true;
1043
1043
  this.emit('close');
1044
- this.cleanup();
1044
+ this.cleanup(e);
1045
1045
  });
1046
1046
  }
1047
1047
 
@@ -1133,7 +1133,7 @@
1133
1133
  this.emit('error', err);
1134
1134
  }
1135
1135
 
1136
- cleanup() {
1136
+ cleanup(error) {
1137
1137
  if (this.is_closed()) throw new Error('cleanup() called twice.');
1138
1138
 
1139
1139
  // disable keepalive
@@ -1150,7 +1150,7 @@
1150
1150
 
1151
1151
  if (this.rx_idle_time() > t * 3) {
1152
1152
  this.emit('timeout');
1153
- this.error(new Error('Keepalive timeout.'));
1153
+ this.error(new TimeoutError());
1154
1154
  } else if (this.tx_idle_time() > t * 0.75) {
1155
1155
  /* Try to flush buffers before actually sending out anything. */
1156
1156
  this.flush();
@@ -1527,6 +1527,20 @@
1527
1527
  * @static
1528
1528
  */
1529
1529
 
1530
+ /**
1531
+ * Error class raised when a connection is closed.
1532
+ *
1533
+ * @property {Error} [error] - The actual failure reason. May be undefined,
1534
+ * for instance if the connection was closed using close().
1535
+ */
1536
+ class CloseError extends Error {
1537
+ constructor(error) {
1538
+ super(`Connection has been closed.`);
1539
+ this.name = 'aes70.CloseError';
1540
+ this.error = error;
1541
+ }
1542
+ }
1543
+
1530
1544
  class PendingCommand {
1531
1545
  get handle() {
1532
1546
  return this.command.handle;
@@ -1610,14 +1624,14 @@
1610
1624
  this._subscribers = new Map();
1611
1625
  }
1612
1626
 
1613
- cleanup() {
1614
- super.cleanup();
1627
+ cleanup(error) {
1628
+ super.cleanup(error);
1615
1629
  const subscribers = this._subscribers;
1616
1630
  this._subscribers = null;
1617
1631
  const pendingCommands = this._pendingCommands;
1618
1632
  this._pendingCommands = null;
1619
1633
 
1620
- const e = new Error('closed');
1634
+ const e = new CloseError(error);
1621
1635
  pendingCommands.forEach((pendingCommand, id) => {
1622
1636
  pendingCommand.handleError(structuredClone(e));
1623
1637
  });
@@ -2716,6 +2730,7 @@
2716
2730
  console.error('Exception thrown by error event handler: ', e);
2717
2731
  }
2718
2732
  } else {
2733
+ if (error instanceof CloseError) return;
2719
2734
  console.warn('No handler for error', error);
2720
2735
  }
2721
2736
  }
@@ -3011,6 +3026,130 @@
3011
3026
  * @static
3012
3027
  */
3013
3028
 
3029
+ /**
3030
+ * Observe a property in an object.
3031
+ *
3032
+ * The callback function is called when a property value or change is received. In
3033
+ * that case the callback is called with the arguments `true, value, changeIndex`. When an
3034
+ * error occurs, e.g. when fetching the initial property value using the corresponding
3035
+ * getter, the callback is called with arguments `false, Error`.
3036
+ *
3037
+ * The meaning of the changeIndex argument is meaningful in situations where a property has
3038
+ * associated min and max value. In that situation the value received by the callback is
3039
+ * the return value of the getter, which is an instance of Arguments. When either the property
3040
+ * itself or min/max changes, the changeIndex will be the corresponding index in the arguments
3041
+ * list. If the property has no min and max or when the initial value is returned from the
3042
+ * getter, the value of changeIndex is undefined.
3043
+ *
3044
+ * @param {ObjectBase} o The remote object.
3045
+ * @param {string|Property} property
3046
+ * @param {Function} callback The callback function
3047
+ * @returns An unsubscribe/cleanup function.
3048
+ */
3049
+ function observeProperty(o, property, callback) {
3050
+ let propertyName;
3051
+
3052
+ if (typeof property === 'string') {
3053
+ propertyName = property;
3054
+ property = o.get_properties().find_property(propertyName);
3055
+
3056
+ if (!property)
3057
+ throw new Error(
3058
+ `Could not find property ${propertyName} in ${o.ClassName}`
3059
+ );
3060
+ } else {
3061
+ propertyName = property.name;
3062
+ }
3063
+
3064
+ if (property.static) {
3065
+ callback(true, o[propertyName]);
3066
+ return () => {};
3067
+ }
3068
+
3069
+ let lastValue = null;
3070
+
3071
+ const notify = (changeIndex) => {
3072
+ try {
3073
+ callback(true, lastValue, changeIndex);
3074
+ } catch (error) {
3075
+ console.error(
3076
+ 'Subscriber',
3077
+ callback,
3078
+ 'to property',
3079
+ propertyName,
3080
+ 'in',
3081
+ o,
3082
+ 'threw exception',
3083
+ error
3084
+ );
3085
+ }
3086
+ };
3087
+
3088
+ const eventCallback = (value, changeType, eventId) => {
3089
+ if (lastValue === null) return;
3090
+ switch (changeType.value) {
3091
+ case 1 /* OcaPropertyChangeType.CurrentChanged*/:
3092
+ if (lastValue instanceof Arguments) {
3093
+ lastValue.values[0] = value;
3094
+ notify(0);
3095
+ return;
3096
+ } else {
3097
+ lastValue = value;
3098
+ notify();
3099
+ return;
3100
+ }
3101
+ case 2 /*OcaPropertyChangeType.MinChanged*/:
3102
+ if (lastValue instanceof Arguments) {
3103
+ lastValue.values[1] = value;
3104
+ notify(1);
3105
+ return;
3106
+ }
3107
+ break;
3108
+ case 3 /*OcaPropertyChangeType.MaxChanged*/:
3109
+ if (lastValue instanceof Arguments) {
3110
+ lastValue.values[2] = value;
3111
+ notify(2);
3112
+ return;
3113
+ }
3114
+ break;
3115
+ }
3116
+ console.warn('Unhandled event', value, changeType, eventId);
3117
+ };
3118
+
3119
+ const errorCallback = (error) => {
3120
+ callback(false, error);
3121
+ };
3122
+
3123
+ let active = true;
3124
+ const event = property.event(o);
3125
+ const getter = property.getter(o);
3126
+
3127
+ if (!getter) {
3128
+ throw new Error(`No getter found for ${propertyName} in ${o.ClassName}`);
3129
+ }
3130
+
3131
+ let unsubscribe = null;
3132
+
3133
+ if (event) {
3134
+ unsubscribe = event.subscribe(eventCallback, errorCallback);
3135
+ }
3136
+
3137
+ getter((ok, result) => {
3138
+ if (!active) return;
3139
+ if (!ok) {
3140
+ callback(false, result);
3141
+ } else {
3142
+ lastValue = result;
3143
+ notify();
3144
+ }
3145
+ });
3146
+
3147
+ return () => {
3148
+ active = false;
3149
+ if (unsubscribe) unsubscribe();
3150
+ };
3151
+ }
3152
+
3014
3153
  /**
3015
3154
  * Objects of this class can be used to keep a synchronized object containing
3016
3155
  * all properties of a remote OCA object. Instances of this class are usually
@@ -3040,7 +3179,7 @@
3040
3179
  sync() {
3041
3180
  if (this.synchronized) return Promise.resolve();
3042
3181
 
3043
- let index = 0;
3182
+ let i = 0;
3044
3183
  const tasks = [];
3045
3184
 
3046
3185
  this.o.get_properties().forEach((prop) => {
@@ -3048,31 +3187,21 @@
3048
3187
 
3049
3188
  if (!getter) return;
3050
3189
 
3051
- const event = prop.event(this.o);
3052
-
3053
- const change_handler = function (index, value, changeType) {
3054
- if (changeType !== OcaPropertyChangeType$1.CurrentChanged) return;
3055
-
3056
- this.values[index] = value;
3057
- };
3058
-
3059
- const get_handler = function (index, value) {
3060
- if (value instanceof Arguments) value = value.item(0);
3061
- this.values[index] = value;
3062
- };
3063
-
3064
- if (event) {
3065
- const cb = change_handler.bind(this, index);
3066
- // NOTE: we do not want to wait for the promise to resolve
3067
- // before storing this unsubscription handler because that
3068
- // would have a potential race condition.
3069
- this.subscriptions.push(event.unsubscribe.bind(event, cb));
3070
- tasks.push(event.subscribe(cb).catch(function () {}));
3071
- }
3072
-
3073
- tasks.push(getter().then(get_handler.bind(this, index), function () {}));
3190
+ const index = i++;
3074
3191
 
3075
- index++;
3192
+ const task = new Promise((resolve, reject) => {
3193
+ const unsubscribe = observeProperty(this.o, prop, (ok, result) => {
3194
+ if (ok) {
3195
+ this.values[index] =
3196
+ result instanceof Arguments ? result.item(0) : result;
3197
+ resolve();
3198
+ } else if (result instanceof RemoteError) {
3199
+ resolve();
3200
+ }
3201
+ });
3202
+ this.subscriptions.push(unsubscribe);
3203
+ });
3204
+ tasks.push(task);
3076
3205
  });
3077
3206
 
3078
3207
  return Promise.all(tasks);
@@ -23316,130 +23445,6 @@
23316
23445
  }
23317
23446
  }
23318
23447
 
23319
- /**
23320
- * Observe a property in an object.
23321
- *
23322
- * The callback function is called when a property value or change is received. In
23323
- * that case the callback is called with the arguments `true, value, changeIndex`. When an
23324
- * error occurs, e.g. when fetching the initial property value using the corresponding
23325
- * getter, the callback is called with arguments `false, Error`.
23326
- *
23327
- * The meaning of the changeIndex argument is meaningful in situations where a property has
23328
- * associated min and max value. In that situation the value received by the callback is
23329
- * the return value of the getter, which is an instance of Arguments. When either the property
23330
- * itself or min/max changes, the changeIndex will be the corresponding index in the arguments
23331
- * list. If the property has no min and max or when the initial value is returned from the
23332
- * getter, the value of changeIndex is undefined.
23333
- *
23334
- * @param {ObjectBase} o The remote object.
23335
- * @param {string|Property} property
23336
- * @param {Function} callback The callback function
23337
- * @returns An unsubscribe/cleanup function.
23338
- */
23339
- function observeProperty(o, property, callback) {
23340
- let propertyName;
23341
-
23342
- if (typeof property === 'string') {
23343
- propertyName = property;
23344
- property = o.get_properties().find_property(propertyName);
23345
-
23346
- if (!property)
23347
- throw new Error(
23348
- `Could not find property ${propertyName} in ${o.ClassName}`
23349
- );
23350
- } else {
23351
- propertyName = property.name;
23352
- }
23353
-
23354
- if (property.static) {
23355
- callback(true, o[propertyName]);
23356
- return () => {};
23357
- }
23358
-
23359
- let lastValue = null;
23360
-
23361
- const notify = (changeIndex) => {
23362
- try {
23363
- callback(true, lastValue, changeIndex);
23364
- } catch (error) {
23365
- console.error(
23366
- 'Subscriber',
23367
- callback,
23368
- 'to property',
23369
- propertyName,
23370
- 'in',
23371
- o,
23372
- 'threw exception',
23373
- error
23374
- );
23375
- }
23376
- };
23377
-
23378
- const eventCallback = (value, changeType, eventId) => {
23379
- if (lastValue === null) return;
23380
- switch (changeType.value) {
23381
- case 1 /* OcaPropertyChangeType.CurrentChanged*/:
23382
- if (lastValue instanceof Arguments) {
23383
- lastValue.values[0] = value;
23384
- notify(0);
23385
- return;
23386
- } else {
23387
- lastValue = value;
23388
- notify();
23389
- return;
23390
- }
23391
- case 2 /*OcaPropertyChangeType.MinChanged*/:
23392
- if (lastValue instanceof Arguments) {
23393
- lastValue.values[1] = value;
23394
- notify(1);
23395
- return;
23396
- }
23397
- break;
23398
- case 3 /*OcaPropertyChangeType.MaxChanged*/:
23399
- if (lastValue instanceof Arguments) {
23400
- lastValue.values[2] = value;
23401
- notify(2);
23402
- return;
23403
- }
23404
- break;
23405
- }
23406
- console.warn('Unhandled event', value, changeType, eventId);
23407
- };
23408
-
23409
- const errorCallback = (error) => {
23410
- callback(false, error);
23411
- };
23412
-
23413
- let active = true;
23414
- const event = property.event(o);
23415
- const getter = property.getter(o);
23416
-
23417
- if (!getter) {
23418
- throw new Error(`Not getter found for ${propertyName} in ${o.ClassName}`);
23419
- }
23420
-
23421
- let unsubscribe = null;
23422
-
23423
- if (event) {
23424
- unsubscribe = event.subscribe(eventCallback, errorCallback);
23425
- }
23426
-
23427
- getter((ok, result) => {
23428
- if (!active) return;
23429
- if (!ok) {
23430
- callback(false, result);
23431
- } else {
23432
- lastValue = result;
23433
- notify();
23434
- }
23435
- });
23436
-
23437
- return () => {
23438
- active = false;
23439
- if (unsubscribe) unsubscribe();
23440
- };
23441
- }
23442
-
23443
23448
  class PropertyObserver {
23444
23449
  constructor(o, propertyName, cacheSubscriptions) {
23445
23450
  this.o = o;
@@ -23702,6 +23707,16 @@
23702
23707
  return await fetchDeviceContentRec(objects);
23703
23708
  }
23704
23709
 
23710
+ /**
23711
+ * Error class raised when a connection is closed due to a timeout.
23712
+ */
23713
+ let TimeoutError$1 = class TimeoutError extends Error {
23714
+ constructor(error) {
23715
+ super(`Connection has timed out.`);
23716
+ this.name = 'aes70.TimeoutError';
23717
+ }
23718
+ };
23719
+
23705
23720
  /*
23706
23721
  * This file has been generated.
23707
23722
  */
@@ -24488,6 +24503,7 @@
24488
24503
  __proto__: null,
24489
24504
  AbstractUDPConnection: AbstractUDPConnection,
24490
24505
  ClientConnection: ClientConnection,
24506
+ CloseError: CloseError,
24491
24507
  Command: Command,
24492
24508
  CommandRrq: CommandRrq,
24493
24509
  Connection: Connection,
@@ -24500,6 +24516,7 @@
24500
24516
  RemoteDevice: RemoteDevice,
24501
24517
  RemoteError: RemoteError,
24502
24518
  Response: Response$1,
24519
+ TimeoutError: TimeoutError$1,
24503
24520
  Types: types,
24504
24521
  WebSocketConnection: WebSocketConnection,
24505
24522
  currentProtocolVersion: currentProtocolVersion,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aes70",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "A controller library for the AES70 protocol.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -16,7 +16,7 @@
16
16
  "scripts": {
17
17
  "test": "node --test --test-concurrency=1 tests/*.test.js",
18
18
  "test:only": "node --test --test-only --test-concurrency=1 tests/*.test.js",
19
- "prepublishOnly": "npx rollup@4.52.0 src/bundle.browser.js --file dist/AES70.es5.js --format iife",
19
+ "prepublishOnly": "npx rollup@4.52.4 src/bundle.browser.js --file dist/AES70.es5.js --format iife",
20
20
  "docs": "cd esdoc && npm run build",
21
21
  "prettier": "npx prettier@2.0.5 --write .",
22
22
  "format": "npx prettier@2.0.5 --write .",
@@ -0,0 +1,4 @@
1
+ export declare class CloseError extends Error {
2
+ error?: Error;
3
+ constructor(error?: Error);
4
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Error class raised when a connection is closed.
3
+ *
4
+ * @property {Error} [error] - The actual failure reason. May be undefined,
5
+ * for instance if the connection was closed using close().
6
+ */
7
+ export class CloseError extends Error {
8
+ constructor(error) {
9
+ super(`Connection has been closed.`);
10
+ this.name = 'aes70.CloseError';
11
+ this.error = error;
12
+ }
13
+ }
package/src/connection.js CHANGED
@@ -51,7 +51,7 @@ export class Connection extends Events {
51
51
  if (this._closed) return;
52
52
  this._closed = true;
53
53
  this.emit('close');
54
- this.cleanup();
54
+ this.cleanup(e);
55
55
  });
56
56
  }
57
57
 
@@ -143,7 +143,7 @@ export class Connection extends Events {
143
143
  this.emit('error', err);
144
144
  }
145
145
 
146
- cleanup() {
146
+ cleanup(error) {
147
147
  if (this.is_closed()) throw new Error('cleanup() called twice.');
148
148
 
149
149
  // disable keepalive
@@ -160,7 +160,7 @@ export class Connection extends Events {
160
160
 
161
161
  if (this.rx_idle_time() > t * 3) {
162
162
  this.emit('timeout');
163
- this.error(new Error('Keepalive timeout.'));
163
+ this.error(new TimeoutError());
164
164
  } else if (this.tx_idle_time() > t * 0.75) {
165
165
  /* Try to flush buffers before actually sending out anything. */
166
166
  this.flush();
@@ -1,3 +1,4 @@
1
+ import { CloseError } from '../close_error.js';
1
2
  import { OcaEvent } from '../types/OcaEvent.js';
2
3
 
3
4
  class EventSubscriber {
@@ -22,6 +23,7 @@ class EventSubscriber {
22
23
  console.error('Exception thrown by error event handler: ', e);
23
24
  }
24
25
  } else {
26
+ if (error instanceof CloseError) return;
25
27
  console.warn('No handler for error', error);
26
28
  }
27
29
  }
@@ -8,6 +8,7 @@ import { Notification2 } from '../OCP1/notification2.js';
8
8
  import { Arguments } from './arguments.js';
9
9
  import { OcaStatus } from '../types/OcaStatus.js';
10
10
  import { EncodedArguments } from '../OCP1/encoded_arguments.js';
11
+ import { CloseError } from '../close_error.js';
11
12
 
12
13
  class PendingCommand {
13
14
  get handle() {
@@ -92,14 +93,14 @@ export class ClientConnection extends Connection {
92
93
  this._subscribers = new Map();
93
94
  }
94
95
 
95
- cleanup() {
96
- super.cleanup();
96
+ cleanup(error) {
97
+ super.cleanup(error);
97
98
  const subscribers = this._subscribers;
98
99
  this._subscribers = null;
99
100
  const pendingCommands = this._pendingCommands;
100
101
  this._pendingCommands = null;
101
102
 
102
- const e = new Error('closed');
103
+ const e = new CloseError(error);
103
104
  pendingCommands.forEach((pendingCommand, id) => {
104
105
  pendingCommand.handleError(structuredClone(e));
105
106
  });
@@ -103,7 +103,7 @@ export function observeProperty(o, property, callback) {
103
103
  const getter = property.getter(o);
104
104
 
105
105
  if (!getter) {
106
- throw new Error(`Not getter found for ${propertyName} in ${o.ClassName}`);
106
+ throw new Error(`No getter found for ${propertyName} in ${o.ClassName}`);
107
107
  }
108
108
 
109
109
  let unsubscribe = null;
@@ -1,5 +1,7 @@
1
1
  import { Arguments } from './arguments.js';
2
2
  import { OcaPropertyChangeType } from '../types/OcaPropertyChangeType.js';
3
+ import { observeProperty } from './observeProperty.js';
4
+ import { RemoteError } from './remote_error.js';
3
5
 
4
6
  /**
5
7
  * Objects of this class can be used to keep a synchronized object containing
@@ -30,7 +32,7 @@ export class PropertySync {
30
32
  sync() {
31
33
  if (this.synchronized) return Promise.resolve();
32
34
 
33
- let index = 0;
35
+ let i = 0;
34
36
  const tasks = [];
35
37
 
36
38
  this.o.get_properties().forEach((prop) => {
@@ -38,31 +40,21 @@ export class PropertySync {
38
40
 
39
41
  if (!getter) return;
40
42
 
41
- const event = prop.event(this.o);
42
-
43
- const change_handler = function (index, value, changeType) {
44
- if (changeType !== OcaPropertyChangeType.CurrentChanged) return;
45
-
46
- this.values[index] = value;
47
- };
48
-
49
- const get_handler = function (index, value) {
50
- if (value instanceof Arguments) value = value.item(0);
51
- this.values[index] = value;
52
- };
53
-
54
- if (event) {
55
- const cb = change_handler.bind(this, index);
56
- // NOTE: we do not want to wait for the promise to resolve
57
- // before storing this unsubscription handler because that
58
- // would have a potential race condition.
59
- this.subscriptions.push(event.unsubscribe.bind(event, cb));
60
- tasks.push(event.subscribe(cb).catch(function () {}));
61
- }
62
-
63
- tasks.push(getter().then(get_handler.bind(this, index), function () {}));
64
-
65
- index++;
43
+ const index = i++;
44
+
45
+ const task = new Promise((resolve, reject) => {
46
+ const unsubscribe = observeProperty(this.o, prop, (ok, result) => {
47
+ if (ok) {
48
+ this.values[index] =
49
+ result instanceof Arguments ? result.item(0) : result;
50
+ resolve();
51
+ } else if (result instanceof RemoteError) {
52
+ resolve();
53
+ }
54
+ });
55
+ this.subscriptions.push(unsubscribe);
56
+ });
57
+ tasks.push(task);
66
58
  });
67
59
 
68
60
  return Promise.all(tasks);
@@ -16,6 +16,8 @@ export * from './controller/abstract_udp_connection';
16
16
  export * from './controller/fetch_device_content';
17
17
  export * from './controller/remote_error';
18
18
  export * from './controller/notification_error';
19
+ export * from './close_error';
20
+ export * from './timeout_error';
19
21
 
20
22
  import * as RemoteControlClasses from './controller/ControlClasses';
21
23
  import * as Types from './types';
@@ -18,6 +18,8 @@ export * from './controller/PropertyObserver.js';
18
18
  export * from './controller/fetch_device_content.js';
19
19
  export * from './controller/remote_error.js';
20
20
  export * from './controller/notification_error.js';
21
+ export * from './close_error.js';
22
+ export * from './timeout_error.js';
21
23
 
22
24
  import * as RemoteControlClasses from './controller/ControlClasses.js';
23
25
  import * as Types from './types.js';
@@ -0,0 +1,3 @@
1
+ export declare class TimeoutError extends Error {
2
+ constructor();
3
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Error class raised when a connection is closed due to a timeout.
3
+ */
4
+ export class TimeoutError extends Error {
5
+ constructor(error) {
6
+ super(`Connection has timed out.`);
7
+ this.name = 'aes70.TimeoutError';
8
+ }
9
+ }