@firebase/data-connect 0.0.2-dataconnect-preview.877f8b7d0 → 0.0.3-dataconnect-preview.d986d4bf2

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.
Files changed (40) hide show
  1. package/dist/index.cjs.js +204 -29
  2. package/dist/index.cjs.js.map +1 -1
  3. package/dist/index.esm2017.js +203 -30
  4. package/dist/index.esm2017.js.map +1 -1
  5. package/dist/index.esm5.js +204 -25
  6. package/dist/index.esm5.js.map +1 -1
  7. package/dist/index.node.cjs.js +205 -24
  8. package/dist/index.node.cjs.js.map +1 -1
  9. package/dist/internal.d.ts +159 -13
  10. package/dist/node-esm/index.node.esm.js +203 -30
  11. package/dist/node-esm/index.node.esm.js.map +1 -1
  12. package/dist/node-esm/src/api/DataConnect.d.ts +42 -0
  13. package/dist/node-esm/src/api/Mutation.d.ts +26 -1
  14. package/dist/node-esm/src/api/Reference.d.ts +6 -0
  15. package/dist/node-esm/src/api/index.d.ts +1 -0
  16. package/dist/node-esm/src/api/query.d.ts +51 -1
  17. package/dist/node-esm/src/api.browser.d.ts +12 -7
  18. package/dist/node-esm/src/core/FirebaseAuthProvider.d.ts +1 -1
  19. package/dist/node-esm/src/core/QueryManager.d.ts +1 -1
  20. package/dist/node-esm/src/core/error.d.ts +2 -1
  21. package/dist/node-esm/src/network/fetch.d.ts +1 -1
  22. package/dist/node-esm/src/network/transport/index.d.ts +1 -1
  23. package/dist/node-esm/src/network/transport/rest.d.ts +20 -9
  24. package/dist/node-esm/src/util/validateArgs.d.ts +33 -0
  25. package/dist/private.d.ts +141 -19
  26. package/dist/public.d.ts +130 -15
  27. package/dist/src/api/DataConnect.d.ts +42 -0
  28. package/dist/src/api/Mutation.d.ts +26 -1
  29. package/dist/src/api/Reference.d.ts +6 -0
  30. package/dist/src/api/index.d.ts +1 -0
  31. package/dist/src/api/query.d.ts +51 -1
  32. package/dist/src/api.browser.d.ts +12 -7
  33. package/dist/src/core/FirebaseAuthProvider.d.ts +1 -1
  34. package/dist/src/core/QueryManager.d.ts +1 -1
  35. package/dist/src/core/error.d.ts +2 -1
  36. package/dist/src/network/fetch.d.ts +1 -1
  37. package/dist/src/network/transport/index.d.ts +1 -1
  38. package/dist/src/network/transport/rest.d.ts +20 -9
  39. package/dist/src/util/validateArgs.d.ts +33 -0
  40. package/package.json +10 -6
package/dist/index.cjs.js CHANGED
@@ -8,7 +8,7 @@ var util = require('@firebase/util');
8
8
  var logger$1 = require('@firebase/logger');
9
9
 
10
10
  const name = "@firebase/data-connect";
11
- const version = "0.0.2-dataconnect-preview.877f8b7d0";
11
+ const version = "0.0.3-dataconnect-preview.d986d4bf2";
12
12
 
13
13
  /**
14
14
  * @license
@@ -58,7 +58,8 @@ const Code = {
58
58
  NOT_INITIALIZED: 'not-initialized',
59
59
  NOT_SUPPORTED: 'not-supported',
60
60
  INVALID_ARGUMENT: 'invalid-argument',
61
- PARTIAL_ERROR: 'partial-error'
61
+ PARTIAL_ERROR: 'partial-error',
62
+ UNAUTHORIZED: 'unauthorized'
62
63
  };
63
64
  /** An error returned by a DataConnect operation. */
64
65
  class DataConnectError extends util.FirebaseError {
@@ -167,7 +168,8 @@ class FirebaseAuthProvider {
167
168
  removeTokenChangeListener(listener) {
168
169
  this._authProvider
169
170
  .get()
170
- .then(auth => auth.removeAuthTokenListener(listener));
171
+ .then(auth => auth.removeAuthTokenListener(listener))
172
+ .catch(err => logError(err));
171
173
  }
172
174
  }
173
175
 
@@ -435,12 +437,20 @@ function addToken(url, apiKey) {
435
437
  * limitations under the License.
436
438
  */
437
439
  let connectFetch = globalThis.fetch;
438
- function dcFetch(url, body, { signal }, accessToken) {
440
+ function getGoogApiClientValue(_isUsingGen) {
441
+ let str = 'gl-js/ fire/' + SDK_VERSION;
442
+ if (_isUsingGen) {
443
+ str += ' web/gen';
444
+ }
445
+ return str;
446
+ }
447
+ function dcFetch(url, body, { signal }, accessToken, _isUsingGen) {
439
448
  if (!connectFetch) {
440
449
  throw new DataConnectError(Code.OTHER, 'No Fetch Implementation detected!');
441
450
  }
442
451
  const headers = {
443
- 'Content-Type': 'application/json'
452
+ 'Content-Type': 'application/json',
453
+ 'X-Goog-Api-Client': getGoogApiClientValue(_isUsingGen)
444
454
  };
445
455
  if (accessToken) {
446
456
  headers['X-Firebase-Auth-Token'] = accessToken;
@@ -452,8 +462,9 @@ function dcFetch(url, body, { signal }, accessToken) {
452
462
  method: 'POST',
453
463
  headers,
454
464
  signal
455
- }).catch(err => {
456
- throw new DataConnectError(Code.OTHER, "Failed to fetch: " + JSON.stringify(err));
465
+ })
466
+ .catch(err => {
467
+ throw new DataConnectError(Code.OTHER, 'Failed to fetch: ' + JSON.stringify(err));
457
468
  })
458
469
  .then(async (response) => {
459
470
  let jsonResponse = null;
@@ -463,9 +474,13 @@ function dcFetch(url, body, { signal }, accessToken) {
463
474
  catch (e) {
464
475
  throw new DataConnectError(Code.OTHER, JSON.stringify(e));
465
476
  }
477
+ const message = getMessage(jsonResponse);
466
478
  if (response.status >= 400) {
467
479
  logError('Error while performing request: ' + JSON.stringify(jsonResponse));
468
- throw new DataConnectError(Code.OTHER, JSON.stringify(jsonResponse));
480
+ if (response.status === 401) {
481
+ throw new DataConnectError(Code.UNAUTHORIZED, message);
482
+ }
483
+ throw new DataConnectError(Code.OTHER, message);
469
484
  }
470
485
  return jsonResponse;
471
486
  })
@@ -477,6 +492,12 @@ function dcFetch(url, body, { signal }, accessToken) {
477
492
  }
478
493
  return res;
479
494
  });
495
+ }
496
+ function getMessage(obj) {
497
+ if ('message' in obj) {
498
+ return obj.message;
499
+ }
500
+ return JSON.stringify(obj);
480
501
  }
481
502
 
482
503
  /**
@@ -496,10 +517,11 @@ function dcFetch(url, body, { signal }, accessToken) {
496
517
  * limitations under the License.
497
518
  */
498
519
  class RESTTransport {
499
- constructor(options, apiKey, authProvider, transportOptions) {
520
+ constructor(options, apiKey, authProvider, transportOptions, _isUsingGen = false) {
500
521
  var _a;
501
522
  this.apiKey = apiKey;
502
523
  this.authProvider = authProvider;
524
+ this._isUsingGen = _isUsingGen;
503
525
  this._host = '';
504
526
  this._location = 'l';
505
527
  this._connectorName = '';
@@ -507,30 +529,30 @@ class RESTTransport {
507
529
  this._project = 'p';
508
530
  this._accessToken = null;
509
531
  this._authInitialized = false;
532
+ this._lastToken = null;
510
533
  // TODO(mtewani): Update U to include shape of body defined in line 13.
511
534
  this.invokeQuery = (queryName, body) => {
512
535
  const abortController = new AbortController();
513
536
  // TODO(mtewani): Update to proper value
514
- const withAuth = this.getWithAuth().then(() => {
515
- return dcFetch(addToken(`${this.endpointUrl}:executeQuery`, this.apiKey), {
516
- name: `projects/${this._project}/locations/${this._location}/services/${this._serviceName}/connectors/${this._connectorName}`,
517
- operationName: queryName,
518
- variables: body
519
- }, // TODO(mtewani): This is a patch, fix this.
520
- abortController, this._accessToken);
521
- });
537
+ const withAuth = this.withRetry(() => dcFetch(addToken(`${this.endpointUrl}:executeQuery`, this.apiKey), {
538
+ name: `projects/${this._project}/locations/${this._location}/services/${this._serviceName}/connectors/${this._connectorName}`,
539
+ operationName: queryName,
540
+ variables: body
541
+ }, // TODO(mtewani): This is a patch, fix this.
542
+ abortController, this._accessToken, this._isUsingGen));
522
543
  return {
523
- then: withAuth.then.bind(withAuth)
544
+ then: withAuth.then.bind(withAuth),
545
+ catch: withAuth.catch.bind(withAuth)
524
546
  };
525
547
  };
526
548
  this.invokeMutation = (mutationName, body) => {
527
549
  const abortController = new AbortController();
528
- const taskResult = this.getWithAuth().then(() => {
550
+ const taskResult = this.withRetry(() => {
529
551
  return dcFetch(addToken(`${this.endpointUrl}:executeMutation`, this.apiKey), {
530
552
  name: `projects/${this._project}/locations/${this._location}/services/${this._serviceName}/connectors/${this._connectorName}`,
531
553
  operationName: mutationName,
532
554
  variables: body
533
- }, abortController, this._accessToken);
555
+ }, abortController, this._accessToken, this._isUsingGen);
534
556
  });
535
557
  return {
536
558
  then: taskResult.then.bind(taskResult),
@@ -585,12 +607,12 @@ class RESTTransport {
585
607
  onTokenChanged(newToken) {
586
608
  this._accessToken = newToken;
587
609
  }
588
- getWithAuth() {
610
+ getWithAuth(forceToken = false) {
589
611
  let starterPromise = new Promise(resolve => resolve(this._accessToken));
590
612
  if (!this._authInitialized) {
591
613
  if (this.authProvider) {
592
614
  starterPromise = this.authProvider
593
- .getToken(/*forceToken=*/ false)
615
+ .getToken(/*forceToken=*/ forceToken)
594
616
  .then(data => {
595
617
  if (!data) {
596
618
  return null;
@@ -605,6 +627,30 @@ class RESTTransport {
605
627
  }
606
628
  return starterPromise;
607
629
  }
630
+ _setLastToken(lastToken) {
631
+ this._lastToken = lastToken;
632
+ }
633
+ withRetry(promiseFactory, retry = false) {
634
+ let isNewToken = false;
635
+ return this.getWithAuth(retry)
636
+ .then(res => {
637
+ isNewToken = this._lastToken !== res;
638
+ this._lastToken = res;
639
+ return res;
640
+ })
641
+ .then(promiseFactory)
642
+ .catch(err => {
643
+ // Only retry if the result is unauthorized and the last token isn't the same as the new one.
644
+ if ('code' in err &&
645
+ err.code === Code.UNAUTHORIZED &&
646
+ !retry &&
647
+ isNewToken) {
648
+ logDebug('Retrying due to unauthorized');
649
+ return this.withRetry(promiseFactory, true);
650
+ }
651
+ throw err;
652
+ });
653
+ }
608
654
  }
609
655
 
610
656
  /**
@@ -623,16 +669,26 @@ class RESTTransport {
623
669
  * See the License for the specific language governing permissions and
624
670
  * limitations under the License.
625
671
  */
626
- function mutationRef(dcInstance, queryName, variables) {
672
+ /**
673
+ *
674
+ * @param dcInstance Data Connect instance
675
+ * @param mutationName name of mutation
676
+ * @param variables variables to send with mutation
677
+ * @returns `MutationRef`
678
+ */
679
+ function mutationRef(dcInstance, mutationName, variables) {
627
680
  dcInstance.setInitialized();
628
681
  const ref = {
629
682
  dataConnect: dcInstance,
630
- name: queryName,
683
+ name: mutationName,
631
684
  refType: MUTATION_STR,
632
685
  variables: variables
633
686
  };
634
687
  return ref;
635
688
  }
689
+ /**
690
+ * @internal
691
+ */
636
692
  class MutationManager {
637
693
  constructor(_transport) {
638
694
  this._transport = _transport;
@@ -650,6 +706,11 @@ class MutationManager {
650
706
  return withRefPromise;
651
707
  }
652
708
  }
709
+ /**
710
+ * Execute Mutation
711
+ * @param mutationRef mutation to execute
712
+ * @returns `MutationRef`
713
+ */
653
714
  function executeMutation(mutationRef) {
654
715
  return mutationRef.dataConnect._mutationManager.executeMutation(mutationRef);
655
716
  }
@@ -684,6 +745,9 @@ function parseOptions(fullHost) {
684
745
  const port = Number(portAsString);
685
746
  return { host, port, sslEnabled: isSecure };
686
747
  }
748
+ /**
749
+ * Class representing Firebase Data Connect
750
+ */
687
751
  class DataConnect {
688
752
  constructor(app,
689
753
  // TODO(mtewani): Replace with _dataConnectOptions in the future
@@ -693,6 +757,7 @@ class DataConnect {
693
757
  this._authProvider = _authProvider;
694
758
  this.isEmulator = false;
695
759
  this.initialized = false;
760
+ this._isUsingGeneratedSdk = false;
696
761
  if (typeof process !== 'undefined' && process.env) {
697
762
  const host = process.env[FIREBASE_DATA_CONNECT_EMULATOR_HOST_VAR];
698
763
  if (host) {
@@ -702,6 +767,14 @@ class DataConnect {
702
767
  }
703
768
  }
704
769
  }
770
+ /*
771
+ @internal
772
+ */
773
+ _useGeneratedSdk() {
774
+ if (!this._isUsingGeneratedSdk) {
775
+ this._isUsingGeneratedSdk = true;
776
+ }
777
+ }
705
778
  _delete() {
706
779
  app._removeServiceInstance(this.app, 'data-connect', JSON.stringify(this.getSettings()));
707
780
  return Promise.resolve();
@@ -723,7 +796,7 @@ class DataConnect {
723
796
  this._authTokenProvider = new FirebaseAuthProvider(this.app.name, this.app.options, this._authProvider);
724
797
  }
725
798
  this.initialized = true;
726
- this._transport = new this._transportClass(this.dataConnectOptions, this.app.options.apiKey, this._authTokenProvider);
799
+ this._transport = new this._transportClass(this.dataConnectOptions, this.app.options.apiKey, this._authTokenProvider, undefined, this._isUsingGeneratedSdk);
727
800
  if (this._transportOptions) {
728
801
  this._transport.useEmulator(this._transportOptions.host, this._transportOptions.port, this._transportOptions.sslEnabled);
729
802
  }
@@ -739,6 +812,13 @@ class DataConnect {
739
812
  this.isEmulator = true;
740
813
  }
741
814
  }
815
+ /**
816
+ * Connect to the DataConnect Emulator
817
+ * @param dc Data Connect instance
818
+ * @param host host of emulator server
819
+ * @param port port of emulator server
820
+ * @param sslEnabled use https
821
+ */
742
822
  function connectDataConnectEmulator(dc, host, port, sslEnabled = false) {
743
823
  dc.enableEmulator({ host, port, sslEnabled });
744
824
  }
@@ -753,7 +833,7 @@ function getDataConnect(appOrOptions, optionalOptions) {
753
833
  dcOptions = optionalOptions;
754
834
  app$1 = appOrOptions;
755
835
  }
756
- if (!app$1) {
836
+ if (!app$1 || Object.keys(app$1).length === 0) {
757
837
  app$1 = app.getApp();
758
838
  }
759
839
  const provider = app._getProvider(app$1, 'data-connect');
@@ -767,9 +847,7 @@ function getDataConnect(appOrOptions, optionalOptions) {
767
847
  return dcInstance;
768
848
  }
769
849
  }
770
- if (!dcOptions) {
771
- throw new DataConnectError(Code.INVALID_ARGUMENT, 'DC Option Required');
772
- }
850
+ validateDCOptions(dcOptions);
773
851
  logDebug('Creating new DataConnect instance');
774
852
  // Initialize with options.
775
853
  return provider.initialize({
@@ -777,6 +855,29 @@ function getDataConnect(appOrOptions, optionalOptions) {
777
855
  options: dcOptions
778
856
  });
779
857
  }
858
+ /**
859
+ *
860
+ * @param dcOptions
861
+ * @returns {void}
862
+ * @internal
863
+ */
864
+ function validateDCOptions(dcOptions) {
865
+ const fields = ['connector', 'location', 'service'];
866
+ if (!dcOptions) {
867
+ throw new DataConnectError(Code.INVALID_ARGUMENT, 'DC Option Required');
868
+ }
869
+ fields.forEach(field => {
870
+ if (dcOptions[field] === null || dcOptions[field] === undefined) {
871
+ throw new DataConnectError(Code.INVALID_ARGUMENT, `${field} Required`);
872
+ }
873
+ });
874
+ return true;
875
+ }
876
+ /**
877
+ * Delete DataConnect instance
878
+ * @param dataConnect DataConnect instance
879
+ * @returns
880
+ */
780
881
  function terminate(dataConnect) {
781
882
  return dataConnect._delete();
782
883
  // TODO(mtewani): Stop pending tasks
@@ -807,6 +908,9 @@ function registerDataConnect(variant) {
807
908
  if (settings) {
808
909
  newOpts = JSON.parse(settings);
809
910
  }
911
+ if (!app.options.projectId) {
912
+ throw new DataConnectError(Code.INVALID_ARGUMENT, 'Project ID must be provided. Did you pass in a proper projectId to initializeApp?');
913
+ }
810
914
  return new DataConnect(app, Object.assign(Object.assign({}, newOpts), { projectId: app.options.projectId }), authProvider);
811
915
  }, "PUBLIC" /* ComponentType.PUBLIC */).setMultipleInstances(true));
812
916
  app.registerVersion(name, version, variant);
@@ -830,9 +934,22 @@ function registerDataConnect(variant) {
830
934
  * See the License for the specific language governing permissions and
831
935
  * limitations under the License.
832
936
  */
937
+ /**
938
+ * Execute Query
939
+ * @param queryRef query to execute.
940
+ * @returns `QueryPromise`
941
+ */
833
942
  function executeQuery(queryRef) {
834
943
  return queryRef.dataConnect._queryManager.executeQuery(queryRef);
835
944
  }
945
+ /**
946
+ * Execute Query
947
+ * @param dcInstance Data Connect instance to use.
948
+ * @param queryName Query to execute
949
+ * @param variables Variables to execute with
950
+ * @param initialCache initial cache to use for client hydration
951
+ * @returns `QueryRef`
952
+ */
836
953
  function queryRef(dcInstance, queryName, variables, initialCache) {
837
954
  dcInstance.setInitialized();
838
955
  dcInstance._queryManager.track(queryName, variables, initialCache);
@@ -843,6 +960,11 @@ function queryRef(dcInstance, queryName, variables, initialCache) {
843
960
  variables: variables
844
961
  };
845
962
  }
963
+ /**
964
+ * Converts serialized ref to query ref
965
+ * @param serializedRef ref to convert to `QueryRef`
966
+ * @returns `QueryRef`
967
+ */
846
968
  function toQueryRef(serializedRef) {
847
969
  const { refInfo: { name, variables, connectorConfig } } = serializedRef;
848
970
  return queryRef(getDataConnect(connectorConfig), name, variables);
@@ -864,6 +986,57 @@ function toQueryRef(serializedRef) {
864
986
  * See the License for the specific language governing permissions and
865
987
  * limitations under the License.
866
988
  */
989
+ /**
990
+ * The generated SDK will allow the user to pass in either the variable or the data connect instance with the variable,
991
+ * and this function validates the variables and returns back the DataConnect instance and variables based on the arguments passed in.
992
+ * @param connectorConfig
993
+ * @param dcOrVars
994
+ * @param vars
995
+ * @param validateVars
996
+ * @returns {DataConnect} and {Variables} instance
997
+ * @internal
998
+ */
999
+ function validateArgs(connectorConfig, dcOrVars, vars, validateVars) {
1000
+ let dcInstance;
1001
+ let realVars;
1002
+ if (dcOrVars && 'enableEmulator' in dcOrVars) {
1003
+ dcInstance = dcOrVars;
1004
+ realVars = vars;
1005
+ }
1006
+ else {
1007
+ dcInstance = getDataConnect(connectorConfig);
1008
+ realVars = dcOrVars;
1009
+ }
1010
+ if (!dcInstance || (!realVars && validateVars)) {
1011
+ throw new DataConnectError(Code.INVALID_ARGUMENT, 'Variables required.');
1012
+ }
1013
+ return { dc: dcInstance, vars: realVars };
1014
+ }
1015
+
1016
+ /**
1017
+ * @license
1018
+ * Copyright 2024 Google LLC
1019
+ *
1020
+ * Licensed under the Apache License, Version 2.0 (the "License");
1021
+ * you may not use this file except in compliance with the License.
1022
+ * You may obtain a copy of the License at
1023
+ *
1024
+ * http://www.apache.org/licenses/LICENSE-2.0
1025
+ *
1026
+ * Unless required by applicable law or agreed to in writing, software
1027
+ * distributed under the License is distributed on an "AS IS" BASIS,
1028
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1029
+ * See the License for the specific language governing permissions and
1030
+ * limitations under the License.
1031
+ */
1032
+ /**
1033
+ * Subscribe to a `QueryRef`
1034
+ * @param queryRefOrSerializedResult query ref or serialized result.
1035
+ * @param observerOrOnNext observer object or next function.
1036
+ * @param onError Callback to call when error gets thrown.
1037
+ * @param onComplete Called when subscription completes.
1038
+ * @returns `SubscriptionOptions`
1039
+ */
867
1040
  function subscribe(queryRefOrSerializedResult, observerOrOnNext, onError, onComplete) {
868
1041
  let ref;
869
1042
  let initialCache;
@@ -921,4 +1094,6 @@ exports.setLogLevel = setLogLevel;
921
1094
  exports.subscribe = subscribe;
922
1095
  exports.terminate = terminate;
923
1096
  exports.toQueryRef = toQueryRef;
1097
+ exports.validateArgs = validateArgs;
1098
+ exports.validateDCOptions = validateDCOptions;
924
1099
  //# sourceMappingURL=index.cjs.js.map