@tdxvolt/volt-client-grpc 0.11.0 → 0.11.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/lib/index.cjs CHANGED
@@ -34,192 +34,192 @@ var bent__default = /*#__PURE__*/_interopDefaultLegacy(bent);
34
34
 
35
35
  // eslint-disable-next-line import/prefer-default-export
36
36
  const constants = {
37
- authTokenName: "tdx-token",
38
- collectionOperation: {
39
- add: "ADD",
40
- remove: "REMOVE",
41
- update: "UPDATE",
42
- },
43
- crypto: {
44
- subjectDefaults: [
45
- {
46
- name: "countryName",
47
- value: "GB",
48
- },
49
- {
50
- name: "localityName",
51
- value: "Southampton",
52
- },
53
- {
54
- name: "organizationName",
55
- value: "nquiringminds Ltd",
56
- },
57
- ],
58
- userIdOID: "0.9.2342.19200300.100.1.1",
59
- },
60
- defaultDIDHostName: "cloud.tdxvolt.com",
61
- didServiceType: {
62
- voltConfig: "tdx-volt-config",
63
- },
64
- identityType: {
65
- did: "did",
66
- volt: "volt",
67
- },
68
- onlineStatus: {
69
- offline: "offline",
70
- online: "online",
71
- unknown: "unknown",
72
- },
73
- publicKeyPrefix: "-----BEGIN PUBLIC KEY-----",
74
- privateKeyPrefix: "-----BEGIN RSA PRIVATE KEY-----",
75
- privateEncryptedKeyPrefix: "-----BEGIN ENCRYPTED PRIVATE KEY-----",
76
- resource: {
77
- identity: "ident",
78
- identityView: "ident-view",
79
- proxy: "proxy",
80
- proxyView: "proxy-view",
81
- vc: "vc",
82
- vcView: "vc-view",
83
- },
84
- voltStatus: {
85
- pending: "",
86
- online: "online",
87
- offline: "offline",
88
- deleted: "deleted",
89
- revoked: "revoked",
90
- },
91
- /**
92
- * These top-level service types should map to a protobuf definition
93
- * in @tdxvolt/volt-proto.
94
- */
95
- serviceType: {
96
- fileAPI: "tdx.volt_api.volt.v1.FileAPI",
97
- sqliteServerAPI: "tdx.volt_api.data.v1.SqliteServerAPI",
98
- sqliteDatabaseAPI: "tdx.volt_api.data.v1.SqliteDatabaseAPI",
99
- ssiAPI: "tdx.volt_api.ssi.v1.SsiAPI",
100
- relayAPI: "tdx.volt_api.relay.v1.RelayAPI",
101
- voltAPI: "tdx.volt_api.volt.v1.VoltAPI",
102
- wireAPI: "tdx.volt_api.volt.v1.WireAPI",
103
- },
104
- tunnelInvokeMethod: "/tdx.volt_api.volt.v1.VoltAPI/Invoke",
105
- tunnelPingInterval: 10000,
106
- tunnelPingTimeout: 2500,
107
- tunnelReconnectAfter: 30000,
108
- tunnelTimeoutInterval: 10000 * 2, // This must always be greater than `tunnelPingInterval`.
37
+ authTokenName: "tdx-token",
38
+ collectionOperation: {
39
+ add: "ADD",
40
+ remove: "REMOVE",
41
+ update: "UPDATE",
42
+ },
43
+ crypto: {
44
+ subjectDefaults: [
45
+ {
46
+ name: "countryName",
47
+ value: "GB",
48
+ },
49
+ {
50
+ name: "localityName",
51
+ value: "Southampton",
52
+ },
53
+ {
54
+ name: "organizationName",
55
+ value: "nquiringminds Ltd",
56
+ },
57
+ ],
58
+ userIdOID: "0.9.2342.19200300.100.1.1",
59
+ },
60
+ defaultDIDHostName: "cloud.tdxvolt.com",
61
+ didServiceType: {
62
+ voltConfig: "tdx-volt-config",
63
+ },
64
+ identityType: {
65
+ did: "did",
66
+ volt: "volt",
67
+ },
68
+ onlineStatus: {
69
+ offline: "offline",
70
+ online: "online",
71
+ unknown: "unknown",
72
+ },
73
+ publicKeyPrefix: "-----BEGIN PUBLIC KEY-----",
74
+ privateKeyPrefix: "-----BEGIN RSA PRIVATE KEY-----",
75
+ privateEncryptedKeyPrefix: "-----BEGIN ENCRYPTED PRIVATE KEY-----",
76
+ resource: {
77
+ identity: "ident",
78
+ identityView: "ident-view",
79
+ proxy: "proxy",
80
+ proxyView: "proxy-view",
81
+ vc: "vc",
82
+ vcView: "vc-view",
83
+ },
84
+ voltStatus: {
85
+ pending: "",
86
+ online: "online",
87
+ offline: "offline",
88
+ deleted: "deleted",
89
+ revoked: "revoked",
90
+ },
91
+ /**
92
+ * These top-level service types should map to a protobuf definition
93
+ * in @tdxvolt/volt-proto.
94
+ */
95
+ serviceType: {
96
+ fileAPI: "tdx.volt_api.volt.v1.FileAPI",
97
+ sqliteServerAPI: "tdx.volt_api.data.v1.SqliteServerAPI",
98
+ sqliteDatabaseAPI: "tdx.volt_api.data.v1.SqliteDatabaseAPI",
99
+ ssiAPI: "tdx.volt_api.ssi.v1.SsiAPI",
100
+ relayAPI: "tdx.volt_api.relay.v1.RelayAPI",
101
+ voltAPI: "tdx.volt_api.volt.v1.VoltAPI",
102
+ wireAPI: "tdx.volt_api.volt.v1.WireAPI",
103
+ },
104
+ tunnelInvokeMethod: "/tdx.volt_api.volt.v1.VoltAPI/Invoke",
105
+ tunnelPingInterval: 10000,
106
+ tunnelPingTimeout: 2500,
107
+ tunnelReconnectAfter: 30000,
108
+ tunnelTimeoutInterval: 10000 * 2, // This must always be greater than `tunnelPingInterval`.
109
109
  };
110
110
 
111
111
  const { pki: pki$2 } = forge__default["default"];
112
112
 
113
113
  const createSecureContextOptions = (cryptoOptions) => {
114
- const tlsOptions = {
115
- cert: Buffer.from(cryptoOptions.cert),
116
- ca: Buffer.from(cryptoOptions.ca),
117
- key: Buffer.from(cryptoOptions.key),
118
- };
119
- return tlsOptions;
114
+ const tlsOptions = {
115
+ cert: Buffer.from(cryptoOptions.cert),
116
+ ca: Buffer.from(cryptoOptions.ca),
117
+ key: Buffer.from(cryptoOptions.key),
118
+ };
119
+ return tlsOptions;
120
120
  };
121
121
 
122
122
  const getServiceAddress = (mdnsService) => {
123
- let address;
124
-
125
- if (mdnsService.txtRecord?.ip) {
126
- // Use the advertised IP address if present.
127
- address = `${mdnsService.txtRecord.ip}:${mdnsService.port}`;
128
- } else {
129
- // Find a valid ipv4 address.
130
- const ipv4 = lodash__default["default"].find(mdnsService.addresses, (addy) => ip__default["default"].isV4Format(addy));
131
- const serviceDetails = {
132
- host: ipv4,
133
- port: mdnsService.port,
134
- };
135
-
136
- address = `${serviceDetails.host}:${serviceDetails.port}`;
137
- }
138
-
139
- return address;
123
+ let address;
124
+
125
+ if (mdnsService.txtRecord?.ip) {
126
+ // Use the advertised IP address if present.
127
+ address = `${mdnsService.txtRecord.ip}:${mdnsService.port}`;
128
+ } else {
129
+ // Find a valid ipv4 address.
130
+ const ipv4 = lodash__default["default"].find(mdnsService.addresses, (addy) => ip__default["default"].isV4Format(addy));
131
+ const serviceDetails = {
132
+ host: ipv4,
133
+ port: mdnsService.port,
134
+ };
135
+
136
+ address = `${serviceDetails.host}:${serviceDetails.port}`;
137
+ }
138
+
139
+ return address;
140
140
  };
141
141
 
142
142
  const privateKeyToPem = (keyPair) => {
143
- return pki$2.privateKeyToPem(keyPair.privateKey);
143
+ return pki$2.privateKeyToPem(keyPair.privateKey);
144
144
  };
145
145
 
146
146
  const publicKeyToPem = (keyPair) => {
147
- return pki$2.publicKeyToPem(keyPair.publicKey);
147
+ return pki$2.publicKeyToPem(keyPair.publicKey);
148
148
  };
149
149
 
150
150
  const deserialisePEM = (pem) => {
151
- return pem.replace(/\\r\\n/g, "\r\n");
151
+ return pem.replace(/\\r\\n/g, "\r\n");
152
152
  };
153
153
 
154
154
  const removeCarriageReturn = (inp) => {
155
- return inp.replace(/\r\n/g, "\n");
155
+ return inp.replace(/\r\n/g, "\n");
156
156
  };
157
157
 
158
158
  const createBase58Hash = (inp) => {
159
- const md = forge__default["default"].md.sha256.create();
160
- md.update(inp);
161
- const hashBuf = Buffer.from(md.digest().getBytes(), "binary");
162
- return bs58__default["default"].encode(hashBuf);
159
+ const md = forge__default["default"].md.sha256.create();
160
+ md.update(inp);
161
+ const hashBuf = Buffer.from(md.digest().getBytes(), "binary");
162
+ return bs58__default["default"].encode(hashBuf);
163
163
  };
164
164
 
165
165
  const createPublicKeyFingerprint = (key) => {
166
- // Remove carriage returns before creating hash to bring into line with openSSL / volt format.
167
- return createBase58Hash(removeCarriageReturn(key));
166
+ // Remove carriage returns before creating hash to bring into line with openSSL / volt format.
167
+ return createBase58Hash(removeCarriageReturn(key));
168
168
  };
169
169
 
170
170
  const signBase64 = (key, msg) => {
171
- const md = forge__default["default"].md.sha256.create();
172
- md.update(msg);
173
- const signature = key.sign(md);
174
- return forge__default["default"].util.encode64(signature);
171
+ const md = forge__default["default"].md.sha256.create();
172
+ md.update(msg);
173
+ const signature = key.sign(md);
174
+ return forge__default["default"].util.encode64(signature);
175
175
  };
176
176
 
177
177
  const verifyBase64 = (key, msg, signature) => {
178
- const md = forge__default["default"].md.sha256.create();
179
- md.update(msg);
180
- return key.verify(md.digest().bytes(), forge__default["default"].util.decode64(signature));
178
+ const md = forge__default["default"].md.sha256.create();
179
+ md.update(msg);
180
+ return key.verify(md.digest().bytes(), forge__default["default"].util.decode64(signature));
181
181
  };
182
182
 
183
183
  const getDIDResolutionURL = (didIn) => {
184
- const did = didIn.toLowerCase().split(":");
185
- if (did.length < 3) {
186
- return "";
187
- }
188
-
189
- if (did[0] !== "did") {
190
- return "";
191
- }
192
-
193
- if (did[1] !== "tdx") {
194
- return "";
195
- }
196
-
197
- let hostName;
198
- let didGUID;
199
- if (did.length === 3) {
200
- hostName = constants.defaultDIDHostName;
201
- didGUID = did[2];
202
- } else {
203
- hostName = did[2];
204
- didGUID = did[3];
205
- }
206
-
207
- return `https://${hostName}/api/identity/${didGUID}`;
184
+ const did = didIn.toLowerCase().split(":");
185
+ if (did.length < 3) {
186
+ return "";
187
+ }
188
+
189
+ if (did[0] !== "did") {
190
+ return "";
191
+ }
192
+
193
+ if (did[1] !== "tdx") {
194
+ return "";
195
+ }
196
+
197
+ let hostName;
198
+ let didGUID;
199
+ if (did.length === 3) {
200
+ hostName = constants.defaultDIDHostName;
201
+ didGUID = did[2];
202
+ } else {
203
+ hostName = did[2];
204
+ didGUID = did[3];
205
+ }
206
+
207
+ return `https://${hostName}/api/identity/${didGUID}`;
208
208
  };
209
209
 
210
210
  var utils = /*#__PURE__*/Object.freeze({
211
- __proto__: null,
212
- createSecureContextOptions: createSecureContextOptions,
213
- getServiceAddress: getServiceAddress,
214
- privateKeyToPem: privateKeyToPem,
215
- publicKeyToPem: publicKeyToPem,
216
- deserialisePEM: deserialisePEM,
217
- removeCarriageReturn: removeCarriageReturn,
218
- createBase58Hash: createBase58Hash,
219
- createPublicKeyFingerprint: createPublicKeyFingerprint,
220
- signBase64: signBase64,
221
- verifyBase64: verifyBase64,
222
- getDIDResolutionURL: getDIDResolutionURL
211
+ __proto__: null,
212
+ createSecureContextOptions: createSecureContextOptions,
213
+ getServiceAddress: getServiceAddress,
214
+ privateKeyToPem: privateKeyToPem,
215
+ publicKeyToPem: publicKeyToPem,
216
+ deserialisePEM: deserialisePEM,
217
+ removeCarriageReturn: removeCarriageReturn,
218
+ createBase58Hash: createBase58Hash,
219
+ createPublicKeyFingerprint: createPublicKeyFingerprint,
220
+ signBase64: signBase64,
221
+ verifyBase64: verifyBase64,
222
+ getDIDResolutionURL: getDIDResolutionURL
223
223
  });
224
224
 
225
225
  /* eslint-disable no-underscore-dangle */
@@ -227,77 +227,77 @@ var utils = /*#__PURE__*/Object.freeze({
227
227
  const { forEach, isEmpty } = lodash__default["default"];
228
228
 
229
229
  const serialiseNOP = (arg) => {
230
- return arg;
230
+ return arg;
231
231
  };
232
232
 
233
233
  function createClient(
234
- grpc,
235
- serviceDescriptors,
236
- address,
237
- credentials,
238
- noSerialise,
239
- voltId,
240
- serviceId,
234
+ grpc,
235
+ serviceDescriptors,
236
+ address,
237
+ credentials,
238
+ noSerialise,
239
+ voltId,
240
+ serviceId,
241
241
  ) {
242
- let grpcCredentials;
243
- if (credentials) {
244
- const ca = credentials.config?.relay?.ca_pem || credentials.cache.ca;
245
- const cert = credentials.cache.cloud_cert || credentials.cache.cert;
246
- const { key } = credentials.cache;
247
-
248
- if (key && cert) {
249
- const cryyptoOptions = { ca, cert, key };
250
- const tlsOptions = createSecureContextOptions(cryyptoOptions);
251
- grpcCredentials = grpc.credentials.createSsl(
252
- tlsOptions.ca,
253
- tlsOptions.key,
254
- tlsOptions.cert,
255
- );
256
- } else {
257
- // Cache has no private key, this implies we are connecting without supplying a client certificate.
258
- // Simply connect using the CA certificate.
259
- grpcCredentials = grpc.credentials.createSsl(Buffer.from(ca));
260
- }
261
- } else {
262
- grpcCredentials = grpc.credentials.createInsecure();
263
- }
264
-
265
- // Force all methods to appear bi-directional, since we manually handle the call flow (see grpc-call.js).
266
- forEach(serviceDescriptors, (method) => {
267
- method.requestStream = true;
268
- method.responseStream = true;
269
- });
270
-
271
- if (noSerialise || voltId) {
272
- if (voltId && !serviceId) {
273
- // We must have a service identifier id when tunnelling in order to be able to route the rpc.
274
- throw new Error("createClient - no serviceId given");
275
- }
276
- forEach(serviceDescriptors, (method) => {
277
- if (voltId) {
278
- // Prepend the resource id to the method path if tunnelling.
279
- if (voltId === serviceId) {
280
- method.path = `/${voltId}${method.path}`;
281
- } else {
282
- method.path = `/${voltId}/${serviceId}${method.path}`;
283
- }
284
- }
285
- if (noSerialise) {
286
- // Save original serialisation methods so we can encrypt/decrypt in the tunnel.
287
- method.requestSerializeOriginal = method.requestSerialize;
288
- method.responseDeserializeOriginal = method.responseDeserialize;
289
-
290
- // Replace default serialisation with NOP, since when tunnelling we encrypt the payload
291
- // before sending it to the channel, so we want it to be treated as a pure byte array (pass-through).
292
- method.requestSerialize = serialiseNOP;
293
- method.responseDeserialize = serialiseNOP;
294
- }
295
- });
296
- }
297
-
298
- const SubClass = grpc.makeGenericClientConstructor(serviceDescriptors);
299
-
300
- return new SubClass(address, grpcCredentials);
242
+ let grpcCredentials;
243
+ if (credentials) {
244
+ const ca = credentials.config?.relay?.ca_pem || credentials.cache.ca;
245
+ const cert = credentials.cache.cloud_cert || credentials.cache.cert;
246
+ const { key } = credentials.cache;
247
+
248
+ if (key && cert) {
249
+ const cryyptoOptions = { ca, cert, key };
250
+ const tlsOptions = createSecureContextOptions(cryyptoOptions);
251
+ grpcCredentials = grpc.credentials.createSsl(
252
+ tlsOptions.ca,
253
+ tlsOptions.key,
254
+ tlsOptions.cert,
255
+ );
256
+ } else {
257
+ // Cache has no private key, this implies we are connecting without supplying a client certificate.
258
+ // Simply connect using the CA certificate.
259
+ grpcCredentials = grpc.credentials.createSsl(Buffer.from(ca));
260
+ }
261
+ } else {
262
+ grpcCredentials = grpc.credentials.createInsecure();
263
+ }
264
+
265
+ // Force all methods to appear bi-directional, since we manually handle the call flow (see grpc-call.js).
266
+ forEach(serviceDescriptors, (method) => {
267
+ method.requestStream = true;
268
+ method.responseStream = true;
269
+ });
270
+
271
+ if (noSerialise || voltId) {
272
+ if (voltId && !serviceId) {
273
+ // We must have a service identifier id when tunnelling in order to be able to route the rpc.
274
+ throw new Error("createClient - no serviceId given");
275
+ }
276
+ forEach(serviceDescriptors, (method) => {
277
+ if (voltId) {
278
+ // Prepend the resource id to the method path if tunnelling.
279
+ if (voltId === serviceId) {
280
+ method.path = `/${voltId}${method.path}`;
281
+ } else {
282
+ method.path = `/${voltId}/${serviceId}${method.path}`;
283
+ }
284
+ }
285
+ if (noSerialise) {
286
+ // Save original serialisation methods so we can encrypt/decrypt in the tunnel.
287
+ method.requestSerializeOriginal = method.requestSerialize;
288
+ method.responseDeserializeOriginal = method.responseDeserialize;
289
+
290
+ // Replace default serialisation with NOP, since when tunnelling we encrypt the payload
291
+ // before sending it to the channel, so we want it to be treated as a pure byte array (pass-through).
292
+ method.requestSerialize = serialiseNOP;
293
+ method.responseDeserialize = serialiseNOP;
294
+ }
295
+ });
296
+ }
297
+
298
+ const SubClass = grpc.makeGenericClientConstructor(serviceDescriptors);
299
+
300
+ return new SubClass(address, grpcCredentials);
301
301
  }
302
302
 
303
303
  /**
@@ -305,158 +305,158 @@ function createClient(
305
305
  * @param {*} api
306
306
  */
307
307
  const grpcWrap = (api) => {
308
- const grpcRoutes = {};
309
-
310
- // Don't bother trying to wrap class constructors.
311
- const ignoreMethods = ["constructor"];
312
-
313
- // Support class instances and PoJos
314
- const isClass = api.constructor !== Object;
315
- const iterate = Object.getOwnPropertyNames(
316
- isClass ? api.constructor.prototype : api,
317
- );
318
-
319
- // Wrap each method on the api.
320
- forEach(iterate, (name) => {
321
- if (!ignoreMethods.includes(name)) {
322
- const method = api[name];
323
- grpcRoutes[name] = (call, callback) => {
324
- // Execute the method in the context of the api.
325
- const resultPromise = method.call(api, call.request, call);
326
-
327
- // Enforce promise results.
328
- if (!(resultPromise?.then && resultPromise?.catch)) {
329
- throw new Error(
330
- `implementation methods must return a Promise [${name}]`,
331
- );
332
- }
333
-
334
- resultPromise
335
- .then((response) => {
336
- if (callback) {
337
- callback(null, response);
338
- }
339
- })
340
- .catch((err) => {
341
- if (callback) {
342
- callback(err);
343
- } else {
344
- // TODO - verify this => how to send error to stream clients?
345
- // Destroy stream on error.
346
- call.destroy(err);
347
- }
348
- });
349
- };
350
- }
351
- });
352
-
353
- return grpcRoutes;
308
+ const grpcRoutes = {};
309
+
310
+ // Don't bother trying to wrap class constructors.
311
+ const ignoreMethods = ["constructor"];
312
+
313
+ // Support class instances and PoJos
314
+ const isClass = api.constructor !== Object;
315
+ const iterate = Object.getOwnPropertyNames(
316
+ isClass ? api.constructor.prototype : api,
317
+ );
318
+
319
+ // Wrap each method on the api.
320
+ forEach(iterate, (name) => {
321
+ if (!ignoreMethods.includes(name)) {
322
+ const method = api[name];
323
+ grpcRoutes[name] = (call, callback) => {
324
+ // Execute the method in the context of the api.
325
+ const resultPromise = method.call(api, call.request, call);
326
+
327
+ // Enforce promise results.
328
+ if (!(resultPromise?.then && resultPromise?.catch)) {
329
+ throw new Error(
330
+ `implementation methods must return a Promise [${name}]`,
331
+ );
332
+ }
333
+
334
+ resultPromise
335
+ .then((response) => {
336
+ if (callback) {
337
+ callback(null, response);
338
+ }
339
+ })
340
+ .catch((err) => {
341
+ if (callback) {
342
+ callback(err);
343
+ } else {
344
+ // TODO - verify this => how to send error to stream clients?
345
+ // Destroy stream on error.
346
+ call.destroy(err);
347
+ }
348
+ });
349
+ };
350
+ }
351
+ });
352
+
353
+ return grpcRoutes;
354
354
  };
355
355
 
356
356
  class GrpcServer {
357
- constructor(options) {
358
- const { grpc, serviceDescriptors, routes, tlsOptions, requireClientCert } =
359
- options;
360
- this._grpc = grpc;
361
- this._server = new this._grpc.Server({
362
- "grpc.keepalive_time_ms": 500,
363
- "grpc.keepalive_timeout_ms": 5000,
364
- "grpc.http2.max_pings_without_data": 0,
365
- "grpc.keepalive_permit_without_calls": 1,
366
- "grpc.http2.min_ping_interval_without_data_ms": 250,
367
- "grpc.http2.max_ping_strikes": 0,
368
- });
369
-
370
- if (tlsOptions) {
371
- const caList = [];
372
- const certChains = [];
373
- [].concat(tlsOptions).forEach((tlsOption) => {
374
- caList.push(tlsOption.ca);
375
- certChains.push({
376
- private_key: tlsOption.key,
377
- cert_chain: tlsOption.cert,
378
- });
379
- });
380
-
381
- // Create credentials from the volt CA and our own pub/priv key.
382
- this._credentials = grpc.ServerCredentials.createSsl(
383
- // The root CAs we trust when verifying client certificates.
384
- Buffer.concat(caList),
385
- // The certificate chain(s) we present to the client.
386
- certChains,
387
- // Flag indicating if we insist on a client certificate.
388
- requireClientCert,
389
- );
390
- } else {
391
- this._credentials = grpc.ServerCredentials.createInsecure();
392
- }
393
-
394
- if (serviceDescriptors && !isEmpty(routes)) {
395
- // Enforce decoupling of the service implementation.
396
- const router = grpcWrap(routes);
397
- this._server.addService(serviceDescriptors, router);
398
- }
399
- }
400
-
401
- close(force = false) {
402
- return new Promise((resolve) => {
403
- if (force) {
404
- resolve(this._server.forceShutdown());
405
- } else {
406
- this._server.tryShutdown(resolve);
407
- }
408
- });
409
- }
410
-
411
- listen(address) {
412
- return new Promise((resolve, reject) => {
413
- // If no port is specified, grpc will return the port number assigned.
414
- this._server.bindAsync(address, this._credentials, (err, port) => {
415
- if (err) {
416
- reject(err);
417
- } else {
418
- this._port = port;
419
- this._server.start();
420
- resolve(port);
421
- }
422
- });
423
- });
424
- }
425
-
426
- get server() {
427
- return this._server;
428
- }
429
-
430
- get port() {
431
- return this._port;
432
- }
357
+ constructor(options) {
358
+ const { grpc, serviceDescriptors, routes, tlsOptions, requireClientCert } =
359
+ options;
360
+ this._grpc = grpc;
361
+ this._server = new this._grpc.Server({
362
+ "grpc.keepalive_time_ms": 500,
363
+ "grpc.keepalive_timeout_ms": 5000,
364
+ "grpc.http2.max_pings_without_data": 0,
365
+ "grpc.keepalive_permit_without_calls": 1,
366
+ "grpc.http2.min_ping_interval_without_data_ms": 250,
367
+ "grpc.http2.max_ping_strikes": 0,
368
+ });
369
+
370
+ if (tlsOptions) {
371
+ const caList = [];
372
+ const certChains = [];
373
+ [].concat(tlsOptions).forEach((tlsOption) => {
374
+ caList.push(tlsOption.ca);
375
+ certChains.push({
376
+ private_key: tlsOption.key,
377
+ cert_chain: tlsOption.cert,
378
+ });
379
+ });
380
+
381
+ // Create credentials from the volt CA and our own pub/priv key.
382
+ this._credentials = grpc.ServerCredentials.createSsl(
383
+ // The root CAs we trust when verifying client certificates.
384
+ Buffer.concat(caList),
385
+ // The certificate chain(s) we present to the client.
386
+ certChains,
387
+ // Flag indicating if we insist on a client certificate.
388
+ requireClientCert,
389
+ );
390
+ } else {
391
+ this._credentials = grpc.ServerCredentials.createInsecure();
392
+ }
393
+
394
+ if (serviceDescriptors && !isEmpty(routes)) {
395
+ // Enforce decoupling of the service implementation.
396
+ const router = grpcWrap(routes);
397
+ this._server.addService(serviceDescriptors, router);
398
+ }
399
+ }
400
+
401
+ close(force = false) {
402
+ return new Promise((resolve) => {
403
+ if (force) {
404
+ resolve(this._server.forceShutdown());
405
+ } else {
406
+ this._server.tryShutdown(resolve);
407
+ }
408
+ });
409
+ }
410
+
411
+ listen(address) {
412
+ return new Promise((resolve, reject) => {
413
+ // If no port is specified, grpc will return the port number assigned.
414
+ this._server.bindAsync(address, this._credentials, (err, port) => {
415
+ if (err) {
416
+ reject(err);
417
+ } else {
418
+ this._port = port;
419
+ this._server.start();
420
+ resolve(port);
421
+ }
422
+ });
423
+ });
424
+ }
425
+
426
+ get server() {
427
+ return this._server;
428
+ }
429
+
430
+ get port() {
431
+ return this._port;
432
+ }
433
433
  }
434
434
 
435
435
  function createServer(
436
- grpc,
437
- serviceDescriptors,
438
- routes,
439
- cryptoCache,
440
- requireClientCert,
436
+ grpc,
437
+ serviceDescriptors,
438
+ routes,
439
+ cryptoCache,
440
+ requireClientCert,
441
441
  ) {
442
- let tlsOptions;
443
- if (cryptoCache) {
444
- tlsOptions = createSecureContextOptions(cryptoCache);
445
- }
446
- const serverArgs = {
447
- grpc,
448
- serviceDescriptors,
449
- routes,
450
- tlsOptions,
451
- requireClientCert,
452
- };
453
- return new GrpcServer(serverArgs);
442
+ let tlsOptions;
443
+ if (cryptoCache) {
444
+ tlsOptions = createSecureContextOptions(cryptoCache);
445
+ }
446
+ const serverArgs = {
447
+ grpc,
448
+ serviceDescriptors,
449
+ routes,
450
+ tlsOptions,
451
+ requireClientCert,
452
+ };
453
+ return new GrpcServer(serverArgs);
454
454
  }
455
455
 
456
456
  var grpcUtils = /*#__PURE__*/Object.freeze({
457
- __proto__: null,
458
- createClient: createClient,
459
- createServer: createServer
457
+ __proto__: null,
458
+ createClient: createClient,
459
+ createServer: createServer
460
460
  });
461
461
 
462
462
  /* eslint-disable no-underscore-dangle */
@@ -467,227 +467,227 @@ const aesAlgorithm = "aes-256-cbc";
467
467
  const rs256Algorithm = "RS256";
468
468
 
469
469
  class VoltCredential {
470
- constructor(config, configPath) {
471
- this._config = config;
472
- this._voltConfig = config.volt;
473
- this._configPath = configPath;
474
- this._persistCache = !!configPath;
475
-
476
- if (this._config.credential) {
477
- this._cryptoCache = this._config.credential;
478
- } else {
479
- this._cryptoCache = {};
480
- }
481
-
482
- // Determine if we're dealing with an encrypted key.
483
- if (
484
- this._cryptoCache.key?.startsWith(constants.privateEncryptedKeyPrefix)
485
- ) {
486
- if (!config.p) {
487
- throw new Error("encrypted key but no passphrase given");
488
- }
489
-
490
- // Need to unencrypt the private key.
491
- const decrypted = pki$1.decryptRsaPrivateKey(
492
- this._cryptoCache.key,
493
- config.p,
494
- );
495
- if (!decrypted) {
496
- throw new Error("failed to decrypt key - check passphrase");
497
- }
498
- this._cryptoCache.key = pki$1.privateKeyToPem(decrypted);
499
- }
500
-
501
- if (this._voltConfig.ca_pem) {
502
- if (!this._cryptoCache.ca) {
503
- // Copy the volt signing CA to the crypto cache.
504
- this._cryptoCache.ca = this._voltConfig.ca_pem;
505
- }
506
-
507
- // Pre-cache the target Volt public key by extracting it from the signing certificate.
508
- const voltCert = forge__default["default"].pki.certificateFromPem(this._voltConfig.ca_pem);
509
-
510
- // Extract Volt public key (used for encrypting Relay payloads).
511
- this._voltPublicKey = forge__default["default"].pki.publicKeyToPem(voltCert.publicKey);
512
- this._voltFingerprint = createPublicKeyFingerprint(
513
- this._voltPublicKey,
514
- );
515
- }
516
- }
517
-
518
- get config() {
519
- return this._config;
520
- }
521
-
522
- get cache() {
523
- return this._cryptoCache;
524
- }
525
-
526
- get voltFingerprint() {
527
- return this._voltFingerprint;
528
- }
529
-
530
- getKey() {
531
- const privateKey = pki$1.privateKeyFromPem(this._cryptoCache.key);
532
- const publicKey = pki$1.rsa.setPublicKey(privateKey.n, privateKey.e);
533
- return { privateKey, publicKey };
534
- }
535
-
536
- /**
537
- * Retrieves and re-hydrates the pub/priv key data from the crypto cache, or creates one if not found.
538
- */
539
- createKey() {
540
- if (this._cryptoCache.key) {
541
- // Already have key data in the cache in pem format => create fully-formed pki instances.
542
- log$4("createKey - key already exists");
543
- return Promise.resolve(this.getKey());
544
- } else {
545
- log$4("creating key");
546
- return new Promise((resolve, reject) => {
547
- pki$1.rsa.generateKeyPair({ bits: 2048, workers: -1 }, (err, keypair) => {
548
- if (err) {
549
- log$4("failure creating key [%s]", err.message);
550
- reject(err);
551
- } else {
552
- log$4("successfully created key");
553
- const pem = pki$1.privateKeyToPem(keypair.privateKey);
554
- this._cryptoCache.key = pem;
555
- resolve(keypair);
556
- }
557
- });
558
- });
559
- }
560
- }
561
-
562
- /**
563
- * Create cryptocache from scratch.
564
- */
565
- initialise() {
566
- return this.createKey()
567
- .then((/* keypair */) => {
568
- return this.saveCache();
569
- })
570
- .then(() => {
571
- return this._cryptoCache;
572
- })
573
- .catch((err) => {
574
- log$4("failure initialising crypto [%s]", err.message);
575
- return Promise.reject(err);
576
- });
577
- }
578
-
579
- get isBound() {
580
- return !!this._cryptoCache.cert;
581
- }
582
-
583
- /**
584
- * Signs the identity resource id using the private key.
585
- * @param {*} audience the token audience (usually the target volt)
586
- * @param {*} tunnelling flag indicating if the token is required for a tunnelled connection
587
- * @param {*} ttl time to live in seconds (default to 1 minute)
588
- */
589
- getIdentityToken(audience, tunnelling = false, ttl = 60) {
590
- if (!this._cryptoCache.key) {
591
- throw new Error("crypto cache invalid");
592
- }
593
-
594
- const keyPair = this.getKey();
595
- const publicKeyPem = pki$1.publicKeyToPem(keyPair.publicKey);
596
- const base58Key = createPublicKeyFingerprint(publicKeyPem);
597
-
598
- log$4("base58 key is %s", base58Key);
599
-
600
- // Allow TTL either side of the current time.
601
- // @todo - fix with server time sync on volt connection.
602
- const payload = {
603
- aud: audience,
604
- iat: Math.floor(Date.now() / 1000) - ttl,
605
- exp: Math.floor(Date.now() / 1000) + ttl,
606
- };
607
-
608
- let sharedKey;
609
- if (tunnelling) {
610
- // Initialise the Relay encryption key.
611
- sharedKey = VoltCredential.aesCreateKey();
612
-
613
- // Encrypt the key details using the target Volt public key and include this in the JWT payload.
614
- payload.sk = VoltCredential.rsaEncrypt(
615
- this._voltPublicKey,
616
- sharedKey.key,
617
- );
618
- payload.iv = VoltCredential.rsaEncrypt(this._voltPublicKey, sharedKey.iv);
619
- }
620
-
621
- // Sign synchronously.
622
- const token = jwt__default["default"].sign(payload, this._cryptoCache.key, {
623
- algorithm: rs256Algorithm,
624
- keyid: base58Key,
625
- });
626
-
627
- // Return the token along with any encryption key details.
628
- return {
629
- token,
630
- sharedKey,
631
- };
632
- }
633
-
634
- /**
635
- * Creates the metadata required for a grpc call, including the Volt authentication token
636
- * and optionally tunnel encryption parameters.
637
- * @param {*} grpc
638
- * @param {*} audience
639
- * @param {*} tunnelling
640
- * @param {*} ttl
641
- * @returns
642
- */
643
- getIdentityMetadata(grpc, audience, tunnelling = false, ttl = 60) {
644
- const identityToken = this.getIdentityToken(
645
- audience || this._voltConfig.id,
646
- tunnelling,
647
- ttl,
648
- );
649
- const metadata = new grpc.Metadata();
650
- metadata.add(constants.authTokenName, identityToken.token);
651
- return { ...identityToken, metadata };
652
- }
653
-
654
- saveCache() {
655
- if (this._persistCache) {
656
- const clone = { ...this._config, credential: { ...this._cryptoCache } };
657
- if (this._config.p) {
658
- // A passphrase option exists => encrypt the key before writing the cache file.
659
- const privateKey = pki$1.privateKeyFromPem(this._cryptoCache.key);
660
- clone.credential.key = pki$1.encryptRsaPrivateKey(
661
- privateKey,
662
- this._config.p,
663
- );
664
- }
665
- fs__default["default"].writeFileSync(this._configPath, JSON.stringify(clone, null, 2));
666
- }
667
- }
668
-
669
- static aesCreateKey() {
670
- // Generate random key for the AES-256 algorithm (32 bytes = 256 bits).
671
- const key = crypto__default["default"].randomBytes(32);
672
- const iv = crypto__default["default"].randomBytes(16);
673
- return { key, iv };
674
- }
675
-
676
- static aesEncrypt(buffer, key, iv) {
677
- const cipher = crypto__default["default"].createCipheriv(aesAlgorithm, key, iv);
678
- const crypted = Buffer.concat([cipher.update(buffer), cipher.final()]);
679
- return crypted;
680
- }
681
-
682
- static aesDecrypt(buffer, key, iv) {
683
- const cipher = crypto__default["default"].createDecipheriv(aesAlgorithm, key, iv);
684
- const decrypted = Buffer.concat([cipher.update(buffer), cipher.final()]);
685
- return decrypted;
686
- }
687
-
688
- static rsaEncrypt(key, buffer) {
689
- return crypto__default["default"].publicEncrypt(key, buffer).toString("base64");
690
- }
470
+ constructor(config, configPath) {
471
+ this._config = config;
472
+ this._voltConfig = config.volt;
473
+ this._configPath = configPath;
474
+ this._persistCache = !!configPath;
475
+
476
+ if (this._config.credential) {
477
+ this._cryptoCache = this._config.credential;
478
+ } else {
479
+ this._cryptoCache = {};
480
+ }
481
+
482
+ // Determine if we're dealing with an encrypted key.
483
+ if (
484
+ this._cryptoCache.key?.startsWith(constants.privateEncryptedKeyPrefix)
485
+ ) {
486
+ if (!config.p) {
487
+ throw new Error("encrypted key but no passphrase given");
488
+ }
489
+
490
+ // Need to unencrypt the private key.
491
+ const decrypted = pki$1.decryptRsaPrivateKey(
492
+ this._cryptoCache.key,
493
+ config.p,
494
+ );
495
+ if (!decrypted) {
496
+ throw new Error("failed to decrypt key - check passphrase");
497
+ }
498
+ this._cryptoCache.key = pki$1.privateKeyToPem(decrypted);
499
+ }
500
+
501
+ if (this._voltConfig.ca_pem) {
502
+ if (!this._cryptoCache.ca) {
503
+ // Copy the volt signing CA to the crypto cache.
504
+ this._cryptoCache.ca = this._voltConfig.ca_pem;
505
+ }
506
+
507
+ // Pre-cache the target Volt public key by extracting it from the signing certificate.
508
+ const voltCert = forge__default["default"].pki.certificateFromPem(this._voltConfig.ca_pem);
509
+
510
+ // Extract Volt public key (used for encrypting Relay payloads).
511
+ this._voltPublicKey = forge__default["default"].pki.publicKeyToPem(voltCert.publicKey);
512
+ this._voltFingerprint = createPublicKeyFingerprint(
513
+ this._voltPublicKey,
514
+ );
515
+ }
516
+ }
517
+
518
+ get config() {
519
+ return this._config;
520
+ }
521
+
522
+ get cache() {
523
+ return this._cryptoCache;
524
+ }
525
+
526
+ get voltFingerprint() {
527
+ return this._voltFingerprint;
528
+ }
529
+
530
+ getKey() {
531
+ const privateKey = pki$1.privateKeyFromPem(this._cryptoCache.key);
532
+ const publicKey = pki$1.rsa.setPublicKey(privateKey.n, privateKey.e);
533
+ return { privateKey, publicKey };
534
+ }
535
+
536
+ /**
537
+ * Retrieves and re-hydrates the pub/priv key data from the crypto cache, or creates one if not found.
538
+ */
539
+ createKey() {
540
+ if (this._cryptoCache.key) {
541
+ // Already have key data in the cache in pem format => create fully-formed pki instances.
542
+ log$4("createKey - key already exists");
543
+ return Promise.resolve(this.getKey());
544
+ } else {
545
+ log$4("creating key");
546
+ return new Promise((resolve, reject) => {
547
+ pki$1.rsa.generateKeyPair({ bits: 2048, workers: -1 }, (err, keypair) => {
548
+ if (err) {
549
+ log$4("failure creating key [%s]", err.message);
550
+ reject(err);
551
+ } else {
552
+ log$4("successfully created key");
553
+ const pem = pki$1.privateKeyToPem(keypair.privateKey);
554
+ this._cryptoCache.key = pem;
555
+ resolve(keypair);
556
+ }
557
+ });
558
+ });
559
+ }
560
+ }
561
+
562
+ /**
563
+ * Create cryptocache from scratch.
564
+ */
565
+ initialise() {
566
+ return this.createKey()
567
+ .then((/* keypair */) => {
568
+ return this.saveCache();
569
+ })
570
+ .then(() => {
571
+ return this._cryptoCache;
572
+ })
573
+ .catch((err) => {
574
+ log$4("failure initialising crypto [%s]", err.message);
575
+ return Promise.reject(err);
576
+ });
577
+ }
578
+
579
+ get isBound() {
580
+ return !!this._cryptoCache.cert;
581
+ }
582
+
583
+ /**
584
+ * Signs the identity resource id using the private key.
585
+ * @param {*} audience the token audience (usually the target volt)
586
+ * @param {*} tunnelling flag indicating if the token is required for a tunnelled connection
587
+ * @param {*} ttl time to live in seconds (default to 1 minute)
588
+ */
589
+ getIdentityToken(audience, tunnelling = false, ttl = 60) {
590
+ if (!this._cryptoCache.key) {
591
+ throw new Error("crypto cache invalid");
592
+ }
593
+
594
+ const keyPair = this.getKey();
595
+ const publicKeyPem = pki$1.publicKeyToPem(keyPair.publicKey);
596
+ const base58Key = createPublicKeyFingerprint(publicKeyPem);
597
+
598
+ log$4("base58 key is %s", base58Key);
599
+
600
+ // Allow TTL either side of the current time.
601
+ // @todo - fix with server time sync on volt connection.
602
+ const payload = {
603
+ aud: audience,
604
+ iat: Math.floor(Date.now() / 1000) - ttl,
605
+ exp: Math.floor(Date.now() / 1000) + ttl,
606
+ };
607
+
608
+ let sharedKey;
609
+ if (tunnelling) {
610
+ // Initialise the Relay encryption key.
611
+ sharedKey = VoltCredential.aesCreateKey();
612
+
613
+ // Encrypt the key details using the target Volt public key and include this in the JWT payload.
614
+ payload.sk = VoltCredential.rsaEncrypt(
615
+ this._voltPublicKey,
616
+ sharedKey.key,
617
+ );
618
+ payload.iv = VoltCredential.rsaEncrypt(this._voltPublicKey, sharedKey.iv);
619
+ }
620
+
621
+ // Sign synchronously.
622
+ const token = jwt__default["default"].sign(payload, this._cryptoCache.key, {
623
+ algorithm: rs256Algorithm,
624
+ keyid: base58Key,
625
+ });
626
+
627
+ // Return the token along with any encryption key details.
628
+ return {
629
+ token,
630
+ sharedKey,
631
+ };
632
+ }
633
+
634
+ /**
635
+ * Creates the metadata required for a grpc call, including the Volt authentication token
636
+ * and optionally tunnel encryption parameters.
637
+ * @param {*} grpc
638
+ * @param {*} audience
639
+ * @param {*} tunnelling
640
+ * @param {*} ttl
641
+ * @returns
642
+ */
643
+ getIdentityMetadata(grpc, audience, tunnelling = false, ttl = 60) {
644
+ const identityToken = this.getIdentityToken(
645
+ audience || this._voltConfig.id,
646
+ tunnelling,
647
+ ttl,
648
+ );
649
+ const metadata = new grpc.Metadata();
650
+ metadata.add(constants.authTokenName, identityToken.token);
651
+ return { ...identityToken, metadata };
652
+ }
653
+
654
+ saveCache() {
655
+ if (this._persistCache) {
656
+ const clone = { ...this._config, credential: { ...this._cryptoCache } };
657
+ if (this._config.p) {
658
+ // A passphrase option exists => encrypt the key before writing the cache file.
659
+ const privateKey = pki$1.privateKeyFromPem(this._cryptoCache.key);
660
+ clone.credential.key = pki$1.encryptRsaPrivateKey(
661
+ privateKey,
662
+ this._config.p,
663
+ );
664
+ }
665
+ fs__default["default"].writeFileSync(this._configPath, JSON.stringify(clone, null, 2));
666
+ }
667
+ }
668
+
669
+ static aesCreateKey() {
670
+ // Generate random key for the AES-256 algorithm (32 bytes = 256 bits).
671
+ const key = crypto__default["default"].randomBytes(32);
672
+ const iv = crypto__default["default"].randomBytes(16);
673
+ return { key, iv };
674
+ }
675
+
676
+ static aesEncrypt(buffer, key, iv) {
677
+ const cipher = crypto__default["default"].createCipheriv(aesAlgorithm, key, iv);
678
+ const crypted = Buffer.concat([cipher.update(buffer), cipher.final()]);
679
+ return crypted;
680
+ }
681
+
682
+ static aesDecrypt(buffer, key, iv) {
683
+ const cipher = crypto__default["default"].createDecipheriv(aesAlgorithm, key, iv);
684
+ const decrypted = Buffer.concat([cipher.update(buffer), cipher.final()]);
685
+ return decrypted;
686
+ }
687
+
688
+ static rsaEncrypt(key, buffer) {
689
+ return crypto__default["default"].publicEncrypt(key, buffer).toString("base64");
690
+ }
691
691
  }
692
692
 
693
693
  const { get: getProp, snakeCase } = lodash__default["default"];
@@ -696,93 +696,93 @@ const __dirname$1 = path.dirname(url.fileURLToPath((typeof document === 'undefin
696
696
  const defaultProtoPath = path.join(__dirname$1, "../protobuf");
697
697
 
698
698
  const defaultLoaderOptions = {
699
- keepCase: true,
700
- longs: String,
701
- enums: String,
702
- defaults: true,
703
- oneofs: true,
704
- includeDirs: [defaultProtoPath],
699
+ keepCase: true,
700
+ longs: String,
701
+ enums: String,
702
+ defaults: true,
703
+ oneofs: true,
704
+ includeDirs: [defaultProtoPath],
705
705
  };
706
706
 
707
707
  const getProtoDescriptors = (grpc, protoPath, opts) => {
708
- const definition = protoLoader__default["default"].loadSync(
709
- protoPath,
710
- opts || defaultLoaderOptions,
711
- );
712
- const descriptors = grpc.loadPackageDefinition(definition);
713
- return descriptors;
708
+ const definition = protoLoader__default["default"].loadSync(
709
+ protoPath,
710
+ opts || defaultLoaderOptions,
711
+ );
712
+ const descriptors = grpc.loadPackageDefinition(definition);
713
+ return descriptors;
714
714
  };
715
715
 
716
716
  const getProtoService = (descriptors, servicePath) => {
717
- const serviceDef = getProp(descriptors, servicePath);
718
- if (serviceDef?.service) {
719
- return serviceDef.service;
720
- } else {
721
- return null;
722
- }
717
+ const serviceDef = getProp(descriptors, servicePath);
718
+ if (serviceDef?.service) {
719
+ return serviceDef.service;
720
+ } else {
721
+ return null;
722
+ }
723
723
  };
724
724
 
725
725
  function getServiceDescriptorsFromPath(
726
- grpc,
727
- protoPath,
728
- servicePackageName,
729
- opts,
726
+ grpc,
727
+ protoPath,
728
+ servicePackageName,
729
+ opts,
730
730
  ) {
731
- let serviceDescriptors;
732
- const descriptors = getProtoDescriptors(grpc, protoPath, opts);
733
- if (descriptors) {
734
- serviceDescriptors = getProtoService(descriptors, servicePackageName);
735
- }
731
+ let serviceDescriptors;
732
+ const descriptors = getProtoDescriptors(grpc, protoPath, opts);
733
+ if (descriptors) {
734
+ serviceDescriptors = getProtoService(descriptors, servicePackageName);
735
+ }
736
736
 
737
- return serviceDescriptors;
737
+ return serviceDescriptors;
738
738
  }
739
739
 
740
740
  function getServiceDescriptors(grpc, servicePackageName, opts) {
741
- // Extract the package components of the service proto package. The package name should be of the
742
- // fully qualified proto path, e.g. tdx.volt_api.webcam.v1.WebcamControlAPI.
743
- const protoServiceComponents = servicePackageName.split(".");
744
-
745
- // The service name is the last component of the path.
746
- const protoServiceName = protoServiceComponents.pop();
747
-
748
- // The actual file name should match the snake case of the service name.
749
- const protoFileName = `${snakeCase(protoServiceName).toLowerCase()}.proto`;
750
-
751
- // The path to the proto file should match each component of the package name.
752
- const protoPath = path.join(
753
- defaultProtoPath,
754
- ...protoServiceComponents,
755
- protoFileName,
756
- );
757
-
758
- let serviceDescriptors;
759
- const descriptors = getProtoDescriptors(grpc, protoPath, opts);
760
- if (descriptors) {
761
- serviceDescriptors = getProtoService(descriptors, servicePackageName);
762
- }
763
-
764
- return serviceDescriptors;
741
+ // Extract the package components of the service proto package. The package name should be of the
742
+ // fully qualified proto path, e.g. tdx.volt_api.webcam.v1.WebcamControlAPI.
743
+ const protoServiceComponents = servicePackageName.split(".");
744
+
745
+ // The service name is the last component of the path.
746
+ const protoServiceName = protoServiceComponents.pop();
747
+
748
+ // The actual file name should match the snake case of the service name.
749
+ const protoFileName = `${snakeCase(protoServiceName).toLowerCase()}.proto`;
750
+
751
+ // The path to the proto file should match each component of the package name.
752
+ const protoPath = path.join(
753
+ defaultProtoPath,
754
+ ...protoServiceComponents,
755
+ protoFileName,
756
+ );
757
+
758
+ let serviceDescriptors;
759
+ const descriptors = getProtoDescriptors(grpc, protoPath, opts);
760
+ if (descriptors) {
761
+ serviceDescriptors = getProtoService(descriptors, servicePackageName);
762
+ }
763
+
764
+ return serviceDescriptors;
765
765
  }
766
766
 
767
767
  function getProtoDescriptorMethods(protoDescriptor) {
768
- const methods = [];
769
- Object.keys(protoDescriptor).forEach((methodName) => {
770
- const method = protoDescriptor[methodName];
771
- methods.push({
772
- path: method.path,
773
- client_streaming: method.requestStream,
774
- server_streaming: method.responseStream,
775
- });
776
- });
777
- return methods;
768
+ const methods = [];
769
+ Object.keys(protoDescriptor).forEach((methodName) => {
770
+ const method = protoDescriptor[methodName];
771
+ methods.push({
772
+ path: method.path,
773
+ client_streaming: method.requestStream,
774
+ server_streaming: method.responseStream,
775
+ });
776
+ });
777
+ return methods;
778
778
  }
779
779
 
780
780
  var protoUtils = /*#__PURE__*/Object.freeze({
781
- __proto__: null,
782
- getServiceDescriptors: getServiceDescriptors,
783
- getProtoDescriptors: getProtoDescriptors,
784
- getProtoDescriptorMethods: getProtoDescriptorMethods,
785
- getServiceDescriptorsFromPath: getServiceDescriptorsFromPath
781
+ __proto__: null,
782
+ getServiceDescriptors: getServiceDescriptors,
783
+ getProtoDescriptors: getProtoDescriptors,
784
+ getProtoDescriptorMethods: getProtoDescriptorMethods,
785
+ getServiceDescriptorsFromPath: getServiceDescriptorsFromPath
786
786
  });
787
787
 
788
788
  /* eslint-disable no-underscore-dangle */
@@ -790,282 +790,282 @@ var protoUtils = /*#__PURE__*/Object.freeze({
790
790
  const log$3 = debug__default["default"]("volt-client-grpc:grpc-call");
791
791
 
792
792
  function _prepareInvokeRequest(
793
- request,
794
- method,
795
- methodType = "METHOD_TYPE_UNARY",
793
+ request,
794
+ method,
795
+ methodType = "METHOD_TYPE_UNARY",
796
796
  ) {
797
- const meta = this._voltClient.credential.getIdentityMetadata(
798
- this._voltClient.grpc,
799
- this._voltClient.voltConfig.id,
800
- this._voltClient.isRemote,
801
- );
802
- let invokeRequest;
803
- let callMethod = method;
804
-
805
- if (this._voltClient.isVoltRelay) {
806
- let requestPayload;
807
- if (request) {
808
- requestPayload = this._grpcClient[method].requestSerialize(request);
809
- } else if (request !== null) {
810
- log$3("****************LOOKOUT****************** - empty request");
811
- } else {
812
- log$3("explicit empty request payload");
813
- }
814
-
815
- const tunnelMethod = this._grpcClient.Tunnel;
816
-
817
- const tunnelResp = {
818
- method_invoke: {
819
- method_name: this._grpcClient[method].path,
820
- method_type: methodType,
821
- token: meta.token,
822
- request: requestPayload,
823
- },
824
- };
825
-
826
- const invokePayload = VoltCredential.aesEncrypt(
827
- tunnelMethod.responseSerialize(tunnelResp),
828
- meta.sharedKey.key,
829
- meta.sharedKey.iv,
830
- );
831
-
832
- log$3("target volt is %s", this._voltClient.credential.voltFingerprint);
833
-
834
- invokeRequest = {
835
- identity_fingerprint: this._voltClient.credential.voltFingerprint,
836
- payload: invokePayload,
837
- };
838
-
839
- callMethod = "Invoke";
840
- } else if (this._voltClient.isRemote && meta.sharedKey.key) {
841
- const requestPayload =
842
- this._grpcClient[method].requestSerializeOriginal(request);
843
- invokeRequest = VoltCredential.aesEncrypt(
844
- requestPayload,
845
- meta.sharedKey.key,
846
- meta.sharedKey.iv,
847
- );
848
- } else {
849
- invokeRequest = request;
850
- }
851
-
852
- return {
853
- meta,
854
- method: callMethod,
855
- methodName: method,
856
- request: invokeRequest,
857
- };
797
+ const meta = this._voltClient.credential.getIdentityMetadata(
798
+ this._voltClient.grpc,
799
+ this._voltClient.voltConfig.id,
800
+ this._voltClient.isRemote,
801
+ );
802
+ let invokeRequest;
803
+ let callMethod = method;
804
+
805
+ if (this._voltClient.isVoltRelay) {
806
+ let requestPayload;
807
+ if (request) {
808
+ requestPayload = this._grpcClient[method].requestSerialize(request);
809
+ } else if (request !== null) {
810
+ log$3("****************LOOKOUT****************** - empty request");
811
+ } else {
812
+ log$3("explicit empty request payload");
813
+ }
814
+
815
+ const tunnelMethod = this._grpcClient.Tunnel;
816
+
817
+ const tunnelResp = {
818
+ method_invoke: {
819
+ method_name: this._grpcClient[method].path,
820
+ method_type: methodType,
821
+ token: meta.token,
822
+ request: requestPayload,
823
+ },
824
+ };
825
+
826
+ const invokePayload = VoltCredential.aesEncrypt(
827
+ tunnelMethod.responseSerialize(tunnelResp),
828
+ meta.sharedKey.key,
829
+ meta.sharedKey.iv,
830
+ );
831
+
832
+ log$3("target volt is %s", this._voltClient.credential.voltFingerprint);
833
+
834
+ invokeRequest = {
835
+ identity_fingerprint: this._voltClient.credential.voltFingerprint,
836
+ payload: invokePayload,
837
+ };
838
+
839
+ callMethod = "Invoke";
840
+ } else if (this._voltClient.isRemote && meta.sharedKey.key) {
841
+ const requestPayload =
842
+ this._grpcClient[method].requestSerializeOriginal(request);
843
+ invokeRequest = VoltCredential.aesEncrypt(
844
+ requestPayload,
845
+ meta.sharedKey.key,
846
+ meta.sharedKey.iv,
847
+ );
848
+ } else {
849
+ invokeRequest = request;
850
+ }
851
+
852
+ return {
853
+ meta,
854
+ method: callMethod,
855
+ methodName: method,
856
+ request: invokeRequest,
857
+ };
858
858
  }
859
859
 
860
860
  function _preparePayloadRequest(request, invokeInfo) {
861
- let payloadRequest;
862
-
863
- if (this._voltClient.isVoltRelay) {
864
- const requestPayload =
865
- this._grpcClient[invokeInfo.methodName].requestSerialize(request);
866
- const tunnelMethod = this._grpcClient.Tunnel;
867
-
868
- const tunnelResp = {
869
- method_payload: {
870
- payload: requestPayload,
871
- },
872
- };
873
-
874
- const invokePayload = VoltCredential.aesEncrypt(
875
- tunnelMethod.responseSerialize(tunnelResp),
876
- invokeInfo.meta.sharedKey.key,
877
- invokeInfo.meta.sharedKey.iv,
878
- );
879
-
880
- payloadRequest = {
881
- payload: invokePayload,
882
- };
883
- } else if (this._voltClient.isRemote && invokeInfo.meta.sharedKey.key) {
884
- const requestPayload =
885
- this._grpcClient[invokeInfo.methodName].requestSerializeOriginal(request);
886
- payloadRequest = VoltCredential.aesEncrypt(
887
- requestPayload,
888
- invokeInfo.meta.sharedKey.key,
889
- invokeInfo.meta.sharedKey.iv,
890
- );
891
- } else {
892
- payloadRequest = request;
893
- }
894
-
895
- return payloadRequest;
861
+ let payloadRequest;
862
+
863
+ if (this._voltClient.isVoltRelay) {
864
+ const requestPayload =
865
+ this._grpcClient[invokeInfo.methodName].requestSerialize(request);
866
+ const tunnelMethod = this._grpcClient.Tunnel;
867
+
868
+ const tunnelResp = {
869
+ method_payload: {
870
+ payload: requestPayload,
871
+ },
872
+ };
873
+
874
+ const invokePayload = VoltCredential.aesEncrypt(
875
+ tunnelMethod.responseSerialize(tunnelResp),
876
+ invokeInfo.meta.sharedKey.key,
877
+ invokeInfo.meta.sharedKey.iv,
878
+ );
879
+
880
+ payloadRequest = {
881
+ payload: invokePayload,
882
+ };
883
+ } else if (this._voltClient.isRemote && invokeInfo.meta.sharedKey.key) {
884
+ const requestPayload =
885
+ this._grpcClient[invokeInfo.methodName].requestSerializeOriginal(request);
886
+ payloadRequest = VoltCredential.aesEncrypt(
887
+ requestPayload,
888
+ invokeInfo.meta.sharedKey.key,
889
+ invokeInfo.meta.sharedKey.iv,
890
+ );
891
+ } else {
892
+ payloadRequest = request;
893
+ }
894
+
895
+ return payloadRequest;
896
896
  }
897
897
 
898
898
  function _parseResponse(method, meta, response) {
899
- let invokeResponse = {};
900
-
901
- if (this._voltClient.isVoltRelay) {
902
- if (response.payload) {
903
- const decryptedPayload = VoltCredential.aesDecrypt(
904
- response.payload,
905
- meta.sharedKey.key,
906
- meta.sharedKey.iv,
907
- );
908
- const tunnelMethod = this._grpcClient.Tunnel;
909
- const responsePayload = tunnelMethod.requestDeserialize(decryptedPayload);
910
- if (responsePayload.payload === "method_payload") {
911
- invokeResponse.payload = this._grpcClient[method].responseDeserialize(
912
- responsePayload.method_payload.payload,
913
- );
914
- } else if (responsePayload.payload === "method_end") {
915
- invokeResponse = responsePayload.method_end;
916
- } else {
917
- log$3("unexpected tunnel payload type: %s", responsePayload.payload);
918
- }
919
- } else if (response.status?.message) {
920
- // There's been an error in the tunnel.
921
- log$3(
922
- "Tunnel status received: %s, code %d, description: %s",
923
- response.status.message,
924
- response.status.code,
925
- response.status.description || "n/a",
926
- );
927
- invokeResponse = {
928
- ended: true,
929
- error: `Error in tunnel: ${response.status.message}`,
930
- };
931
- } else {
932
- invokeResponse.methodId = response.invoke_id;
933
- log$3("started method %d for %s", invokeResponse.methodId, method);
934
- }
935
- } else if (this._voltClient.isRemote && meta.sharedKey.key) {
936
- const decryptedPayload = VoltCredential.aesDecrypt(
937
- response,
938
- meta.sharedKey.key,
939
- meta.sharedKey.iv,
940
- );
941
- invokeResponse = {
942
- payload:
943
- this._grpcClient[method].responseDeserializeOriginal(decryptedPayload),
944
- };
945
- } else {
946
- invokeResponse = { payload: response };
947
- }
948
-
949
- return invokeResponse;
899
+ let invokeResponse = {};
900
+
901
+ if (this._voltClient.isVoltRelay) {
902
+ if (response.payload) {
903
+ const decryptedPayload = VoltCredential.aesDecrypt(
904
+ response.payload,
905
+ meta.sharedKey.key,
906
+ meta.sharedKey.iv,
907
+ );
908
+ const tunnelMethod = this._grpcClient.Tunnel;
909
+ const responsePayload = tunnelMethod.requestDeserialize(decryptedPayload);
910
+ if (responsePayload.payload === "method_payload") {
911
+ invokeResponse.payload = this._grpcClient[method].responseDeserialize(
912
+ responsePayload.method_payload.payload,
913
+ );
914
+ } else if (responsePayload.payload === "method_end") {
915
+ invokeResponse = responsePayload.method_end;
916
+ } else {
917
+ log$3("unexpected tunnel payload type: %s", responsePayload.payload);
918
+ }
919
+ } else if (response.status?.message) {
920
+ // There's been an error in the tunnel.
921
+ log$3(
922
+ "Tunnel status received: %s, code %d, description: %s",
923
+ response.status.message,
924
+ response.status.code,
925
+ response.status.description || "n/a",
926
+ );
927
+ invokeResponse = {
928
+ ended: true,
929
+ error: `Error in tunnel: ${response.status.message}`,
930
+ };
931
+ } else {
932
+ invokeResponse.methodId = response.invoke_id;
933
+ log$3("started method %d for %s", invokeResponse.methodId, method);
934
+ }
935
+ } else if (this._voltClient.isRemote && meta.sharedKey.key) {
936
+ const decryptedPayload = VoltCredential.aesDecrypt(
937
+ response,
938
+ meta.sharedKey.key,
939
+ meta.sharedKey.iv,
940
+ );
941
+ invokeResponse = {
942
+ payload:
943
+ this._grpcClient[method].responseDeserializeOriginal(decryptedPayload),
944
+ };
945
+ } else {
946
+ invokeResponse = { payload: response };
947
+ }
948
+
949
+ return invokeResponse;
950
950
  }
951
951
 
952
952
  class GRPCCall extends EventEmitter__default["default"] {
953
- constructor(voltClient, methodName, methodType) {
954
- super();
955
- this._methodName = methodName;
956
- this._methodType = methodType;
957
- this._call = null;
958
- this._voltClient = voltClient;
959
- }
960
-
961
- start(grpcClient, request) {
962
- this._grpcClient = grpcClient;
963
-
964
- this._initialRequest = _prepareInvokeRequest.call(
965
- this,
966
- request,
967
- this._methodName,
968
- this._methodType,
969
- );
970
-
971
- if (!this._grpcClient[this._initialRequest.method]) {
972
- return reject(
973
- new Error(`method not found: '${this._initialRequest.method}'`),
974
- );
975
- }
976
-
977
- this._call = this._grpcClient[this._initialRequest.method](
978
- this._initialRequest.meta.metadata,
979
- );
980
-
981
- this._call.on("data", (streamResponse) => {
982
- try {
983
- const parsedResponse = _parseResponse.call(
984
- this,
985
- this._methodName,
986
- this._initialRequest.meta,
987
- streamResponse,
988
- );
989
- if (parsedResponse.payload) {
990
- this.emit("data", parsedResponse.payload);
991
- } else if (parsedResponse.ended) {
992
- if (parsedResponse.error) {
993
- this.emit("error", new Error(parsedResponse.error));
994
- } else {
995
- // Not sure this is necessary - we should receive "end" from grpc soon...
996
- this.emit("end");
997
- }
998
- } else {
999
- log$3("method id is %s", parsedResponse.methodId);
1000
- }
1001
- } catch (err) {
1002
- log$3("failure processing connect response: %s", err.message);
1003
- this.emit("error", err);
1004
- }
1005
- });
1006
-
1007
- this._call.on("error", (err) => {
1008
- log$3("ERROR - intercepted stream error %s", err.message);
1009
- this.emit("error", err);
1010
- });
1011
-
1012
- this._call.on("finish", () => {
1013
- // The read side has ended (i.e. we called end()).
1014
- log$3("finished call %s", this._methodName);
1015
- this.emit("finish");
1016
- });
1017
-
1018
- this._call.on("end", () => {
1019
- // The remote peer ended the stream.
1020
- log$3(
1021
- "ended call %s, method id: %s",
1022
- this._methodName,
1023
- this._initialRequest.methodId || "n/a - not tunnelling",
1024
- );
1025
- this.emit("end");
1026
- });
1027
-
1028
- if (this._initialRequest.request) {
1029
- this._call.write(this._initialRequest.request);
1030
- }
1031
-
1032
- return this;
1033
- }
1034
-
1035
- end() {
1036
- if (this._call) {
1037
- return this._call.end();
1038
- } else {
1039
- throw new Error("call not initialised");
1040
- }
1041
- }
1042
-
1043
- get writable() {
1044
- return this._call?.writable;
1045
- }
1046
-
1047
- write(request) {
1048
- if (!this._call) {
1049
- log$3("ERROR - call not initialised");
1050
- throw new Error("call not initialised");
1051
- } else if (this._initialRequest) {
1052
- const payloadRequest = _preparePayloadRequest.call(
1053
- this,
1054
- request,
1055
- this._initialRequest,
1056
- );
1057
- return this._call.write(payloadRequest);
1058
- } else {
1059
- this._initialRequest = _prepareInvokeRequest.call(
1060
- this,
1061
- request,
1062
- this._methodName,
1063
- this._methodType,
1064
- );
1065
-
1066
- return this._call.write(this._initialRequest.request);
1067
- }
1068
- }
953
+ constructor(voltClient, methodName, methodType) {
954
+ super();
955
+ this._methodName = methodName;
956
+ this._methodType = methodType;
957
+ this._call = null;
958
+ this._voltClient = voltClient;
959
+ }
960
+
961
+ start(grpcClient, request) {
962
+ this._grpcClient = grpcClient;
963
+
964
+ this._initialRequest = _prepareInvokeRequest.call(
965
+ this,
966
+ request,
967
+ this._methodName,
968
+ this._methodType,
969
+ );
970
+
971
+ if (!this._grpcClient[this._initialRequest.method]) {
972
+ return reject(
973
+ new Error(`method not found: '${this._initialRequest.method}'`),
974
+ );
975
+ }
976
+
977
+ this._call = this._grpcClient[this._initialRequest.method](
978
+ this._initialRequest.meta.metadata,
979
+ );
980
+
981
+ this._call.on("data", (streamResponse) => {
982
+ try {
983
+ const parsedResponse = _parseResponse.call(
984
+ this,
985
+ this._methodName,
986
+ this._initialRequest.meta,
987
+ streamResponse,
988
+ );
989
+ if (parsedResponse.payload) {
990
+ this.emit("data", parsedResponse.payload);
991
+ } else if (parsedResponse.ended) {
992
+ if (parsedResponse.error) {
993
+ this.emit("error", new Error(parsedResponse.error));
994
+ } else {
995
+ // Not sure this is necessary - we should receive "end" from grpc soon...
996
+ this.emit("end");
997
+ }
998
+ } else {
999
+ log$3("method id is %s", parsedResponse.methodId);
1000
+ }
1001
+ } catch (err) {
1002
+ log$3("failure processing connect response: %s", err.message);
1003
+ this.emit("error", err);
1004
+ }
1005
+ });
1006
+
1007
+ this._call.on("error", (err) => {
1008
+ log$3("ERROR - intercepted stream error %s", err.message);
1009
+ this.emit("error", err);
1010
+ });
1011
+
1012
+ this._call.on("finish", () => {
1013
+ // The read side has ended (i.e. we called end()).
1014
+ log$3("finished call %s", this._methodName);
1015
+ this.emit("finish");
1016
+ });
1017
+
1018
+ this._call.on("end", () => {
1019
+ // The remote peer ended the stream.
1020
+ log$3(
1021
+ "ended call %s, method id: %s",
1022
+ this._methodName,
1023
+ this._initialRequest.methodId || "n/a - not tunnelling",
1024
+ );
1025
+ this.emit("end");
1026
+ });
1027
+
1028
+ if (this._initialRequest.request) {
1029
+ this._call.write(this._initialRequest.request);
1030
+ }
1031
+
1032
+ return this;
1033
+ }
1034
+
1035
+ end() {
1036
+ if (this._call) {
1037
+ return this._call.end();
1038
+ } else {
1039
+ throw new Error("call not initialised");
1040
+ }
1041
+ }
1042
+
1043
+ get writable() {
1044
+ return this._call?.writable;
1045
+ }
1046
+
1047
+ write(request) {
1048
+ if (!this._call) {
1049
+ log$3("ERROR - call not initialised");
1050
+ throw new Error("call not initialised");
1051
+ } else if (this._initialRequest) {
1052
+ const payloadRequest = _preparePayloadRequest.call(
1053
+ this,
1054
+ request,
1055
+ this._initialRequest,
1056
+ );
1057
+ return this._call.write(payloadRequest);
1058
+ } else {
1059
+ this._initialRequest = _prepareInvokeRequest.call(
1060
+ this,
1061
+ request,
1062
+ this._methodName,
1063
+ this._methodType,
1064
+ );
1065
+
1066
+ return this._call.write(this._initialRequest.request);
1067
+ }
1068
+ }
1069
1069
  }
1070
1070
 
1071
1071
  /* eslint-disable no-use-before-define */
@@ -1116,58 +1116,53 @@ function _doConnect() {
1116
1116
  const helloPayload = {
1117
1117
  hello: {},
1118
1118
  };
1119
-
1120
- this.call.start(grpcClient, helloPayload, (err, response) => {
1121
- if (err) {
1122
- log$2("error on connection stream [%s]", err.message);
1123
- _cleanUp.call(this);
1124
- this.emit("error", err);
1125
- } else {
1126
- switch (response.payload) {
1127
- case "ping": {
1128
- log$2("pinged");
1129
-
1130
- // Received ping response from server => clear timeout.
1131
- if (this._pingTimeoutTimer) {
1132
- clearTimeout(this._pingTimeoutTimer);
1133
- }
1134
-
1135
- // Schedule the next ping.
1136
- _startPingTimer.call(this);
1137
- break;
1138
- }
1139
- case "hello": {
1140
- if (!this._session) {
1141
- this._session = true;
1142
- this.emit("connected", this._session);
1143
- }
1144
- break;
1145
- }
1146
- case "evt": {
1147
- log$2("received event %s", response.evt.event_type);
1148
- this.emit("evt", response.evt);
1149
- break;
1150
- }
1151
- default:
1152
- log$2("unimplemented connect response payload: %s", response.payload);
1153
- break;
1154
- }
1155
- }
1156
- });
1157
-
1158
- this.call
1159
- .wait()
1160
- .then(() => {
1161
- if (this._session) {
1162
- this.emit("connected", false);
1163
- }
1164
- })
1165
- .catch((err) => {
1166
- log$2("unexpected error on connect call [%s]", err.message);
1167
- })
1168
- .finally(() => {
1169
- _cleanUp.call(this);
1170
- });
1119
+
1120
+ this.call.on("error", (err) => {
1121
+ log$2("error on connection stream [%s]", err.message);
1122
+ _cleanUp.call(this);
1123
+ this.emit("error", err);
1124
+ });
1125
+
1126
+ this.call.on("data", (response) => {
1127
+ switch (response.payload) {
1128
+ case "ping": {
1129
+ log$2("pinged");
1130
+
1131
+ // Received ping response from server => clear timeout.
1132
+ if (this._pingTimeoutTimer) {
1133
+ clearTimeout(this._pingTimeoutTimer);
1134
+ }
1135
+
1136
+ // Schedule the next ping.
1137
+ _startPingTimer.call(this);
1138
+ break;
1139
+ }
1140
+ case "hello": {
1141
+ if (!this._session) {
1142
+ this._session = true;
1143
+ this.emit("connected", this._session);
1144
+ }
1145
+ break;
1146
+ }
1147
+ case "evt": {
1148
+ log$2("received event %s", response.evt.event_type);
1149
+ this.emit("evt", response.evt);
1150
+ break;
1151
+ }
1152
+ default:
1153
+ log$2("unimplemented connect response payload: %s", response.payload);
1154
+ break;
1155
+ }
1156
+ });
1157
+
1158
+ this.call.on("end", () => {
1159
+ if (this._session) {
1160
+ this.emit("connected", false);
1161
+ }
1162
+ _cleanUp.call(this);
1163
+ });
1164
+
1165
+ this.call.start(grpcClient, helloPayload);
1171
1166
 
1172
1167
  _startPingTimer.call(this);
1173
1168
  }
@@ -1238,890 +1233,890 @@ const { filter, pick } = lodash__default["default"];
1238
1233
 
1239
1234
  const log$1 = debug__default["default"]("volt-client-grpc:volt-client-internal");
1240
1235
  const voltServices = [
1241
- constants.serviceType.voltAPI,
1242
- constants.serviceType.fileAPI,
1243
- constants.serviceType.sqliteDatabaseAPI,
1244
- constants.serviceType.sqliteServerAPI,
1245
- constants.serviceType.ssiAPI,
1246
- constants.serviceType.relayAPI,
1247
- constants.serviceType.wireAPI,
1236
+ constants.serviceType.voltAPI,
1237
+ constants.serviceType.fileAPI,
1238
+ constants.serviceType.sqliteDatabaseAPI,
1239
+ constants.serviceType.sqliteServerAPI,
1240
+ constants.serviceType.ssiAPI,
1241
+ constants.serviceType.relayAPI,
1242
+ constants.serviceType.wireAPI,
1248
1243
  ];
1249
1244
 
1250
1245
  function issueBind(bindRequest, ttl) {
1251
- // eslint-disable-next-line no-use-before-define
1252
- return unaryCall.call(this, "Bind", bindRequest).then((bindResponse) => {
1253
- log$1("got binding request response %j", bindResponse);
1254
- if (bindResponse.status?.code) {
1255
- log$1("error in bind response: %s", bindResponse.status.message);
1256
- return Promise.reject(new Error(bindResponse.status.message));
1257
- } else {
1258
- switch (bindResponse.decision) {
1259
- case "POLICY_DECISION_PERMIT": {
1260
- //
1261
- // The request status is permit => cache the bind response info.
1262
- //
1263
-
1264
- // This is the certificate assigned to us by the volt.
1265
- this._credential.cache.cert = bindResponse.cert;
1266
-
1267
- // This is the signing CA used by the volt (don't ov)
1268
- this._credential.cache.ca = bindResponse.chain;
1269
-
1270
- // Resource id is assigned by the volt.
1271
- this._credential.cache.client_id = bindResponse.identity_id;
1272
- this._credential.saveCache();
1273
- break;
1274
- }
1275
- case "POLICY_DECISION_DENY": {
1276
- log$1(">>>>>>>>>>>>>>> access request is DENIED <<<<<<<<<<<<<<<<<<<");
1277
- break;
1278
- }
1279
- case "POLICY_DECISION_PROMPT":
1280
- case "POLICY_DECISION_PENDING": {
1281
- log$1("access pending approval - waiting...");
1282
- break;
1283
- }
1284
- default:
1285
- log$1("ignoring unknown bind descision %s", bindResponse.status);
1286
- break;
1287
- }
1288
- }
1289
-
1290
- return bindResponse.decision;
1291
- });
1246
+ // eslint-disable-next-line no-use-before-define
1247
+ return unaryCall.call(this, "Bind", bindRequest).then((bindResponse) => {
1248
+ log$1("got binding request response %j", bindResponse);
1249
+ if (bindResponse.status?.code) {
1250
+ log$1("error in bind response: %s", bindResponse.status.message);
1251
+ return Promise.reject(new Error(bindResponse.status.message));
1252
+ } else {
1253
+ switch (bindResponse.decision) {
1254
+ case "POLICY_DECISION_PERMIT": {
1255
+ //
1256
+ // The request status is permit => cache the bind response info.
1257
+ //
1258
+
1259
+ // This is the certificate assigned to us by the volt.
1260
+ this._credential.cache.cert = bindResponse.cert;
1261
+
1262
+ // This is the signing CA used by the volt (don't ov)
1263
+ this._credential.cache.ca = bindResponse.chain;
1264
+
1265
+ // Resource id is assigned by the volt.
1266
+ this._credential.cache.client_id = bindResponse.identity_id;
1267
+ this._credential.saveCache();
1268
+ break;
1269
+ }
1270
+ case "POLICY_DECISION_DENY": {
1271
+ log$1(">>>>>>>>>>>>>>> access request is DENIED <<<<<<<<<<<<<<<<<<<");
1272
+ break;
1273
+ }
1274
+ case "POLICY_DECISION_PROMPT":
1275
+ case "POLICY_DECISION_PENDING": {
1276
+ log$1("access pending approval - waiting...");
1277
+ break;
1278
+ }
1279
+ default:
1280
+ log$1("ignoring unknown bind descision %s", bindResponse.status);
1281
+ break;
1282
+ }
1283
+ }
1284
+
1285
+ return bindResponse.decision;
1286
+ });
1292
1287
  }
1293
1288
 
1294
1289
  function findDIDDocumentService(document, serviceType) {
1295
- if (!document.service) {
1296
- return [];
1297
- }
1290
+ if (!document.service) {
1291
+ return [];
1292
+ }
1298
1293
 
1299
- const matches = filter(document.service, (svc) => svc.type === serviceType);
1300
- return matches;
1294
+ const matches = filter(document.service, (svc) => svc.type === serviceType);
1295
+ return matches;
1301
1296
  }
1302
1297
 
1303
1298
  async function bindInternal() {
1304
- try {
1305
- // Check if we need to resolve a Relay volt.
1306
- if (
1307
- typeof this._voltConfig?.relay === "string" ||
1308
- this._voltConfig?.relay?.http_address
1309
- ) {
1310
- const relayURL =
1311
- this._voltConfig.relay?.http_address || this._voltConfig.relay;
1312
- log$1("attempting to retrieve Relay information from %s", relayURL);
1313
- this._voltConfig.relay = await fetchVoltConfig.call(
1314
- this,
1315
- `${relayURL}/discovery`,
1316
- );
1317
- if (this._voltConfig.relay.ca_pem) {
1318
- log$1(
1319
- "Auto-fetched Relay CA %s, remote address %s",
1320
- this._voltConfig.relay.ca_pem,
1321
- this._voltConfig.relay.address,
1322
- );
1323
- } else {
1324
- throw new Error("Unable to fetch Relay CA - cannot securely connect.");
1325
- }
1326
- }
1327
-
1328
- if (!this._credential.cache.ca) {
1329
- this._credential.cache.ca = this._voltConfig.ca_pem;
1330
- }
1331
-
1332
- if (!this._credential.cache.ca) {
1333
- throw new Error(
1334
- "No certificate authority found in configuration for target Volt - check configuration",
1335
- );
1336
- }
1337
-
1338
- const keyPair = this._credential.getKey();
1339
- const publicKeyPem = pki.publicKeyToPem(keyPair.publicKey);
1340
-
1341
- if (!this._credential.cache.bindIp) {
1342
- // Default the IP address if none is specified. This is used to set the SAN in the
1343
- // certificate issued by the Volt.
1344
- this._credential.cache.bindIp = ip__default["default"].address();
1345
- }
1346
-
1347
- this._credential.saveCache();
1348
-
1349
- // Form the request message.
1350
- const bindRequest = {
1351
- binding_name: this._config.client_name,
1352
- public_key: publicKeyPem,
1353
- host: this._credential.cache.bindIp,
1354
- x509_credential: [],
1355
- };
1356
-
1357
- if (this._credential.cache.cert) {
1358
- bindRequest.x509_credential = [
1359
- this._credential.cache.cert + this._credential.cache.ca,
1360
- ];
1361
- }
1362
-
1363
- if (this._voltConfig.challenge_code) {
1364
- bindRequest.challenge = signBase64(
1365
- keyPair.privateKey,
1366
- this._voltConfig.challenge_code,
1367
- );
1368
- } else {
1369
- log$1(
1370
- "****no Volt challenge code available**** => not sending challenge signature",
1371
- );
1372
- }
1373
-
1374
- // For remote connections, add our cloud-issued certificate as an additional credential.
1375
- if (this._config?.relay?.ca_pem && this._credential.cache.cloud_cert) {
1376
- bindRequest.x509_credential = bindRequest.x509_credential.concat(
1377
- this._credential.cache.cloud_cert + this._config.relay.ca_pem,
1378
- );
1379
- }
1380
-
1381
- log$1("sending %d x509 credentials", bindRequest.x509_credential.length);
1382
- log$1("bind TTL is ", this._config.bindRequestTTL);
1383
-
1384
- // This will block while the request is pending.
1385
- const pollInterval = 10000;
1386
- let decision = "";
1387
- do {
1388
- if (decision) {
1389
- // Wait a while before retrying.
1390
- // eslint-disable-next-line no-await-in-loop
1391
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
1392
- }
1393
- // eslint-disable-next-line no-await-in-loop
1394
- decision = await issueBind.call(
1395
- this,
1396
- bindRequest,
1397
- this._voltConfig.bindRequestTTL,
1398
- );
1399
- log$1("binding decision: %s", decision);
1400
- } while (
1401
- decision === "POLICY_DECISION_PROMPT" ||
1402
- decision === "POLICY_DECISION_PENDING"
1403
- );
1404
-
1405
- return decision === "POLICY_DECISION_PERMIT";
1406
- } catch (err) {
1407
- log$1("failure binding to Volt [%s]", err.message);
1408
- throw err;
1409
- }
1299
+ try {
1300
+ // Check if we need to resolve a Relay volt.
1301
+ if (
1302
+ typeof this._voltConfig?.relay === "string" ||
1303
+ this._voltConfig?.relay?.http_address
1304
+ ) {
1305
+ const relayURL =
1306
+ this._voltConfig.relay?.http_address || this._voltConfig.relay;
1307
+ log$1("attempting to retrieve Relay information from %s", relayURL);
1308
+ this._voltConfig.relay = await fetchVoltConfig.call(
1309
+ this,
1310
+ `${relayURL}/discovery`,
1311
+ );
1312
+ if (this._voltConfig.relay.ca_pem) {
1313
+ log$1(
1314
+ "Auto-fetched Relay CA %s, remote address %s",
1315
+ this._voltConfig.relay.ca_pem,
1316
+ this._voltConfig.relay.address,
1317
+ );
1318
+ } else {
1319
+ throw new Error("Unable to fetch Relay CA - cannot securely connect.");
1320
+ }
1321
+ }
1322
+
1323
+ if (!this._credential.cache.ca) {
1324
+ this._credential.cache.ca = this._voltConfig.ca_pem;
1325
+ }
1326
+
1327
+ if (!this._credential.cache.ca) {
1328
+ throw new Error(
1329
+ "No certificate authority found in configuration for target Volt - check configuration",
1330
+ );
1331
+ }
1332
+
1333
+ const keyPair = this._credential.getKey();
1334
+ const publicKeyPem = pki.publicKeyToPem(keyPair.publicKey);
1335
+
1336
+ if (!this._credential.cache.bindIp) {
1337
+ // Default the IP address if none is specified. This is used to set the SAN in the
1338
+ // certificate issued by the Volt.
1339
+ this._credential.cache.bindIp = ip__default["default"].address();
1340
+ }
1341
+
1342
+ this._credential.saveCache();
1343
+
1344
+ // Form the request message.
1345
+ const bindRequest = {
1346
+ binding_name: this._config.client_name,
1347
+ public_key: publicKeyPem,
1348
+ host: this._credential.cache.bindIp,
1349
+ x509_credential: [],
1350
+ };
1351
+
1352
+ if (this._credential.cache.cert) {
1353
+ bindRequest.x509_credential = [
1354
+ this._credential.cache.cert + this._credential.cache.ca,
1355
+ ];
1356
+ }
1357
+
1358
+ if (this._voltConfig.challenge_code) {
1359
+ bindRequest.challenge = signBase64(
1360
+ keyPair.privateKey,
1361
+ this._voltConfig.challenge_code,
1362
+ );
1363
+ } else {
1364
+ log$1(
1365
+ "****no Volt challenge code available**** => not sending challenge signature",
1366
+ );
1367
+ }
1368
+
1369
+ // For remote connections, add our cloud-issued certificate as an additional credential.
1370
+ if (this._config?.relay?.ca_pem && this._credential.cache.cloud_cert) {
1371
+ bindRequest.x509_credential = bindRequest.x509_credential.concat(
1372
+ this._credential.cache.cloud_cert + this._config.relay.ca_pem,
1373
+ );
1374
+ }
1375
+
1376
+ log$1("sending %d x509 credentials", bindRequest.x509_credential.length);
1377
+ log$1("bind TTL is ", this._config.bindRequestTTL);
1378
+
1379
+ // This will block while the request is pending.
1380
+ const pollInterval = 10000;
1381
+ let decision = "";
1382
+ do {
1383
+ if (decision) {
1384
+ // Wait a while before retrying.
1385
+ // eslint-disable-next-line no-await-in-loop
1386
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
1387
+ }
1388
+ // eslint-disable-next-line no-await-in-loop
1389
+ decision = await issueBind.call(
1390
+ this,
1391
+ bindRequest,
1392
+ this._voltConfig.bindRequestTTL,
1393
+ );
1394
+ log$1("binding decision: %s", decision);
1395
+ } while (
1396
+ decision === "POLICY_DECISION_PROMPT" ||
1397
+ decision === "POLICY_DECISION_PENDING"
1398
+ );
1399
+
1400
+ return decision === "POLICY_DECISION_PERMIT";
1401
+ } catch (err) {
1402
+ log$1("failure binding to Volt [%s]", err.message);
1403
+ throw err;
1404
+ }
1410
1405
  }
1411
1406
 
1412
1407
  function connectInternal() {
1413
- try {
1414
- if (this._voltConnection) {
1415
- log$1("connectInternal - Volt connection already exists");
1416
- return Promise.resolve();
1417
- }
1418
-
1419
- const onDisconnected = () => {
1420
- this._connected = false;
1421
- this._session = null;
1422
-
1423
- if (this._cachedClient) {
1424
- this._grpc.closeClient(this._cachedClient);
1425
- this._cachedClient = null;
1426
- }
1427
-
1428
- if (this._cachedRemoteClient) {
1429
- this._grpc.closeClient(this._cachedRemoteClient);
1430
- this._cachedRemoteClient = null;
1431
- }
1432
- };
1433
-
1434
- this._voltConnection = new VoltConnection(this, this._grpc);
1435
-
1436
- this._voltConnection.on("connected", (session) => {
1437
- this._session = session;
1438
- this._connected = !!session;
1439
- if (!this._connected) {
1440
- onDisconnected();
1441
- }
1442
- this.emit("connected", !!session);
1443
- });
1444
-
1445
- this._voltConnection.on("error", (err) => {
1446
- // Should auto-reconnect.
1447
- log$1("Volt connection error [%s]", err.message);
1448
- if (this._connected) {
1449
- onDisconnected();
1450
- this.emit("connected", false);
1451
- }
1452
- });
1453
-
1454
- this._voltConnection.on("evt", (evt) => {
1455
- this.emit("evt", evt);
1456
- });
1457
-
1458
- return this._voltConnection.connect();
1459
- } catch (err) {
1460
- log$1("connect - error [%s]", err.message);
1461
- throw err;
1462
- }
1408
+ try {
1409
+ if (this._voltConnection) {
1410
+ log$1("connectInternal - Volt connection already exists");
1411
+ return Promise.resolve();
1412
+ }
1413
+
1414
+ const onDisconnected = () => {
1415
+ this._connected = false;
1416
+ this._session = null;
1417
+
1418
+ if (this._cachedClient) {
1419
+ this._grpc.closeClient(this._cachedClient);
1420
+ this._cachedClient = null;
1421
+ }
1422
+
1423
+ if (this._cachedRemoteClient) {
1424
+ this._grpc.closeClient(this._cachedRemoteClient);
1425
+ this._cachedRemoteClient = null;
1426
+ }
1427
+ };
1428
+
1429
+ this._voltConnection = new VoltConnection(this, this._grpc);
1430
+
1431
+ this._voltConnection.on("connected", (session) => {
1432
+ this._session = session;
1433
+ this._connected = !!session;
1434
+ if (!this._connected) {
1435
+ onDisconnected();
1436
+ }
1437
+ this.emit("connected", !!session);
1438
+ });
1439
+
1440
+ this._voltConnection.on("error", (err) => {
1441
+ // Should auto-reconnect.
1442
+ log$1("Volt connection error [%s]", err.message);
1443
+ if (this._connected) {
1444
+ onDisconnected();
1445
+ this.emit("connected", false);
1446
+ }
1447
+ });
1448
+
1449
+ this._voltConnection.on("evt", (evt) => {
1450
+ this.emit("evt", evt);
1451
+ });
1452
+
1453
+ return this._voltConnection.connect();
1454
+ } catch (err) {
1455
+ log$1("connect - error [%s]", err.message);
1456
+ throw err;
1457
+ }
1463
1458
  }
1464
1459
 
1465
1460
  function getVoltAPIClientInternal() {
1466
- if (!this._cachedClient) {
1467
- const serviceAddress = this.isRemote
1468
- ? this._config.relay.address
1469
- : this._voltConfig.address;
1470
- log$1("creating cached VoltAPI service client on %s", serviceAddress);
1471
- let serviceDescriptors = {};
1472
- voltServices.forEach((pkg) => {
1473
- const packageDescriptors = getServiceDescriptors(this._grpc, pkg);
1474
- serviceDescriptors = { ...serviceDescriptors, ...packageDescriptors };
1475
- });
1476
-
1477
- this._cachedClient = createClient(
1478
- this._grpc,
1479
- serviceDescriptors,
1480
- serviceAddress,
1481
- this._credential,
1482
- this.isRemote && !this.isVoltRelay,
1483
- this._config?.relay?.cloud ? this._voltConfig.id : "",
1484
- this._config?.relay?.cloud ? this._voltConfig.id : "",
1485
- );
1486
- }
1487
-
1488
- return this._cachedClient;
1461
+ if (!this._cachedClient) {
1462
+ const serviceAddress = this.isRemote
1463
+ ? this._config.relay.address
1464
+ : this._voltConfig.address;
1465
+ log$1("creating cached VoltAPI service client on %s", serviceAddress);
1466
+ let serviceDescriptors = {};
1467
+ voltServices.forEach((pkg) => {
1468
+ const packageDescriptors = getServiceDescriptors(this._grpc, pkg);
1469
+ serviceDescriptors = { ...serviceDescriptors, ...packageDescriptors };
1470
+ });
1471
+
1472
+ this._cachedClient = createClient(
1473
+ this._grpc,
1474
+ serviceDescriptors,
1475
+ serviceAddress,
1476
+ this._credential,
1477
+ this.isRemote && !this.isVoltRelay,
1478
+ this._config?.relay?.cloud ? this._voltConfig.id : "",
1479
+ this._config?.relay?.cloud ? this._voltConfig.id : "",
1480
+ );
1481
+ }
1482
+
1483
+ return this._cachedClient;
1489
1484
  }
1490
1485
 
1491
1486
  function unaryCall(method, request) {
1492
- return new Promise((resolve, reject) => {
1493
- const grpcClient = this.getVoltAPIClient();
1487
+ return new Promise((resolve, reject) => {
1488
+ const grpcClient = this.getVoltAPIClient();
1494
1489
 
1495
- const call = new GRPCCall(this, method, "METHOD_TYPE_UNARY");
1490
+ const call = new GRPCCall(this, method, "METHOD_TYPE_UNARY");
1496
1491
 
1497
- let response = null;
1492
+ let response = null;
1498
1493
 
1499
- call.on("data", (payload) => {
1500
- // Cache the response and wait for `end`.
1501
- response = payload;
1502
- });
1494
+ call.on("data", (payload) => {
1495
+ // Cache the response and wait for `end`.
1496
+ response = payload;
1497
+ });
1503
1498
 
1504
- call.on("error", reject);
1499
+ call.on("error", reject);
1505
1500
 
1506
- call.on("end", () => {
1507
- if (!response) {
1508
- return reject(new Error("no response received"));
1509
- }
1501
+ call.on("end", () => {
1502
+ if (!response) {
1503
+ return reject(new Error("no response received"));
1504
+ }
1510
1505
 
1511
- // Convert non-OK status to a JS error.
1512
- if (response.status?.message) {
1513
- reject(new Error(response.status.message));
1514
- } else {
1515
- resolve(response);
1516
- }
1517
- });
1506
+ // Convert non-OK status to a JS error.
1507
+ if (response.status?.message) {
1508
+ reject(new Error(response.status.message));
1509
+ } else {
1510
+ resolve(response);
1511
+ }
1512
+ });
1518
1513
 
1519
- call.start(grpcClient, request);
1520
- });
1514
+ call.start(grpcClient, request);
1515
+ });
1521
1516
  }
1522
1517
 
1523
1518
  async function fetchVoltConfig(discovery_url) {
1524
- try {
1525
- const getJSON = bent__default["default"]("json");
1526
- const fullVoltConfig = await getJSON(discovery_url);
1527
-
1528
- if (!fullVoltConfig || typeof fullVoltConfig !== "object") {
1529
- throw new Error(`invalid config resolved from: ${discovery_url}`);
1530
- }
1531
-
1532
- const voltConfig = pick(
1533
- fullVoltConfig,
1534
- "id",
1535
- "display_name",
1536
- "public_key",
1537
- "fingerprint",
1538
- "address",
1539
- "ca_pem",
1540
- "challenge_code",
1541
- "cloud",
1542
- "relay",
1543
- );
1544
-
1545
- return voltConfig;
1546
- } catch (err) {
1547
- throw new Error(
1548
- `Failure loading config from ${discovery_url}: ${err.message}`,
1549
- );
1550
- }
1519
+ try {
1520
+ const getJSON = bent__default["default"]("json");
1521
+ const fullVoltConfig = await getJSON(discovery_url);
1522
+
1523
+ if (!fullVoltConfig || typeof fullVoltConfig !== "object") {
1524
+ throw new Error(`invalid config resolved from: ${discovery_url}`);
1525
+ }
1526
+
1527
+ const voltConfig = pick(
1528
+ fullVoltConfig,
1529
+ "id",
1530
+ "display_name",
1531
+ "public_key",
1532
+ "fingerprint",
1533
+ "address",
1534
+ "ca_pem",
1535
+ "challenge_code",
1536
+ "cloud",
1537
+ "relay",
1538
+ );
1539
+
1540
+ return voltConfig;
1541
+ } catch (err) {
1542
+ throw new Error(
1543
+ `Failure loading config from ${discovery_url}: ${err.message}`,
1544
+ );
1545
+ }
1551
1546
  }
1552
1547
 
1553
1548
  async function fetchVoltConfigFromDID(volt_did) {
1554
- try {
1555
- const didResolution = getDIDResolutionURL(volt_did);
1556
- if (!didResolution) {
1557
- throw new Error(`Invalid Volt DID: ${volt_did}`);
1558
- }
1559
-
1560
- const getJSON = bent__default["default"]("json");
1561
- const didDocument = await getJSON(didResolution);
1562
- const voltConfigServices = findDIDDocumentService(
1563
- didDocument,
1564
- constants.didServiceType.voltConfig,
1565
- );
1566
- if (voltConfigServices.length !== 1) {
1567
- log$1(
1568
- "Unexpected number of Volt configuration endpoints found in DID document for %s, services found: %d",
1569
- volt_did,
1570
- voltConfigServices.length,
1571
- );
1572
- }
1573
-
1574
- const serviceEndpoint = voltConfigServices[0].serviceEndpoint;
1575
- return await fetchVoltConfig.call(this, serviceEndpoint);
1576
- } catch (err) {
1577
- throw new Error(
1578
- `Failure fetching Volt config from DID document ${volt_did}: ${err.message}`,
1579
- );
1580
- }
1549
+ try {
1550
+ const didResolution = getDIDResolutionURL(volt_did);
1551
+ if (!didResolution) {
1552
+ throw new Error(`Invalid Volt DID: ${volt_did}`);
1553
+ }
1554
+
1555
+ const getJSON = bent__default["default"]("json");
1556
+ const didDocument = await getJSON(didResolution);
1557
+ const voltConfigServices = findDIDDocumentService(
1558
+ didDocument,
1559
+ constants.didServiceType.voltConfig,
1560
+ );
1561
+ if (voltConfigServices.length !== 1) {
1562
+ log$1(
1563
+ "Unexpected number of Volt configuration endpoints found in DID document for %s, services found: %d",
1564
+ volt_did,
1565
+ voltConfigServices.length,
1566
+ );
1567
+ }
1568
+
1569
+ const serviceEndpoint = voltConfigServices[0].serviceEndpoint;
1570
+ return await fetchVoltConfig.call(this, serviceEndpoint);
1571
+ } catch (err) {
1572
+ throw new Error(
1573
+ `Failure fetching Volt config from DID document ${volt_did}: ${err.message}`,
1574
+ );
1575
+ }
1581
1576
  }
1582
1577
 
1583
1578
  const { readFile } = fs__default["default"].promises;
1584
1579
  const log = debug__default["default"]("volt-client-grpc:volt-client");
1585
1580
 
1586
1581
  class VoltClient extends EventEmitter__default["default"] {
1587
- /**
1588
- * @param {object} grpc - an instance of the grpc module (see https://www.npmjs.com/package/grpc)
1589
- */
1590
- constructor(grpc) {
1591
- super();
1592
-
1593
- if (!grpc) {
1594
- throw new Error("invalid arguments, grpc is required");
1595
- }
1596
- this._grpc = grpc;
1597
- this._cachedClient = null;
1598
- this._cachedRemoteClient = null;
1599
- this._session = null;
1600
- this._config = null;
1601
- this._voltConfig = null;
1602
- this._voltConnection = null;
1603
- }
1604
-
1605
- get config() {
1606
- return this._config;
1607
- }
1608
-
1609
- get credential() {
1610
- return this._credential;
1611
- }
1612
-
1613
- get grpc() {
1614
- return this._grpc;
1615
- }
1616
-
1617
- get voltConfig() {
1618
- return this._voltConfig;
1619
- }
1620
-
1621
- /**
1622
- * Initialises a binding and connection to the volt.
1623
- * @param {string} [config] - the location of the Volt configuration file, or a configuration object
1624
- */
1625
- async initialise(config, extras = {}) {
1626
- try {
1627
- if (!config) {
1628
- throw new Error(
1629
- "configPath argument is required to be a non-empty string",
1630
- );
1631
- }
1632
-
1633
- let configPath;
1634
- let configJSON;
1635
- if (typeof config === "string") {
1636
- configPath = path__default["default"].resolve(config);
1637
- log("Attempt to load config from file %s", configPath);
1638
- try {
1639
- const configContents = await readFile(
1640
- new URL(configPath, (typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.src || new URL('index.cjs', document.baseURI).href))),
1641
- );
1642
- configJSON = JSON.parse(configContents);
1643
- } catch (err) {
1644
- log("failure loading config file %s [%s]", configPath, err.message);
1645
- throw new Error(
1646
- `failed to load configuration - check JSON format in ${configPath}`,
1647
- );
1648
- }
1649
- } else if (typeof config === "object") {
1650
- configJSON = config;
1651
- } else {
1652
- throw new Error("config must be either a file path or a config object");
1653
- }
1654
-
1655
- if (!configJSON || typeof configJSON !== "object") {
1656
- throw new Error("config argument is required to be an object");
1657
- }
1658
-
1659
- if (
1660
- !configJSON.client_name ||
1661
- typeof configJSON.client_name !== "string"
1662
- ) {
1663
- throw new Error("client_name property required in config");
1664
- }
1665
-
1666
- this._config = { ...configJSON, ...extras };
1667
-
1668
- let voltDID = "";
1669
- let voltHttpAddress = "";
1670
- if (typeof this._config.volt === "string") {
1671
- if (this._config.volt.indexOf("did:") === 0) {
1672
- voltDID = this._config.volt;
1673
- } else {
1674
- voltHttpAddress = this._config.volt;
1675
- }
1676
- } else if (typeof this._config.volt !== "object") {
1677
- throw new Error("configuration is missing the 'volt' object");
1678
- } else {
1679
- voltDID = this._config.volt.did;
1680
- voltHttpAddress = this._config.volt.http_address;
1681
- }
1682
-
1683
- if (voltDID) {
1684
- const didConfig = await fetchVoltConfigFromDID.call(this, voltDID);
1685
- this._config = { ...this._config, volt: didConfig };
1686
- log("resolved config from DID: %s", JSON.stringify(didConfig, null, 2));
1687
- } else if (voltHttpAddress) {
1688
- const discoConfig = await fetchVoltConfig.call(
1689
- this,
1690
- `${voltHttpAddress}/discovery`,
1691
- );
1692
- this._config = {
1693
- ...this._config,
1694
- volt: { ...this._config.volt, ...discoConfig },
1695
- };
1696
- }
1697
-
1698
- this._voltConfig = this._config.volt;
1699
- this._voltConfig.did = voltDID;
1700
- this._voltConfig.http_address = voltHttpAddress;
1701
-
1702
- if (!this._voltConfig.id || typeof this._voltConfig.id !== "string") {
1703
- throw new Error("'id' property is missing in Volt configuration");
1704
- }
1705
-
1706
- this._credential = new VoltCredential(this._config, configPath);
1707
-
1708
- await this._credential.initialise();
1709
-
1710
- if (!this.isRemote) {
1711
- // If we're not connecting via a Relay, we must have the address and CA.
1712
- if (
1713
- !this._voltConfig.address ||
1714
- typeof this._voltConfig.address !== "string"
1715
- ) {
1716
- throw new Error("'address' property missing in Volt configuration");
1717
- }
1718
-
1719
- if (
1720
- !this._voltConfig.ca_pem ||
1721
- typeof this._voltConfig.ca_pem !== "string"
1722
- ) {
1723
- throw new Error("'ca_pPem' property missing in Volt configuration");
1724
- }
1725
- }
1726
-
1727
- let isBound;
1728
- if (this._credential._cryptoCache.cert) {
1729
- isBound = true;
1730
- } else {
1731
- // No certificate present in the cache yet => we need to bind.
1732
- try {
1733
- isBound = await bindInternal.call(this);
1734
- } catch (bindErr) {
1735
- if (bindErr.message === "invalid arguments") {
1736
- // This is usually the result of a missing challenge signature or credential.
1737
- log(
1738
- "failure binding to Volt - did you supply a challenge code or verified credential?",
1739
- );
1740
- throw bindErr;
1741
- }
1742
- }
1743
- }
1744
-
1745
- if (!isBound) {
1746
- // This is usually because of request being denied.
1747
- throw new Error("Volt binding failed");
1748
- }
1749
-
1750
- return Promise.resolve();
1751
- } catch (err) {
1752
- log("Failure starting Volt client [%s]", err.message);
1753
- throw err;
1754
- }
1755
- }
1756
-
1757
- initialiseAndConnect(configPath, extras = {}) {
1758
- return this.initialise(configPath, extras)
1759
- .then(() => {
1760
- return connectInternal.call(this);
1761
- })
1762
- .catch((err) => {
1763
- log("initialiseAndConnect failure: %s", err.message);
1764
- return Promise.reject(err);
1765
- });
1766
- }
1767
-
1768
- /**
1769
- * Creates a GRPC client for the given Volt service description.
1770
- *
1771
- * Automatically handles remote connections.
1772
- *
1773
- * n.b. for this javascript client Protobuf definitions should conform to the recommended
1774
- * structure, which is that a package proto file resides in a folder path that matches the
1775
- * package name, and services contained in the package are named in in Pascal Case and using
1776
- * the suffix 'API'. For example the WebcamControlAPI in package tdx.volt_api.webcam.v1 should reside
1777
- * in a protobuf file at tdx/api/webcam/v1/webcam_control_api.proto
1778
- *
1779
- * @param {object} service a service description.
1780
- * @param {object} descriptors the service descriptors, must be specified unless the service
1781
- * packages are well-known Volt APIs.
1782
- */
1783
- createServiceClient(service, descriptors) {
1784
- const pkg = service.service_description.service_api;
1785
-
1786
- let serviceDescriptors;
1787
- if (descriptors) {
1788
- serviceDescriptors = descriptors;
1789
- } else {
1790
- serviceDescriptors = {};
1791
- pkg.forEach((svc) => {
1792
- log("adding service %s to service client", svc);
1793
- const packageDescriptors = getServiceDescriptors(this._grpc, svc);
1794
- serviceDescriptors = { ...serviceDescriptors, ...packageDescriptors };
1795
- });
1796
- }
1797
-
1798
- if (this.isRemote) {
1799
- if (!this._config?.relay?.address) {
1800
- throw new Error(
1801
- "Remote connection enabled but no Relay address - have you called start()?",
1802
- );
1803
- }
1804
-
1805
- // In remote mode => create a client using the discovered Relay address **and** passing the service reourceId
1806
- return createClient(
1807
- this._grpc,
1808
- serviceDescriptors,
1809
- this._config.relay.address,
1810
- this._credential,
1811
- false,
1812
- service.volt_id,
1813
- service.id,
1814
- );
1815
- } else {
1816
- return createClient(
1817
- this._grpc,
1818
- serviceDescriptors,
1819
- service.service_description.address,
1820
- this._credential,
1821
- );
1822
- }
1823
- }
1824
-
1825
- get isRemote() {
1826
- return !!(this._config?.relay && this._voltConfig.id);
1827
- }
1828
-
1829
- get isVoltRelay() {
1830
- return this.isRemote && !this._config?.relay.cloud;
1831
- }
1832
-
1833
- connect() {
1834
- return connectInternal.call(this);
1835
- }
1836
-
1837
- disconnect() {
1838
- if (this._voltConnection) {
1839
- this._voltConnection.disconnect();
1840
- this._voltConnection = null;
1841
- }
1842
- }
1843
-
1844
- getVoltAPIClient() {
1845
- return getVoltAPIClientInternal.call(this);
1846
- }
1847
-
1848
- /**
1849
- * Request resource access.
1850
- * @param {*} targetResourceId
1851
- * @param {*} accessType - see volt.proto for values.
1852
- */
1853
- async RequestAccessBlocking(
1854
- targetResourceId,
1855
- accessType = "VOLT_ACCESS_READ",
1856
- ) {
1857
- try {
1858
- const accessRequest = {
1859
- access: accessType,
1860
- resource_id: targetResourceId,
1861
- };
1862
-
1863
- // This will block while the request is pending.
1864
- // TODO - implement streamed rpc on volt? Not sure there is a need really...
1865
- const pollInterval = 10000;
1866
- let decision = "";
1867
- do {
1868
- if (decision) {
1869
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
1870
- }
1871
- const accessResponse = await this.RequestAccess(accessRequest);
1872
- decision = accessResponse.decision;
1873
- log("access decision: %s", decision);
1874
- } while (
1875
- decision === "POLICY_DECISION_PROMPT" ||
1876
- decision === "POLICY_DECISION_PENDING"
1877
- );
1878
-
1879
- log("requestResourceAccess result is %j", decision);
1880
- return decision === "POLICY_DECISION_PERMIT";
1881
- } catch (err) {
1882
- log("failure requesting resource access [%s]", err.message);
1883
- return Promise.reject(err);
1884
- }
1885
- }
1886
-
1887
- /**
1888
- * VoltAPI - resource management
1889
- */
1890
-
1891
- CanAccessResource(request) {
1892
- return unaryCall.call(this, "CanAccessResource", request);
1893
- }
1894
-
1895
- DeleteResource(request) {
1896
- return unaryCall.call(this, "DeleteResource", request);
1897
- }
1898
-
1899
- DiscoverServices(request) {
1900
- return unaryCall.call(this, "DiscoverServices", request);
1901
- }
1902
-
1903
- GetResource(request) {
1904
- return unaryCall.call(this, "GetResource", request);
1905
- }
1906
-
1907
- GetResources(request) {
1908
- return unaryCall.call(this, "GetResources", request);
1909
- }
1910
-
1911
- GetResourceAncestors(request) {
1912
- return unaryCall.call(this, "GetResourceAncestors", request);
1913
- }
1914
-
1915
- GetResourceDescendants(request) {
1916
- return unaryCall.call(this, "GetResourceDescendants", request);
1917
- }
1918
-
1919
- RequestAccess(request) {
1920
- return unaryCall.call(this, "RequestAccess", request);
1921
- }
1922
-
1923
- SaveResource(request) {
1924
- return unaryCall.call(this, "SaveResource", request);
1925
- }
1926
-
1927
- SaveResourceAttribute(request) {
1928
- return unaryCall.call(this, "SaveResourceAttribute", request);
1929
- }
1930
-
1931
- SetServiceStatus(request) {
1932
- return unaryCall.call(this, "SetServiceStatus", request);
1933
- }
1934
-
1935
- /**
1936
- * VoltAPI - volt management
1937
- */
1938
-
1939
- Bind(request) {
1940
- return unaryCall.call(this, "Bind", request);
1941
- }
1942
-
1943
- DeleteAccess(request) {
1944
- return unaryCall.call(this, "DeleteAccess", request);
1945
- }
1946
-
1947
- DeleteVolt(request) {
1948
- return unaryCall.call(this, "DeleteVolt", request);
1949
- }
1950
-
1951
- GetAccess(request) {
1952
- return unaryCall.call(this, "GetAccess", request);
1953
- }
1954
-
1955
- GetBindings(request) {
1956
- return unaryCall.call(this, "GetBindings", request);
1957
- }
1958
-
1959
- GetIdentities(request) {
1960
- return unaryCall.call(this, "GetIdentities", request);
1961
- }
1962
-
1963
- GetIdentity(request) {
1964
- return unaryCall.call(this, "GetIdentity", request);
1965
- }
1966
-
1967
- GetIdentityToken(request) {
1968
- return unaryCall.call(this, "GetIdentityToken", request);
1969
- }
1970
-
1971
- GetPolicy(request) {
1972
- return unaryCall.call(this, "GetPolicy", request);
1973
- }
1974
-
1975
- GetSettings(request) {
1976
- return unaryCall.call(this, "GetSettings", request);
1977
- }
1978
-
1979
- SaveAccess(request) {
1980
- return unaryCall.call(this, "SaveAccess", request);
1981
- }
1982
-
1983
- SaveCloudConnection(request) {
1984
- return unaryCall.call(this, "SaveCloudConnection", request);
1985
- }
1986
-
1987
- SaveIdentity(request) {
1988
- return unaryCall.call(this, "SaveIdentity", request);
1989
- }
1990
-
1991
- SaveSettings(request) {
1992
- return unaryCall.call(this, "SaveSettings", request);
1993
- }
1994
-
1995
- SetAccessRequestDecision(request) {
1996
- return unaryCall.call(this, "SetAccessRequestDecision", request);
1997
- }
1998
-
1999
- SetBindingDecision(request) {
2000
- return unaryCall.call(this, "SetBindingDecision", request);
2001
- }
2002
-
2003
- Shutdown(request) {
2004
- return unaryCall.call(this, "Shutdown", request);
2005
- }
2006
-
2007
- SignVerify(request) {
2008
- return unaryCall.call(this, "SignVerify", request);
2009
- }
2010
-
2011
- /**
2012
- * FileAPI
2013
- */
2014
- GetFileDescendants(request) {
2015
- return unaryCall.call(this, "GetFileDescendants", request);
2016
- }
2017
-
2018
- /**
2019
- * Asynchronous file download
2020
- * @param {*} request
2021
- * @returns stream
2022
- */
2023
- DownloadFile(request) {
2024
- const grpcClient = this.getVoltAPIClient();
2025
-
2026
- const downloadCall = new GRPCCall(
2027
- this,
2028
- "DownloadFile",
2029
- "METHOD_TYPE_SERVER_STREAM",
2030
- );
2031
- return downloadCall.start(grpcClient, request);
2032
- }
2033
-
2034
- UploadFile(request) {
2035
- const grpcClient = this.getVoltAPIClient();
2036
-
2037
- const uploadCall = new GRPCCall(this, "UploadFile", "METHOD_TYPE_BIDI");
2038
- return uploadCall.start(grpcClient, request);
2039
- }
2040
-
2041
- /**
2042
- * Synchronous file download
2043
- * @param {*} request
2044
- * @returns file buffer, base64 encoded
2045
- */
2046
- DownloadFileSync(request) {
2047
- return new Promise((resolve, reject) => {
2048
- const grpcClient = this.getVoltAPIClient();
2049
-
2050
- const blocks = [];
2051
-
2052
- const call = new GRPCCall(
2053
- this,
2054
- "DownloadFile",
2055
- "METHOD_TYPE_SERVER_STREAM",
2056
- );
2057
-
2058
- call.on("data", (response) => {
2059
- switch (response.payload) {
2060
- case "block": {
2061
- blocks.push(response.block);
2062
- break;
2063
- }
2064
- case "status": {
2065
- if (response.status.message) {
2066
- return call.reject(new Error(response.status.message));
2067
- }
2068
- break;
2069
- }
2070
- default:
2071
- log("unrecognised response payload %s", response.payload);
2072
- break;
2073
- }
2074
- });
2075
-
2076
- call.on("error", (err) => {
2077
- log("error during DownloadFile [%s]", err.message);
2078
- reject(err);
2079
- });
2080
-
2081
- call.on("end", () => {
2082
- log("Download finished");
2083
- resolve({ buffer: Buffer.concat(blocks).toString("base64") });
2084
- });
2085
-
2086
- call.start(grpcClient, request);
2087
- });
2088
- }
2089
-
2090
- /**
2091
- * SqliteDatabaseAPI
2092
- */
2093
- SqlExecute(request) {
2094
- const grpcClient = this.getVoltAPIClient();
2095
-
2096
- const subscribeCall = new GRPCCall(this, "Execute", "METHOD_TYPE_BIDI");
2097
- return subscribeCall.start(grpcClient, request);
2098
- }
2099
-
2100
- /**
2101
- * WireAPI
2102
- */
2103
- PublishWire(request) {
2104
- const grpcClient = this.getVoltAPIClient();
2105
-
2106
- const publishCall = new GRPCCall(this, "PublishWire", "METHOD_TYPE_BIDI");
2107
- return publishCall.start(grpcClient, request);
2108
- }
2109
-
2110
- SubscribeWire(request) {
2111
- const grpcClient = this.getVoltAPIClient();
2112
-
2113
- const subscribeCall = new GRPCCall(
2114
- this,
2115
- "SubscribeWire",
2116
- "METHOD_TYPE_BIDI",
2117
- );
2118
- return subscribeCall.start(grpcClient, request);
2119
- }
1582
+ /**
1583
+ * @param {object} grpc - an instance of the grpc module (see https://www.npmjs.com/package/grpc)
1584
+ */
1585
+ constructor(grpc) {
1586
+ super();
1587
+
1588
+ if (!grpc) {
1589
+ throw new Error("invalid arguments, grpc is required");
1590
+ }
1591
+ this._grpc = grpc;
1592
+ this._cachedClient = null;
1593
+ this._cachedRemoteClient = null;
1594
+ this._session = null;
1595
+ this._config = null;
1596
+ this._voltConfig = null;
1597
+ this._voltConnection = null;
1598
+ }
1599
+
1600
+ get config() {
1601
+ return this._config;
1602
+ }
1603
+
1604
+ get credential() {
1605
+ return this._credential;
1606
+ }
1607
+
1608
+ get grpc() {
1609
+ return this._grpc;
1610
+ }
1611
+
1612
+ get voltConfig() {
1613
+ return this._voltConfig;
1614
+ }
1615
+
1616
+ /**
1617
+ * Initialises a binding and connection to the volt.
1618
+ * @param {string} [config] - the location of the Volt configuration file, or a configuration object
1619
+ */
1620
+ async initialise(config, extras = {}) {
1621
+ try {
1622
+ if (!config) {
1623
+ throw new Error(
1624
+ "configPath argument is required to be a non-empty string",
1625
+ );
1626
+ }
1627
+
1628
+ let configPath;
1629
+ let configJSON;
1630
+ if (typeof config === "string") {
1631
+ configPath = path__default["default"].resolve(config);
1632
+ log("Attempt to load config from file %s", configPath);
1633
+ try {
1634
+ const configContents = await readFile(
1635
+ new URL(configPath, (typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.src || new URL('index.cjs', document.baseURI).href))),
1636
+ );
1637
+ configJSON = JSON.parse(configContents);
1638
+ } catch (err) {
1639
+ log("failure loading config file %s [%s]", configPath, err.message);
1640
+ throw new Error(
1641
+ `failed to load configuration - check JSON format in ${configPath}`,
1642
+ );
1643
+ }
1644
+ } else if (typeof config === "object") {
1645
+ configJSON = config;
1646
+ } else {
1647
+ throw new Error("config must be either a file path or a config object");
1648
+ }
1649
+
1650
+ if (!configJSON || typeof configJSON !== "object") {
1651
+ throw new Error("config argument is required to be an object");
1652
+ }
1653
+
1654
+ if (
1655
+ !configJSON.client_name ||
1656
+ typeof configJSON.client_name !== "string"
1657
+ ) {
1658
+ throw new Error("client_name property required in config");
1659
+ }
1660
+
1661
+ this._config = { ...configJSON, ...extras };
1662
+
1663
+ let voltDID = "";
1664
+ let voltHttpAddress = "";
1665
+ if (typeof this._config.volt === "string") {
1666
+ if (this._config.volt.indexOf("did:") === 0) {
1667
+ voltDID = this._config.volt;
1668
+ } else {
1669
+ voltHttpAddress = this._config.volt;
1670
+ }
1671
+ } else if (typeof this._config.volt !== "object") {
1672
+ throw new Error("configuration is missing the 'volt' object");
1673
+ } else {
1674
+ voltDID = this._config.volt.did;
1675
+ voltHttpAddress = this._config.volt.http_address;
1676
+ }
1677
+
1678
+ if (voltDID) {
1679
+ const didConfig = await fetchVoltConfigFromDID.call(this, voltDID);
1680
+ this._config = { ...this._config, volt: didConfig };
1681
+ log("resolved config from DID: %s", JSON.stringify(didConfig, null, 2));
1682
+ } else if (voltHttpAddress) {
1683
+ const discoConfig = await fetchVoltConfig.call(
1684
+ this,
1685
+ `${voltHttpAddress}/discovery`,
1686
+ );
1687
+ this._config = {
1688
+ ...this._config,
1689
+ volt: { ...this._config.volt, ...discoConfig },
1690
+ };
1691
+ }
1692
+
1693
+ this._voltConfig = this._config.volt;
1694
+ this._voltConfig.did = voltDID;
1695
+ this._voltConfig.http_address = voltHttpAddress;
1696
+
1697
+ if (!this._voltConfig.id || typeof this._voltConfig.id !== "string") {
1698
+ throw new Error("'id' property is missing in Volt configuration");
1699
+ }
1700
+
1701
+ this._credential = new VoltCredential(this._config, configPath);
1702
+
1703
+ await this._credential.initialise();
1704
+
1705
+ if (!this.isRemote) {
1706
+ // If we're not connecting via a Relay, we must have the address and CA.
1707
+ if (
1708
+ !this._voltConfig.address ||
1709
+ typeof this._voltConfig.address !== "string"
1710
+ ) {
1711
+ throw new Error("'address' property missing in Volt configuration");
1712
+ }
1713
+
1714
+ if (
1715
+ !this._voltConfig.ca_pem ||
1716
+ typeof this._voltConfig.ca_pem !== "string"
1717
+ ) {
1718
+ throw new Error("'ca_pPem' property missing in Volt configuration");
1719
+ }
1720
+ }
1721
+
1722
+ let isBound;
1723
+ if (this._credential._cryptoCache.cert) {
1724
+ isBound = true;
1725
+ } else {
1726
+ // No certificate present in the cache yet => we need to bind.
1727
+ try {
1728
+ isBound = await bindInternal.call(this);
1729
+ } catch (bindErr) {
1730
+ if (bindErr.message === "invalid arguments") {
1731
+ // This is usually the result of a missing challenge signature or credential.
1732
+ log(
1733
+ "failure binding to Volt - did you supply a challenge code or verified credential?",
1734
+ );
1735
+ throw bindErr;
1736
+ }
1737
+ }
1738
+ }
1739
+
1740
+ if (!isBound) {
1741
+ // This is usually because of request being denied.
1742
+ throw new Error("Volt binding failed");
1743
+ }
1744
+
1745
+ return Promise.resolve();
1746
+ } catch (err) {
1747
+ log("Failure starting Volt client [%s]", err.message);
1748
+ throw err;
1749
+ }
1750
+ }
1751
+
1752
+ initialiseAndConnect(configPath, extras = {}) {
1753
+ return this.initialise(configPath, extras)
1754
+ .then(() => {
1755
+ return connectInternal.call(this);
1756
+ })
1757
+ .catch((err) => {
1758
+ log("initialiseAndConnect failure: %s", err.message);
1759
+ return Promise.reject(err);
1760
+ });
1761
+ }
1762
+
1763
+ /**
1764
+ * Creates a GRPC client for the given Volt service description.
1765
+ *
1766
+ * Automatically handles remote connections.
1767
+ *
1768
+ * n.b. for this javascript client Protobuf definitions should conform to the recommended
1769
+ * structure, which is that a package proto file resides in a folder path that matches the
1770
+ * package name, and services contained in the package are named in in Pascal Case and using
1771
+ * the suffix 'API'. For example the WebcamControlAPI in package tdx.volt_api.webcam.v1 should reside
1772
+ * in a protobuf file at tdx/api/webcam/v1/webcam_control_api.proto
1773
+ *
1774
+ * @param {object} service a service description.
1775
+ * @param {object} descriptors the service descriptors, must be specified unless the service
1776
+ * packages are well-known Volt APIs.
1777
+ */
1778
+ createServiceClient(service, descriptors) {
1779
+ const pkg = service.service_description.service_api;
1780
+
1781
+ let serviceDescriptors;
1782
+ if (descriptors) {
1783
+ serviceDescriptors = descriptors;
1784
+ } else {
1785
+ serviceDescriptors = {};
1786
+ pkg.forEach((svc) => {
1787
+ log("adding service %s to service client", svc);
1788
+ const packageDescriptors = getServiceDescriptors(this._grpc, svc);
1789
+ serviceDescriptors = { ...serviceDescriptors, ...packageDescriptors };
1790
+ });
1791
+ }
1792
+
1793
+ if (this.isRemote) {
1794
+ if (!this._config?.relay?.address) {
1795
+ throw new Error(
1796
+ "Remote connection enabled but no Relay address - have you called start()?",
1797
+ );
1798
+ }
1799
+
1800
+ // In remote mode => create a client using the discovered Relay address **and** passing the service reourceId
1801
+ return createClient(
1802
+ this._grpc,
1803
+ serviceDescriptors,
1804
+ this._config.relay.address,
1805
+ this._credential,
1806
+ false,
1807
+ service.volt_id,
1808
+ service.id,
1809
+ );
1810
+ } else {
1811
+ return createClient(
1812
+ this._grpc,
1813
+ serviceDescriptors,
1814
+ service.service_description.address,
1815
+ this._credential,
1816
+ );
1817
+ }
1818
+ }
1819
+
1820
+ get isRemote() {
1821
+ return !!(this._config?.relay && this._voltConfig.id);
1822
+ }
1823
+
1824
+ get isVoltRelay() {
1825
+ return this.isRemote && !this._config?.relay.cloud;
1826
+ }
1827
+
1828
+ connect() {
1829
+ return connectInternal.call(this);
1830
+ }
1831
+
1832
+ disconnect() {
1833
+ if (this._voltConnection) {
1834
+ this._voltConnection.disconnect();
1835
+ this._voltConnection = null;
1836
+ }
1837
+ }
1838
+
1839
+ getVoltAPIClient() {
1840
+ return getVoltAPIClientInternal.call(this);
1841
+ }
1842
+
1843
+ /**
1844
+ * Request resource access.
1845
+ * @param {*} targetResourceId
1846
+ * @param {*} accessType - see volt.proto for values.
1847
+ */
1848
+ async RequestAccessBlocking(
1849
+ targetResourceId,
1850
+ accessType = "VOLT_ACCESS_READ",
1851
+ ) {
1852
+ try {
1853
+ const accessRequest = {
1854
+ access: accessType,
1855
+ resource_id: targetResourceId,
1856
+ };
1857
+
1858
+ // This will block while the request is pending.
1859
+ // TODO - implement streamed rpc on volt? Not sure there is a need really...
1860
+ const pollInterval = 10000;
1861
+ let decision = "";
1862
+ do {
1863
+ if (decision) {
1864
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
1865
+ }
1866
+ const accessResponse = await this.RequestAccess(accessRequest);
1867
+ decision = accessResponse.decision;
1868
+ log("access decision: %s", decision);
1869
+ } while (
1870
+ decision === "POLICY_DECISION_PROMPT" ||
1871
+ decision === "POLICY_DECISION_PENDING"
1872
+ );
1873
+
1874
+ log("requestResourceAccess result is %j", decision);
1875
+ return decision === "POLICY_DECISION_PERMIT";
1876
+ } catch (err) {
1877
+ log("failure requesting resource access [%s]", err.message);
1878
+ return Promise.reject(err);
1879
+ }
1880
+ }
1881
+
1882
+ /**
1883
+ * VoltAPI - resource management
1884
+ */
1885
+
1886
+ CanAccessResource(request) {
1887
+ return unaryCall.call(this, "CanAccessResource", request);
1888
+ }
1889
+
1890
+ DeleteResource(request) {
1891
+ return unaryCall.call(this, "DeleteResource", request);
1892
+ }
1893
+
1894
+ DiscoverServices(request) {
1895
+ return unaryCall.call(this, "DiscoverServices", request);
1896
+ }
1897
+
1898
+ GetResource(request) {
1899
+ return unaryCall.call(this, "GetResource", request);
1900
+ }
1901
+
1902
+ GetResources(request) {
1903
+ return unaryCall.call(this, "GetResources", request);
1904
+ }
1905
+
1906
+ GetResourceAncestors(request) {
1907
+ return unaryCall.call(this, "GetResourceAncestors", request);
1908
+ }
1909
+
1910
+ GetResourceDescendants(request) {
1911
+ return unaryCall.call(this, "GetResourceDescendants", request);
1912
+ }
1913
+
1914
+ RequestAccess(request) {
1915
+ return unaryCall.call(this, "RequestAccess", request);
1916
+ }
1917
+
1918
+ SaveResource(request) {
1919
+ return unaryCall.call(this, "SaveResource", request);
1920
+ }
1921
+
1922
+ SaveResourceAttribute(request) {
1923
+ return unaryCall.call(this, "SaveResourceAttribute", request);
1924
+ }
1925
+
1926
+ SetServiceStatus(request) {
1927
+ return unaryCall.call(this, "SetServiceStatus", request);
1928
+ }
1929
+
1930
+ /**
1931
+ * VoltAPI - volt management
1932
+ */
1933
+
1934
+ Bind(request) {
1935
+ return unaryCall.call(this, "Bind", request);
1936
+ }
1937
+
1938
+ DeleteAccess(request) {
1939
+ return unaryCall.call(this, "DeleteAccess", request);
1940
+ }
1941
+
1942
+ DeleteVolt(request) {
1943
+ return unaryCall.call(this, "DeleteVolt", request);
1944
+ }
1945
+
1946
+ GetAccess(request) {
1947
+ return unaryCall.call(this, "GetAccess", request);
1948
+ }
1949
+
1950
+ GetBindings(request) {
1951
+ return unaryCall.call(this, "GetBindings", request);
1952
+ }
1953
+
1954
+ GetIdentities(request) {
1955
+ return unaryCall.call(this, "GetIdentities", request);
1956
+ }
1957
+
1958
+ GetIdentity(request) {
1959
+ return unaryCall.call(this, "GetIdentity", request);
1960
+ }
1961
+
1962
+ GetIdentityToken(request) {
1963
+ return unaryCall.call(this, "GetIdentityToken", request);
1964
+ }
1965
+
1966
+ GetPolicy(request) {
1967
+ return unaryCall.call(this, "GetPolicy", request);
1968
+ }
1969
+
1970
+ GetSettings(request) {
1971
+ return unaryCall.call(this, "GetSettings", request);
1972
+ }
1973
+
1974
+ SaveAccess(request) {
1975
+ return unaryCall.call(this, "SaveAccess", request);
1976
+ }
1977
+
1978
+ SaveCloudConnection(request) {
1979
+ return unaryCall.call(this, "SaveCloudConnection", request);
1980
+ }
1981
+
1982
+ SaveIdentity(request) {
1983
+ return unaryCall.call(this, "SaveIdentity", request);
1984
+ }
1985
+
1986
+ SaveSettings(request) {
1987
+ return unaryCall.call(this, "SaveSettings", request);
1988
+ }
1989
+
1990
+ SetAccessRequestDecision(request) {
1991
+ return unaryCall.call(this, "SetAccessRequestDecision", request);
1992
+ }
1993
+
1994
+ SetBindingDecision(request) {
1995
+ return unaryCall.call(this, "SetBindingDecision", request);
1996
+ }
1997
+
1998
+ Shutdown(request) {
1999
+ return unaryCall.call(this, "Shutdown", request);
2000
+ }
2001
+
2002
+ SignVerify(request) {
2003
+ return unaryCall.call(this, "SignVerify", request);
2004
+ }
2005
+
2006
+ /**
2007
+ * FileAPI
2008
+ */
2009
+ GetFileDescendants(request) {
2010
+ return unaryCall.call(this, "GetFileDescendants", request);
2011
+ }
2012
+
2013
+ /**
2014
+ * Asynchronous file download
2015
+ * @param {*} request
2016
+ * @returns stream
2017
+ */
2018
+ DownloadFile(request) {
2019
+ const grpcClient = this.getVoltAPIClient();
2020
+
2021
+ const downloadCall = new GRPCCall(
2022
+ this,
2023
+ "DownloadFile",
2024
+ "METHOD_TYPE_SERVER_STREAM",
2025
+ );
2026
+ return downloadCall.start(grpcClient, request);
2027
+ }
2028
+
2029
+ UploadFile(request) {
2030
+ const grpcClient = this.getVoltAPIClient();
2031
+
2032
+ const uploadCall = new GRPCCall(this, "UploadFile", "METHOD_TYPE_BIDI");
2033
+ return uploadCall.start(grpcClient, request);
2034
+ }
2035
+
2036
+ /**
2037
+ * Synchronous file download
2038
+ * @param {*} request
2039
+ * @returns file buffer, base64 encoded
2040
+ */
2041
+ DownloadFileSync(request) {
2042
+ return new Promise((resolve, reject) => {
2043
+ const grpcClient = this.getVoltAPIClient();
2044
+
2045
+ const blocks = [];
2046
+
2047
+ const call = new GRPCCall(
2048
+ this,
2049
+ "DownloadFile",
2050
+ "METHOD_TYPE_SERVER_STREAM",
2051
+ );
2052
+
2053
+ call.on("data", (response) => {
2054
+ switch (response.payload) {
2055
+ case "block": {
2056
+ blocks.push(response.block);
2057
+ break;
2058
+ }
2059
+ case "status": {
2060
+ if (response.status.message) {
2061
+ return call.reject(new Error(response.status.message));
2062
+ }
2063
+ break;
2064
+ }
2065
+ default:
2066
+ log("unrecognised response payload %s", response.payload);
2067
+ break;
2068
+ }
2069
+ });
2070
+
2071
+ call.on("error", (err) => {
2072
+ log("error during DownloadFile [%s]", err.message);
2073
+ reject(err);
2074
+ });
2075
+
2076
+ call.on("end", () => {
2077
+ log("Download finished");
2078
+ resolve({ buffer: Buffer.concat(blocks).toString("base64") });
2079
+ });
2080
+
2081
+ call.start(grpcClient, request);
2082
+ });
2083
+ }
2084
+
2085
+ /**
2086
+ * SqliteDatabaseAPI
2087
+ */
2088
+ SqlExecute(request) {
2089
+ const grpcClient = this.getVoltAPIClient();
2090
+
2091
+ const subscribeCall = new GRPCCall(this, "Execute", "METHOD_TYPE_BIDI");
2092
+ return subscribeCall.start(grpcClient, request);
2093
+ }
2094
+
2095
+ /**
2096
+ * WireAPI
2097
+ */
2098
+ PublishWire(request) {
2099
+ const grpcClient = this.getVoltAPIClient();
2100
+
2101
+ const publishCall = new GRPCCall(this, "PublishWire", "METHOD_TYPE_BIDI");
2102
+ return publishCall.start(grpcClient, request);
2103
+ }
2104
+
2105
+ SubscribeWire(request) {
2106
+ const grpcClient = this.getVoltAPIClient();
2107
+
2108
+ const subscribeCall = new GRPCCall(
2109
+ this,
2110
+ "SubscribeWire",
2111
+ "METHOD_TYPE_BIDI",
2112
+ );
2113
+ return subscribeCall.start(grpcClient, request);
2114
+ }
2120
2115
  }
2121
2116
 
2122
2117
  Object.defineProperty(exports, 'generateId', {
2123
- enumerable: true,
2124
- get: function () { return uuid.v4; }
2118
+ enumerable: true,
2119
+ get: function () { return uuid.v4; }
2125
2120
  });
2126
2121
  exports.VoltClient = VoltClient;
2127
2122
  exports.VoltCredential = VoltCredential;