@tdxvolt/volt-client-grpc 0.1.1 → 0.1.2

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,258 +34,270 @@ 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: "volt.tdxcloud.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
- tunnelAPI: "tdx.volt_api.tunnel.v1.TunnelAPI",
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
- const {pki: pki$2} = forge__default["default"];
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 && 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 */
226
226
 
227
- const {forEach, isEmpty} = lodash__default["default"];
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
- function createClient(grpc, serviceDescriptors, address, cryptoOptions, noSerialise, voltId, serviceId) {
234
- let credentials;
235
- if (cryptoOptions) {
236
- const ca = cryptoOptions.tunnel_ca_pem || cryptoOptions.ca;
237
- const cert = cryptoOptions.cloud_cert || cryptoOptions.cert;
238
- const {key} = cryptoOptions;
239
-
240
- if (key && cert) {
241
- const cryyptoOptions = {ca, cert, key};
242
- const tlsOptions = createSecureContextOptions(cryyptoOptions);
243
- credentials = grpc.credentials.createSsl(tlsOptions.ca, tlsOptions.key, tlsOptions.cert);
244
- } else {
245
- // Cache has no private key, this implies we are connecting without supplying a client certificate.
246
- // Simply connect using the CA certificate.
247
- credentials = grpc.credentials.createSsl(Buffer.from(ca));
248
- }
249
- } else {
250
- credentials = grpc.credentials.createInsecure();
251
- }
252
-
253
- // Force all methods to appear bi-directional, since we manually handle the call flow (see grpc-call.js).
254
- forEach(serviceDescriptors, (method) => {
255
- method.requestStream = true;
256
- method.responseStream = true;
257
- });
258
-
259
- if (noSerialise || voltId) {
260
- if (voltId && !serviceId) {
261
- // We must have a service identifier id when tunnelling in order to be able to route the rpc.
262
- throw new Error("createClient - no serviceId given");
263
- }
264
- forEach(serviceDescriptors, (method) => {
265
- if (voltId) {
266
- // Prepend the resource id to the method path if tunnelling.
267
- if (voltId === serviceId) {
268
- method.path = `/${voltId}${method.path}`;
269
- } else {
270
- method.path = `/${voltId}/${serviceId}${method.path}`;
271
- }
272
- }
273
- if (noSerialise) {
274
- // Save original serialisation methods so we can encrypt/decrypt in the tunnel.
275
- method.requestSerializeOriginal = method.requestSerialize;
276
- method.responseDeserializeOriginal = method.responseDeserialize;
277
-
278
- // Replace default serialisation with NOP, since when tunnelling we encrypt the payload
279
- // before sending it to the channel, so we want it to be treated as a pure byte array (pass-through).
280
- method.requestSerialize = serialiseNOP;
281
- method.responseDeserialize = serialiseNOP;
282
- }
283
- });
284
- }
285
-
286
- const SubClass = grpc.makeGenericClientConstructor(serviceDescriptors);
287
-
288
- return new SubClass(address, credentials);
233
+ function createClient(
234
+ grpc,
235
+ serviceDescriptors,
236
+ address,
237
+ credentials,
238
+ noSerialise,
239
+ voltId,
240
+ serviceId,
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);
289
301
  }
290
302
 
291
303
  /**
@@ -293,424 +305,484 @@ function createClient(grpc, serviceDescriptors, address, cryptoOptions, noSerial
293
305
  * @param {*} api
294
306
  */
295
307
  const grpcWrap = (api) => {
296
- const grpcRoutes = {};
297
-
298
- // Don't bother trying to wrap class constructors.
299
- const ignoreMethods = ["constructor"];
300
-
301
- // Support class instances and PoJos
302
- const isClass = api.constructor !== Object;
303
- const iterate = Object.getOwnPropertyNames(isClass ? api.constructor.prototype : api);
304
-
305
- // Wrap each method on the api.
306
- forEach(iterate, (name) => {
307
- if (!ignoreMethods.includes(name)) {
308
- const method = api[name];
309
- grpcRoutes[name] = (call, callback) => {
310
- // Execute the method in the context of the api.
311
- const resultPromise = method.call(api, call.request, call);
312
-
313
- // Enforce promise results.
314
- if (!resultPromise || !resultPromise.then || !resultPromise.catch) {
315
- throw new Error(`implementation methods must return a Promise [${name}]`);
316
- }
317
-
318
- resultPromise
319
- .then((response) => {
320
- if (callback) {
321
- callback(null, response);
322
- }
323
- })
324
- .catch((err) => {
325
- if (callback) {
326
- callback(err);
327
- } else {
328
- // TODO - verify this => how to send error to stream clients?
329
- // Destroy stream on error.
330
- call.destroy(err);
331
- }
332
- });
333
- };
334
- }
335
- });
336
-
337
- 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;
338
354
  };
339
355
 
340
356
  class GrpcServer {
341
- constructor(options) {
342
- const {grpc, serviceDescriptors, routes, tlsOptions, requireClientCert} = options;
343
- this._grpc = grpc;
344
- this._server = new this._grpc.Server();
345
-
346
- if (tlsOptions) {
347
- const caList = [];
348
- const certChains = [];
349
- [].concat(tlsOptions).forEach((tlsOption) => {
350
- caList.push(tlsOption.ca);
351
- certChains.push({
352
- private_key: tlsOption.key,
353
- cert_chain: tlsOption.cert,
354
- });
355
- });
356
-
357
- // Create credentials from the volt CA and our own pub/priv key.
358
- this._credentials = grpc.ServerCredentials.createSsl(
359
- // The root CAs we trust when verifying client certificates.
360
- Buffer.concat(caList),
361
- // The certificate chain(s) we present to the client.
362
- certChains,
363
- // Flag indicating if we insist on a client certificate.
364
- requireClientCert
365
- );
366
- } else {
367
- this._credentials = grpc.ServerCredentials.createInsecure();
368
- }
369
-
370
- if (serviceDescriptors && !isEmpty(routes)) {
371
- // Enforce decoupling of the service implementation.
372
- const router = grpcWrap(routes);
373
- this._server.addService(serviceDescriptors, router);
374
- }
375
- }
376
-
377
- close(force = false) {
378
- return new Promise((resolve) => {
379
- if (force) {
380
- resolve(this._server.forceShutdown());
381
- } else {
382
- this._server.tryShutdown(resolve);
383
- }
384
- });
385
- }
386
-
387
- listen(address) {
388
- return new Promise((resolve, reject) => {
389
- // If no port is specified, grpc will return the port number assigned.
390
- this._server.bindAsync(address, this._credentials, (err, port) => {
391
- if (err) {
392
- reject(err);
393
- } else {
394
- this._port = port;
395
- this._server.start();
396
- resolve(port);
397
- }
398
- });
399
- });
400
- }
401
-
402
- get server() {
403
- return this._server;
404
- }
405
-
406
- get port() {
407
- return this._port;
408
- }
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
+ }
409
433
  }
410
434
 
411
- function createServer(grpc, serviceDescriptors, routes, cryptoCache, requireClientCert) {
412
- let tlsOptions;
413
- if (cryptoCache) {
414
- tlsOptions = createSecureContextOptions(cryptoCache);
415
- }
416
- const serverArgs = {grpc, serviceDescriptors, routes, tlsOptions, requireClientCert};
417
- return new GrpcServer(serverArgs);
435
+ function createServer(
436
+ grpc,
437
+ serviceDescriptors,
438
+ routes,
439
+ cryptoCache,
440
+ requireClientCert,
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);
418
454
  }
419
455
 
420
456
  var grpcUtils = /*#__PURE__*/Object.freeze({
421
- __proto__: null,
422
- createClient: createClient,
423
- createServer: createServer
457
+ __proto__: null,
458
+ createClient: createClient,
459
+ createServer: createServer
424
460
  });
425
461
 
426
462
  /* eslint-disable no-underscore-dangle */
427
463
 
428
- const {pki: pki$1} = forge__default["default"];
429
- const log$4 = debug__default["default"]("volt-client-grpc:volt-crypto");
464
+ const { pki: pki$1 } = forge__default["default"];
465
+ const log$4 = debug__default["default"]("volt-client-grpc:volt-credential");
430
466
  const aesAlgorithm = "aes-256-cbc";
431
467
  const rs256Algorithm = "RS256";
432
468
 
433
- class VoltCrypto {
434
- constructor(config, configPath) {
435
- this._config = config;
436
- this._voltConfig = config.volt;
437
- this._configPath = configPath;
438
- this._persistCache = !!configPath;
439
-
440
- if (!this._config.crypto) {
441
- this._cryptoCache = {};
442
- } else {
443
- this._cryptoCache = this._config.crypto;
444
- }
445
-
446
- // Determine if we're dealing with an encrypted key.
447
- if (this._cryptoCache.key && this._cryptoCache.key.startsWith(constants.privateEncryptedKeyPrefix)) {
448
- if (!config.p) {
449
- throw new Error("encrypted key but no passphrase given");
450
- }
451
-
452
- // Need to unencrypt the private key.
453
- const decrypted = pki$1.decryptRsaPrivateKey(this._cryptoCache.key, config.p);
454
- if (!decrypted) {
455
- throw new Error("failed to decrypt key - check passphrase");
456
- }
457
- this._cryptoCache.key = pki$1.privateKeyToPem(decrypted);
458
- }
459
-
460
- if (this._voltConfig.ca_pem) {
461
- if (!this._cryptoCache.ca) {
462
- // Copy the volt signing CA to the crypto cache.
463
- this._cryptoCache.ca = this._voltConfig.ca_pem;
464
- }
465
-
466
- // Pre-cache the target Volt public key by extracting it from the signing certificate.
467
- const voltCert = forge__default["default"].pki.certificateFromPem(this._voltConfig.ca_pem);
468
-
469
- // Extract Volt public key (used for encrypting tunnel payloads).
470
- this._voltPublicKey = forge__default["default"].pki.publicKeyToPem(voltCert.publicKey);
471
- this._voltFingerprint = createPublicKeyFingerprint(this._voltPublicKey);
472
- }
473
- }
474
-
475
- get cache() {
476
- return this._cryptoCache;
477
- }
478
-
479
- get voltFingerprint() {
480
- return this._voltFingerprint;
481
- }
482
-
483
- getKey() {
484
- const privateKey = pki$1.privateKeyFromPem(this._cryptoCache.key);
485
- const publicKey = pki$1.rsa.setPublicKey(privateKey.n, privateKey.e);
486
- return {privateKey, publicKey};
487
- }
488
-
489
- /**
490
- * Retrieves and re-hydrates the pub/priv key data from the crypto cache, or creates one if not found.
491
- */
492
- createKey() {
493
- if (!this._cryptoCache.key) {
494
- log$4("creating key");
495
- return new Promise((resolve, reject) => {
496
- pki$1.rsa.generateKeyPair({bits: 2048, workers: -1}, (err, keypair) => {
497
- if (err) {
498
- log$4("failure creating key [%s]", err.message);
499
- reject(err);
500
- } else {
501
- log$4("successfully created key");
502
- const pem = pki$1.privateKeyToPem(keypair.privateKey);
503
- this._cryptoCache.key = pem;
504
- resolve(keypair);
505
- }
506
- });
507
- });
508
- } else {
509
- // Already have key data in the cache in pem format => create fully-formed pki instances.
510
- log$4("createKey - key already exists");
511
- return Promise.resolve(this.getKey());
512
- }
513
- }
514
-
515
- /**
516
- * Create cryptocache from scratch.
517
- */
518
- initialise() {
519
- return this.createKey()
520
- .then((/* keypair */) => {
521
- return this.saveCache();
522
- })
523
- .then(() => {
524
- return this._cryptoCache;
525
- })
526
- .catch((err) => {
527
- log$4("failure initialising crypto [%s]", err.message);
528
- return Promise.reject(err);
529
- });
530
- }
531
-
532
- get isBound() {
533
- return !!this._cryptoCache.cert;
534
- }
535
-
536
- /**
537
- * Signs the identity resource id using the private key.
538
- * @param {*} audience the token audience (usually the target volt)
539
- * @param {*} tunnelling flag indicating if the token is required for a tunnelled connection
540
- * @param {*} ttl time to live in seconds (default to 1 minute)
541
- */
542
- getIdentityToken(audience, tunnelling = false, ttl = 60) {
543
- if (!this._cryptoCache.key) {
544
- throw new Error("crypto cache invalid");
545
- }
546
-
547
- const keyPair = this.getKey();
548
- const publicKeyPem = pki$1.publicKeyToPem(keyPair.publicKey);
549
- const base58Key = createPublicKeyFingerprint(publicKeyPem);
550
-
551
- log$4("base58 key is %s", base58Key);
552
-
553
- // Allow TTL either side of the current time.
554
- // @todo - fix with server time sync on volt connection.
555
- const payload = {
556
- aud: audience,
557
- iat: Math.floor(Date.now() / 1000) - ttl,
558
- exp: Math.floor(Date.now() / 1000) + ttl,
559
- };
560
-
561
- let sharedKey;
562
- if (tunnelling) {
563
- // Initialise the tunnel encryption key.
564
- sharedKey = VoltCrypto.aesCreateKey();
565
-
566
- // Encrypt the key details using the target Volt public key and include this in the JWT payload.
567
- payload.sk = VoltCrypto.rsaEncrypt(this._voltPublicKey, sharedKey.key);
568
- payload.iv = VoltCrypto.rsaEncrypt(this._voltPublicKey, sharedKey.iv);
569
- }
570
-
571
- // Sign synchronously.
572
- const token = jwt__default["default"].sign(payload, this._cryptoCache.key, {algorithm: rs256Algorithm, keyid: base58Key});
573
-
574
- // Return the token along with any encryption key details.
575
- return {
576
- token,
577
- sharedKey,
578
- };
579
- }
580
-
581
- /**
582
- * Creates the metadata required for a grpc call, including the Volt authentication token
583
- * and optionally tunnel encryption parameters.
584
- * @param {*} grpc
585
- * @param {*} audience
586
- * @param {*} tunnelling
587
- * @param {*} ttl
588
- * @returns
589
- */
590
- getIdentityMetadata(grpc, audience, tunnelling = false, ttl = 60) {
591
- const identityToken = this.getIdentityToken(audience || this._voltConfig.id, tunnelling, ttl);
592
- const metadata = new grpc.Metadata();
593
- metadata.add(constants.authTokenName, identityToken.token);
594
- return {...identityToken, metadata};
595
- }
596
-
597
- saveCache() {
598
- if (this._persistCache) {
599
- const clone = {...this._config, crypto: {...this._cryptoCache}};
600
- if (this._config.p) {
601
- // A passphrase option exists => encrypt the key before writing the cache file.
602
- const privateKey = pki$1.privateKeyFromPem(this._cryptoCache.key);
603
- clone.crypto.key = pki$1.encryptRsaPrivateKey(privateKey, this._config.p);
604
- }
605
- fs__default["default"].writeFileSync(this._configPath, JSON.stringify(clone, null, 2));
606
- }
607
- }
608
-
609
- static aesCreateKey() {
610
- // Generate random key for the AES-256 algorithm (32 bytes = 256 bits).
611
- const key = crypto__default["default"].randomBytes(32);
612
- const iv = crypto__default["default"].randomBytes(16);
613
- return {key, iv};
614
- }
615
-
616
- static aesEncrypt(buffer, key, iv) {
617
- const cipher = crypto__default["default"].createCipheriv(aesAlgorithm, key, iv);
618
- const crypted = Buffer.concat([cipher.update(buffer), cipher.final()]);
619
- return crypted;
620
- }
621
-
622
- static aesDecrypt(buffer, key, iv) {
623
- const cipher = crypto__default["default"].createDecipheriv(aesAlgorithm, key, iv);
624
- const decrypted = Buffer.concat([cipher.update(buffer), cipher.final()]);
625
- return decrypted;
626
- }
627
-
628
- static rsaEncrypt(key, buffer) {
629
- return crypto__default["default"].publicEncrypt(key, buffer).toString("base64");
630
- }
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
+ }
631
691
  }
632
692
 
633
- const {get: getProp, snakeCase} = lodash__default["default"];
693
+ const { get: getProp, snakeCase } = lodash__default["default"];
634
694
  const __dirname$1 = path.dirname(url.fileURLToPath((typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.src || new URL('index.cjs', document.baseURI).href))));
635
695
 
636
696
  const defaultProtoPath = path.join(__dirname$1, "../protobuf");
637
697
 
638
698
  const defaultLoaderOptions = {
639
- keepCase: true,
640
- longs: String,
641
- enums: String,
642
- defaults: true,
643
- oneofs: true,
644
- includeDirs: [defaultProtoPath],
699
+ keepCase: true,
700
+ longs: String,
701
+ enums: String,
702
+ defaults: true,
703
+ oneofs: true,
704
+ includeDirs: [defaultProtoPath],
645
705
  };
646
706
 
647
707
  const getProtoDescriptors = (grpc, protoPath, opts) => {
648
- const definition = protoLoader__default["default"].loadSync(protoPath, opts || defaultLoaderOptions);
649
- const descriptors = grpc.loadPackageDefinition(definition);
650
- return descriptors;
708
+ const definition = protoLoader__default["default"].loadSync(
709
+ protoPath,
710
+ opts || defaultLoaderOptions,
711
+ );
712
+ const descriptors = grpc.loadPackageDefinition(definition);
713
+ return descriptors;
651
714
  };
652
715
 
653
716
  const getProtoService = (descriptors, servicePath) => {
654
- const serviceDef = getProp(descriptors, servicePath);
655
- if (serviceDef && serviceDef.service) {
656
- return serviceDef.service;
657
- } else {
658
- return null;
659
- }
717
+ const serviceDef = getProp(descriptors, servicePath);
718
+ if (serviceDef?.service) {
719
+ return serviceDef.service;
720
+ } else {
721
+ return null;
722
+ }
660
723
  };
661
724
 
662
- function getServiceDescriptorsFromPath(grpc, protoPath, servicePackageName, opts) {
663
- let serviceDescriptors;
664
- const descriptors = getProtoDescriptors(grpc, protoPath, opts);
665
- if (descriptors) {
666
- serviceDescriptors = getProtoService(descriptors, servicePackageName);
667
- }
668
-
669
- return serviceDescriptors;
725
+ function getServiceDescriptorsFromPath(
726
+ grpc,
727
+ protoPath,
728
+ servicePackageName,
729
+ opts,
730
+ ) {
731
+ let serviceDescriptors;
732
+ const descriptors = getProtoDescriptors(grpc, protoPath, opts);
733
+ if (descriptors) {
734
+ serviceDescriptors = getProtoService(descriptors, servicePackageName);
735
+ }
736
+
737
+ return serviceDescriptors;
670
738
  }
671
739
 
672
740
  function getServiceDescriptors(grpc, servicePackageName, opts) {
673
- // Extract the package components of the service proto package. The package name should be of the
674
- // fully qualified proto path, e.g. tdx.volt_api.webcam.v1.WebcamControlAPI.
675
- const protoServiceComponents = servicePackageName.split(".");
676
-
677
- // The service name is the last component of the path.
678
- const protoServiceName = protoServiceComponents.pop();
679
-
680
- // The actual file name should match the snake case of the service name.
681
- const protoFileName = snakeCase(protoServiceName).toLowerCase() + ".proto";
682
-
683
- // The path to the proto file should match each component of the package name.
684
- const protoPath = path.join(defaultProtoPath, ...protoServiceComponents, protoFileName);
685
-
686
- let serviceDescriptors;
687
- const descriptors = getProtoDescriptors(grpc, protoPath, opts);
688
- if (descriptors) {
689
- serviceDescriptors = getProtoService(descriptors, servicePackageName);
690
- }
691
-
692
- 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;
693
765
  }
694
766
 
695
767
  function getProtoDescriptorMethods(protoDescriptor) {
696
- const methods = [];
697
- Object.keys(protoDescriptor).forEach((methodName) => {
698
- const method = protoDescriptor[methodName];
699
- methods.push({
700
- path: method.path,
701
- client_streaming: method.requestStream,
702
- server_streaming: method.responseStream,
703
- });
704
- });
705
- 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;
706
778
  }
707
779
 
708
780
  var protoUtils = /*#__PURE__*/Object.freeze({
709
- __proto__: null,
710
- getServiceDescriptors: getServiceDescriptors,
711
- getProtoDescriptors: getProtoDescriptors,
712
- getProtoDescriptorMethods: getProtoDescriptorMethods,
713
- getServiceDescriptorsFromPath: getServiceDescriptorsFromPath
781
+ __proto__: null,
782
+ getServiceDescriptors: getServiceDescriptors,
783
+ getProtoDescriptors: getProtoDescriptors,
784
+ getProtoDescriptorMethods: getProtoDescriptorMethods,
785
+ getServiceDescriptorsFromPath: getServiceDescriptorsFromPath
714
786
  });
715
787
 
716
788
  /* eslint-disable no-underscore-dangle */
@@ -718,15 +790,15 @@ var protoUtils = /*#__PURE__*/Object.freeze({
718
790
  const log$3 = debug__default["default"]("volt-client-grpc:grpc-call");
719
791
 
720
792
  function _prepareInvokeRequest(request, method, methodType = "METHOD_TYPE_UNARY") {
721
- const meta = this._voltClient.crypto.getIdentityMetadata(
793
+ const meta = this._voltClient.credential.getIdentityMetadata(
722
794
  this._voltClient.grpc,
723
- this._voltClient.options.id,
795
+ this._voltClient.voltConfig.id,
724
796
  this._voltClient.isRemote
725
797
  );
726
798
  let invokeRequest;
727
799
  let callMethod = method;
728
800
 
729
- if (this._voltClient.isVoltTunnel) {
801
+ if (this._voltClient.isVoltRelay) {
730
802
  let requestPayload;
731
803
  if (request) {
732
804
  requestPayload = this._grpcClient[method].requestSerialize(request);
@@ -747,23 +819,23 @@ function _prepareInvokeRequest(request, method, methodType = "METHOD_TYPE_UNARY"
747
819
  },
748
820
  };
749
821
 
750
- const invokePayload = VoltCrypto.aesEncrypt(
822
+ const invokePayload = VoltCredential.aesEncrypt(
751
823
  tunnelMethod.responseSerialize(tunnelResp),
752
824
  meta.sharedKey.key,
753
825
  meta.sharedKey.iv
754
826
  );
755
827
 
756
- log$3("target volt is %s", this._voltClient.crypto.voltFingerprint);
828
+ log$3("target volt is %s", this._voltClient.credential.voltFingerprint);
757
829
 
758
830
  invokeRequest = {
759
- identity_fingerprint: this._voltClient.crypto.voltFingerprint,
831
+ identity_fingerprint: this._voltClient.credential.voltFingerprint,
760
832
  payload: invokePayload,
761
833
  };
762
834
 
763
835
  callMethod = "Invoke";
764
836
  } else if (this._voltClient.isRemote && meta.sharedKey.key) {
765
837
  const requestPayload = this._grpcClient[method].requestSerializeOriginal(request);
766
- invokeRequest = VoltCrypto.aesEncrypt(requestPayload, meta.sharedKey.key, meta.sharedKey.iv);
838
+ invokeRequest = VoltCredential.aesEncrypt(requestPayload, meta.sharedKey.key, meta.sharedKey.iv);
767
839
  } else {
768
840
  invokeRequest = request;
769
841
  }
@@ -779,7 +851,7 @@ function _prepareInvokeRequest(request, method, methodType = "METHOD_TYPE_UNARY"
779
851
  function _preparePayloadRequest(request, invokeInfo) {
780
852
  let payloadRequest;
781
853
 
782
- if (this._voltClient.isVoltTunnel) {
854
+ if (this._voltClient.isVoltRelay) {
783
855
  const requestPayload = this._grpcClient[invokeInfo.methodName].requestSerialize(request);
784
856
  const tunnelMethod = this._grpcClient.Tunnel;
785
857
 
@@ -789,7 +861,7 @@ function _preparePayloadRequest(request, invokeInfo) {
789
861
  },
790
862
  };
791
863
 
792
- const invokePayload = VoltCrypto.aesEncrypt(
864
+ const invokePayload = VoltCredential.aesEncrypt(
793
865
  tunnelMethod.responseSerialize(tunnelResp),
794
866
  invokeInfo.meta.sharedKey.key,
795
867
  invokeInfo.meta.sharedKey.iv
@@ -800,7 +872,11 @@ function _preparePayloadRequest(request, invokeInfo) {
800
872
  };
801
873
  } else if (this._voltClient.isRemote && invokeInfo.meta.sharedKey.key) {
802
874
  const requestPayload = this._grpcClient[invokeInfo.methodName].requestSerializeOriginal(request);
803
- payloadRequest = VoltCrypto.aesEncrypt(requestPayload, invokeInfo.meta.sharedKey.key, invokeInfo.meta.sharedKey.iv);
875
+ payloadRequest = VoltCredential.aesEncrypt(
876
+ requestPayload,
877
+ invokeInfo.meta.sharedKey.key,
878
+ invokeInfo.meta.sharedKey.iv
879
+ );
804
880
  } else {
805
881
  payloadRequest = request;
806
882
  }
@@ -811,9 +887,9 @@ function _preparePayloadRequest(request, invokeInfo) {
811
887
  function _parseResponse(method, meta, response) {
812
888
  let invokeResponse = {};
813
889
 
814
- if (this._voltClient.isVoltTunnel) {
890
+ if (this._voltClient.isVoltRelay) {
815
891
  if (response.payload) {
816
- const decryptedPayload = VoltCrypto.aesDecrypt(response.payload, meta.sharedKey.key, meta.sharedKey.iv);
892
+ const decryptedPayload = VoltCredential.aesDecrypt(response.payload, meta.sharedKey.key, meta.sharedKey.iv);
817
893
  const tunnelMethod = this._grpcClient.Tunnel;
818
894
  const responsePayload = tunnelMethod.requestDeserialize(decryptedPayload);
819
895
  if (responsePayload.payload === "method_payload") {
@@ -823,7 +899,7 @@ function _parseResponse(method, meta, response) {
823
899
  } else {
824
900
  log$3("unexpected tunnel payload type: %s", responsePayload.payload);
825
901
  }
826
- } else if (response.status && response.status.message) {
902
+ } else if (response.status?.message) {
827
903
  // There's been an error in the tunnel.
828
904
  log$3(
829
905
  "Tunnel status received: %s, code %d, description: %s",
@@ -837,7 +913,7 @@ function _parseResponse(method, meta, response) {
837
913
  log$3("started method %d for %s", invokeResponse.methodId, method);
838
914
  }
839
915
  } else if (this._voltClient.isRemote && meta.sharedKey.key) {
840
- const decryptedPayload = VoltCrypto.aesDecrypt(response, meta.sharedKey.key, meta.sharedKey.iv);
916
+ const decryptedPayload = VoltCredential.aesDecrypt(response, meta.sharedKey.key, meta.sharedKey.iv);
841
917
  invokeResponse = {payload: this._grpcClient[method].responseDeserializeOriginal(decryptedPayload)};
842
918
  } else {
843
919
  invokeResponse = {payload: response};
@@ -943,28 +1019,28 @@ class GRPCCall extends EventEmitter__default["default"] {
943
1019
  }
944
1020
 
945
1021
  end() {
946
- if (!this._call) {
947
- throw new Error("call not initialised");
948
- } else {
1022
+ if (this._call) {
949
1023
  return this._call.end();
1024
+ } else {
1025
+ throw new Error("call not initialised");
950
1026
  }
951
1027
  }
952
1028
 
953
1029
  get writable() {
954
- return this._call && this._call.writable;
1030
+ return this._call?.writable;
955
1031
  }
956
1032
 
957
1033
  write(request) {
958
1034
  if (!this._call) {
959
1035
  log$3("ERROR - call not initialised");
960
1036
  throw new Error("call not initialised");
961
- } else if (!this._initialRequest) {
1037
+ } else if (this._initialRequest) {
1038
+ const payloadRequest = _preparePayloadRequest.call(this, request, this._initialRequest);
1039
+ return this._call.write(payloadRequest);
1040
+ } else {
962
1041
  this._initialRequest = _prepareInvokeRequest.call(this, request, this._methodName, this._methodType);
963
1042
 
964
1043
  return this._call.write(this._initialRequest.request);
965
- } else {
966
- const payloadRequest = _preparePayloadRequest.call(this, request, this._initialRequest);
967
- return this._call.write(payloadRequest);
968
1044
  }
969
1045
  }
970
1046
  }
@@ -974,924 +1050,1015 @@ class GRPCCall extends EventEmitter__default["default"] {
974
1050
  const log$2 = debug__default["default"]("volt-client-grpc:volt-connection");
975
1051
 
976
1052
  function _cleanUp(immediateReconnect) {
977
- this._session = false;
978
-
979
- if (this._pingTimer) {
980
- clearTimeout(this._pingTimer);
981
- this._pingTimer = 0;
982
- }
983
-
984
- if (this._pingTimeoutTimer) {
985
- clearTimeout(this._pingTimeoutTimer);
986
- this._pingTimeoutTimer = 0;
987
- }
988
-
989
- if (!this._dying && (immediateReconnect || (this._autoRetry && !this._reconnectTimer))) {
990
- this._reconnectTimer = setTimeout(
991
- () => {
992
- this._reconnectTimer = 0;
993
- _doConnect.call(this);
994
- },
995
- immediateReconnect ? 0 : this._reconnectInterval
996
- );
997
- }
998
-
999
- if (this.call && this.call.writable) {
1000
- this.call.end();
1001
- }
1053
+ this._session = false;
1054
+
1055
+ if (this._pingTimer) {
1056
+ clearTimeout(this._pingTimer);
1057
+ this._pingTimer = 0;
1058
+ }
1059
+
1060
+ if (this._pingTimeoutTimer) {
1061
+ clearTimeout(this._pingTimeoutTimer);
1062
+ this._pingTimeoutTimer = 0;
1063
+ }
1064
+
1065
+ if (
1066
+ !this._dying &&
1067
+ (immediateReconnect || (this._autoRetry && !this._reconnectTimer))
1068
+ ) {
1069
+ this._reconnectTimer = setTimeout(
1070
+ () => {
1071
+ this._reconnectTimer = 0;
1072
+ _doConnect.call(this);
1073
+ },
1074
+ immediateReconnect ? 0 : this._reconnectInterval,
1075
+ );
1076
+ }
1077
+
1078
+ if (this.call?.writable) {
1079
+ this.call.end();
1080
+ }
1002
1081
  }
1003
1082
 
1004
1083
  function _connectionTimeoutHandler() {
1005
- log$2("server timed out");
1006
- this.emit("connected", false);
1007
- _cleanUp.call(this);
1084
+ log$2("server timed out");
1085
+ this.emit("connected", false);
1086
+ _cleanUp.call(this);
1008
1087
  }
1009
1088
 
1010
1089
  function _doConnect() {
1011
- this.call = new GRPCCall(this._voltClient, "Connect", "METHOD_TYPE_BIDI");
1012
-
1013
- const grpcClient = this._voltClient.getVoltAPIClient();
1014
- const helloPayload = {
1015
- hello: {},
1016
- };
1017
-
1018
- this.call.start(grpcClient, helloPayload, (err, response) => {
1019
- if (err) {
1020
- log$2("error on connection stream [%s]", err.message);
1021
- _cleanUp.call(this);
1022
- this.emit("error", err);
1023
- } else {
1024
- switch (response.payload) {
1025
- case "ping": {
1026
- log$2("pinged");
1027
-
1028
- // Received ping response from server => clear timeout.
1029
- if (this._pingTimeoutTimer) {
1030
- clearTimeout(this._pingTimeoutTimer);
1031
- }
1032
-
1033
- // Schedule the next ping.
1034
- _startPingTimer.call(this);
1035
- break;
1036
- }
1037
- case "hello":
1038
- if (!this._session) {
1039
- this._session = true;
1040
- this.emit("connected", this._session);
1041
- }
1042
- break;
1043
- case "evt":
1044
- log$2("received event %s", response.evt.event_type);
1045
- this.emit("evt", response.evt);
1046
- break;
1047
- default:
1048
- log$2("unimplemented connect response payload: %s", response.payload);
1049
- break;
1050
- }
1051
- }
1052
- });
1053
-
1054
- this.call
1055
- .wait()
1056
- .then(() => {
1057
- if (this._session) {
1058
- this.emit("connected", false);
1059
- }
1060
- })
1061
- .catch((err) => {
1062
- log$2("unexpected error on connect call [%s]", err.message);
1063
- })
1064
- .finally(() => {
1065
- _cleanUp.call(this);
1066
- });
1067
-
1068
- _startPingTimer.call(this);
1090
+ this.call = new GRPCCall(this._voltClient, "Connect", "METHOD_TYPE_BIDI");
1091
+
1092
+ const grpcClient = this._voltClient.getVoltAPIClient();
1093
+ const helloPayload = {
1094
+ hello: {},
1095
+ };
1096
+
1097
+ this.call.start(grpcClient, helloPayload, (err, response) => {
1098
+ if (err) {
1099
+ log$2("error on connection stream [%s]", err.message);
1100
+ _cleanUp.call(this);
1101
+ this.emit("error", err);
1102
+ } else {
1103
+ switch (response.payload) {
1104
+ case "ping": {
1105
+ log$2("pinged");
1106
+
1107
+ // Received ping response from server => clear timeout.
1108
+ if (this._pingTimeoutTimer) {
1109
+ clearTimeout(this._pingTimeoutTimer);
1110
+ }
1111
+
1112
+ // Schedule the next ping.
1113
+ _startPingTimer.call(this);
1114
+ break;
1115
+ }
1116
+ case "hello": {
1117
+ if (!this._session) {
1118
+ this._session = true;
1119
+ this.emit("connected", this._session);
1120
+ }
1121
+ break;
1122
+ }
1123
+ case "evt": {
1124
+ log$2("received event %s", response.evt.event_type);
1125
+ this.emit("evt", response.evt);
1126
+ break;
1127
+ }
1128
+ default:
1129
+ log$2("unimplemented connect response payload: %s", response.payload);
1130
+ break;
1131
+ }
1132
+ }
1133
+ });
1134
+
1135
+ this.call
1136
+ .wait()
1137
+ .then(() => {
1138
+ if (this._session) {
1139
+ this.emit("connected", false);
1140
+ }
1141
+ })
1142
+ .catch((err) => {
1143
+ log$2("unexpected error on connect call [%s]", err.message);
1144
+ })
1145
+ .finally(() => {
1146
+ _cleanUp.call(this);
1147
+ });
1148
+
1149
+ _startPingTimer.call(this);
1069
1150
  }
1070
1151
 
1071
1152
  function _startPingTimer() {
1072
- if (this._pingTimer) {
1073
- log$2("ping timer running");
1074
- return;
1075
- }
1076
-
1077
- this._pingTimer = setTimeout(() => {
1078
- // Send next ping to server.
1079
- const request = {ping: {timestamp: Date.now()}};
1080
-
1081
- log$2("pinging Volt");
1082
- this.call.write(request);
1083
-
1084
- this._pingTimer = 0;
1085
-
1086
- // Schedule a timeout if we don't hear back from the server.
1087
- if (this._pingTimeoutTimer) {
1088
- clearTimeout(this._pingTimeoutTimer);
1089
- }
1090
-
1091
- this._pingTimeoutTimer = setTimeout(_connectionTimeoutHandler.bind(this), this._timeoutInterval);
1092
- }, this._pingInterval);
1153
+ if (this._pingTimer) {
1154
+ log$2("ping timer running");
1155
+ return;
1156
+ }
1157
+
1158
+ this._pingTimer = setTimeout(() => {
1159
+ // Send next ping to server.
1160
+ const request = { ping: { timestamp: Date.now() } };
1161
+
1162
+ log$2("pinging Volt");
1163
+ this.call.write(request);
1164
+
1165
+ this._pingTimer = 0;
1166
+
1167
+ // Schedule a timeout if we don't hear back from the server.
1168
+ if (this._pingTimeoutTimer) {
1169
+ clearTimeout(this._pingTimeoutTimer);
1170
+ }
1171
+
1172
+ this._pingTimeoutTimer = setTimeout(
1173
+ _connectionTimeoutHandler.bind(this),
1174
+ this._timeoutInterval,
1175
+ );
1176
+ }, this._pingInterval);
1093
1177
  }
1094
1178
 
1095
1179
  class VoltConnection extends EventEmitter__default["default"] {
1096
- constructor(voltClient, grpc, options = {}) {
1097
- super();
1098
-
1099
- this._grpc = grpc;
1100
- this._autoRetry = options.autoReconnect === undefined ? true : !!options.autoReconnect;
1101
- this._dying = false;
1102
- this._voltClient = voltClient;
1103
- this._options = options;
1104
- this._pingTimer = 0;
1105
- this._pingTimeoutTimer = 0;
1106
- this._session = false;
1107
- this._pingInterval = options.pingInterval || 10000;
1108
- this._reconnectInterval = options.reconnectInterval || 5000;
1109
- this._timeoutInterval = options.timeoutInterval || 60000;
1110
- }
1111
-
1112
- connect(throwOnFail) {
1113
- if (throwOnFail) {
1114
- return _doConnect.call(this);
1115
- } else {
1116
- return Promise.resolve(_cleanUp.call(this, true));
1117
- }
1118
- }
1119
-
1120
- disconnect() {
1121
- this._dying = true;
1122
- _cleanUp.call(this);
1123
- }
1180
+ constructor(voltClient, grpc) {
1181
+ super();
1182
+
1183
+ const config = voltClient.config;
1184
+ this._grpc = grpc;
1185
+ this._autoRetry =
1186
+ config.autoReconnect === undefined ? true : !!config.autoReconnect;
1187
+ this._dying = false;
1188
+ this._voltClient = voltClient;
1189
+ this._pingTimer = 0;
1190
+ this._pingTimeoutTimer = 0;
1191
+ this._session = false;
1192
+ this._pingInterval = config.pingInterval || 10000;
1193
+ this._reconnectInterval = config.reconnectInterval || 5000;
1194
+ this._timeoutInterval = config.timeoutInterval || 60000;
1195
+ }
1196
+
1197
+ connect(throwOnFail) {
1198
+ if (throwOnFail) {
1199
+ return _doConnect.call(this);
1200
+ } else {
1201
+ return Promise.resolve(_cleanUp.call(this, true));
1202
+ }
1203
+ }
1204
+
1205
+ disconnect() {
1206
+ this._dying = true;
1207
+ _cleanUp.call(this);
1208
+ }
1124
1209
  }
1125
1210
 
1126
1211
  /* eslint-disable no-underscore-dangle */
1127
1212
 
1128
- const {pki} = forge__default["default"];
1129
- const {filter, pick} = lodash__default["default"];
1213
+ const { pki } = forge__default["default"];
1214
+ const { filter, pick } = lodash__default["default"];
1130
1215
 
1131
1216
  const log$1 = debug__default["default"]("volt-client-grpc:volt-client-internal");
1132
1217
  const voltServices = [
1133
- constants.serviceType.voltAPI,
1134
- constants.serviceType.fileAPI,
1135
- constants.serviceType.sqliteDatabaseAPI,
1136
- constants.serviceType.sqliteServerAPI,
1137
- constants.serviceType.ssiAPI,
1138
- constants.serviceType.tunnelAPI,
1139
- constants.serviceType.wireAPI,
1218
+ constants.serviceType.voltAPI,
1219
+ constants.serviceType.fileAPI,
1220
+ constants.serviceType.sqliteDatabaseAPI,
1221
+ constants.serviceType.sqliteServerAPI,
1222
+ constants.serviceType.ssiAPI,
1223
+ constants.serviceType.relayAPI,
1224
+ constants.serviceType.wireAPI,
1140
1225
  ];
1141
1226
 
1142
1227
  function issueBind(bindRequest, ttl) {
1143
- // eslint-disable-next-line no-use-before-define
1144
- return unaryCall.call(this, "Bind", bindRequest).then((bindResponse) => {
1145
- log$1("got binding request response %j", bindResponse);
1146
- if (bindResponse.status && bindResponse.status.code) {
1147
- log$1("error in bind response: %s", bindResponse.status.message);
1148
- return Promise.reject(new Error(bindResponse.status.message));
1149
- } else {
1150
- switch (bindResponse.decision) {
1151
- case "POLICY_DECISION_PERMIT":
1152
- //
1153
- // The request status is permit => cache the bind response info.
1154
- //
1155
-
1156
- // This is the certificate assigned to us by the volt.
1157
- this._crypto.cache.cert = bindResponse.cert;
1158
-
1159
- // This is the signing CA used by the volt (don't ov)
1160
- this._crypto.cache.ca = bindResponse.chain;
1161
-
1162
- // Resource id is assigned by the volt.
1163
- this._crypto.cache.client_id = bindResponse.identity_id;
1164
- this._crypto.saveCache();
1165
- break;
1166
- case "POLICY_DECISION_DENY":
1167
- log$1(">>>>>>>>>>>>>>> access request is DENIED <<<<<<<<<<<<<<<<<<<");
1168
- break;
1169
- case "POLICY_DECISION_PROMPT":
1170
- case "POLICY_DECISION_PENDING":
1171
- log$1("access pending approval - waiting...");
1172
- break;
1173
- default:
1174
- log$1("ignoring unknown bind descision %s", bindResponse.status);
1175
- break;
1176
- }
1177
- }
1178
-
1179
- return bindResponse.decision;
1180
- });
1228
+ // eslint-disable-next-line no-use-before-define
1229
+ return unaryCall.call(this, "Bind", bindRequest).then((bindResponse) => {
1230
+ log$1("got binding request response %j", bindResponse);
1231
+ if (bindResponse.status?.code) {
1232
+ log$1("error in bind response: %s", bindResponse.status.message);
1233
+ return Promise.reject(new Error(bindResponse.status.message));
1234
+ } else {
1235
+ switch (bindResponse.decision) {
1236
+ case "POLICY_DECISION_PERMIT": {
1237
+ //
1238
+ // The request status is permit => cache the bind response info.
1239
+ //
1240
+
1241
+ // This is the certificate assigned to us by the volt.
1242
+ this._credential.cache.cert = bindResponse.cert;
1243
+
1244
+ // This is the signing CA used by the volt (don't ov)
1245
+ this._credential.cache.ca = bindResponse.chain;
1246
+
1247
+ // Resource id is assigned by the volt.
1248
+ this._credential.cache.client_id = bindResponse.identity_id;
1249
+ this._credential.saveCache();
1250
+ break;
1251
+ }
1252
+ case "POLICY_DECISION_DENY": {
1253
+ log$1(">>>>>>>>>>>>>>> access request is DENIED <<<<<<<<<<<<<<<<<<<");
1254
+ break;
1255
+ }
1256
+ case "POLICY_DECISION_PROMPT":
1257
+ case "POLICY_DECISION_PENDING": {
1258
+ log$1("access pending approval - waiting...");
1259
+ break;
1260
+ }
1261
+ default:
1262
+ log$1("ignoring unknown bind descision %s", bindResponse.status);
1263
+ break;
1264
+ }
1265
+ }
1266
+
1267
+ return bindResponse.decision;
1268
+ });
1181
1269
  }
1182
1270
 
1183
1271
  function findDIDDocumentService(document, serviceType) {
1184
- if (!document.service) {
1185
- return [];
1186
- }
1272
+ if (!document.service) {
1273
+ return [];
1274
+ }
1187
1275
 
1188
- const matches = filter(document.service, (svc) => svc.type === serviceType);
1189
- return matches;
1276
+ const matches = filter(document.service, (svc) => svc.type === serviceType);
1277
+ return matches;
1190
1278
  }
1191
1279
 
1192
1280
  async function bindInternal() {
1193
- try {
1194
- // Build grpc client info.
1195
- if (this.isRemote) {
1196
- const getJSON = bent__default["default"]("json");
1197
- // @todo - currently Volt tunnel CA discovery is not secure.
1198
- const protocol =
1199
- this._config.tunnel_host?.startsWith("localhost:") || this._config.cloud_host?.startsWith("localhost:")
1200
- ? "http"
1201
- : "https";
1202
- const tunnelURL = `${protocol}://${this._config.tunnel_host || this._config.cloud_host}/info`;
1203
- log$1("attempting to retrieve tunnel information from %s", tunnelURL);
1204
- const tunnelInfo = await getJSON(tunnelURL);
1205
- if (!tunnelInfo.ca_pem) {
1206
- throw new Error("Unable to fetch tunnel CA - cannot securely connect.");
1207
- } else {
1208
- log$1("Auto-fetched tunnel CA %s, remote address %s", tunnelInfo.ca_pem, tunnelInfo.local_address);
1209
- this._crypto.cache.tunnel_ca_pem = tunnelInfo.ca_pem;
1210
- this._crypto.cache.tunnel_address = tunnelInfo.local_address;
1211
- }
1212
- }
1213
-
1214
- if (!this._crypto.cache.ca) {
1215
- this._crypto.cache.ca = this._options.ca_pem;
1216
- }
1217
-
1218
- if (!this._crypto.cache.ca) {
1219
- throw new Error("No certificate authority found in configuration for target Volt - check configuration");
1220
- }
1221
-
1222
- const keyPair = this._crypto.getKey();
1223
- const publicKeyPem = pki.publicKeyToPem(keyPair.publicKey);
1224
-
1225
- if (!this._crypto.cache.bindIp) {
1226
- // Default the IP address if none is specified. This is used to set the SAN in the
1227
- // certificate issued by the Volt.
1228
- this._crypto.cache.bindIp = ip__default["default"].address();
1229
- }
1230
-
1231
- this._crypto.saveCache();
1232
-
1233
- // Form the request message.
1234
- const bindRequest = {
1235
- binding_name: this._config.client_name,
1236
- public_key: publicKeyPem,
1237
- host: this._crypto.cache.bindIp,
1238
- x509_credential: [],
1239
- };
1240
-
1241
- if (this._crypto.cache.cert) {
1242
- bindRequest.x509_credential = [this._crypto.cache.cert + this._crypto.cache.ca];
1243
- }
1244
-
1245
- if (this._options.challenge_code) {
1246
- bindRequest.challenge = signBase64(keyPair.privateKey, this._options.challenge_code);
1247
- } else {
1248
- log$1("****no Volt challenge code available**** => not sending challenge signature");
1249
- }
1250
-
1251
- // For remote connections, add our cloud-issued certificate as an additional credential.
1252
- if (this._crypto.cache.tunnel_ca_pem && this._crypto.cache.cloud_cert) {
1253
- bindRequest.x509_credential = bindRequest.x509_credential.concat(
1254
- this._crypto.cache.cloud_cert + this._crypto.cache.tunnel_ca_pem
1255
- );
1256
- }
1257
-
1258
- log$1("sending %d x509 credentials", bindRequest.x509_credential.length);
1259
- log$1("bind TTL is ", this._config.bindRequestTTL);
1260
-
1261
- // This will block while the request is pending.
1262
- const pollInterval = 10000;
1263
- let decision = "";
1264
- do {
1265
- if (decision) {
1266
- // Wait a while before retrying.
1267
- // eslint-disable-next-line no-await-in-loop
1268
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
1269
- }
1270
- // eslint-disable-next-line no-await-in-loop
1271
- decision = await issueBind.call(this, bindRequest, this._options.bindRequestTTL);
1272
- log$1("binding decision: %s", decision);
1273
- } while (decision === "POLICY_DECISION_PROMPT" || decision === "POLICY_DECISION_PENDING");
1274
-
1275
- return decision === "POLICY_DECISION_PERMIT";
1276
- } catch (err) {
1277
- log$1("failure binding to Volt [%s]", err.message);
1278
- throw err;
1279
- }
1281
+ try {
1282
+ // Check if we need to resolve a Relay volt.
1283
+ if (
1284
+ this._config?.relay?.discovery_url ||
1285
+ typeof this._config?.relay === "string"
1286
+ ) {
1287
+ const relayURL = this._config.relay?.discovery_url || this._config.relay;
1288
+ log$1("attempting to retrieve Relay information from %s", relayURL);
1289
+ const voltConfig = await fetchVoltConfig.call(this, relayURL);
1290
+ this._config.relay = voltConfig.volt;
1291
+ this._config.relay.discovery_url = relayURL;
1292
+ if (this._config.relay.ca_pem) {
1293
+ log$1(
1294
+ "Auto-fetched Relay CA %s, remote address %s",
1295
+ this._config.relay.ca_pem,
1296
+ this._config.relay.address,
1297
+ );
1298
+ } else {
1299
+ throw new Error("Unable to fetch Relay CA - cannot securely connect.");
1300
+ }
1301
+ }
1302
+
1303
+ if (!this._credential.cache.ca) {
1304
+ this._credential.cache.ca = this._voltConfig.ca_pem;
1305
+ }
1306
+
1307
+ if (!this._credential.cache.ca) {
1308
+ throw new Error(
1309
+ "No certificate authority found in configuration for target Volt - check configuration",
1310
+ );
1311
+ }
1312
+
1313
+ const keyPair = this._credential.getKey();
1314
+ const publicKeyPem = pki.publicKeyToPem(keyPair.publicKey);
1315
+
1316
+ if (!this._credential.cache.bindIp) {
1317
+ // Default the IP address if none is specified. This is used to set the SAN in the
1318
+ // certificate issued by the Volt.
1319
+ this._credential.cache.bindIp = ip__default["default"].address();
1320
+ }
1321
+
1322
+ this._credential.saveCache();
1323
+
1324
+ // Form the request message.
1325
+ const bindRequest = {
1326
+ binding_name: this._config.client_name,
1327
+ public_key: publicKeyPem,
1328
+ host: this._credential.cache.bindIp,
1329
+ x509_credential: [],
1330
+ };
1331
+
1332
+ if (this._credential.cache.cert) {
1333
+ bindRequest.x509_credential = [
1334
+ this._credential.cache.cert + this._credential.cache.ca,
1335
+ ];
1336
+ }
1337
+
1338
+ if (this._voltConfig.challenge_code) {
1339
+ bindRequest.challenge = signBase64(
1340
+ keyPair.privateKey,
1341
+ this._voltConfig.challenge_code,
1342
+ );
1343
+ } else {
1344
+ log$1(
1345
+ "****no Volt challenge code available**** => not sending challenge signature",
1346
+ );
1347
+ }
1348
+
1349
+ // For remote connections, add our cloud-issued certificate as an additional credential.
1350
+ if (this._config?.relay?.ca_pem && this._credential.cache.cloud_cert) {
1351
+ bindRequest.x509_credential = bindRequest.x509_credential.concat(
1352
+ this._credential.cache.cloud_cert + this._config.relay.ca_pem,
1353
+ );
1354
+ }
1355
+
1356
+ log$1("sending %d x509 credentials", bindRequest.x509_credential.length);
1357
+ log$1("bind TTL is ", this._config.bindRequestTTL);
1358
+
1359
+ // This will block while the request is pending.
1360
+ const pollInterval = 10000;
1361
+ let decision = "";
1362
+ do {
1363
+ if (decision) {
1364
+ // Wait a while before retrying.
1365
+ // eslint-disable-next-line no-await-in-loop
1366
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
1367
+ }
1368
+ // eslint-disable-next-line no-await-in-loop
1369
+ decision = await issueBind.call(
1370
+ this,
1371
+ bindRequest,
1372
+ this._voltConfig.bindRequestTTL,
1373
+ );
1374
+ log$1("binding decision: %s", decision);
1375
+ } while (
1376
+ decision === "POLICY_DECISION_PROMPT" ||
1377
+ decision === "POLICY_DECISION_PENDING"
1378
+ );
1379
+
1380
+ return decision === "POLICY_DECISION_PERMIT";
1381
+ } catch (err) {
1382
+ log$1("failure binding to Volt [%s]", err.message);
1383
+ throw err;
1384
+ }
1280
1385
  }
1281
1386
 
1282
1387
  function connectInternal() {
1283
- try {
1284
- if (this._voltConnection) {
1285
- log$1("connectInternal - Volt connection already exists");
1286
- return Promise.resolve();
1287
- }
1288
-
1289
- const onDisconnected = () => {
1290
- this._connected = false;
1291
- this._session = null;
1292
-
1293
- if (this._cachedClient) {
1294
- this._grpc.closeClient(this._cachedClient);
1295
- this._cachedClient = null;
1296
- }
1297
-
1298
- if (this._cachedRemoteClient) {
1299
- this._grpc.closeClient(this._cachedRemoteClient);
1300
- this._cachedRemoteClient = null;
1301
- }
1302
- };
1303
-
1304
- this._voltConnection = new VoltConnection(this, this._grpc, this._options);
1305
-
1306
- this._voltConnection.on("connected", (session) => {
1307
- this._session = session;
1308
- this._connected = !!session;
1309
- if (!this._connected) {
1310
- onDisconnected();
1311
- }
1312
- this.emit("connected", !!session);
1313
- });
1314
-
1315
- this._voltConnection.on("error", (err) => {
1316
- // Should auto-reconnect.
1317
- log$1("Volt connection error [%s]", err.message);
1318
- if (this._connected) {
1319
- onDisconnected();
1320
- this.emit("connected", false);
1321
- }
1322
- });
1323
-
1324
- this._voltConnection.on("evt", (evt) => {
1325
- this.emit("evt", evt);
1326
- });
1327
-
1328
- return this._voltConnection.connect();
1329
- } catch (err) {
1330
- log$1("connect - error [%s]", err.message);
1331
- throw err;
1332
- }
1388
+ try {
1389
+ if (this._voltConnection) {
1390
+ log$1("connectInternal - Volt connection already exists");
1391
+ return Promise.resolve();
1392
+ }
1393
+
1394
+ const onDisconnected = () => {
1395
+ this._connected = false;
1396
+ this._session = null;
1397
+
1398
+ if (this._cachedClient) {
1399
+ this._grpc.closeClient(this._cachedClient);
1400
+ this._cachedClient = null;
1401
+ }
1402
+
1403
+ if (this._cachedRemoteClient) {
1404
+ this._grpc.closeClient(this._cachedRemoteClient);
1405
+ this._cachedRemoteClient = null;
1406
+ }
1407
+ };
1408
+
1409
+ this._voltConnection = new VoltConnection(this, this._grpc);
1410
+
1411
+ this._voltConnection.on("connected", (session) => {
1412
+ this._session = session;
1413
+ this._connected = !!session;
1414
+ if (!this._connected) {
1415
+ onDisconnected();
1416
+ }
1417
+ this.emit("connected", !!session);
1418
+ });
1419
+
1420
+ this._voltConnection.on("error", (err) => {
1421
+ // Should auto-reconnect.
1422
+ log$1("Volt connection error [%s]", err.message);
1423
+ if (this._connected) {
1424
+ onDisconnected();
1425
+ this.emit("connected", false);
1426
+ }
1427
+ });
1428
+
1429
+ this._voltConnection.on("evt", (evt) => {
1430
+ this.emit("evt", evt);
1431
+ });
1432
+
1433
+ return this._voltConnection.connect();
1434
+ } catch (err) {
1435
+ log$1("connect - error [%s]", err.message);
1436
+ throw err;
1437
+ }
1333
1438
  }
1334
1439
 
1335
1440
  function getVoltAPIClientInternal() {
1336
- if (!this._cachedClient) {
1337
- const serviceAddress = this.isRemote ? this._crypto.cache.tunnel_address : this._options.local_address;
1338
- log$1("creating cached VoltAPI service client on %s", serviceAddress);
1339
- let serviceDescriptors = {};
1340
- voltServices.forEach((pkg) => {
1341
- const packageDescriptors = getServiceDescriptors(this._grpc, pkg);
1342
- serviceDescriptors = {...serviceDescriptors, ...packageDescriptors};
1343
- });
1344
-
1345
- this._cachedClient = createClient(
1346
- this._grpc,
1347
- serviceDescriptors,
1348
- serviceAddress,
1349
- this._crypto.cache,
1350
- this.isRemote && !this.isVoltTunnel,
1351
- this._config.cloud_host ? this._options.id : "",
1352
- this._config.cloud_host ? this._options.id : ""
1353
- );
1354
- }
1355
-
1356
- return this._cachedClient;
1441
+ if (!this._cachedClient) {
1442
+ const serviceAddress = this.isRemote
1443
+ ? this._config.relay.address
1444
+ : this._voltConfig.address;
1445
+ log$1("creating cached VoltAPI service client on %s", serviceAddress);
1446
+ let serviceDescriptors = {};
1447
+ voltServices.forEach((pkg) => {
1448
+ const packageDescriptors = getServiceDescriptors(this._grpc, pkg);
1449
+ serviceDescriptors = { ...serviceDescriptors, ...packageDescriptors };
1450
+ });
1451
+
1452
+ this._cachedClient = createClient(
1453
+ this._grpc,
1454
+ serviceDescriptors,
1455
+ serviceAddress,
1456
+ this._credential,
1457
+ this.isRemote && !this.isVoltRelay,
1458
+ this._config?.relay?.cloud_relay ? this._voltConfig.id : "",
1459
+ this._config?.relay?.cloud_relay ? this._voltConfig.id : "",
1460
+ );
1461
+ }
1462
+
1463
+ return this._cachedClient;
1357
1464
  }
1358
1465
 
1359
1466
  function unaryCall(method, request) {
1360
- const grpcClient = this.getVoltAPIClient();
1467
+ const grpcClient = this.getVoltAPIClient();
1361
1468
 
1362
- const call = new GRPCCall(this, method, "METHOD_TYPE_UNARY");
1363
- call.start(grpcClient, request);
1364
- return call.wait();
1469
+ const call = new GRPCCall(this, method, "METHOD_TYPE_UNARY");
1470
+ call.start(grpcClient, request);
1471
+ return call.wait();
1365
1472
  }
1366
1473
 
1367
- async function fetchVoltConfig(volt_did) {
1368
- const didResolution = getDIDResolutionURL(volt_did);
1369
- if (!didResolution) {
1370
- throw new Error(`Invalid Volt DID: ${volt_did}`);
1371
- }
1372
-
1373
- const getJSON = bent__default["default"]("json");
1374
- const didDocument = await getJSON(didResolution);
1375
- const voltConfigServices = findDIDDocumentService(didDocument, constants.didServiceType.voltConfig);
1376
- if (voltConfigServices.length !== 1) {
1377
- log$1(
1378
- "Unexpected number of Volt configuration endpoints found in DID document for %s, services found: %d",
1379
- volt_did,
1380
- voltConfigServices.length
1381
- );
1382
- }
1383
- const serviceEndpoint = voltConfigServices[0].serviceEndpoint;
1384
- const resolvedConfig = await getJSON(serviceEndpoint);
1385
-
1386
- const voltConfig = pick(
1387
- resolvedConfig,
1388
- "id",
1389
- "display_name",
1390
- "public_key",
1391
- "fingerprint",
1392
- "local_address",
1393
- "ca_pem",
1394
- "challenge_code"
1395
- );
1474
+ async function fetchVoltConfig(disovery_url) {
1475
+ const getJSON = bent__default["default"]("json");
1476
+ const resolvedConfig = await getJSON(disovery_url);
1477
+
1478
+ const voltConfig = pick(
1479
+ resolvedConfig,
1480
+ "id",
1481
+ "display_name",
1482
+ "public_key",
1483
+ "fingerprint",
1484
+ "address",
1485
+ "ca_pem",
1486
+ "challenge_code",
1487
+ "cloud_relay",
1488
+ );
1489
+
1490
+ const relayConfig = pick(resolvedConfig, "relay");
1491
+
1492
+ return { volt: voltConfig, ...relayConfig };
1493
+ }
1396
1494
 
1397
- return {
1398
- volt: voltConfig,
1399
- tunnel_ca_pem: resolvedConfig.tunnel_ca_pem,
1400
- tunnel_address: resolvedConfig.tunnel_address,
1401
- cloud_host: resolvedConfig.tunnel_address.split(":")[0],
1402
- };
1495
+ async function fetchVoltConfigFromDID(volt_did) {
1496
+ const didResolution = getDIDResolutionURL(volt_did);
1497
+ if (!didResolution) {
1498
+ throw new Error(`Invalid Volt DID: ${volt_did}`);
1499
+ }
1500
+
1501
+ const getJSON = bent__default["default"]("json");
1502
+ const didDocument = await getJSON(didResolution);
1503
+ const voltConfigServices = findDIDDocumentService(
1504
+ didDocument,
1505
+ constants.didServiceType.voltConfig,
1506
+ );
1507
+ if (voltConfigServices.length !== 1) {
1508
+ log$1(
1509
+ "Unexpected number of Volt configuration endpoints found in DID document for %s, services found: %d",
1510
+ volt_did,
1511
+ voltConfigServices.length,
1512
+ );
1513
+ }
1514
+ const serviceEndpoint = voltConfigServices[0].serviceEndpoint;
1515
+ return await fetchVoltConfig.call(this, serviceEndpoint);
1403
1516
  }
1404
1517
 
1405
- const {readFile} = fs__default["default"].promises;
1518
+ const { readFile } = fs__default["default"].promises;
1406
1519
  const log = debug__default["default"]("volt-client-grpc:volt-client");
1407
1520
 
1408
1521
  class VoltClient extends EventEmitter__default["default"] {
1409
- /**
1410
- * @param {object} grpc - an instance of the grpc module (see https://www.npmjs.com/package/grpc)
1411
- */
1412
- constructor(grpc) {
1413
- super();
1414
-
1415
- if (!grpc) {
1416
- throw new Error("invalid arguments, grpc is required");
1417
- }
1418
- this._grpc = grpc;
1419
- this._cachedClient = null;
1420
- this._cachedRemoteClient = null;
1421
- this._session = null;
1422
- this._config = null;
1423
- this._options = null;
1424
- this._voltConnection = null;
1425
- }
1426
-
1427
- get config() {
1428
- return this._config;
1429
- }
1430
-
1431
- get crypto() {
1432
- return this._crypto;
1433
- }
1434
-
1435
- get grpc() {
1436
- return this._grpc;
1437
- }
1438
-
1439
- get options() {
1440
- return this._options;
1441
- }
1442
-
1443
- /**
1444
- * Initialises a binding and connection to the volt.
1445
- * @param {string} [config] - the location of the Volt configuration file, or a configuration object
1446
- */
1447
- async initialise(config, extras = {}) {
1448
- try {
1449
- if (!config) {
1450
- throw new Error("configPath argument is required to be a non-empty string");
1451
- }
1452
-
1453
- let configPath;
1454
- let configJSON;
1455
- if (typeof config === "string") {
1456
- configPath = path__default["default"].resolve(config);
1457
- log("Attempt to load config from file %s", configPath);
1458
- try {
1459
- const configContents = await readFile(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))));
1460
- configJSON = JSON.parse(configContents);
1461
- } catch (err) {
1462
- log("failure loading config file %s [%s]", configPath, err.message);
1463
- throw new Error(`failed to load configuration - check JSON format in ${configPath}`);
1464
- }
1465
- } else if (typeof config === "object") {
1466
- configJSON = config;
1467
- } else {
1468
- throw new Error("config must be either a file path or a config object");
1469
- }
1470
-
1471
- if (!configJSON || typeof configJSON !== "object") {
1472
- throw new Error("config argument is required to be an object");
1473
- }
1474
-
1475
- if (!configJSON.client_name || typeof configJSON.client_name !== "string") {
1476
- throw new Error("client_name property required in config");
1477
- }
1478
-
1479
- this._config = {...configJSON, ...extras};
1480
-
1481
- if (this._config.volt_did && !this._config.volt) {
1482
- // No Volt configuration, attempt to discover it via the given DID.
1483
- const didConfig = await fetchVoltConfig.call(this, this._config.volt_did);
1484
- log("resolved config from DID: %s", JSON.stringify(didConfig, null, 2));
1485
- this._config = {...this._config, ...didConfig};
1486
- }
1487
-
1488
- this._options = this._config.volt;
1489
-
1490
- if (!this._options || typeof this._options !== "object") {
1491
- throw new Error("configuration is missing the 'volt' object");
1492
- }
1493
-
1494
- if (!this._options.id || typeof this._options.id !== "string") {
1495
- throw new Error("'id' property is missing in Volt configuration");
1496
- }
1497
-
1498
- this._crypto = new VoltCrypto(this._config, configPath);
1499
-
1500
- await this._crypto.initialise();
1501
-
1502
- if (!this.isRemote) {
1503
- // If we're not connecting via a tunnel, we must have the address and CA.
1504
- if (!this._options.local_address || typeof this._options.local_address !== "string") {
1505
- throw new Error("'local_address' property missing in Volt configuration");
1506
- }
1507
-
1508
- if (!this._options.ca_pem || typeof this._options.ca_pem !== "string") {
1509
- throw new Error("'ca_pPem' property missing in Volt configuration");
1510
- }
1511
- }
1512
-
1513
- let isBound;
1514
- if (!this._crypto._cryptoCache.cert) {
1515
- // No certificate present in the cache yet => we need to bind.
1516
- try {
1517
- isBound = await bindInternal.call(this);
1518
- } catch (bindErr) {
1519
- if (bindErr.message === "invalid arguments") {
1520
- // This is usually the result of a missing challenge signature or credential.
1521
- log("failure binding to Volt - did you supply a challenge code or verified credential?");
1522
- throw bindErr;
1523
- }
1524
- }
1525
- } else {
1526
- isBound = true;
1527
- }
1528
-
1529
- if (!isBound) {
1530
- // This is usually because of request being denied.
1531
- throw new Error("Volt binding failed");
1532
- }
1533
-
1534
- return Promise.resolve();
1535
- } catch (err) {
1536
- log("Failure starting Volt client [%s]", err.message);
1537
- throw err;
1538
- }
1539
- }
1540
-
1541
- initialiseAndConnect(configPath, extras = {}) {
1542
- return this.initialise(configPath, extras)
1543
- .then(() => {
1544
- return connectInternal.call(this);
1545
- })
1546
- .catch((err) => {
1547
- log("startAndConnect failure: %s", err.message);
1548
- return Promise.reject(err);
1549
- });
1550
- }
1551
-
1552
- /**
1553
- * Creates a GRPC client for the given Volt service description.
1554
- *
1555
- * Automatically handles remote connections.
1556
- *
1557
- * n.b. for this javascript client Protobuf definitions should conform to the recommended
1558
- * structure, which is that a package proto file resides in a folder path that matches the
1559
- * package name, and services contained in the package are named in in Pascal Case and using
1560
- * the suffix 'API'. For example the WebcamControlAPI in package tdx.volt_api.webcam.v1 should reside
1561
- * in a protobuf file at tdx/api/webcam/v1/webcam_control_api.proto
1562
- *
1563
- * @param {object} service a service description.
1564
- * @param {object} descriptors the service descriptors, must be specified unless the service
1565
- * packages are well-known Volt APIs.
1566
- */
1567
- createServiceClient(service, descriptors) {
1568
- const pkg = service.service_description.service_api;
1569
-
1570
- let serviceDescriptors;
1571
- if (!descriptors) {
1572
- serviceDescriptors = {};
1573
- pkg.forEach((svc) => {
1574
- log("adding service %s to service client", svc);
1575
- const packageDescriptors = getServiceDescriptors(this._grpc, svc);
1576
- serviceDescriptors = {...serviceDescriptors, ...packageDescriptors};
1577
- });
1578
- } else {
1579
- serviceDescriptors = descriptors;
1580
- }
1581
-
1582
- if (this.isRemote) {
1583
- if (!this._crypto.cache.tunnel_address) {
1584
- throw new Error("Remote connection enabled but no tunnel address - have you called start()?");
1585
- }
1586
-
1587
- // In remote mode => create a client using the discovered tunnel address **and** passing the service reourceId
1588
- return createClient(
1589
- this._grpc,
1590
- serviceDescriptors,
1591
- this._crypto.cache.tunnel_address,
1592
- this._crypto.cache,
1593
- false,
1594
- service.volt_id,
1595
- service.id
1596
- );
1597
- } else {
1598
- return createClient(
1599
- this._grpc,
1600
- serviceDescriptors,
1601
- service.service_description.local_address,
1602
- this._crypto.cache
1603
- );
1604
- }
1605
- }
1606
-
1607
- get isRemote() {
1608
- return (this._config.cloud_host || this._config.tunnel_host) && this._options.id;
1609
- }
1610
-
1611
- get isVoltTunnel() {
1612
- return this.isRemote && this._config.tunnel_host;
1613
- }
1614
-
1615
- connect() {
1616
- return connectInternal.call(this);
1617
- }
1618
-
1619
- disconnect() {
1620
- if (this._voltConnection) {
1621
- this._voltConnection.disconnect();
1622
- this._voltConnection = null;
1623
- }
1624
- }
1625
-
1626
- getVoltAPIClient() {
1627
- return getVoltAPIClientInternal.call(this);
1628
- }
1629
-
1630
- /**
1631
- * Request resource access.
1632
- * @param {*} targetResourceId
1633
- * @param {*} accessType - see vault.proto for enum values.
1634
- */
1635
- async RequestAccessBlocking(targetResourceId, accessType = "VOLT_ACCESS_READ") {
1636
- try {
1637
- const accessRequest = {
1638
- access: accessType,
1639
- resource_id: targetResourceId,
1640
- };
1641
-
1642
- // This will block while the request is pending.
1643
- // TODO - implement streamed rpc on volt? Not sure there is a need really...
1644
- const pollInterval = 10000;
1645
- let decision = "";
1646
- do {
1647
- if (decision) {
1648
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
1649
- }
1650
- const accessResponse = await this.RequestAccess(accessRequest);
1651
- decision = accessResponse.decision;
1652
- log("access decision: %s", decision);
1653
- } while (decision === "POLICY_DECISION_PROMPT" || decision === "POLICY_DECISION_PENDING");
1654
-
1655
- log("requestResourceAccess result is %j", decision);
1656
- return decision === "POLICY_DECISION_PERMIT";
1657
- } catch (err) {
1658
- log("failure requesting resource access [%s]", err.message);
1659
- return Promise.reject(err);
1660
- }
1661
- }
1662
-
1663
- /**
1664
- * VoltAPI - resource management
1665
- */
1666
-
1667
- CanAccessResource(request) {
1668
- return unaryCall.call(this, "CanAccessResource", request);
1669
- }
1670
-
1671
- DeleteResource(request) {
1672
- return unaryCall.call(this, "DeleteResource", request);
1673
- }
1674
-
1675
- DiscoverServices(request) {
1676
- return unaryCall.call(this, "DiscoverServices", request);
1677
- }
1678
-
1679
- GetResource(request) {
1680
- return unaryCall.call(this, "GetResource", request);
1681
- }
1682
-
1683
- GetResources(request) {
1684
- return unaryCall.call(this, "GetResources", request);
1685
- }
1686
-
1687
- GetResourceAncestors(request) {
1688
- return unaryCall.call(this, "GetResourceAncestors", request);
1689
- }
1690
-
1691
- GetResourceDescendants(request) {
1692
- return unaryCall.call(this, "GetResourceDescendants", request);
1693
- }
1694
-
1695
- RequestAccess(request) {
1696
- return unaryCall.call(this, "RequestAccess", request);
1697
- }
1698
-
1699
- SaveResource(request) {
1700
- return unaryCall.call(this, "SaveResource", request);
1701
- }
1702
-
1703
- SaveResourceAttribute(request) {
1704
- return unaryCall.call(this, "SaveResourceAttribute", request);
1705
- }
1706
-
1707
- SetServiceStatus(request) {
1708
- return unaryCall.call(this, "SetServiceStatus", request);
1709
- }
1710
-
1711
- /**
1712
- * VoltAPI - volt management
1713
- */
1714
-
1715
- Bind(request) {
1716
- return unaryCall.call(this, "Bind", request);
1717
- }
1718
-
1719
- DeleteAccess(request) {
1720
- return unaryCall.call(this, "DeleteAccess", request);
1721
- }
1722
-
1723
- DeleteVolt(request) {
1724
- return unaryCall.call(this, "DeleteVolt", request);
1725
- }
1726
-
1727
- GetAccess(request) {
1728
- return unaryCall.call(this, "GetAccess", request);
1729
- }
1730
-
1731
- GetBindings(request) {
1732
- return unaryCall.call(this, "GetBindings", request);
1733
- }
1734
-
1735
- GetIdentities(request) {
1736
- return unaryCall.call(this, "GetIdentities", request);
1737
- }
1738
-
1739
- GetIdentity(request) {
1740
- return unaryCall.call(this, "GetIdentity", request);
1741
- }
1742
-
1743
- GetIdentityToken(request) {
1744
- return unaryCall.call(this, "GetIdentityToken", request);
1745
- }
1746
-
1747
- GetPolicy(request) {
1748
- return unaryCall.call(this, "GetPolicy", request);
1749
- }
1750
-
1751
- GetSettings(request) {
1752
- return unaryCall.call(this, "GetSettings", request);
1753
- }
1754
-
1755
- SaveAccess(request) {
1756
- return unaryCall.call(this, "SaveAccess", request);
1757
- }
1758
-
1759
- SaveCloudConnection(request) {
1760
- return unaryCall.call(this, "SaveCloudConnection", request);
1761
- }
1762
-
1763
- SaveIdentity(request) {
1764
- return unaryCall.call(this, "SaveIdentity", request);
1765
- }
1766
-
1767
- SaveSettings(request) {
1768
- return unaryCall.call(this, "SaveSettings", request);
1769
- }
1770
-
1771
- SetAccessRequestDecision(request) {
1772
- return unaryCall.call(this, "SetAccessRequestDecision", request);
1773
- }
1774
-
1775
- SetBindingDecision(request) {
1776
- return unaryCall.call(this, "SetBindingDecision", request);
1777
- }
1778
-
1779
- Shutdown(request) {
1780
- return unaryCall.call(this, "Shutdown", request);
1781
- }
1782
-
1783
- SignVerify(request) {
1784
- return unaryCall.call(this, "SignVerify", request);
1785
- }
1786
-
1787
- /**
1788
- * FileAPI
1789
- */
1790
- GetFileDescendants(request) {
1791
- return unaryCall.call(this, "GetFileDescendants", request);
1792
- }
1793
-
1794
- /**
1795
- * Asynchronous file download
1796
- * @param {*} request
1797
- * @returns stream
1798
- */
1799
- DownloadFile(request) {
1800
- const grpcClient = this.getVoltAPIClient();
1801
-
1802
- return new Promise((resolve, reject) => {
1803
- const meta = this._crypto.getIdentityMetadata(this._grpc, this._options.id, this.isRemote);
1804
- resolve(grpcClient.DownloadFile(request, meta.metadata));
1805
- }).catch((err) => {
1806
- log("error during %s [%s]", method, err.message);
1807
- return Promise.reject(err);
1808
- });
1809
- }
1810
-
1811
- UploadFile(request, callback) {
1812
- const grpcClient = this.getVoltAPIClient();
1813
-
1814
- const uploadCall = new GRPCCall(this, "UploadFile", "METHOD_TYPE_BIDI");
1815
- return uploadCall.start(grpcClient, request, callback);
1816
- }
1817
-
1818
- /**
1819
- * Synchronous file download
1820
- * @param {*} request
1821
- * @returns file buffer, base64 encoded
1822
- */
1823
- DownloadFileSync(request) {
1824
- const grpcClient = this.getVoltAPIClient();
1825
-
1826
- const blocks = [];
1827
-
1828
- const call = new GRPCCall(this, "DownloadFile", "METHOD_TYPE_SERVER_STREAM");
1829
- call.start(grpcClient, request, (err, response) => {
1830
- if (err) {
1831
- return call.reject(err);
1832
- }
1833
-
1834
- switch (response.payload) {
1835
- case "block":
1836
- blocks.push(response.block);
1837
- break;
1838
- case "status":
1839
- if (response.status.message) {
1840
- return call.reject(new Error(response.status.message));
1841
- }
1842
- break;
1843
- default:
1844
- log("unrecognised response payload %s", response.payload);
1845
- break;
1846
- }
1847
- });
1848
-
1849
- return call
1850
- .wait()
1851
- .then(() => {
1852
- log("Download finished");
1853
- return {buffer: Buffer.concat(blocks).toString("base64")};
1854
- })
1855
- .catch((err) => {
1856
- log("error during DownloadFile [%s]", err.message);
1857
- return Promise.reject(err);
1858
- });
1859
- }
1860
-
1861
- /**
1862
- * SqliteDatabaseAPI
1863
- */
1864
- SqlExecute(request, callback) {
1865
- const grpcClient = this.getVoltAPIClient();
1866
-
1867
- const subscribeCall = new GRPCCall(this, "Execute", "METHOD_TYPE_BIDI");
1868
- return subscribeCall.start(grpcClient, request, callback);
1869
- }
1870
-
1871
- /**
1872
- * WireAPI
1873
- */
1874
- PublishWire(request, callback) {
1875
- const grpcClient = this.getVoltAPIClient();
1876
-
1877
- const publishCall = new GRPCCall(this, "PublishWire", "METHOD_TYPE_CLIENT_STREAM");
1878
- return publishCall.start(grpcClient, request, callback);
1879
- }
1880
-
1881
- SubscribeWire(request, callback) {
1882
- const grpcClient = this.getVoltAPIClient();
1883
-
1884
- const subscribeCall = new GRPCCall(this, "SubscribeWire", "METHOD_TYPE_BIDI");
1885
- return subscribeCall.start(grpcClient, request, callback);
1886
- }
1522
+ /**
1523
+ * @param {object} grpc - an instance of the grpc module (see https://www.npmjs.com/package/grpc)
1524
+ */
1525
+ constructor(grpc) {
1526
+ super();
1527
+
1528
+ if (!grpc) {
1529
+ throw new Error("invalid arguments, grpc is required");
1530
+ }
1531
+ this._grpc = grpc;
1532
+ this._cachedClient = null;
1533
+ this._cachedRemoteClient = null;
1534
+ this._session = null;
1535
+ this._config = null;
1536
+ this._voltConfig = null;
1537
+ this._voltConnection = null;
1538
+ }
1539
+
1540
+ get config() {
1541
+ return this._config;
1542
+ }
1543
+
1544
+ get credential() {
1545
+ return this._credential;
1546
+ }
1547
+
1548
+ get grpc() {
1549
+ return this._grpc;
1550
+ }
1551
+
1552
+ get voltConfig() {
1553
+ return this._voltConfig;
1554
+ }
1555
+
1556
+ /**
1557
+ * Initialises a binding and connection to the volt.
1558
+ * @param {string} [config] - the location of the Volt configuration file, or a configuration object
1559
+ */
1560
+ async initialise(config, extras = {}) {
1561
+ try {
1562
+ if (!config) {
1563
+ throw new Error(
1564
+ "configPath argument is required to be a non-empty string",
1565
+ );
1566
+ }
1567
+
1568
+ let configPath;
1569
+ let configJSON;
1570
+ if (typeof config === "string") {
1571
+ configPath = path__default["default"].resolve(config);
1572
+ log("Attempt to load config from file %s", configPath);
1573
+ try {
1574
+ const configContents = await readFile(
1575
+ 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))),
1576
+ );
1577
+ configJSON = JSON.parse(configContents);
1578
+ } catch (err) {
1579
+ log("failure loading config file %s [%s]", configPath, err.message);
1580
+ throw new Error(
1581
+ `failed to load configuration - check JSON format in ${configPath}`,
1582
+ );
1583
+ }
1584
+ } else if (typeof config === "object") {
1585
+ configJSON = config;
1586
+ } else {
1587
+ throw new Error("config must be either a file path or a config object");
1588
+ }
1589
+
1590
+ if (!configJSON || typeof configJSON !== "object") {
1591
+ throw new Error("config argument is required to be an object");
1592
+ }
1593
+
1594
+ if (
1595
+ !configJSON.client_name ||
1596
+ typeof configJSON.client_name !== "string"
1597
+ ) {
1598
+ throw new Error("client_name property required in config");
1599
+ }
1600
+
1601
+ this._config = { ...configJSON, ...extras };
1602
+
1603
+ let voltDID = "";
1604
+ let voltDiscovery = "";
1605
+ if (typeof this._config.volt === "string") {
1606
+ if (this._config.volt.indexOf("did:") === 0) {
1607
+ voltDID = this._config.volt;
1608
+ } else {
1609
+ voltDiscovery = this._config.volt;
1610
+ }
1611
+ } else if (typeof this._config.volt !== "object") {
1612
+ throw new Error("configuration is missing the 'volt' object");
1613
+ } else {
1614
+ voltDID = this._config.volt.did;
1615
+ voltDiscovery = this._config.volt.discovery_url;
1616
+ }
1617
+
1618
+ if (voltDID) {
1619
+ const didConfig = await fetchVoltConfigFromDID.call(this, voltDID);
1620
+ this._config = { ...this._config, ...didConfig };
1621
+ log("resolved config from DID: %s", JSON.stringify(didConfig, null, 2));
1622
+ } else if (voltDiscovery) {
1623
+ const discoConfig = await fetchVoltConfig.call(this, voltDiscovery);
1624
+ this._config = { ...this._config, ...discoConfig };
1625
+ }
1626
+
1627
+ this._voltConfig = this._config.volt;
1628
+ this._voltConfig.did = voltDID;
1629
+ this._voltConfig.discovery_url = voltDiscovery;
1630
+
1631
+ if (!this._voltConfig.id || typeof this._voltConfig.id !== "string") {
1632
+ throw new Error("'id' property is missing in Volt configuration");
1633
+ }
1634
+
1635
+ this._credential = new VoltCredential(this._config, configPath);
1636
+
1637
+ await this._credential.initialise();
1638
+
1639
+ if (!this.isRemote) {
1640
+ // If we're not connecting via a Relay, we must have the address and CA.
1641
+ if (
1642
+ !this._voltConfig.address ||
1643
+ typeof this._voltConfig.address !== "string"
1644
+ ) {
1645
+ throw new Error("'address' property missing in Volt configuration");
1646
+ }
1647
+
1648
+ if (
1649
+ !this._voltConfig.ca_pem ||
1650
+ typeof this._voltConfig.ca_pem !== "string"
1651
+ ) {
1652
+ throw new Error("'ca_pPem' property missing in Volt configuration");
1653
+ }
1654
+ }
1655
+
1656
+ let isBound;
1657
+ if (this._credential._cryptoCache.cert) {
1658
+ isBound = true;
1659
+ } else {
1660
+ // No certificate present in the cache yet => we need to bind.
1661
+ try {
1662
+ isBound = await bindInternal.call(this);
1663
+ } catch (bindErr) {
1664
+ if (bindErr.message === "invalid arguments") {
1665
+ // This is usually the result of a missing challenge signature or credential.
1666
+ log(
1667
+ "failure binding to Volt - did you supply a challenge code or verified credential?",
1668
+ );
1669
+ throw bindErr;
1670
+ }
1671
+ }
1672
+ }
1673
+
1674
+ if (!isBound) {
1675
+ // This is usually because of request being denied.
1676
+ throw new Error("Volt binding failed");
1677
+ }
1678
+
1679
+ return Promise.resolve();
1680
+ } catch (err) {
1681
+ log("Failure starting Volt client [%s]", err.message);
1682
+ throw err;
1683
+ }
1684
+ }
1685
+
1686
+ initialiseAndConnect(configPath, extras = {}) {
1687
+ return this.initialise(configPath, extras)
1688
+ .then(() => {
1689
+ return connectInternal.call(this);
1690
+ })
1691
+ .catch((err) => {
1692
+ log("initialiseAndConnect failure: %s", err.message);
1693
+ return Promise.reject(err);
1694
+ });
1695
+ }
1696
+
1697
+ /**
1698
+ * Creates a GRPC client for the given Volt service description.
1699
+ *
1700
+ * Automatically handles remote connections.
1701
+ *
1702
+ * n.b. for this javascript client Protobuf definitions should conform to the recommended
1703
+ * structure, which is that a package proto file resides in a folder path that matches the
1704
+ * package name, and services contained in the package are named in in Pascal Case and using
1705
+ * the suffix 'API'. For example the WebcamControlAPI in package tdx.volt_api.webcam.v1 should reside
1706
+ * in a protobuf file at tdx/api/webcam/v1/webcam_control_api.proto
1707
+ *
1708
+ * @param {object} service a service description.
1709
+ * @param {object} descriptors the service descriptors, must be specified unless the service
1710
+ * packages are well-known Volt APIs.
1711
+ */
1712
+ createServiceClient(service, descriptors) {
1713
+ const pkg = service.service_description.service_api;
1714
+
1715
+ let serviceDescriptors;
1716
+ if (descriptors) {
1717
+ serviceDescriptors = descriptors;
1718
+ } else {
1719
+ serviceDescriptors = {};
1720
+ pkg.forEach((svc) => {
1721
+ log("adding service %s to service client", svc);
1722
+ const packageDescriptors = getServiceDescriptors(this._grpc, svc);
1723
+ serviceDescriptors = { ...serviceDescriptors, ...packageDescriptors };
1724
+ });
1725
+ }
1726
+
1727
+ if (this.isRemote) {
1728
+ if (!this._config?.relay?.address) {
1729
+ throw new Error(
1730
+ "Remote connection enabled but no Relay address - have you called start()?",
1731
+ );
1732
+ }
1733
+
1734
+ // In remote mode => create a client using the discovered Relay address **and** passing the service reourceId
1735
+ return createClient(
1736
+ this._grpc,
1737
+ serviceDescriptors,
1738
+ this._config.relay.address,
1739
+ this._credential,
1740
+ false,
1741
+ service.volt_id,
1742
+ service.id,
1743
+ );
1744
+ } else {
1745
+ return createClient(
1746
+ this._grpc,
1747
+ serviceDescriptors,
1748
+ service.service_description.address,
1749
+ this._credential,
1750
+ );
1751
+ }
1752
+ }
1753
+
1754
+ get isRemote() {
1755
+ return !!(this._config?.relay && this._voltConfig.id);
1756
+ }
1757
+
1758
+ get isVoltRelay() {
1759
+ return this.isRemote && !this._config?.relay.cloud_relay;
1760
+ }
1761
+
1762
+ connect() {
1763
+ return connectInternal.call(this);
1764
+ }
1765
+
1766
+ disconnect() {
1767
+ if (this._voltConnection) {
1768
+ this._voltConnection.disconnect();
1769
+ this._voltConnection = null;
1770
+ }
1771
+ }
1772
+
1773
+ getVoltAPIClient() {
1774
+ return getVoltAPIClientInternal.call(this);
1775
+ }
1776
+
1777
+ /**
1778
+ * Request resource access.
1779
+ * @param {*} targetResourceId
1780
+ * @param {*} accessType - see vault.proto for enum values.
1781
+ */
1782
+ async RequestAccessBlocking(
1783
+ targetResourceId,
1784
+ accessType = "VOLT_ACCESS_READ",
1785
+ ) {
1786
+ try {
1787
+ const accessRequest = {
1788
+ access: accessType,
1789
+ resource_id: targetResourceId,
1790
+ };
1791
+
1792
+ // This will block while the request is pending.
1793
+ // TODO - implement streamed rpc on volt? Not sure there is a need really...
1794
+ const pollInterval = 10000;
1795
+ let decision = "";
1796
+ do {
1797
+ if (decision) {
1798
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
1799
+ }
1800
+ const accessResponse = await this.RequestAccess(accessRequest);
1801
+ decision = accessResponse.decision;
1802
+ log("access decision: %s", decision);
1803
+ } while (
1804
+ decision === "POLICY_DECISION_PROMPT" ||
1805
+ decision === "POLICY_DECISION_PENDING"
1806
+ );
1807
+
1808
+ log("requestResourceAccess result is %j", decision);
1809
+ return decision === "POLICY_DECISION_PERMIT";
1810
+ } catch (err) {
1811
+ log("failure requesting resource access [%s]", err.message);
1812
+ return Promise.reject(err);
1813
+ }
1814
+ }
1815
+
1816
+ /**
1817
+ * VoltAPI - resource management
1818
+ */
1819
+
1820
+ CanAccessResource(request) {
1821
+ return unaryCall.call(this, "CanAccessResource", request);
1822
+ }
1823
+
1824
+ DeleteResource(request) {
1825
+ return unaryCall.call(this, "DeleteResource", request);
1826
+ }
1827
+
1828
+ DiscoverServices(request) {
1829
+ return unaryCall.call(this, "DiscoverServices", request);
1830
+ }
1831
+
1832
+ GetResource(request) {
1833
+ return unaryCall.call(this, "GetResource", request);
1834
+ }
1835
+
1836
+ GetResources(request) {
1837
+ return unaryCall.call(this, "GetResources", request);
1838
+ }
1839
+
1840
+ GetResourceAncestors(request) {
1841
+ return unaryCall.call(this, "GetResourceAncestors", request);
1842
+ }
1843
+
1844
+ GetResourceDescendants(request) {
1845
+ return unaryCall.call(this, "GetResourceDescendants", request);
1846
+ }
1847
+
1848
+ RequestAccess(request) {
1849
+ return unaryCall.call(this, "RequestAccess", request);
1850
+ }
1851
+
1852
+ SaveResource(request) {
1853
+ return unaryCall.call(this, "SaveResource", request);
1854
+ }
1855
+
1856
+ SaveResourceAttribute(request) {
1857
+ return unaryCall.call(this, "SaveResourceAttribute", request);
1858
+ }
1859
+
1860
+ SetServiceStatus(request) {
1861
+ return unaryCall.call(this, "SetServiceStatus", request);
1862
+ }
1863
+
1864
+ /**
1865
+ * VoltAPI - volt management
1866
+ */
1867
+
1868
+ Bind(request) {
1869
+ return unaryCall.call(this, "Bind", request);
1870
+ }
1871
+
1872
+ DeleteAccess(request) {
1873
+ return unaryCall.call(this, "DeleteAccess", request);
1874
+ }
1875
+
1876
+ DeleteVolt(request) {
1877
+ return unaryCall.call(this, "DeleteVolt", request);
1878
+ }
1879
+
1880
+ GetAccess(request) {
1881
+ return unaryCall.call(this, "GetAccess", request);
1882
+ }
1883
+
1884
+ GetBindings(request) {
1885
+ return unaryCall.call(this, "GetBindings", request);
1886
+ }
1887
+
1888
+ GetIdentities(request) {
1889
+ return unaryCall.call(this, "GetIdentities", request);
1890
+ }
1891
+
1892
+ GetIdentity(request) {
1893
+ return unaryCall.call(this, "GetIdentity", request);
1894
+ }
1895
+
1896
+ GetIdentityToken(request) {
1897
+ return unaryCall.call(this, "GetIdentityToken", request);
1898
+ }
1899
+
1900
+ GetPolicy(request) {
1901
+ return unaryCall.call(this, "GetPolicy", request);
1902
+ }
1903
+
1904
+ GetSettings(request) {
1905
+ return unaryCall.call(this, "GetSettings", request);
1906
+ }
1907
+
1908
+ SaveAccess(request) {
1909
+ return unaryCall.call(this, "SaveAccess", request);
1910
+ }
1911
+
1912
+ SaveCloudConnection(request) {
1913
+ return unaryCall.call(this, "SaveCloudConnection", request);
1914
+ }
1915
+
1916
+ SaveIdentity(request) {
1917
+ return unaryCall.call(this, "SaveIdentity", request);
1918
+ }
1919
+
1920
+ SaveSettings(request) {
1921
+ return unaryCall.call(this, "SaveSettings", request);
1922
+ }
1923
+
1924
+ SetAccessRequestDecision(request) {
1925
+ return unaryCall.call(this, "SetAccessRequestDecision", request);
1926
+ }
1927
+
1928
+ SetBindingDecision(request) {
1929
+ return unaryCall.call(this, "SetBindingDecision", request);
1930
+ }
1931
+
1932
+ Shutdown(request) {
1933
+ return unaryCall.call(this, "Shutdown", request);
1934
+ }
1935
+
1936
+ SignVerify(request) {
1937
+ return unaryCall.call(this, "SignVerify", request);
1938
+ }
1939
+
1940
+ /**
1941
+ * FileAPI
1942
+ */
1943
+ GetFileDescendants(request) {
1944
+ return unaryCall.call(this, "GetFileDescendants", request);
1945
+ }
1946
+
1947
+ /**
1948
+ * Asynchronous file download
1949
+ * @param {*} request
1950
+ * @returns stream
1951
+ */
1952
+ DownloadFile(request) {
1953
+ const grpcClient = this.getVoltAPIClient();
1954
+
1955
+ return new Promise((resolve, reject) => {
1956
+ const meta = this._credential.getIdentityMetadata(
1957
+ this._grpc,
1958
+ this._voltConfig.id,
1959
+ this.isRemote,
1960
+ );
1961
+ resolve(grpcClient.DownloadFile(request, meta.metadata));
1962
+ }).catch((err) => {
1963
+ log("error during %s [%s]", method, err.message);
1964
+ return Promise.reject(err);
1965
+ });
1966
+ }
1967
+
1968
+ UploadFile(request, callback) {
1969
+ const grpcClient = this.getVoltAPIClient();
1970
+
1971
+ const uploadCall = new GRPCCall(this, "UploadFile", "METHOD_TYPE_BIDI");
1972
+ return uploadCall.start(grpcClient, request, callback);
1973
+ }
1974
+
1975
+ /**
1976
+ * Synchronous file download
1977
+ * @param {*} request
1978
+ * @returns file buffer, base64 encoded
1979
+ */
1980
+ DownloadFileSync(request) {
1981
+ const grpcClient = this.getVoltAPIClient();
1982
+
1983
+ const blocks = [];
1984
+
1985
+ const call = new GRPCCall(
1986
+ this,
1987
+ "DownloadFile",
1988
+ "METHOD_TYPE_SERVER_STREAM",
1989
+ );
1990
+ call.start(grpcClient, request, (err, response) => {
1991
+ if (err) {
1992
+ return call.reject(err);
1993
+ }
1994
+
1995
+ switch (response.payload) {
1996
+ case "block": {
1997
+ blocks.push(response.block);
1998
+ break;
1999
+ }
2000
+ case "status": {
2001
+ if (response.status.message) {
2002
+ return call.reject(new Error(response.status.message));
2003
+ }
2004
+ break;
2005
+ }
2006
+ default:
2007
+ log("unrecognised response payload %s", response.payload);
2008
+ break;
2009
+ }
2010
+ });
2011
+
2012
+ return call
2013
+ .wait()
2014
+ .then(() => {
2015
+ log("Download finished");
2016
+ return { buffer: Buffer.concat(blocks).toString("base64") };
2017
+ })
2018
+ .catch((err) => {
2019
+ log("error during DownloadFile [%s]", err.message);
2020
+ return Promise.reject(err);
2021
+ });
2022
+ }
2023
+
2024
+ /**
2025
+ * SqliteDatabaseAPI
2026
+ */
2027
+ SqlExecute(request, callback) {
2028
+ const grpcClient = this.getVoltAPIClient();
2029
+
2030
+ const subscribeCall = new GRPCCall(this, "Execute", "METHOD_TYPE_BIDI");
2031
+ return subscribeCall.start(grpcClient, request, callback);
2032
+ }
2033
+
2034
+ /**
2035
+ * WireAPI
2036
+ */
2037
+ PublishWire(request, callback) {
2038
+ const grpcClient = this.getVoltAPIClient();
2039
+
2040
+ const publishCall = new GRPCCall(this, "PublishWire", "METHOD_TYPE_BIDI");
2041
+ return publishCall.start(grpcClient, request, callback);
2042
+ }
2043
+
2044
+ SubscribeWire(request, callback) {
2045
+ const grpcClient = this.getVoltAPIClient();
2046
+
2047
+ const subscribeCall = new GRPCCall(
2048
+ this,
2049
+ "SubscribeWire",
2050
+ "METHOD_TYPE_BIDI",
2051
+ );
2052
+ return subscribeCall.start(grpcClient, request, callback);
2053
+ }
1887
2054
  }
1888
2055
 
1889
2056
  Object.defineProperty(exports, 'generateId', {
1890
- enumerable: true,
1891
- get: function () { return uuid.v4; }
2057
+ enumerable: true,
2058
+ get: function () { return uuid.v4; }
1892
2059
  });
1893
2060
  exports.VoltClient = VoltClient;
1894
- exports.VoltCrypto = VoltCrypto;
2061
+ exports.VoltCredential = VoltCredential;
1895
2062
  exports.constants = constants;
1896
2063
  exports.grpcUtils = grpcUtils;
1897
2064
  exports.protoUtils = protoUtils;