rclnodejs 1.6.0 → 1.7.0
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/index.js +59 -0
- package/lib/action/client.js +55 -9
- package/lib/action/deferred.js +8 -2
- package/lib/action/server.js +10 -1
- package/lib/action/uuid.js +4 -1
- package/lib/client.js +152 -3
- package/lib/clock.js +4 -1
- package/lib/context.js +12 -2
- package/lib/duration.js +37 -12
- package/lib/errors.js +571 -0
- package/lib/event_handler.js +21 -4
- package/lib/interface_loader.js +52 -12
- package/lib/lifecycle.js +8 -2
- package/lib/logging.js +12 -3
- package/lib/message_serialization.js +10 -2
- package/lib/native_loader.js +9 -4
- package/lib/node.js +270 -49
- package/lib/parameter.js +172 -35
- package/lib/parameter_client.js +506 -0
- package/lib/parameter_watcher.js +309 -0
- package/lib/qos.js +22 -5
- package/lib/rate.js +6 -1
- package/lib/serialization.js +7 -2
- package/lib/time.js +136 -21
- package/lib/time_source.js +13 -4
- package/lib/utils.js +27 -1
- package/lib/validator.js +11 -12
- package/package.json +1 -1
- package/prebuilds/linux-x64/jazzy-noble-x64-rclnodejs.node +0 -0
- package/types/base.d.ts +3 -0
- package/types/client.d.ts +36 -0
- package/types/errors.d.ts +447 -0
- package/types/interfaces.d.ts +1910 -1
- package/types/node.d.ts +40 -0
- package/types/parameter_client.d.ts +252 -0
- package/types/parameter_watcher.d.ts +104 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
// Copyright (c) 2025 Mahmoud Alghalayini. All rights reserved.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
'use strict';
|
|
16
|
+
|
|
17
|
+
const EventEmitter = require('events');
|
|
18
|
+
const { TypeValidationError, OperationError } = require('./errors');
|
|
19
|
+
const { normalizeNodeName } = require('./utils');
|
|
20
|
+
const debug = require('debug')('rclnodejs:parameter_watcher');
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @class ParameterWatcher - Watches parameter changes on a remote node
|
|
24
|
+
*
|
|
25
|
+
* Subscribes to /parameter_events and emits 'change' events when
|
|
26
|
+
* watched parameters on the target node are modified.
|
|
27
|
+
*
|
|
28
|
+
* @extends EventEmitter
|
|
29
|
+
*/
|
|
30
|
+
class ParameterWatcher extends EventEmitter {
|
|
31
|
+
#node;
|
|
32
|
+
#paramClient;
|
|
33
|
+
#subscription;
|
|
34
|
+
#watchedParams;
|
|
35
|
+
#remoteNodeName;
|
|
36
|
+
#destroyed;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Create a ParameterWatcher instance.
|
|
40
|
+
* Note: Use node.createParameterWatcher() instead of calling this directly.
|
|
41
|
+
*
|
|
42
|
+
* @param {object} node - The local rclnodejs Node instance
|
|
43
|
+
* @param {string} remoteNodeName - Name of the remote node to watch
|
|
44
|
+
* @param {string[]} parameterNames - Array of parameter names to watch
|
|
45
|
+
* @param {object} [options] - Options for the parameter client
|
|
46
|
+
* @param {number} [options.timeout=5000] - Default timeout for parameter operations
|
|
47
|
+
* @hideconstructor
|
|
48
|
+
*/
|
|
49
|
+
constructor(node, remoteNodeName, parameterNames, options = {}) {
|
|
50
|
+
super();
|
|
51
|
+
|
|
52
|
+
if (!node || typeof node.createParameterClient !== 'function') {
|
|
53
|
+
throw new TypeValidationError('node', node, 'Node instance', {
|
|
54
|
+
entityType: 'parameter watcher',
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (typeof remoteNodeName !== 'string' || remoteNodeName.trim() === '') {
|
|
59
|
+
throw new TypeValidationError(
|
|
60
|
+
'remoteNodeName',
|
|
61
|
+
remoteNodeName,
|
|
62
|
+
'non-empty string',
|
|
63
|
+
{
|
|
64
|
+
entityType: 'parameter watcher',
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (!Array.isArray(parameterNames) || parameterNames.length === 0) {
|
|
70
|
+
throw new TypeValidationError(
|
|
71
|
+
'parameterNames',
|
|
72
|
+
parameterNames,
|
|
73
|
+
'non-empty array',
|
|
74
|
+
{
|
|
75
|
+
entityType: 'parameter watcher',
|
|
76
|
+
}
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
this.#node = node;
|
|
81
|
+
this.#watchedParams = new Set(parameterNames);
|
|
82
|
+
this.#paramClient = node.createParameterClient(remoteNodeName, options);
|
|
83
|
+
// Cache the remote node name for error messages (in case paramClient is destroyed)
|
|
84
|
+
this.#remoteNodeName = this.#paramClient.remoteNodeName;
|
|
85
|
+
this.#subscription = null;
|
|
86
|
+
this.#destroyed = false;
|
|
87
|
+
|
|
88
|
+
debug(
|
|
89
|
+
'Created ParameterWatcher for node=%s, params=%o',
|
|
90
|
+
remoteNodeName,
|
|
91
|
+
parameterNames
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Get the remote node name being watched.
|
|
97
|
+
* @type {string}
|
|
98
|
+
* @readonly
|
|
99
|
+
*/
|
|
100
|
+
get remoteNodeName() {
|
|
101
|
+
return this.#remoteNodeName;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Get the list of watched parameter names.
|
|
106
|
+
* @type {string[]}
|
|
107
|
+
* @readonly
|
|
108
|
+
*/
|
|
109
|
+
get watchedParameters() {
|
|
110
|
+
return Array.from(this.#watchedParams);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Start watching for parameter changes.
|
|
115
|
+
* Waits for the remote node's parameter services and subscribes to parameter events.
|
|
116
|
+
*
|
|
117
|
+
* @param {number} [timeout=5000] - Timeout in milliseconds to wait for services
|
|
118
|
+
* @returns {Promise<boolean>} Resolves to true when watching has started
|
|
119
|
+
* @throws {Error} If the watcher has been destroyed
|
|
120
|
+
*/
|
|
121
|
+
async start(timeout = 5000) {
|
|
122
|
+
this.#checkNotDestroyed();
|
|
123
|
+
|
|
124
|
+
debug('Starting ParameterWatcher for node=%s', this.remoteNodeName);
|
|
125
|
+
|
|
126
|
+
const available = await this.#paramClient.waitForService(timeout);
|
|
127
|
+
|
|
128
|
+
if (!available) {
|
|
129
|
+
debug(
|
|
130
|
+
'Parameter services not available for node=%s',
|
|
131
|
+
this.remoteNodeName
|
|
132
|
+
);
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (!this.#subscription) {
|
|
137
|
+
this.#subscription = this.#node.createSubscription(
|
|
138
|
+
'rcl_interfaces/msg/ParameterEvent',
|
|
139
|
+
'/parameter_events',
|
|
140
|
+
(event) => this.#handleParameterEvent(event)
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
debug('Subscribed to /parameter_events');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Get current values of all watched parameters.
|
|
151
|
+
*
|
|
152
|
+
* @param {object} [options] - Options for the parameter client
|
|
153
|
+
* @param {number} [options.timeout] - Timeout in milliseconds
|
|
154
|
+
* @param {AbortSignal} [options.signal] - AbortSignal for cancellation
|
|
155
|
+
* @returns {Promise<Parameter[]>} Array of Parameter objects
|
|
156
|
+
* @throws {Error} If the watcher has been destroyed
|
|
157
|
+
*/
|
|
158
|
+
async getCurrentValues(options) {
|
|
159
|
+
this.#checkNotDestroyed();
|
|
160
|
+
return await this.#paramClient.getParameters(
|
|
161
|
+
Array.from(this.#watchedParams),
|
|
162
|
+
options
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Add a parameter name to the watch list.
|
|
168
|
+
*
|
|
169
|
+
* @param {string} name - Parameter name to watch
|
|
170
|
+
* @throws {TypeError} If name is not a string
|
|
171
|
+
* @throws {Error} If the watcher has been destroyed
|
|
172
|
+
*/
|
|
173
|
+
addParameter(name) {
|
|
174
|
+
this.#checkNotDestroyed();
|
|
175
|
+
|
|
176
|
+
if (typeof name !== 'string' || name.trim() === '') {
|
|
177
|
+
throw new TypeValidationError('name', name, 'non-empty string', {
|
|
178
|
+
entityType: 'parameter watcher',
|
|
179
|
+
entityName: this.remoteNodeName,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const wasAdded = !this.#watchedParams.has(name);
|
|
184
|
+
this.#watchedParams.add(name);
|
|
185
|
+
|
|
186
|
+
if (wasAdded) {
|
|
187
|
+
debug('Added parameter to watch list: %s', name);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Remove a parameter name from the watch list.
|
|
193
|
+
*
|
|
194
|
+
* @param {string} name - Parameter name to stop watching
|
|
195
|
+
* @returns {boolean} True if the parameter was in the watch list
|
|
196
|
+
* @throws {Error} If the watcher has been destroyed
|
|
197
|
+
*/
|
|
198
|
+
removeParameter(name) {
|
|
199
|
+
this.#checkNotDestroyed();
|
|
200
|
+
|
|
201
|
+
const wasRemoved = this.#watchedParams.delete(name);
|
|
202
|
+
|
|
203
|
+
if (wasRemoved) {
|
|
204
|
+
debug('Removed parameter from watch list: %s', name);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return wasRemoved;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Check if the watcher has been destroyed.
|
|
212
|
+
*
|
|
213
|
+
* @returns {boolean} True if destroyed
|
|
214
|
+
*/
|
|
215
|
+
isDestroyed() {
|
|
216
|
+
return this.#destroyed;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Destroy the watcher and clean up resources.
|
|
221
|
+
* Unsubscribes from parameter events and destroys the parameter client.
|
|
222
|
+
*/
|
|
223
|
+
destroy() {
|
|
224
|
+
if (this.#destroyed) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
debug('Destroying ParameterWatcher for node=%s', this.remoteNodeName);
|
|
229
|
+
|
|
230
|
+
if (this.#subscription) {
|
|
231
|
+
try {
|
|
232
|
+
this.#node.destroySubscription(this.#subscription);
|
|
233
|
+
} catch (error) {
|
|
234
|
+
debug('Error destroying subscription: %s', error.message);
|
|
235
|
+
}
|
|
236
|
+
this.#subscription = null;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (this.#paramClient) {
|
|
240
|
+
try {
|
|
241
|
+
this.#node.destroyParameterClient(this.#paramClient);
|
|
242
|
+
} catch (error) {
|
|
243
|
+
debug('Error destroying parameter client: %s', error.message);
|
|
244
|
+
}
|
|
245
|
+
this.#paramClient = null;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
this.removeAllListeners();
|
|
249
|
+
|
|
250
|
+
this.#destroyed = true;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Handle parameter event from /parameter_events topic.
|
|
255
|
+
* @private
|
|
256
|
+
*/
|
|
257
|
+
#handleParameterEvent(event) {
|
|
258
|
+
if (normalizeNodeName(event.node) !== this.remoteNodeName) {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const relevantChanges = [];
|
|
263
|
+
|
|
264
|
+
if (event.new_parameters) {
|
|
265
|
+
const newParams = event.new_parameters.filter((p) =>
|
|
266
|
+
this.#watchedParams.has(p.name)
|
|
267
|
+
);
|
|
268
|
+
relevantChanges.push(...newParams);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (event.changed_parameters) {
|
|
272
|
+
const changedParams = event.changed_parameters.filter((p) =>
|
|
273
|
+
this.#watchedParams.has(p.name)
|
|
274
|
+
);
|
|
275
|
+
relevantChanges.push(...changedParams);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (event.deleted_parameters) {
|
|
279
|
+
const deletedParams = event.deleted_parameters.filter((p) =>
|
|
280
|
+
this.#watchedParams.has(p.name)
|
|
281
|
+
);
|
|
282
|
+
relevantChanges.push(...deletedParams);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (relevantChanges.length > 0) {
|
|
286
|
+
debug(
|
|
287
|
+
'Parameter change detected: %o',
|
|
288
|
+
relevantChanges.map((p) => p.name)
|
|
289
|
+
);
|
|
290
|
+
this.emit('change', relevantChanges);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Check if the watcher has been destroyed and throw if so.
|
|
296
|
+
* @private
|
|
297
|
+
*/
|
|
298
|
+
#checkNotDestroyed() {
|
|
299
|
+
if (this.#destroyed) {
|
|
300
|
+
throw new OperationError('ParameterWatcher has been destroyed', {
|
|
301
|
+
code: 'WATCHER_DESTROYED',
|
|
302
|
+
entityType: 'parameter watcher',
|
|
303
|
+
entityName: this.remoteNodeName,
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
module.exports = ParameterWatcher;
|
package/lib/qos.js
CHANGED
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
|
|
15
15
|
'use strict';
|
|
16
16
|
|
|
17
|
+
const { TypeValidationError } = require('./errors.js');
|
|
18
|
+
|
|
17
19
|
/**
|
|
18
20
|
* Enum for HistoryPolicy
|
|
19
21
|
* @readonly
|
|
@@ -129,7 +131,9 @@ class QoS {
|
|
|
129
131
|
*/
|
|
130
132
|
set history(history) {
|
|
131
133
|
if (typeof history !== 'number') {
|
|
132
|
-
throw new
|
|
134
|
+
throw new TypeValidationError('history', history, 'number', {
|
|
135
|
+
entityType: 'qos',
|
|
136
|
+
});
|
|
133
137
|
}
|
|
134
138
|
|
|
135
139
|
this._history = history;
|
|
@@ -154,7 +158,9 @@ class QoS {
|
|
|
154
158
|
*/
|
|
155
159
|
set depth(depth) {
|
|
156
160
|
if (typeof depth !== 'number') {
|
|
157
|
-
throw new
|
|
161
|
+
throw new TypeValidationError('depth', depth, 'number', {
|
|
162
|
+
entityType: 'qos',
|
|
163
|
+
});
|
|
158
164
|
}
|
|
159
165
|
|
|
160
166
|
this._depth = depth;
|
|
@@ -179,7 +185,9 @@ class QoS {
|
|
|
179
185
|
*/
|
|
180
186
|
set reliability(reliability) {
|
|
181
187
|
if (typeof reliability !== 'number') {
|
|
182
|
-
throw new
|
|
188
|
+
throw new TypeValidationError('reliability', reliability, 'number', {
|
|
189
|
+
entityType: 'qos',
|
|
190
|
+
});
|
|
183
191
|
}
|
|
184
192
|
|
|
185
193
|
this._reliability = reliability;
|
|
@@ -204,7 +212,9 @@ class QoS {
|
|
|
204
212
|
*/
|
|
205
213
|
set durability(durability) {
|
|
206
214
|
if (typeof durability !== 'number') {
|
|
207
|
-
throw new
|
|
215
|
+
throw new TypeValidationError('durability', durability, 'number', {
|
|
216
|
+
entityType: 'qos',
|
|
217
|
+
});
|
|
208
218
|
}
|
|
209
219
|
|
|
210
220
|
this._durability = durability;
|
|
@@ -229,7 +239,14 @@ class QoS {
|
|
|
229
239
|
*/
|
|
230
240
|
set avoidRosNameSpaceConventions(avoidRosNameSpaceConventions) {
|
|
231
241
|
if (typeof avoidRosNameSpaceConventions !== 'boolean') {
|
|
232
|
-
throw new
|
|
242
|
+
throw new TypeValidationError(
|
|
243
|
+
'avoidRosNameSpaceConventions',
|
|
244
|
+
avoidRosNameSpaceConventions,
|
|
245
|
+
'boolean',
|
|
246
|
+
{
|
|
247
|
+
entityType: 'qos',
|
|
248
|
+
}
|
|
249
|
+
);
|
|
233
250
|
}
|
|
234
251
|
|
|
235
252
|
this._avoidRosNameSpaceConventions = avoidRosNameSpaceConventions;
|
package/lib/rate.js
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
const rclnodejs = require('../index.js');
|
|
16
16
|
const Context = require('./context.js');
|
|
17
17
|
const NodeOptions = require('./node_options.js');
|
|
18
|
+
const { OperationError } = require('./errors.js');
|
|
18
19
|
|
|
19
20
|
const NOP_FN = () => {};
|
|
20
21
|
|
|
@@ -86,7 +87,11 @@ class Rate {
|
|
|
86
87
|
*/
|
|
87
88
|
async sleep() {
|
|
88
89
|
if (this.isCanceled()) {
|
|
89
|
-
throw new
|
|
90
|
+
throw new OperationError('Rate has been cancelled', {
|
|
91
|
+
code: 'RATE_CANCELLED',
|
|
92
|
+
entityType: 'rate',
|
|
93
|
+
details: { frequency: this._hz },
|
|
94
|
+
});
|
|
90
95
|
}
|
|
91
96
|
|
|
92
97
|
return new Promise((resolve) => {
|
package/lib/serialization.js
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
'use strict';
|
|
16
16
|
|
|
17
17
|
const rclnodejs = require('./native_loader.js');
|
|
18
|
+
const { TypeValidationError } = require('./errors.js');
|
|
18
19
|
|
|
19
20
|
class Serialization {
|
|
20
21
|
/**
|
|
@@ -25,7 +26,9 @@ class Serialization {
|
|
|
25
26
|
*/
|
|
26
27
|
static serializeMessage(message, typeClass) {
|
|
27
28
|
if (!(message instanceof typeClass)) {
|
|
28
|
-
throw new
|
|
29
|
+
throw new TypeValidationError('message', message, typeClass.name, {
|
|
30
|
+
entityType: 'serializer',
|
|
31
|
+
});
|
|
29
32
|
}
|
|
30
33
|
return rclnodejs.serialize(
|
|
31
34
|
typeClass.type().pkgName,
|
|
@@ -43,7 +46,9 @@ class Serialization {
|
|
|
43
46
|
*/
|
|
44
47
|
static deserializeMessage(buffer, typeClass) {
|
|
45
48
|
if (!(buffer instanceof Buffer)) {
|
|
46
|
-
throw new
|
|
49
|
+
throw new TypeValidationError('buffer', buffer, 'Buffer', {
|
|
50
|
+
entityType: 'serializer',
|
|
51
|
+
});
|
|
47
52
|
}
|
|
48
53
|
const rosMsg = new typeClass();
|
|
49
54
|
rclnodejs.deserialize(
|
package/lib/time.js
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
const rclnodejs = require('./native_loader.js');
|
|
18
18
|
const Duration = require('./duration.js');
|
|
19
19
|
const ClockType = require('./clock_type.js');
|
|
20
|
+
const { TypeValidationError, RangeValidationError } = require('./errors.js');
|
|
20
21
|
const S_TO_NS = 10n ** 9n;
|
|
21
22
|
|
|
22
23
|
/**
|
|
@@ -36,29 +37,49 @@ class Time {
|
|
|
36
37
|
clockType = ClockType.SYSTEM_TIME
|
|
37
38
|
) {
|
|
38
39
|
if (typeof seconds !== 'bigint') {
|
|
39
|
-
throw new
|
|
40
|
+
throw new TypeValidationError('seconds', seconds, 'bigint', {
|
|
41
|
+
entityType: 'time',
|
|
42
|
+
});
|
|
40
43
|
}
|
|
41
44
|
|
|
42
45
|
if (typeof nanoseconds !== 'bigint') {
|
|
43
|
-
throw new
|
|
46
|
+
throw new TypeValidationError('nanoseconds', nanoseconds, 'bigint', {
|
|
47
|
+
entityType: 'time',
|
|
48
|
+
});
|
|
44
49
|
}
|
|
45
50
|
|
|
46
51
|
if (typeof clockType !== 'number') {
|
|
47
|
-
throw new
|
|
52
|
+
throw new TypeValidationError('clockType', clockType, 'number', {
|
|
53
|
+
entityType: 'time',
|
|
54
|
+
});
|
|
48
55
|
}
|
|
49
56
|
|
|
50
57
|
if (seconds < 0n) {
|
|
51
|
-
throw new
|
|
58
|
+
throw new RangeValidationError('seconds', seconds, '>= 0', {
|
|
59
|
+
entityType: 'time',
|
|
60
|
+
});
|
|
52
61
|
}
|
|
53
62
|
|
|
54
63
|
if (nanoseconds < 0n) {
|
|
55
|
-
throw new
|
|
64
|
+
throw new RangeValidationError('nanoseconds', nanoseconds, '>= 0', {
|
|
65
|
+
entityType: 'time',
|
|
66
|
+
});
|
|
56
67
|
}
|
|
57
68
|
|
|
58
69
|
const total = seconds * S_TO_NS + nanoseconds;
|
|
59
70
|
if (total >= 2n ** 63n) {
|
|
60
|
-
throw new
|
|
61
|
-
'
|
|
71
|
+
throw new RangeValidationError(
|
|
72
|
+
'total nanoseconds',
|
|
73
|
+
total,
|
|
74
|
+
'< 2^63 (max C time point)',
|
|
75
|
+
{
|
|
76
|
+
entityType: 'time',
|
|
77
|
+
details: {
|
|
78
|
+
seconds: seconds,
|
|
79
|
+
nanoseconds: nanoseconds,
|
|
80
|
+
total: total,
|
|
81
|
+
},
|
|
82
|
+
}
|
|
62
83
|
);
|
|
63
84
|
}
|
|
64
85
|
this._nanoseconds = total;
|
|
@@ -116,7 +137,9 @@ class Time {
|
|
|
116
137
|
this._clockType
|
|
117
138
|
);
|
|
118
139
|
}
|
|
119
|
-
throw new
|
|
140
|
+
throw new TypeValidationError('other', other, 'Duration', {
|
|
141
|
+
entityType: 'time',
|
|
142
|
+
});
|
|
120
143
|
}
|
|
121
144
|
|
|
122
145
|
/**
|
|
@@ -127,7 +150,18 @@ class Time {
|
|
|
127
150
|
sub(other) {
|
|
128
151
|
if (other instanceof Time) {
|
|
129
152
|
if (other._clockType !== this._clockType) {
|
|
130
|
-
throw new
|
|
153
|
+
throw new TypeValidationError(
|
|
154
|
+
'other',
|
|
155
|
+
other,
|
|
156
|
+
`Time with clock type ${this._clockType}`,
|
|
157
|
+
{
|
|
158
|
+
entityType: 'time',
|
|
159
|
+
details: {
|
|
160
|
+
expectedClockType: this._clockType,
|
|
161
|
+
providedClockType: other._clockType,
|
|
162
|
+
},
|
|
163
|
+
}
|
|
164
|
+
);
|
|
131
165
|
}
|
|
132
166
|
return new Duration(0n, this._nanoseconds - other._nanoseconds);
|
|
133
167
|
} else if (other instanceof Duration) {
|
|
@@ -137,7 +171,9 @@ class Time {
|
|
|
137
171
|
this._clockType
|
|
138
172
|
);
|
|
139
173
|
}
|
|
140
|
-
throw new
|
|
174
|
+
throw new TypeValidationError('other', other, 'Time or Duration', {
|
|
175
|
+
entityType: 'time',
|
|
176
|
+
});
|
|
141
177
|
}
|
|
142
178
|
|
|
143
179
|
/**
|
|
@@ -148,11 +184,24 @@ class Time {
|
|
|
148
184
|
eq(other) {
|
|
149
185
|
if (other instanceof Time) {
|
|
150
186
|
if (other._clockType !== this._clockType) {
|
|
151
|
-
throw new
|
|
187
|
+
throw new TypeValidationError(
|
|
188
|
+
'other',
|
|
189
|
+
other,
|
|
190
|
+
`Time with clock type ${this._clockType}`,
|
|
191
|
+
{
|
|
192
|
+
entityType: 'time',
|
|
193
|
+
details: {
|
|
194
|
+
expectedClockType: this._clockType,
|
|
195
|
+
providedClockType: other._clockType,
|
|
196
|
+
},
|
|
197
|
+
}
|
|
198
|
+
);
|
|
152
199
|
}
|
|
153
200
|
return this._nanoseconds === other.nanoseconds;
|
|
154
201
|
}
|
|
155
|
-
throw new
|
|
202
|
+
throw new TypeValidationError('other', other, 'Time', {
|
|
203
|
+
entityType: 'time',
|
|
204
|
+
});
|
|
156
205
|
}
|
|
157
206
|
|
|
158
207
|
/**
|
|
@@ -163,10 +212,24 @@ class Time {
|
|
|
163
212
|
ne(other) {
|
|
164
213
|
if (other instanceof Time) {
|
|
165
214
|
if (other._clockType !== this._clockType) {
|
|
166
|
-
throw new
|
|
215
|
+
throw new TypeValidationError(
|
|
216
|
+
'other',
|
|
217
|
+
other,
|
|
218
|
+
`Time with clock type ${this._clockType}`,
|
|
219
|
+
{
|
|
220
|
+
entityType: 'time',
|
|
221
|
+
details: {
|
|
222
|
+
expectedClockType: this._clockType,
|
|
223
|
+
providedClockType: other._clockType,
|
|
224
|
+
},
|
|
225
|
+
}
|
|
226
|
+
);
|
|
167
227
|
}
|
|
168
228
|
return this._nanoseconds !== other.nanoseconds;
|
|
169
229
|
}
|
|
230
|
+
throw new TypeValidationError('other', other, 'Time', {
|
|
231
|
+
entityType: 'time',
|
|
232
|
+
});
|
|
170
233
|
}
|
|
171
234
|
|
|
172
235
|
/**
|
|
@@ -177,11 +240,24 @@ class Time {
|
|
|
177
240
|
lt(other) {
|
|
178
241
|
if (other instanceof Time) {
|
|
179
242
|
if (other._clockType !== this._clockType) {
|
|
180
|
-
throw new
|
|
243
|
+
throw new TypeValidationError(
|
|
244
|
+
'other',
|
|
245
|
+
other,
|
|
246
|
+
`Time with clock type ${this._clockType}`,
|
|
247
|
+
{
|
|
248
|
+
entityType: 'time',
|
|
249
|
+
details: {
|
|
250
|
+
expectedClockType: this._clockType,
|
|
251
|
+
providedClockType: other._clockType,
|
|
252
|
+
},
|
|
253
|
+
}
|
|
254
|
+
);
|
|
181
255
|
}
|
|
182
256
|
return this._nanoseconds < other.nanoseconds;
|
|
183
257
|
}
|
|
184
|
-
throw new
|
|
258
|
+
throw new TypeValidationError('other', other, 'Time', {
|
|
259
|
+
entityType: 'time',
|
|
260
|
+
});
|
|
185
261
|
}
|
|
186
262
|
|
|
187
263
|
/**
|
|
@@ -192,11 +268,24 @@ class Time {
|
|
|
192
268
|
lte(other) {
|
|
193
269
|
if (other instanceof Time) {
|
|
194
270
|
if (other._clockType !== this._clockType) {
|
|
195
|
-
throw new
|
|
271
|
+
throw new TypeValidationError(
|
|
272
|
+
'other',
|
|
273
|
+
other,
|
|
274
|
+
`Time with clock type ${this._clockType}`,
|
|
275
|
+
{
|
|
276
|
+
entityType: 'time',
|
|
277
|
+
details: {
|
|
278
|
+
expectedClockType: this._clockType,
|
|
279
|
+
providedClockType: other._clockType,
|
|
280
|
+
},
|
|
281
|
+
}
|
|
282
|
+
);
|
|
196
283
|
}
|
|
197
284
|
return this._nanoseconds <= other.nanoseconds;
|
|
198
285
|
}
|
|
199
|
-
throw new
|
|
286
|
+
throw new TypeValidationError('other', other, 'Time', {
|
|
287
|
+
entityType: 'time',
|
|
288
|
+
});
|
|
200
289
|
}
|
|
201
290
|
|
|
202
291
|
/**
|
|
@@ -207,11 +296,24 @@ class Time {
|
|
|
207
296
|
gt(other) {
|
|
208
297
|
if (other instanceof Time) {
|
|
209
298
|
if (other._clockType !== this._clockType) {
|
|
210
|
-
throw new
|
|
299
|
+
throw new TypeValidationError(
|
|
300
|
+
'other',
|
|
301
|
+
other,
|
|
302
|
+
`Time with clock type ${this._clockType}`,
|
|
303
|
+
{
|
|
304
|
+
entityType: 'time',
|
|
305
|
+
details: {
|
|
306
|
+
expectedClockType: this._clockType,
|
|
307
|
+
providedClockType: other._clockType,
|
|
308
|
+
},
|
|
309
|
+
}
|
|
310
|
+
);
|
|
211
311
|
}
|
|
212
312
|
return this._nanoseconds > other.nanoseconds;
|
|
213
313
|
}
|
|
214
|
-
throw new
|
|
314
|
+
throw new TypeValidationError('other', other, 'Time', {
|
|
315
|
+
entityType: 'time',
|
|
316
|
+
});
|
|
215
317
|
}
|
|
216
318
|
|
|
217
319
|
/**
|
|
@@ -222,11 +324,24 @@ class Time {
|
|
|
222
324
|
gte(other) {
|
|
223
325
|
if (other instanceof Time) {
|
|
224
326
|
if (other._clockType !== this._clockType) {
|
|
225
|
-
throw new
|
|
327
|
+
throw new TypeValidationError(
|
|
328
|
+
'other',
|
|
329
|
+
other,
|
|
330
|
+
`Time with clock type ${this._clockType}`,
|
|
331
|
+
{
|
|
332
|
+
entityType: 'time',
|
|
333
|
+
details: {
|
|
334
|
+
expectedClockType: this._clockType,
|
|
335
|
+
providedClockType: other._clockType,
|
|
336
|
+
},
|
|
337
|
+
}
|
|
338
|
+
);
|
|
226
339
|
}
|
|
227
340
|
return this._nanoseconds >= other.nanoseconds;
|
|
228
341
|
}
|
|
229
|
-
throw new
|
|
342
|
+
throw new TypeValidationError('other', other, 'Time', {
|
|
343
|
+
entityType: 'time',
|
|
344
|
+
});
|
|
230
345
|
}
|
|
231
346
|
|
|
232
347
|
/**
|