@tdxvolt/volt-client-grpc 0.14.61 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.cjs CHANGED
@@ -114,7 +114,7 @@ const createSecureContextOptions = (cryptoOptions) => {
114
114
  const tlsOptions = {
115
115
  cert: Buffer.from(cryptoOptions.cert),
116
116
  ca: Buffer.from(cryptoOptions.ca),
117
- key: Buffer.from(cryptoOptions.key),
117
+ key: Buffer.from(cryptoOptions.key)
118
118
  };
119
119
  return tlsOptions;
120
120
  };
@@ -130,7 +130,7 @@ const getServiceAddress = (mdnsService) => {
130
130
  const ipv4 = lodash__default["default"].find(mdnsService.addresses, (addy) => ip__default["default"].isV4Format(addy));
131
131
  const serviceDetails = {
132
132
  host: ipv4,
133
- port: mdnsService.port,
133
+ port: mdnsService.port
134
134
  };
135
135
 
136
136
  address = `${serviceDetails.host}:${serviceDetails.port}`;
@@ -207,6 +207,47 @@ const getDIDResolutionURL = (didIn) => {
207
207
  return `https://${hostName}/api/identity/${didGUID}`;
208
208
  };
209
209
 
210
+ const forgePrivateKeyToPem = (decryptedKey) => {
211
+ // Convert a Forge private key to an ASN.1 RSAPrivateKey
212
+ const asnPrivateKey = pki$2.privateKeyToAsn1(decryptedKey);
213
+
214
+ // Wrap an RSAPrivateKey ASN.1 object in a PKCS#8 ASN.1 PrivateKeyInfo
215
+ const privateKeyInfo = pki$2.wrapRsaPrivateKey(asnPrivateKey);
216
+
217
+ // Convert a PKCS#8 ASN.1 PrivateKeyInfo to PEM
218
+ return pki$2.privateKeyInfoToPem(privateKeyInfo);
219
+ };
220
+
221
+ const createRSAKey = (passphrase = "") => {
222
+ return new Promise((resolve, reject) => {
223
+ pki$2.rsa.generateKeyPair({ bits: 2048, workers: -1 }, (err, keypair) => {
224
+ if (err) {
225
+ reject(err);
226
+ } else {
227
+ let pem;
228
+ if (passphrase) {
229
+ pem = pki$2.encryptRsaPrivateKey(keypair.privateKey, passphrase);
230
+ } else {
231
+ pem = forgePrivateKeyToPem(keypair.privateKey);
232
+ }
233
+
234
+ resolve({ keypair, pem });
235
+ }
236
+ });
237
+ });
238
+ };
239
+
240
+ const sha256Base64 = (msg) => {
241
+ const md = forge__default["default"].md.sha256.create();
242
+ md.update(msg);
243
+ return forge__default["default"].util.encode64(md.digest().bytes());
244
+ };
245
+
246
+ const decryptPrivateKey = (pem, passphrase) => {
247
+ const decryptedKey = pki$2.decryptRsaPrivateKey(pem, passphrase);
248
+ return forgePrivateKeyToPem(decryptedKey);
249
+ };
250
+
210
251
  var utils = /*#__PURE__*/Object.freeze({
211
252
  __proto__: null,
212
253
  createSecureContextOptions: createSecureContextOptions,
@@ -219,7 +260,10 @@ var utils = /*#__PURE__*/Object.freeze({
219
260
  createPublicKeyFingerprint: createPublicKeyFingerprint,
220
261
  signBase64: signBase64,
221
262
  verifyBase64: verifyBase64,
222
- getDIDResolutionURL: getDIDResolutionURL
263
+ getDIDResolutionURL: getDIDResolutionURL,
264
+ createRSAKey: createRSAKey,
265
+ sha256Base64: sha256Base64,
266
+ decryptPrivateKey: decryptPrivateKey
223
267
  });
224
268
 
225
269
  /* eslint-disable no-underscore-dangle */
@@ -237,7 +281,7 @@ function createClient(
237
281
  credentials,
238
282
  noSerialise,
239
283
  voltId,
240
- serviceId,
284
+ serviceId
241
285
  ) {
242
286
  let grpcCredentials;
243
287
  if (credentials) {
@@ -251,7 +295,7 @@ function createClient(
251
295
  grpcCredentials = grpc.credentials.createSsl(
252
296
  tlsOptions.ca,
253
297
  tlsOptions.key,
254
- tlsOptions.cert,
298
+ tlsOptions.cert
255
299
  );
256
300
  } else {
257
301
  // Cache has no private key, this implies we are connecting without supplying a client certificate.
@@ -313,7 +357,7 @@ const grpcWrap = (api) => {
313
357
  // Support class instances and PoJos
314
358
  const isClass = api.constructor !== Object;
315
359
  const iterate = Object.getOwnPropertyNames(
316
- isClass ? api.constructor.prototype : api,
360
+ isClass ? api.constructor.prototype : api
317
361
  );
318
362
 
319
363
  // Wrap each method on the api.
@@ -327,7 +371,7 @@ const grpcWrap = (api) => {
327
371
  // Enforce promise results.
328
372
  if (!(resultPromise?.then && resultPromise?.catch)) {
329
373
  throw new Error(
330
- `implementation methods must return a Promise [${name}]`,
374
+ `implementation methods must return a Promise [${name}]`
331
375
  );
332
376
  }
333
377
 
@@ -364,7 +408,7 @@ class GrpcServer {
364
408
  "grpc.http2.max_pings_without_data": 0,
365
409
  "grpc.keepalive_permit_without_calls": 1,
366
410
  "grpc.http2.min_ping_interval_without_data_ms": 250,
367
- "grpc.http2.max_ping_strikes": 0,
411
+ "grpc.http2.max_ping_strikes": 0
368
412
  });
369
413
 
370
414
  if (tlsOptions) {
@@ -374,7 +418,7 @@ class GrpcServer {
374
418
  caList.push(tlsOption.ca);
375
419
  certChains.push({
376
420
  private_key: tlsOption.key,
377
- cert_chain: tlsOption.cert,
421
+ cert_chain: tlsOption.cert
378
422
  });
379
423
  });
380
424
 
@@ -385,7 +429,7 @@ class GrpcServer {
385
429
  // The certificate chain(s) we present to the client.
386
430
  certChains,
387
431
  // Flag indicating if we insist on a client certificate.
388
- requireClientCert,
432
+ requireClientCert
389
433
  );
390
434
  } else {
391
435
  this._credentials = grpc.ServerCredentials.createInsecure();
@@ -437,7 +481,7 @@ function createServer(
437
481
  serviceDescriptors,
438
482
  routes,
439
483
  cryptoCache,
440
- requireClientCert,
484
+ requireClientCert
441
485
  ) {
442
486
  let tlsOptions;
443
487
  if (cryptoCache) {
@@ -448,7 +492,7 @@ function createServer(
448
492
  serviceDescriptors,
449
493
  routes,
450
494
  tlsOptions,
451
- requireClientCert,
495
+ requireClientCert
452
496
  };
453
497
  return new GrpcServer(serverArgs);
454
498
  }
@@ -462,7 +506,7 @@ var grpcUtils = /*#__PURE__*/Object.freeze({
462
506
  /* eslint-disable no-underscore-dangle */
463
507
 
464
508
  const { pki: pki$1 } = forge__default["default"];
465
- const log$4 = debug__default["default"]("volt-client-grpc:volt-credential");
509
+ const log$5 = debug__default["default"]("volt-client-grpc:volt-credential");
466
510
  const aesAlgorithm = "aes-256-cbc";
467
511
  const rs256Algorithm = "RS256";
468
512
 
@@ -490,7 +534,7 @@ class VoltCredential {
490
534
  // Need to unencrypt the private key.
491
535
  const decrypted = pki$1.decryptRsaPrivateKey(
492
536
  this._cryptoCache.key,
493
- config.p,
537
+ config.p
494
538
  );
495
539
  if (!decrypted) {
496
540
  throw new Error("failed to decrypt key - check passphrase");
@@ -510,7 +554,7 @@ class VoltCredential {
510
554
  // Extract Volt public key (used for encrypting Relay payloads).
511
555
  this._voltPublicKey = forge__default["default"].pki.publicKeyToPem(voltCert.publicKey);
512
556
  this._voltFingerprint = createPublicKeyFingerprint(
513
- this._voltPublicKey,
557
+ this._voltPublicKey
514
558
  );
515
559
  }
516
560
  }
@@ -527,6 +571,10 @@ class VoltCredential {
527
571
  return this._cryptoCache.client_id;
528
572
  }
529
573
 
574
+ get voltPublicKey() {
575
+ return this._voltPublicKey;
576
+ }
577
+
530
578
  get publicKey() {
531
579
  return pki$1.publicKeyToPem(this.getKey().publicKey);
532
580
  }
@@ -547,23 +595,20 @@ class VoltCredential {
547
595
  createKey() {
548
596
  if (this._cryptoCache.key) {
549
597
  // Already have key data in the cache in pem format => create fully-formed pki instances.
550
- log$4("createKey - key already exists");
598
+ log$5("createKey - key already exists");
551
599
  return Promise.resolve(this.getKey());
552
600
  } else {
553
- log$4("creating key");
554
- return new Promise((resolve, reject) => {
555
- pki$1.rsa.generateKeyPair({ bits: 2048, workers: -1 }, (err, keypair) => {
556
- if (err) {
557
- log$4("failure creating key [%s]", err.message);
558
- reject(err);
559
- } else {
560
- log$4("successfully created key");
561
- const pem = pki$1.privateKeyToPem(keypair.privateKey);
562
- this._cryptoCache.key = pem;
563
- resolve(keypair);
564
- }
601
+ log$5("creating key");
602
+ return createRSAKey()
603
+ .then((keyInfo) => {
604
+ log$5("successfully created key");
605
+ this._cryptoCache.key = keyInfo.pem;
606
+ return keyInfo.keypair;
607
+ })
608
+ .catch((err) => {
609
+ log$5("failure creating key [%s]", err.message);
610
+ return Promise.reject(err);
565
611
  });
566
- });
567
612
  }
568
613
  }
569
614
 
@@ -579,7 +624,7 @@ class VoltCredential {
579
624
  return this._cryptoCache;
580
625
  })
581
626
  .catch((err) => {
582
- log$4("failure initialising crypto [%s]", err.message);
627
+ log$5("failure initialising crypto [%s]", err.message);
583
628
  return Promise.reject(err);
584
629
  });
585
630
  }
@@ -594,7 +639,7 @@ class VoltCredential {
594
639
  * @param {*} tunnelling flag indicating if the token is required for a tunnelled connection
595
640
  * @param {*} ttl time to live in seconds (default to 1 minute)
596
641
  */
597
- getIdentityToken(audience, tunnelling = false, ttl = 60) {
642
+ getIdentityToken(audience, publicKey, tunnelling = false, ttl = 60) {
598
643
  if (!this._cryptoCache.key) {
599
644
  throw new Error("crypto cache invalid");
600
645
  }
@@ -603,7 +648,7 @@ class VoltCredential {
603
648
  const publicKeyPem = pki$1.publicKeyToPem(keyPair.publicKey);
604
649
  const base58Key = createPublicKeyFingerprint(publicKeyPem);
605
650
 
606
- log$4("base58 key is %s", base58Key);
651
+ log$5("base58 key is %s", base58Key);
607
652
 
608
653
  // Allow TTL either side of the current time.
609
654
  // @todo - fix with server time sync on volt connection.
@@ -611,7 +656,7 @@ class VoltCredential {
611
656
  aud: audience,
612
657
  iat: Math.floor(Date.now() / 1000) - ttl,
613
658
  exp: Math.floor(Date.now() / 1000) + ttl,
614
- sub: this._cryptoCache.client_id,
659
+ sub: this._cryptoCache.client_id
615
660
  };
616
661
 
617
662
  let sharedKey;
@@ -620,23 +665,20 @@ class VoltCredential {
620
665
  sharedKey = VoltCredential.aesCreateKey();
621
666
 
622
667
  // Encrypt the key details using the target Volt public key and include this in the JWT payload.
623
- payload.sk = VoltCredential.rsaEncrypt(
624
- this._voltPublicKey,
625
- sharedKey.key,
626
- );
627
- payload.iv = VoltCredential.rsaEncrypt(this._voltPublicKey, sharedKey.iv);
668
+ payload.sk = VoltCredential.rsaEncrypt(publicKey, sharedKey.key);
669
+ payload.iv = VoltCredential.rsaEncrypt(publicKey, sharedKey.iv);
628
670
  }
629
671
 
630
672
  // Sign synchronously.
631
673
  const token = jwt__default["default"].sign(payload, this._cryptoCache.key, {
632
674
  algorithm: rs256Algorithm,
633
- keyid: base58Key,
675
+ keyid: base58Key
634
676
  });
635
677
 
636
678
  // Return the token along with any encryption key details.
637
679
  return {
638
680
  token,
639
- sharedKey,
681
+ sharedKey
640
682
  };
641
683
  }
642
684
 
@@ -649,11 +691,12 @@ class VoltCredential {
649
691
  * @param {*} ttl
650
692
  * @returns
651
693
  */
652
- getIdentityMetadata(grpc, audience, tunnelling = false, ttl = 60) {
694
+ getIdentityMetadata(grpc, audience, publicKey, tunnelling = false, ttl = 60) {
653
695
  const identityToken = this.getIdentityToken(
654
696
  audience || this._voltConfig.id,
697
+ publicKey,
655
698
  tunnelling,
656
- ttl,
699
+ ttl
657
700
  );
658
701
  const metadata = new grpc.Metadata();
659
702
  metadata.add(constants.authTokenName, identityToken.token);
@@ -668,7 +711,7 @@ class VoltCredential {
668
711
  const privateKey = pki$1.privateKeyFromPem(this._cryptoCache.key);
669
712
  clone.credential.key = pki$1.encryptRsaPrivateKey(
670
713
  privateKey,
671
- this._config.p,
714
+ this._config.p
672
715
  );
673
716
  }
674
717
  fs__default["default"].writeFileSync(this._configPath, JSON.stringify(clone, null, 2));
@@ -697,6 +740,12 @@ class VoltCredential {
697
740
  static rsaEncrypt(key, buffer) {
698
741
  return crypto__default["default"].publicEncrypt(key, buffer).toString("base64");
699
742
  }
743
+
744
+ static rsaDecrypt(key, buffer) {
745
+ const keyObj = crypto__default["default"].createPrivateKey(key);
746
+ const cipher = Buffer.from(buffer, "base64");
747
+ return Buffer.from(crypto__default["default"].privateDecrypt(keyObj, cipher));
748
+ }
700
749
  }
701
750
 
702
751
  const { get: getProp, snakeCase } = lodash__default["default"];
@@ -713,23 +762,23 @@ const defaultLoaderOptions = {
713
762
  includeDirs: [defaultProtoPath],
714
763
  };
715
764
 
716
- const getProtoDescriptors = (grpc, protoPath, opts) => {
765
+ function getProtoDescriptors(grpc, protoPath, opts) {
717
766
  const definition = protoLoader__default["default"].loadSync(
718
767
  protoPath,
719
768
  opts || defaultLoaderOptions,
720
769
  );
721
770
  const descriptors = grpc.loadPackageDefinition(definition);
722
771
  return descriptors;
723
- };
772
+ }
724
773
 
725
- const getProtoService = (descriptors, servicePath) => {
774
+ function getProtoService(descriptors, servicePath) {
726
775
  const serviceDef = getProp(descriptors, servicePath);
727
776
  if (serviceDef?.service) {
728
777
  return serviceDef.service;
729
778
  } else {
730
779
  return null;
731
780
  }
732
- };
781
+ }
733
782
 
734
783
  function getServiceDescriptorsFromPath(
735
784
  grpc,
@@ -737,8 +786,30 @@ function getServiceDescriptorsFromPath(
737
786
  servicePackageName,
738
787
  opts,
739
788
  ) {
789
+ if (!opts) {
790
+ opts = { ...defaultLoaderOptions };
791
+ opts.includeDirs = [protoPath];
792
+ }
793
+
794
+ // Extract the package components of the service proto package. The package name should be of the
795
+ // fully qualified proto path, e.g. tdx.volt_api.webcam.v1.WebcamControlAPI.
796
+ const protoServiceComponents = servicePackageName.split(".");
797
+
798
+ // The service name is the last component of the path.
799
+ const protoServiceName = protoServiceComponents.pop();
800
+
801
+ // The actual file name should match the snake case of the service name.
802
+ const protoFileName = `${snakeCase(protoServiceName).toLowerCase()}.proto`;
803
+
804
+ // The path to the proto file should match each component of the package name.
805
+ const serviceProtoPath = path.join(
806
+ protoPath,
807
+ ...protoServiceComponents,
808
+ protoFileName,
809
+ );
810
+
740
811
  let serviceDescriptors;
741
- const descriptors = getProtoDescriptors(grpc, protoPath, opts);
812
+ const descriptors = getProtoDescriptors(grpc, serviceProtoPath, opts);
742
813
  if (descriptors) {
743
814
  serviceDescriptors = getProtoService(descriptors, servicePackageName);
744
815
  }
@@ -786,8 +857,17 @@ function getProtoDescriptorMethods(protoDescriptor) {
786
857
  return methods;
787
858
  }
788
859
 
860
+ function createServiceProtobufFiles(service, protoPath) {
861
+ for (let protoFile of service.service_description.proto_file) {
862
+ const filePath = path.join(protoPath, protoFile.file_path);
863
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
864
+ fs.writeFileSync(filePath, protoFile.protobuf);
865
+ }
866
+ }
867
+
789
868
  var protoUtils = /*#__PURE__*/Object.freeze({
790
869
  __proto__: null,
870
+ createServiceProtobufFiles: createServiceProtobufFiles,
791
871
  getServiceDescriptors: getServiceDescriptors,
792
872
  getProtoDescriptors: getProtoDescriptors,
793
873
  getProtoDescriptorMethods: getProtoDescriptorMethods,
@@ -796,70 +876,101 @@ var protoUtils = /*#__PURE__*/Object.freeze({
796
876
 
797
877
  /* eslint-disable no-underscore-dangle */
798
878
 
799
- const log$3 = debug__default["default"]("volt-client-grpc:grpc-call");
879
+ const log$4 = debug__default["default"]("volt-client-grpc:grpc-call");
800
880
 
801
881
  function _prepareInvokeRequest(
802
882
  request,
803
883
  method,
804
884
  methodType = "METHOD_TYPE_UNARY",
805
885
  ) {
806
- const meta = this._voltClient.credential.getIdentityMetadata(
886
+ const isServiceRelayed =
887
+ this._service?.service_description.host_type ===
888
+ "SERVICE_HOST_TYPE_RELAYED";
889
+
890
+ // Use the service's host client id and public key if the service is relayed.
891
+ const methodToken = this._voltClient.credential.getIdentityMetadata(
807
892
  this._voltClient.grpc,
808
- this._voltClient.voltConfig.id,
809
- this._voltClient.isRemote,
893
+ this._service?.service_description.host_client_id ||
894
+ this._voltClient.voltConfig.id,
895
+ this._service?.service_description.host_public_key ||
896
+ this._voltClient.credential.voltPublicKey,
897
+ true,
810
898
  );
899
+
811
900
  let invokeRequest;
812
901
  let callMethod = method;
813
902
 
814
- if (this._voltClient.isVoltRelay) {
903
+ if (this._voltClient.isVoltRelay || isServiceRelayed) {
904
+ //
905
+ // If the volt connection is via a relay (or the target service is relayed),
906
+ // we wrap the rpc in a RemoteRequest message and send it via a call to Invoke().
907
+ //
815
908
  let requestPayload;
909
+
910
+ // Serialise the rpc request.
816
911
  if (request) {
817
912
  requestPayload = this._grpcClient[method].requestSerialize(request);
818
913
  } else if (request !== null) {
819
- log$3("****************LOOKOUT****************** - empty request");
914
+ log$4("****************LOOKOUT****************** - empty request");
820
915
  } else {
821
- log$3("explicit empty request payload");
916
+ log$4("explicit empty request payload");
822
917
  }
823
918
 
824
- const tunnelMethod = this._grpcClient.Tunnel;
919
+ // Use this placeholder to serialise the relay request wrapper.
920
+ const voltGrpcClient = this._voltClient.getVoltAPIClient();
921
+ const tunnelMethod = voltGrpcClient.Tunnel;
825
922
 
826
923
  const tunnelResp = {
827
924
  method_invoke: {
828
925
  method_name: this._grpcClient[method].path,
829
926
  method_type: methodType,
830
- token: meta.token,
927
+ token: methodToken.token,
831
928
  request: requestPayload,
832
929
  },
833
930
  };
834
931
 
835
- const invokePayload = VoltCredential.aesEncrypt(
836
- tunnelMethod.responseSerialize(tunnelResp),
837
- meta.sharedKey.key,
838
- meta.sharedKey.iv,
839
- );
932
+ let targetFingerprint = [];
933
+ let invokePayload;
934
+ if (this._voltClient.isVoltRelay || isServiceRelayed) {
935
+ // When sending via a relay we need to encrypt the payload.
936
+ invokePayload = VoltCredential.aesEncrypt(
937
+ tunnelMethod.responseSerialize(tunnelResp),
938
+ methodToken.sharedKey.key,
939
+ methodToken.sharedKey.iv,
940
+ );
941
+ } else {
942
+ invokePayload = tunnelMethod.responseSerialize(tunnelResp);
943
+ }
840
944
 
841
- log$3("target volt is %s", this._voltClient.credential.voltFingerprint);
945
+ if (this._voltClient.isVoltRelay) {
946
+ targetFingerprint.push(this._voltClient.credential.voltFingerprint);
947
+ }
948
+
949
+ if (isServiceRelayed) {
950
+ // Add another relay hop if the target service is relayed.
951
+ log$4("target service is relayed");
952
+ targetFingerprint.push(
953
+ this._service.service_description.host_fingerprint,
954
+ );
955
+ }
956
+
957
+ log$4("target is %j", targetFingerprint);
842
958
 
843
959
  invokeRequest = {
844
- identity_fingerprint: this._voltClient.credential.voltFingerprint,
960
+ target_fingerprint: targetFingerprint,
961
+ token: methodToken.token,
845
962
  payload: invokePayload,
963
+ target_service_id: isServiceRelayed ? this._service.id : undefined,
846
964
  };
847
965
 
966
+ // Replace the target method with a call to Invoke on the relay Volt.
848
967
  callMethod = "Invoke";
849
- } else if (this._voltClient.isRemote && meta.sharedKey.key) {
850
- const requestPayload =
851
- this._grpcClient[method].requestSerializeOriginal(request);
852
- invokeRequest = VoltCredential.aesEncrypt(
853
- requestPayload,
854
- meta.sharedKey.key,
855
- meta.sharedKey.iv,
856
- );
857
968
  } else {
858
969
  invokeRequest = request;
859
970
  }
860
971
 
861
972
  return {
862
- meta,
973
+ meta: methodToken,
863
974
  method: callMethod,
864
975
  methodName: method,
865
976
  request: invokeRequest,
@@ -869,10 +980,16 @@ function _prepareInvokeRequest(
869
980
  function _preparePayloadRequest(request, invokeInfo) {
870
981
  let payloadRequest;
871
982
 
872
- if (this._voltClient.isVoltRelay) {
983
+ const isServiceRelayed =
984
+ this._service?.service_description.host_type ===
985
+ "SERVICE_HOST_TYPE_RELAYED";
986
+
987
+ if (this._voltClient.isVoltRelay || isServiceRelayed) {
873
988
  const requestPayload =
874
989
  this._grpcClient[invokeInfo.methodName].requestSerialize(request);
875
- const tunnelMethod = this._grpcClient.Tunnel;
990
+
991
+ const voltGrpcClient = this._voltClient.getVoltAPIClient();
992
+ const tunnelMethod = voltGrpcClient.Tunnel;
876
993
 
877
994
  const tunnelResp = {
878
995
  method_payload: {
@@ -889,14 +1006,6 @@ function _preparePayloadRequest(request, invokeInfo) {
889
1006
  payloadRequest = {
890
1007
  payload: invokePayload,
891
1008
  };
892
- } else if (this._voltClient.isRemote && invokeInfo.meta.sharedKey.key) {
893
- const requestPayload =
894
- this._grpcClient[invokeInfo.methodName].requestSerializeOriginal(request);
895
- payloadRequest = VoltCredential.aesEncrypt(
896
- requestPayload,
897
- invokeInfo.meta.sharedKey.key,
898
- invokeInfo.meta.sharedKey.iv,
899
- );
900
1009
  } else {
901
1010
  payloadRequest = request;
902
1011
  }
@@ -905,29 +1014,47 @@ function _preparePayloadRequest(request, invokeInfo) {
905
1014
  }
906
1015
 
907
1016
  function _parseResponse(method, meta, response) {
1017
+ // log("parseResponse for %s", this._methodName);
908
1018
  let invokeResponse = {};
909
1019
 
910
- if (this._voltClient.isVoltRelay) {
1020
+ const isServiceRelayed =
1021
+ this._service?.service_description.host_type ===
1022
+ "SERVICE_HOST_TYPE_RELAYED";
1023
+
1024
+ if (this._voltClient.isVoltRelay || isServiceRelayed) {
911
1025
  if (response.payload) {
912
- const decryptedPayload = VoltCredential.aesDecrypt(
913
- response.payload,
914
- meta.sharedKey.key,
915
- meta.sharedKey.iv,
916
- );
917
- const tunnelMethod = this._grpcClient.Tunnel;
1026
+ let decryptedPayload;
1027
+ if (meta.sharedKey.key) {
1028
+ decryptedPayload = VoltCredential.aesDecrypt(
1029
+ response.payload,
1030
+ meta.sharedKey.key,
1031
+ meta.sharedKey.iv,
1032
+ );
1033
+ } else {
1034
+ decryptedPayload = response.payload;
1035
+ }
1036
+
1037
+ const tunnelMethod = this._voltClient.getVoltAPIClient().Tunnel;
918
1038
  const responsePayload = tunnelMethod.requestDeserialize(decryptedPayload);
919
1039
  if (responsePayload.payload === "method_payload") {
920
- invokeResponse.payload = this._grpcClient[method].responseDeserialize(
921
- responsePayload.method_payload.payload,
922
- );
1040
+ try {
1041
+ invokeResponse.payload = this._grpcClient[method].responseDeserialize(
1042
+ responsePayload.method_payload.payload,
1043
+ );
1044
+ } catch (err) {
1045
+ log$4("failure deserialising payload for %s", this._methodName);
1046
+ throw new Error(
1047
+ "failure deserialising payload - check protobuf definition matches with data being sent",
1048
+ );
1049
+ }
923
1050
  } else if (responsePayload.payload === "method_end") {
924
1051
  invokeResponse = responsePayload.method_end;
925
1052
  } else {
926
- log$3("unexpected tunnel payload type: %s", responsePayload.payload);
1053
+ log$4("unexpected tunnel payload type: %s", responsePayload.payload);
927
1054
  }
928
1055
  } else if (response.status?.message) {
929
1056
  // There's been an error in the tunnel.
930
- log$3(
1057
+ log$4(
931
1058
  "Tunnel status received: %s, code %d, description: %s",
932
1059
  response.status.message,
933
1060
  response.status.code,
@@ -939,7 +1066,7 @@ function _parseResponse(method, meta, response) {
939
1066
  };
940
1067
  } else {
941
1068
  invokeResponse.methodId = response.invoke_id;
942
- log$3("started method %d for %s", invokeResponse.methodId, method);
1069
+ log$4("started method %d for %s", invokeResponse.methodId, method);
943
1070
  }
944
1071
  } else if (this._voltClient.isRemote && meta.sharedKey.key) {
945
1072
  const decryptedPayload = VoltCredential.aesDecrypt(
@@ -959,17 +1086,22 @@ function _parseResponse(method, meta, response) {
959
1086
  }
960
1087
 
961
1088
  class GRPCCall extends EventEmitter__default["default"] {
962
- constructor(voltClient, methodName, methodType) {
1089
+ constructor(voltClient, methodName, methodType, service = undefined) {
963
1090
  super();
964
1091
  this._methodName = methodName;
965
1092
  this._methodType = methodType;
966
1093
  this._call = null;
967
1094
  this._voltClient = voltClient;
1095
+ this._service = service;
968
1096
  }
969
1097
 
970
1098
  start(grpcClient, request) {
971
1099
  this._grpcClient = grpcClient;
972
1100
 
1101
+ if (typeof this._grpcClient[this._methodName] !== "function") {
1102
+ throw new Error(`method not found: '${this._methodName}'`);
1103
+ }
1104
+
973
1105
  this._initialRequest = _prepareInvokeRequest.call(
974
1106
  this,
975
1107
  request,
@@ -977,13 +1109,18 @@ class GRPCCall extends EventEmitter__default["default"] {
977
1109
  this._methodType,
978
1110
  );
979
1111
 
980
- if (!this._grpcClient[this._initialRequest.method]) {
981
- return reject(
982
- new Error(`method not found: '${this._initialRequest.method}'`),
983
- );
1112
+ const isServiceRelayed =
1113
+ this._service?.service_description.host_type ===
1114
+ "SERVICE_HOST_TYPE_RELAYED";
1115
+
1116
+ let callClient;
1117
+ if (this._voltClient.isVoltRelay || isServiceRelayed) {
1118
+ callClient = this._voltClient.getVoltAPIClient();
1119
+ } else {
1120
+ callClient = this._grpcClient;
984
1121
  }
985
1122
 
986
- this._call = this._grpcClient[this._initialRequest.method](
1123
+ this._call = callClient[this._initialRequest.method](
987
1124
  this._initialRequest.meta.metadata,
988
1125
  );
989
1126
 
@@ -1013,28 +1150,32 @@ class GRPCCall extends EventEmitter__default["default"] {
1013
1150
  this.emit("end");
1014
1151
  }
1015
1152
  } else {
1016
- log$3("method id is %s", parsedResponse.methodId);
1153
+ log$4("method id is %s", parsedResponse.methodId);
1017
1154
  }
1018
1155
  } catch (err) {
1019
- log$3("failure processing connect response: %s", err.message);
1156
+ log$4(
1157
+ "failure processing %s call response: %s",
1158
+ this._methodName,
1159
+ err.message,
1160
+ );
1020
1161
  this.emit("error", err);
1021
1162
  }
1022
1163
  });
1023
1164
 
1024
1165
  this._call.on("error", (err) => {
1025
- log$3("ERROR - intercepted stream error %s", err.message);
1166
+ log$4("ERROR - intercepted stream error %s", err.message);
1026
1167
  this.emit("error", err);
1027
1168
  });
1028
1169
 
1029
1170
  this._call.on("finish", () => {
1030
1171
  // The read side has ended (i.e. we called end()).
1031
- log$3("finished call %s", this._methodName);
1172
+ log$4("finished call %s", this._methodName);
1032
1173
  this.emit("finish");
1033
1174
  });
1034
1175
 
1035
1176
  this._call.on("end", () => {
1036
1177
  // The remote peer ended the stream.
1037
- log$3(
1178
+ log$4(
1038
1179
  "ended call %s, method id: %s",
1039
1180
  this._methodName,
1040
1181
  this._initialRequest.methodId || "n/a - not tunnelling",
@@ -1064,7 +1205,7 @@ class GRPCCall extends EventEmitter__default["default"] {
1064
1205
 
1065
1206
  write(request) {
1066
1207
  if (!this._call) {
1067
- log$3("ERROR - call not initialised");
1208
+ log$4("ERROR - call not initialised");
1068
1209
  throw new Error("call not initialised");
1069
1210
  } else if (this._initialRequest) {
1070
1211
  const payloadRequest = _preparePayloadRequest.call(
@@ -1088,7 +1229,7 @@ class GRPCCall extends EventEmitter__default["default"] {
1088
1229
 
1089
1230
  /* eslint-disable no-use-before-define */
1090
1231
 
1091
- const log$2 = debug__default["default"]("volt-client-grpc:volt-connection");
1232
+ const log$3 = debug__default["default"]("volt-client-grpc:volt-connection");
1092
1233
 
1093
1234
  function _cleanUp(immediateReconnect) {
1094
1235
  this._connectionId = "";
@@ -1112,7 +1253,7 @@ function _cleanUp(immediateReconnect) {
1112
1253
  this._reconnectTimer = 0;
1113
1254
  _doConnect.call(this, this._helloPayload);
1114
1255
  },
1115
- immediateReconnect ? 0 : this._reconnectInterval,
1256
+ immediateReconnect ? 0 : this._reconnectInterval
1116
1257
  );
1117
1258
  }
1118
1259
 
@@ -1122,7 +1263,7 @@ function _cleanUp(immediateReconnect) {
1122
1263
  }
1123
1264
 
1124
1265
  function _connectionTimeoutHandler() {
1125
- log$2("server timed out");
1266
+ log$3("server timed out");
1126
1267
  this.emit("connected", false);
1127
1268
  _cleanUp.call(this);
1128
1269
  }
@@ -1133,15 +1274,15 @@ function _doConnect(connectHello) {
1133
1274
  const grpcClient = this._voltClient.getVoltAPIClient();
1134
1275
 
1135
1276
  let helloPayload = connectHello;
1136
- if (!connectHello) {
1137
- log$2("defaulting hello payload");
1277
+ if (!helloPayload) {
1278
+ log$3("defaulting hello payload");
1138
1279
  helloPayload = {
1139
- hello: {},
1280
+ hello: {}
1140
1281
  };
1141
1282
  }
1142
1283
 
1143
1284
  this.call.on("error", (err) => {
1144
- log$2("error on connection stream [%s]", err.message);
1285
+ log$3("error on connection stream [%s]", err.message);
1145
1286
  _cleanUp.call(this);
1146
1287
  this.emit("error", err);
1147
1288
  });
@@ -1149,7 +1290,7 @@ function _doConnect(connectHello) {
1149
1290
  this.call.on("data", (response) => {
1150
1291
  switch (response.payload) {
1151
1292
  case "ping": {
1152
- log$2("pinged");
1293
+ log$3("pinged");
1153
1294
 
1154
1295
  // Received ping response from server => clear timeout.
1155
1296
  if (this._pingTimeoutTimer) {
@@ -1158,23 +1299,30 @@ function _doConnect(connectHello) {
1158
1299
 
1159
1300
  // Schedule the next ping.
1160
1301
  _startPingTimer.call(this);
1302
+
1303
+ this.emit("ping", response.ping.timestamp);
1161
1304
  break;
1162
1305
  }
1163
1306
  case "acknowledge": {
1164
1307
  if (!this._connectionId) {
1165
1308
  this._connectionId = response.acknowledge.connection_id;
1166
- log$2("connection id is %s", this._connectionId);
1309
+ log$3("connection id is %s", this._connectionId);
1167
1310
  this.emit("connected", this._connectionId);
1168
1311
  }
1169
1312
  break;
1170
1313
  }
1314
+ case "invoke_request": {
1315
+ log$3("received invoke request");
1316
+ this.emit("invoke_request", response.invoke_request);
1317
+ break;
1318
+ }
1171
1319
  case "evt": {
1172
- log$2("received event %s", response.evt.event_type);
1320
+ log$3("received event %s", Object.keys(response.evt)[0]);
1173
1321
  this.emit("evt", response.evt);
1174
1322
  break;
1175
1323
  }
1176
1324
  default:
1177
- log$2("unimplemented connect response payload: %s", response.payload);
1325
+ log$3("unimplemented connect response payload: %s", response.payload);
1178
1326
  break;
1179
1327
  }
1180
1328
  });
@@ -1193,7 +1341,7 @@ function _doConnect(connectHello) {
1193
1341
 
1194
1342
  function _startPingTimer() {
1195
1343
  if (this._pingTimer) {
1196
- log$2("ping timer running");
1344
+ log$3("ping timer running");
1197
1345
  return;
1198
1346
  }
1199
1347
 
@@ -1201,7 +1349,7 @@ function _startPingTimer() {
1201
1349
  // Send next ping to server.
1202
1350
  const request = { ping: { timestamp: Date.now() } };
1203
1351
 
1204
- log$2("pinging Volt");
1352
+ log$3("pinging Volt");
1205
1353
  this.call.write(request);
1206
1354
 
1207
1355
  this._pingTimer = 0;
@@ -1213,7 +1361,7 @@ function _startPingTimer() {
1213
1361
 
1214
1362
  this._pingTimeoutTimer = setTimeout(
1215
1363
  _connectionTimeoutHandler.bind(this),
1216
- this._timeoutInterval,
1364
+ this._timeoutInterval
1217
1365
  );
1218
1366
  }, this._pingInterval);
1219
1367
  }
@@ -1250,6 +1398,281 @@ class VoltConnection extends EventEmitter__default["default"] {
1250
1398
  this._dying = true;
1251
1399
  _cleanUp.call(this);
1252
1400
  }
1401
+
1402
+ send(request) {
1403
+ this.call.write(request);
1404
+ }
1405
+ }
1406
+
1407
+ const log$2 = debug__default["default"]("rpc-invocation");
1408
+
1409
+ class RpcInvocation extends EventEmitter__default["default"] {
1410
+ #voltClient = null;
1411
+ #voltConnection = null;
1412
+ #token = null;
1413
+ #encryptKey = null;
1414
+ #encryptIV = null;
1415
+ #invokeId = null;
1416
+ #payload = null;
1417
+ #isJSON = false;
1418
+ #methodDescriptor = null;
1419
+ #methodName = null;
1420
+
1421
+ constructor(voltClient, voltConnection) {
1422
+ super();
1423
+ this.#voltClient = voltClient;
1424
+ this.#voltConnection = voltConnection;
1425
+ }
1426
+
1427
+ get id() {
1428
+ return this.#invokeId;
1429
+ }
1430
+
1431
+ get payload() {
1432
+ if (!this.#payload) {
1433
+ return null;
1434
+ }
1435
+
1436
+ let deserialisedPayload = null;
1437
+
1438
+ if (this.#isJSON) {
1439
+ // Don't need to deserialise JSON payload.
1440
+ deserialisedPayload = this.#payload;
1441
+ } else if (this.#methodDescriptor) {
1442
+ deserialisedPayload = this.#methodDescriptor.requestDeserialize(
1443
+ this.#payload
1444
+ );
1445
+ } else {
1446
+ log$2("unexpected: no method descriptor");
1447
+ throw new Error(
1448
+ "no method descriptor - set this before accessing payload"
1449
+ );
1450
+ }
1451
+
1452
+ return deserialisedPayload;
1453
+ }
1454
+
1455
+ get methodName() {
1456
+ return this.#methodName;
1457
+ }
1458
+
1459
+ set methodDescriptor(methodDescriptor) {
1460
+ this.#methodDescriptor = methodDescriptor;
1461
+ }
1462
+
1463
+ #decodeToken(token) {
1464
+ // We don't verify the token at this point, as we don't know the public key.
1465
+ // The rpc implementation should do the verification using the Volt API (e.g. CanAccessResource).
1466
+ this.#token = jwt__default["default"].decode(token);
1467
+
1468
+ if (this.#token.sk) {
1469
+ // Decrypt the shared key and iv using the private key.
1470
+ this.#encryptKey = VoltCredential.rsaDecrypt(
1471
+ this.#voltClient.credential.cache.key,
1472
+ this.#token.sk
1473
+ );
1474
+ this.#encryptIV = VoltCredential.rsaDecrypt(
1475
+ this.#voltClient.credential.cache.key,
1476
+ this.#token.iv
1477
+ );
1478
+ }
1479
+
1480
+ return Promise.resolve(this.#token);
1481
+ }
1482
+
1483
+ initialise(invoke_request) {
1484
+ this.#invokeId = invoke_request.invoke_id;
1485
+
1486
+ return this.#decodeToken(invoke_request.token)
1487
+ .then(() => {
1488
+ // Parse the initial payload.
1489
+ return this.parsePayload(invoke_request);
1490
+ })
1491
+ .catch((err) => {
1492
+ log$2("failure initialising invocation: %s", err.message);
1493
+ return Promise.reject(err);
1494
+ });
1495
+ }
1496
+
1497
+ parsePayload(invoke_request) {
1498
+ return new Promise((resolve, reject) => {
1499
+ if (invoke_request.payload) {
1500
+ let decryptedPayload;
1501
+
1502
+ if (this.#encryptKey) {
1503
+ // Decrypt the actual payload using the shared key and iv.
1504
+ decryptedPayload = VoltCredential.aesDecrypt(
1505
+ invoke_request.payload,
1506
+ this.#encryptKey,
1507
+ this.#encryptIV
1508
+ );
1509
+ } else {
1510
+ // No encryption => just use the payload.
1511
+ decryptedPayload = invoke_request.payload;
1512
+ }
1513
+
1514
+ const tunnelMethod = this.#voltClient.getVoltAPIClient().Tunnel;
1515
+ const wrappedPayload =
1516
+ tunnelMethod.responseDeserialize(decryptedPayload);
1517
+
1518
+ if (wrappedPayload.method_invoke) {
1519
+ // This is the initial request payload.
1520
+ this.#methodName = wrappedPayload.method_invoke.method_name;
1521
+ this.#payload = wrappedPayload.method_invoke.request;
1522
+ this.emit("payload", this);
1523
+ } else if (wrappedPayload.method_payload) {
1524
+ // This is a subsequent payload, e.g. for streaming rpcs.
1525
+ this.#payload = wrappedPayload.method_payload.payload;
1526
+ this.emit("payload", this);
1527
+ } else if (wrappedPayload.method_end) {
1528
+ this.#payload = wrappedPayload.method_end;
1529
+ if (wrappedPayload.method_end.error) {
1530
+ // The client has sent an error.
1531
+ this.emit("error", this);
1532
+ } else {
1533
+ this.emit("end", this);
1534
+ }
1535
+ }
1536
+
1537
+ resolve();
1538
+ } else if (invoke_request.json_payload) {
1539
+ let decryptedPayload;
1540
+
1541
+ this.#isJSON = true;
1542
+
1543
+ if (this.#encryptKey) {
1544
+ // Decrypt the actual payload using the shared key and iv.
1545
+ decryptedPayload = VoltCredential.aesDecrypt(
1546
+ Buffer.from(invoke_request.json_payload, "base64"),
1547
+ this.#encryptKey,
1548
+ this.#encryptIV
1549
+ );
1550
+ } else {
1551
+ // No encryption => just use the payload.
1552
+ decryptedPayload = invoke_request.json_payload;
1553
+ }
1554
+
1555
+ try {
1556
+ const wrappedPayload = JSON.parse(decryptedPayload.toString());
1557
+
1558
+ if (wrappedPayload.method_invoke) {
1559
+ this.#methodName = wrappedPayload.method_invoke.method_name;
1560
+ this.#payload = JSON.parse(
1561
+ wrappedPayload.method_invoke.json_request
1562
+ );
1563
+ this.emit("payload", this);
1564
+ } else if (wrappedPayload.method_payload) {
1565
+ this.#payload = JSON.parse(
1566
+ wrappedPayload.method_payload.json_payload
1567
+ );
1568
+ this.emit("payload", this);
1569
+ } else if (wrappedPayload.method_end) {
1570
+ // We don't need to do anything here, since we notify the client when we receive the client_end.
1571
+ log$2("method_end received");
1572
+ }
1573
+ } catch (err) {
1574
+ log$2("JSON.parse failure parsing json_payload: %s", err.message);
1575
+ reject(err);
1576
+ }
1577
+
1578
+ resolve();
1579
+ } else if (invoke_request.client_end) {
1580
+ this.emit("end", this);
1581
+ } else {
1582
+ // Do we need to support json_payload here?
1583
+ reject(new Error("No payload in invoke request"));
1584
+ }
1585
+ }).catch((err) => {
1586
+ log$2("failure parsing payload: %s", err.message);
1587
+ return Promise.reject(err);
1588
+ });
1589
+ }
1590
+
1591
+ sendResponse(response) {
1592
+ let responsePayload;
1593
+ if (this.#isJSON) {
1594
+ responsePayload = JSON.stringify({
1595
+ method_payload: { json_payload: JSON.stringify(response) }
1596
+ });
1597
+ } else {
1598
+ // We need to serialise the response, and then wrap it in a RemoteRequest.
1599
+ const serialisedResponse =
1600
+ this.#methodDescriptor.responseSerialize(response);
1601
+ const tunnelMethod = this.#voltClient.getVoltAPIClient().Tunnel;
1602
+ responsePayload = tunnelMethod.requestSerialize({
1603
+ method_payload: { payload: serialisedResponse }
1604
+ });
1605
+ }
1606
+
1607
+ if (this.#token.sk) {
1608
+ // Encrypt the response using the shared key and iv.
1609
+ responsePayload = VoltCredential.aesEncrypt(
1610
+ responsePayload,
1611
+ this.#encryptKey,
1612
+ this.#encryptIV
1613
+ );
1614
+ }
1615
+
1616
+ const invokeResponse = {
1617
+ invoke_id: this.#invokeId
1618
+ };
1619
+
1620
+ if (this.#isJSON) {
1621
+ invokeResponse.json_payload =
1622
+ Buffer.from(responsePayload).toString("base64");
1623
+ } else {
1624
+ invokeResponse.payload = responsePayload;
1625
+ }
1626
+
1627
+ this.#voltConnection.send({
1628
+ invoke_response: invokeResponse
1629
+ });
1630
+ }
1631
+
1632
+ sendEnd(errorMessage) {
1633
+ const tunnelMethod = this.#voltClient.getVoltAPIClient().Tunnel;
1634
+
1635
+ let endPayload;
1636
+ if (errorMessage) {
1637
+ endPayload = {
1638
+ method_end: { error: errorMessage, ended: true }
1639
+ };
1640
+ } else {
1641
+ endPayload = {
1642
+ method_end: { ended: true }
1643
+ };
1644
+ }
1645
+
1646
+ if (this.#isJSON) {
1647
+ endPayload = JSON.stringify(endPayload);
1648
+ } else {
1649
+ endPayload = tunnelMethod.requestSerialize(endPayload);
1650
+ }
1651
+
1652
+ if (this.#token.sk) {
1653
+ // Encrypt the response using the shared key and iv.
1654
+ endPayload = VoltCredential.aesEncrypt(
1655
+ endPayload,
1656
+ this.#encryptKey,
1657
+ this.#encryptIV
1658
+ );
1659
+ }
1660
+
1661
+ const invokeResponse = {
1662
+ invoke_id: this.#invokeId,
1663
+ server_end: true
1664
+ };
1665
+
1666
+ if (this.#isJSON) {
1667
+ invokeResponse.json_payload = Buffer.from(endPayload).toString("base64");
1668
+ } else {
1669
+ invokeResponse.payload = endPayload;
1670
+ }
1671
+
1672
+ this.#voltConnection.send({
1673
+ invoke_response: invokeResponse
1674
+ });
1675
+ }
1253
1676
  }
1254
1677
 
1255
1678
  /* eslint-disable no-underscore-dangle */
@@ -1265,51 +1688,53 @@ const voltServices = [
1265
1688
  constants.serviceType.sqliteServerAPI,
1266
1689
  constants.serviceType.ssiAPI,
1267
1690
  constants.serviceType.relayAPI,
1268
- constants.serviceType.wireAPI,
1691
+ constants.serviceType.wireAPI
1269
1692
  ];
1270
1693
 
1271
1694
  function issueBind(bindRequest, ttl) {
1272
1695
  // eslint-disable-next-line no-use-before-define
1273
- return unaryCall.call(this, "Bind", bindRequest).then((bindResponse) => {
1274
- log$1("got binding request response %j", bindResponse);
1275
- if (bindResponse.status?.code) {
1276
- log$1("error in bind response: %s", bindResponse.status.message);
1277
- return Promise.reject(new Error(bindResponse.status.message));
1278
- } else {
1279
- switch (bindResponse.decision) {
1280
- case "POLICY_DECISION_PERMIT": {
1281
- //
1282
- // The request status is permit => cache the bind response info.
1283
- //
1284
-
1285
- // This is the certificate assigned to us by the volt.
1286
- this._credential.cache.cert = bindResponse.cert;
1287
-
1288
- // This is the signing CA used by the volt.
1289
- this._credential.cache.ca = bindResponse.chain;
1290
-
1291
- // Identity resource id is assigned by the volt.
1292
- this._credential.cache.client_id = bindResponse.identity_id;
1293
- this._credential.saveCache();
1294
- break;
1295
- }
1296
- case "POLICY_DECISION_DENY": {
1297
- log$1(">>>>>>>>>>>>>>> access request is DENIED <<<<<<<<<<<<<<<<<<<");
1298
- break;
1299
- }
1300
- case "POLICY_DECISION_PROMPT":
1301
- case "POLICY_DECISION_PENDING": {
1302
- log$1("access pending approval - waiting...");
1303
- break;
1696
+ return unaryCallInternal
1697
+ .call(this, "Bind", bindRequest)
1698
+ .then((bindResponse) => {
1699
+ log$1("got binding request response %j", bindResponse);
1700
+ if (bindResponse.status?.code) {
1701
+ log$1("error in bind response: %s", bindResponse.status.message);
1702
+ return Promise.reject(new Error(bindResponse.status.message));
1703
+ } else {
1704
+ switch (bindResponse.decision) {
1705
+ case "POLICY_DECISION_PERMIT": {
1706
+ //
1707
+ // The request status is permit => cache the bind response info.
1708
+ //
1709
+
1710
+ // This is the certificate assigned to us by the volt.
1711
+ this._credential.cache.cert = bindResponse.cert;
1712
+
1713
+ // This is the signing CA used by the volt.
1714
+ this._credential.cache.ca = bindResponse.chain;
1715
+
1716
+ // Identity resource id is assigned by the volt.
1717
+ this._credential.cache.client_id = bindResponse.identity_id;
1718
+ this._credential.saveCache();
1719
+ break;
1720
+ }
1721
+ case "POLICY_DECISION_DENY": {
1722
+ log$1(">>>>>>>>>>>>>>> access request is DENIED <<<<<<<<<<<<<<<<<<<");
1723
+ break;
1724
+ }
1725
+ case "POLICY_DECISION_PROMPT":
1726
+ case "POLICY_DECISION_PENDING": {
1727
+ log$1("access pending approval - waiting...");
1728
+ break;
1729
+ }
1730
+ default:
1731
+ log$1("ignoring unknown bind descision %s", bindResponse.status);
1732
+ break;
1304
1733
  }
1305
- default:
1306
- log$1("ignoring unknown bind descision %s", bindResponse.status);
1307
- break;
1308
1734
  }
1309
- }
1310
1735
 
1311
- return bindResponse.decision;
1312
- });
1736
+ return bindResponse.decision;
1737
+ });
1313
1738
  }
1314
1739
 
1315
1740
  function findDIDDocumentService(document, serviceType) {
@@ -1333,13 +1758,13 @@ async function bindInternal() {
1333
1758
  log$1("attempting to retrieve Relay information from %s", relayURL);
1334
1759
  this._voltConfig.relay = await fetchVoltConfig.call(
1335
1760
  this,
1336
- `${relayURL}/discovery`,
1761
+ `${relayURL}/discovery`
1337
1762
  );
1338
1763
  if (this._voltConfig.relay.ca_pem) {
1339
1764
  log$1(
1340
1765
  "Auto-fetched Relay CA %s, remote address %s",
1341
1766
  this._voltConfig.relay.ca_pem,
1342
- this._voltConfig.relay.address,
1767
+ this._voltConfig.relay.address
1343
1768
  );
1344
1769
  } else {
1345
1770
  throw new Error("Unable to fetch Relay CA - cannot securely connect.");
@@ -1352,7 +1777,7 @@ async function bindInternal() {
1352
1777
 
1353
1778
  if (!this._credential.cache.ca) {
1354
1779
  throw new Error(
1355
- "No certificate authority found in configuration for target Volt - check configuration",
1780
+ "No certificate authority found in configuration for target Volt - check configuration"
1356
1781
  );
1357
1782
  }
1358
1783
 
@@ -1372,30 +1797,30 @@ async function bindInternal() {
1372
1797
  binding_name: this._config.client_name,
1373
1798
  public_key: publicKeyPem,
1374
1799
  host: this._credential.cache.bindIp,
1375
- x509_credential: [],
1800
+ x509_credential: []
1376
1801
  };
1377
1802
 
1378
1803
  if (this._credential.cache.cert) {
1379
1804
  bindRequest.x509_credential = [
1380
- this._credential.cache.cert + this._credential.cache.ca,
1805
+ this._credential.cache.cert + this._credential.cache.ca
1381
1806
  ];
1382
1807
  }
1383
1808
 
1384
1809
  if (this._voltConfig.challenge_code) {
1385
1810
  bindRequest.challenge = signBase64(
1386
1811
  keyPair.privateKey,
1387
- this._voltConfig.challenge_code,
1812
+ this._voltConfig.challenge_code
1388
1813
  );
1389
1814
  } else {
1390
1815
  log$1(
1391
- "****no Volt challenge code available**** => not sending challenge signature",
1816
+ "****no Volt challenge code available**** => not sending challenge signature"
1392
1817
  );
1393
1818
  }
1394
1819
 
1395
1820
  // For remote connections, add our cloud-issued certificate as an additional credential.
1396
1821
  if (this._voltConfig?.relay?.ca_pem && this._credential.cache.cloud_cert) {
1397
1822
  bindRequest.x509_credential = bindRequest.x509_credential.concat(
1398
- this._credential.cache.cloud_cert + this._voltConfig.relay.ca_pem,
1823
+ this._credential.cache.cloud_cert + this._voltConfig.relay.ca_pem
1399
1824
  );
1400
1825
  }
1401
1826
 
@@ -1415,7 +1840,7 @@ async function bindInternal() {
1415
1840
  decision = await issueBind.call(
1416
1841
  this,
1417
1842
  bindRequest,
1418
- this._voltConfig.bindRequestTTL,
1843
+ this._voltConfig.bindRequestTTL
1419
1844
  );
1420
1845
  log$1("binding decision: %s", decision);
1421
1846
  } while (
@@ -1476,6 +1901,34 @@ function connectInternal(helloPayload) {
1476
1901
  this.emit("evt", evt);
1477
1902
  });
1478
1903
 
1904
+ this._voltConnection.on("ping", (ping) => {
1905
+ this.emit("ping", ping);
1906
+ });
1907
+
1908
+ this._voltConnection.on("invoke_request", (invoke_request) => {
1909
+ const invokeId = invoke_request.invoke_id;
1910
+ if (this._activeRPC[invokeId]) {
1911
+ this._activeRPC[invokeId].parsePayload(invoke_request);
1912
+ } else {
1913
+ const rpcInvocation = new RpcInvocation(this, this._voltConnection);
1914
+
1915
+ rpcInvocation.on("end", () => {
1916
+ log$1("removing active RPC [%s]", invokeId);
1917
+ this._activeRPC[invokeId] = undefined;
1918
+ });
1919
+
1920
+ rpcInvocation
1921
+ .initialise(invoke_request)
1922
+ .then(() => {
1923
+ this._activeRPC[invokeId] = rpcInvocation;
1924
+ this.emit("invoke_request", rpcInvocation);
1925
+ })
1926
+ .catch((err) => {
1927
+ log$1("invoke_request - error [%s]", err.message);
1928
+ });
1929
+ }
1930
+ });
1931
+
1479
1932
  return this._voltConnection.connect(helloPayload);
1480
1933
  } catch (err) {
1481
1934
  log$1("connect - error [%s]", err.message);
@@ -1502,18 +1955,68 @@ function getVoltAPIClientInternal() {
1502
1955
  this._credential,
1503
1956
  this.isRemote && !this.isVoltRelay,
1504
1957
  this._voltConfig?.relay?.cloud ? this._voltConfig.id : "",
1505
- this._voltConfig?.relay?.cloud ? this._voltConfig.id : "",
1958
+ this._voltConfig?.relay?.cloud ? this._voltConfig.id : ""
1506
1959
  );
1507
1960
  }
1508
1961
 
1509
1962
  return this._cachedClient;
1510
1963
  }
1511
1964
 
1512
- function unaryCall(method, request) {
1513
- return new Promise((resolve, reject) => {
1514
- const grpcClient = this.getVoltAPIClient();
1965
+ function getAPIClientInternal(service) {
1966
+ if (!this._cachedService[service.id]) {
1967
+ service.service_description.host_type === "SERVICE_HOST_TYPE_RELAYED";
1968
+
1969
+ const serviceAddress = this.isRemote
1970
+ ? this._voltConfig.relay.address
1971
+ : service.service_description.host_address;
1972
+
1973
+ createServiceProtobufFiles(service, "./service-proto");
1974
+
1975
+ log$1("creating API service client on %s", serviceAddress);
1976
+ let serviceDescriptors = {};
1977
+ const fullProtoPath = path.join(process.cwd(), "./service-proto");
1978
+ for (let api of service.service_description.service_api) {
1979
+ const packageDescriptors = getServiceDescriptorsFromPath(
1980
+ this._grpc,
1981
+ fullProtoPath,
1982
+ api
1983
+ );
1984
+ serviceDescriptors = { ...serviceDescriptors, ...packageDescriptors };
1985
+ }
1986
+
1987
+ this._cachedService[service.id] = createClient(
1988
+ this._grpc,
1989
+ serviceDescriptors,
1990
+ serviceAddress,
1991
+ this._credential,
1992
+ this.isRemote && !this.isVoltRelay,
1993
+ this._voltConfig?.relay?.cloud ? this._voltConfig.id : "",
1994
+ this._voltConfig?.relay?.cloud ? this._voltConfig.id : ""
1995
+ );
1996
+ }
1997
+
1998
+ return this._cachedService[service.id];
1999
+ }
2000
+
2001
+ function getServiceClient(service) {
2002
+ let grpcClient;
2003
+ if (
2004
+ service &&
2005
+ service?.service_description.host_type !== "SERVICE_HOST_TYPE_BUILTIN"
2006
+ ) {
2007
+ grpcClient = getAPIClientInternal.call(this, service);
2008
+ } else {
2009
+ grpcClient = getVoltAPIClientInternal.call(this);
2010
+ }
2011
+
2012
+ return grpcClient;
2013
+ }
1515
2014
 
1516
- const call = new GRPCCall(this, method, "METHOD_TYPE_UNARY");
2015
+ function unaryCallInternal(method, request, service) {
2016
+ const grpcClient = getServiceClient.call(this, service);
2017
+
2018
+ return new Promise((resolve, reject) => {
2019
+ const call = new GRPCCall(this, method, "METHOD_TYPE_UNARY", service);
1517
2020
 
1518
2021
  let response = null;
1519
2022
 
@@ -1541,6 +2044,16 @@ function unaryCall(method, request) {
1541
2044
  });
1542
2045
  }
1543
2046
 
2047
+ function streamingCallInternal(methodType, method, request, service) {
2048
+ const grpcClient = getServiceClient.call(this, service);
2049
+
2050
+ const call = new GRPCCall(this, method, methodType, service);
2051
+
2052
+ call.start(grpcClient, request);
2053
+
2054
+ return call;
2055
+ }
2056
+
1544
2057
  async function fetchVoltConfig(discovery_url) {
1545
2058
  try {
1546
2059
  const getJSON = bent__default["default"]("json");
@@ -1560,13 +2073,13 @@ async function fetchVoltConfig(discovery_url) {
1560
2073
  "ca_pem",
1561
2074
  "challenge_code",
1562
2075
  "cloud",
1563
- "relay",
2076
+ "relay"
1564
2077
  );
1565
2078
 
1566
2079
  return voltConfig;
1567
2080
  } catch (err) {
1568
2081
  throw new Error(
1569
- `Failure loading config from ${discovery_url}: ${err.message}`,
2082
+ `Failure loading config from ${discovery_url}: ${err.message}`
1570
2083
  );
1571
2084
  }
1572
2085
  }
@@ -1582,13 +2095,13 @@ async function fetchVoltConfigFromDID(volt_did) {
1582
2095
  const didDocument = await getJSON(didResolution);
1583
2096
  const voltConfigServices = findDIDDocumentService(
1584
2097
  didDocument,
1585
- constants.didServiceType.voltConfig,
2098
+ constants.didServiceType.voltConfig
1586
2099
  );
1587
2100
  if (voltConfigServices.length !== 1) {
1588
2101
  log$1(
1589
2102
  "Unexpected number of Volt configuration endpoints found in DID document for %s, services found: %d",
1590
2103
  volt_did,
1591
- voltConfigServices.length,
2104
+ voltConfigServices.length
1592
2105
  );
1593
2106
  }
1594
2107
 
@@ -1596,7 +2109,7 @@ async function fetchVoltConfigFromDID(volt_did) {
1596
2109
  return await fetchVoltConfig.call(this, serviceEndpoint);
1597
2110
  } catch (err) {
1598
2111
  throw new Error(
1599
- `Failure fetching Volt config from DID document ${volt_did}: ${err.message}`,
2112
+ `Failure fetching Volt config from DID document ${volt_did}: ${err.message}`
1600
2113
  );
1601
2114
  }
1602
2115
  }
@@ -1621,6 +2134,8 @@ class VoltClient extends EventEmitter__default["default"] {
1621
2134
  this._config = null;
1622
2135
  this._voltConfig = null;
1623
2136
  this._voltConnection = null;
2137
+ this._cachedService = {};
2138
+ this._activeRPC = {};
1624
2139
  }
1625
2140
 
1626
2141
  get config() {
@@ -1647,7 +2162,7 @@ class VoltClient extends EventEmitter__default["default"] {
1647
2162
  try {
1648
2163
  if (!config) {
1649
2164
  throw new Error(
1650
- "configPath argument is required to be a non-empty string",
2165
+ "configPath argument is required to be a non-empty string"
1651
2166
  );
1652
2167
  }
1653
2168
 
@@ -1658,13 +2173,13 @@ class VoltClient extends EventEmitter__default["default"] {
1658
2173
  log("Attempt to load config from file %s", configPath);
1659
2174
  try {
1660
2175
  const configContents = await readFile(
1661
- 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))),
2176
+ 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)))
1662
2177
  );
1663
2178
  configJSON = JSON.parse(configContents);
1664
2179
  } catch (err) {
1665
2180
  log("failure loading config file %s [%s]", configPath, err.message);
1666
2181
  throw new Error(
1667
- `failed to load configuration - check JSON format in ${configPath}`,
2182
+ `failed to load configuration - check JSON format in ${configPath}`
1668
2183
  );
1669
2184
  }
1670
2185
  } else if (typeof config === "object") {
@@ -1705,14 +2220,14 @@ class VoltClient extends EventEmitter__default["default"] {
1705
2220
  const didConfig = await fetchVoltConfigFromDID.call(this, voltDID);
1706
2221
  this._config = { ...this._config, volt: didConfig };
1707
2222
  log("resolved config from DID: %s", JSON.stringify(didConfig, null, 2));
1708
- } else if (voltHttpAddress) {
2223
+ } else if (voltHttpAddress && !this._config?.volt?.id) {
1709
2224
  const discoConfig = await fetchVoltConfig.call(
1710
2225
  this,
1711
- `${voltHttpAddress}/discovery`,
2226
+ `${voltHttpAddress}/discovery`
1712
2227
  );
1713
2228
  this._config = {
1714
2229
  ...this._config,
1715
- volt: { ...this._config.volt, ...discoConfig },
2230
+ volt: { ...this._config.volt, ...discoConfig }
1716
2231
  };
1717
2232
  }
1718
2233
 
@@ -1756,7 +2271,7 @@ class VoltClient extends EventEmitter__default["default"] {
1756
2271
  if (bindErr.message === "invalid arguments") {
1757
2272
  // This is usually the result of a missing challenge signature or credential.
1758
2273
  log(
1759
- "failure binding to Volt - did you supply a challenge code or verified credential?",
2274
+ "failure binding to Volt - did you supply a challenge code or verified credential?"
1760
2275
  );
1761
2276
  throw bindErr;
1762
2277
  }
@@ -1819,7 +2334,7 @@ class VoltClient extends EventEmitter__default["default"] {
1819
2334
  if (this.isRemote) {
1820
2335
  if (!this._voltConfig?.relay?.address) {
1821
2336
  throw new Error(
1822
- "Remote connection enabled but no Relay address - have you called start()?",
2337
+ "Remote connection enabled but no Relay address - have you called start()?"
1823
2338
  );
1824
2339
  }
1825
2340
 
@@ -1831,14 +2346,14 @@ class VoltClient extends EventEmitter__default["default"] {
1831
2346
  this._credential,
1832
2347
  false,
1833
2348
  service.volt_id,
1834
- service.id,
2349
+ service.id
1835
2350
  );
1836
2351
  } else {
1837
2352
  return createClient(
1838
2353
  this._grpc,
1839
2354
  serviceDescriptors,
1840
2355
  service.service_description.address,
1841
- this._credential,
2356
+ this._credential
1842
2357
  );
1843
2358
  }
1844
2359
  }
@@ -1866,6 +2381,40 @@ class VoltClient extends EventEmitter__default["default"] {
1866
2381
  return getVoltAPIClientInternal.call(this);
1867
2382
  }
1868
2383
 
2384
+ unaryCall(method, request, service) {
2385
+ return unaryCallInternal.call(this, method, request, service);
2386
+ }
2387
+
2388
+ bidiStreamingCall(method, request, service) {
2389
+ return streamingCallInternal.call(
2390
+ this,
2391
+ "METHOD_TYPE_BIDI",
2392
+ method,
2393
+ request,
2394
+ service
2395
+ );
2396
+ }
2397
+
2398
+ clientStreamingCall(method, request, service) {
2399
+ return streamingCallInternal.call(
2400
+ this,
2401
+ "METHOD_TYPE_CLIENT_STREAM",
2402
+ method,
2403
+ request,
2404
+ service
2405
+ );
2406
+ }
2407
+
2408
+ serverStreamingCall(method, request, service) {
2409
+ return streamingCallInternal.call(
2410
+ this,
2411
+ "METHOD_TYPE_SERVER_STREAM",
2412
+ method,
2413
+ request,
2414
+ service
2415
+ );
2416
+ }
2417
+
1869
2418
  /**
1870
2419
  * Request resource access.
1871
2420
  * @param {*} targetResourceId
@@ -1873,12 +2422,12 @@ class VoltClient extends EventEmitter__default["default"] {
1873
2422
  */
1874
2423
  async RequestAccessBlocking(
1875
2424
  targetResourceId,
1876
- accessType = "VOLT_ACCESS_READ",
2425
+ accessType = "VOLT_ACCESS_READ"
1877
2426
  ) {
1878
2427
  try {
1879
2428
  const accessRequest = {
1880
2429
  access: accessType,
1881
- resource_id: targetResourceId,
2430
+ resource_id: targetResourceId
1882
2431
  };
1883
2432
 
1884
2433
  // This will block while the request is pending.
@@ -1910,47 +2459,47 @@ class VoltClient extends EventEmitter__default["default"] {
1910
2459
  */
1911
2460
 
1912
2461
  CanAccessResource(request) {
1913
- return unaryCall.call(this, "CanAccessResource", request);
2462
+ return unaryCallInternal.call(this, "CanAccessResource", request);
1914
2463
  }
1915
2464
 
1916
2465
  DeleteResource(request) {
1917
- return unaryCall.call(this, "DeleteResource", request);
2466
+ return unaryCallInternal.call(this, "DeleteResource", request);
1918
2467
  }
1919
2468
 
1920
2469
  DiscoverServices(request) {
1921
- return unaryCall.call(this, "DiscoverServices", request);
2470
+ return unaryCallInternal.call(this, "DiscoverServices", request);
1922
2471
  }
1923
2472
 
1924
2473
  GetResource(request) {
1925
- return unaryCall.call(this, "GetResource", request);
2474
+ return unaryCallInternal.call(this, "GetResource", request);
1926
2475
  }
1927
2476
 
1928
2477
  GetResources(request) {
1929
- return unaryCall.call(this, "GetResources", request);
2478
+ return unaryCallInternal.call(this, "GetResources", request);
1930
2479
  }
1931
2480
 
1932
2481
  GetResourceAncestors(request) {
1933
- return unaryCall.call(this, "GetResourceAncestors", request);
2482
+ return unaryCallInternal.call(this, "GetResourceAncestors", request);
1934
2483
  }
1935
2484
 
1936
2485
  GetResourceDescendants(request) {
1937
- return unaryCall.call(this, "GetResourceDescendants", request);
2486
+ return unaryCallInternal.call(this, "GetResourceDescendants", request);
1938
2487
  }
1939
2488
 
1940
2489
  RequestAccess(request) {
1941
- return unaryCall.call(this, "RequestAccess", request);
2490
+ return unaryCallInternal.call(this, "RequestAccess", request);
1942
2491
  }
1943
2492
 
1944
2493
  SaveResource(request) {
1945
- return unaryCall.call(this, "SaveResource", request);
2494
+ return unaryCallInternal.call(this, "SaveResource", request);
1946
2495
  }
1947
2496
 
1948
2497
  SaveResourceAttribute(request) {
1949
- return unaryCall.call(this, "SaveResourceAttribute", request);
2498
+ return unaryCallInternal.call(this, "SaveResourceAttribute", request);
1950
2499
  }
1951
2500
 
1952
2501
  SetServiceStatus(request) {
1953
- return unaryCall.call(this, "SetServiceStatus", request);
2502
+ return unaryCallInternal.call(this, "SetServiceStatus", request);
1954
2503
  }
1955
2504
 
1956
2505
  /**
@@ -1958,82 +2507,82 @@ class VoltClient extends EventEmitter__default["default"] {
1958
2507
  */
1959
2508
 
1960
2509
  Bind(request) {
1961
- return unaryCall.call(this, "Bind", request);
2510
+ return unaryCallInternal.call(this, "Bind", request);
1962
2511
  }
1963
2512
 
1964
2513
  DeleteAccess(request) {
1965
- return unaryCall.call(this, "DeleteAccess", request);
2514
+ return unaryCallInternal.call(this, "DeleteAccess", request);
1966
2515
  }
1967
2516
 
1968
2517
  DeleteVolt(request) {
1969
- return unaryCall.call(this, "DeleteVolt", request);
2518
+ return unaryCallInternal.call(this, "DeleteVolt", request);
1970
2519
  }
1971
2520
 
1972
2521
  GetAccess(request) {
1973
- return unaryCall.call(this, "GetAccess", request);
2522
+ return unaryCallInternal.call(this, "GetAccess", request);
1974
2523
  }
1975
2524
 
1976
2525
  GetBindings(request) {
1977
- return unaryCall.call(this, "GetBindings", request);
2526
+ return unaryCallInternal.call(this, "GetBindings", request);
1978
2527
  }
1979
2528
 
1980
2529
  GetIdentities(request) {
1981
- return unaryCall.call(this, "GetIdentities", request);
2530
+ return unaryCallInternal.call(this, "GetIdentities", request);
1982
2531
  }
1983
2532
 
1984
2533
  GetIdentity(request) {
1985
- return unaryCall.call(this, "GetIdentity", request);
2534
+ return unaryCallInternal.call(this, "GetIdentity", request);
1986
2535
  }
1987
2536
 
1988
2537
  GetIdentityToken(request) {
1989
- return unaryCall.call(this, "GetIdentityToken", request);
2538
+ return unaryCallInternal.call(this, "GetIdentityToken", request);
1990
2539
  }
1991
2540
 
1992
2541
  GetPolicy(request) {
1993
- return unaryCall.call(this, "GetPolicy", request);
2542
+ return unaryCallInternal.call(this, "GetPolicy", request);
1994
2543
  }
1995
2544
 
1996
2545
  GetSettings(request) {
1997
- return unaryCall.call(this, "GetSettings", request);
2546
+ return unaryCallInternal.call(this, "GetSettings", request);
1998
2547
  }
1999
2548
 
2000
2549
  SaveAccess(request) {
2001
- return unaryCall.call(this, "SaveAccess", request);
2550
+ return unaryCallInternal.call(this, "SaveAccess", request);
2002
2551
  }
2003
2552
 
2004
2553
  SaveCloudConnection(request) {
2005
- return unaryCall.call(this, "SaveCloudConnection", request);
2554
+ return unaryCallInternal.call(this, "SaveCloudConnection", request);
2006
2555
  }
2007
2556
 
2008
2557
  SaveIdentity(request) {
2009
- return unaryCall.call(this, "SaveIdentity", request);
2558
+ return unaryCallInternal.call(this, "SaveIdentity", request);
2010
2559
  }
2011
2560
 
2012
2561
  SaveSettings(request) {
2013
- return unaryCall.call(this, "SaveSettings", request);
2562
+ return unaryCallInternal.call(this, "SaveSettings", request);
2014
2563
  }
2015
2564
 
2016
2565
  SetAccessRequestDecision(request) {
2017
- return unaryCall.call(this, "SetAccessRequestDecision", request);
2566
+ return unaryCallInternal.call(this, "SetAccessRequestDecision", request);
2018
2567
  }
2019
2568
 
2020
2569
  SetBindingDecision(request) {
2021
- return unaryCall.call(this, "SetBindingDecision", request);
2570
+ return unaryCallInternal.call(this, "SetBindingDecision", request);
2022
2571
  }
2023
2572
 
2024
2573
  Shutdown(request) {
2025
- return unaryCall.call(this, "Shutdown", request);
2574
+ return unaryCallInternal.call(this, "Shutdown", request);
2026
2575
  }
2027
2576
 
2028
2577
  SignVerify(request) {
2029
- return unaryCall.call(this, "SignVerify", request);
2578
+ return unaryCallInternal.call(this, "SignVerify", request);
2030
2579
  }
2031
2580
 
2032
2581
  /**
2033
2582
  * FileAPI
2034
2583
  */
2035
2584
  GetFileDescendants(request) {
2036
- return unaryCall.call(this, "GetFileDescendants", request);
2585
+ return unaryCallInternal.call(this, "GetFileDescendants", request);
2037
2586
  }
2038
2587
 
2039
2588
  /**
@@ -2047,11 +2596,19 @@ class VoltClient extends EventEmitter__default["default"] {
2047
2596
  const downloadCall = new GRPCCall(
2048
2597
  this,
2049
2598
  "DownloadFile",
2050
- "METHOD_TYPE_SERVER_STREAM",
2599
+ "METHOD_TYPE_SERVER_STREAM"
2051
2600
  );
2052
2601
  return downloadCall.start(grpcClient, request);
2053
2602
  }
2054
2603
 
2604
+ GetFileContent(request) {
2605
+ return unaryCallInternal.call(this, "GetFileContent", request);
2606
+ }
2607
+
2608
+ SetFileContent(request) {
2609
+ return unaryCallInternal.call(this, "SetFileContent", request);
2610
+ }
2611
+
2055
2612
  UploadFile(request) {
2056
2613
  const grpcClient = this.getVoltAPIClient();
2057
2614
 
@@ -2073,7 +2630,7 @@ class VoltClient extends EventEmitter__default["default"] {
2073
2630
  const call = new GRPCCall(
2074
2631
  this,
2075
2632
  "DownloadFile",
2076
- "METHOD_TYPE_SERVER_STREAM",
2633
+ "METHOD_TYPE_SERVER_STREAM"
2077
2634
  );
2078
2635
 
2079
2636
  call.on("data", (response) => {
@@ -2111,6 +2668,10 @@ class VoltClient extends EventEmitter__default["default"] {
2111
2668
  /**
2112
2669
  * SqliteDatabaseAPI
2113
2670
  */
2671
+ BulkUpdate(request) {
2672
+ return unaryCallInternal.call(this, "BulkUpdate", request);
2673
+ }
2674
+
2114
2675
  SqlExecute(request) {
2115
2676
  const grpcClient = this.getVoltAPIClient();
2116
2677
 
@@ -2194,7 +2755,7 @@ class VoltClient extends EventEmitter__default["default"] {
2194
2755
  const subscribeCall = new GRPCCall(
2195
2756
  this,
2196
2757
  "SubscribeWire",
2197
- "METHOD_TYPE_BIDI",
2758
+ "METHOD_TYPE_BIDI"
2198
2759
  );
2199
2760
  return subscribeCall.start(grpcClient, request);
2200
2761
  }