@tdxvolt/volt-client-grpc 0.1.2 → 0.11.1

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