@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
@@ -4,7 +4,7 @@ import { FirebaseError } from '@firebase/util';
4
4
  import { Logger } from '@firebase/logger';
5
5
 
6
6
  const name = "@firebase/data-connect";
7
- const version = "0.0.2-dataconnect-preview.877f8b7d0";
7
+ const version = "0.0.3-dataconnect-preview.d986d4bf2";
8
8
 
9
9
  /**
10
10
  * @license
@@ -54,7 +54,8 @@ const Code = {
54
54
  NOT_INITIALIZED: 'not-initialized',
55
55
  NOT_SUPPORTED: 'not-supported',
56
56
  INVALID_ARGUMENT: 'invalid-argument',
57
- PARTIAL_ERROR: 'partial-error'
57
+ PARTIAL_ERROR: 'partial-error',
58
+ UNAUTHORIZED: 'unauthorized'
58
59
  };
59
60
  /** An error returned by a DataConnect operation. */
60
61
  class DataConnectError extends FirebaseError {
@@ -163,7 +164,8 @@ class FirebaseAuthProvider {
163
164
  removeTokenChangeListener(listener) {
164
165
  this._authProvider
165
166
  .get()
166
- .then(auth => auth.removeAuthTokenListener(listener));
167
+ .then(auth => auth.removeAuthTokenListener(listener))
168
+ .catch(err => logError(err));
167
169
  }
168
170
  }
169
171
 
@@ -431,12 +433,20 @@ function addToken(url, apiKey) {
431
433
  * limitations under the License.
432
434
  */
433
435
  let connectFetch = globalThis.fetch;
434
- function dcFetch(url, body, { signal }, accessToken) {
436
+ function getGoogApiClientValue(_isUsingGen) {
437
+ let str = 'gl-js/ fire/' + SDK_VERSION;
438
+ if (_isUsingGen) {
439
+ str += ' web/gen';
440
+ }
441
+ return str;
442
+ }
443
+ function dcFetch(url, body, { signal }, accessToken, _isUsingGen) {
435
444
  if (!connectFetch) {
436
445
  throw new DataConnectError(Code.OTHER, 'No Fetch Implementation detected!');
437
446
  }
438
447
  const headers = {
439
- 'Content-Type': 'application/json'
448
+ 'Content-Type': 'application/json',
449
+ 'X-Goog-Api-Client': getGoogApiClientValue(_isUsingGen)
440
450
  };
441
451
  if (accessToken) {
442
452
  headers['X-Firebase-Auth-Token'] = accessToken;
@@ -448,8 +458,9 @@ function dcFetch(url, body, { signal }, accessToken) {
448
458
  method: 'POST',
449
459
  headers,
450
460
  signal
451
- }).catch(err => {
452
- throw new DataConnectError(Code.OTHER, "Failed to fetch: " + JSON.stringify(err));
461
+ })
462
+ .catch(err => {
463
+ throw new DataConnectError(Code.OTHER, 'Failed to fetch: ' + JSON.stringify(err));
453
464
  })
454
465
  .then(async (response) => {
455
466
  let jsonResponse = null;
@@ -459,9 +470,13 @@ function dcFetch(url, body, { signal }, accessToken) {
459
470
  catch (e) {
460
471
  throw new DataConnectError(Code.OTHER, JSON.stringify(e));
461
472
  }
473
+ const message = getMessage(jsonResponse);
462
474
  if (response.status >= 400) {
463
475
  logError('Error while performing request: ' + JSON.stringify(jsonResponse));
464
- throw new DataConnectError(Code.OTHER, JSON.stringify(jsonResponse));
476
+ if (response.status === 401) {
477
+ throw new DataConnectError(Code.UNAUTHORIZED, message);
478
+ }
479
+ throw new DataConnectError(Code.OTHER, message);
465
480
  }
466
481
  return jsonResponse;
467
482
  })
@@ -473,6 +488,12 @@ function dcFetch(url, body, { signal }, accessToken) {
473
488
  }
474
489
  return res;
475
490
  });
491
+ }
492
+ function getMessage(obj) {
493
+ if ('message' in obj) {
494
+ return obj.message;
495
+ }
496
+ return JSON.stringify(obj);
476
497
  }
477
498
 
478
499
  /**
@@ -492,10 +513,11 @@ function dcFetch(url, body, { signal }, accessToken) {
492
513
  * limitations under the License.
493
514
  */
494
515
  class RESTTransport {
495
- constructor(options, apiKey, authProvider, transportOptions) {
516
+ constructor(options, apiKey, authProvider, transportOptions, _isUsingGen = false) {
496
517
  var _a;
497
518
  this.apiKey = apiKey;
498
519
  this.authProvider = authProvider;
520
+ this._isUsingGen = _isUsingGen;
499
521
  this._host = '';
500
522
  this._location = 'l';
501
523
  this._connectorName = '';
@@ -503,30 +525,30 @@ class RESTTransport {
503
525
  this._project = 'p';
504
526
  this._accessToken = null;
505
527
  this._authInitialized = false;
528
+ this._lastToken = null;
506
529
  // TODO(mtewani): Update U to include shape of body defined in line 13.
507
530
  this.invokeQuery = (queryName, body) => {
508
531
  const abortController = new AbortController();
509
532
  // TODO(mtewani): Update to proper value
510
- const withAuth = this.getWithAuth().then(() => {
511
- return dcFetch(addToken(`${this.endpointUrl}:executeQuery`, this.apiKey), {
512
- name: `projects/${this._project}/locations/${this._location}/services/${this._serviceName}/connectors/${this._connectorName}`,
513
- operationName: queryName,
514
- variables: body
515
- }, // TODO(mtewani): This is a patch, fix this.
516
- abortController, this._accessToken);
517
- });
533
+ const withAuth = this.withRetry(() => dcFetch(addToken(`${this.endpointUrl}:executeQuery`, this.apiKey), {
534
+ name: `projects/${this._project}/locations/${this._location}/services/${this._serviceName}/connectors/${this._connectorName}`,
535
+ operationName: queryName,
536
+ variables: body
537
+ }, // TODO(mtewani): This is a patch, fix this.
538
+ abortController, this._accessToken, this._isUsingGen));
518
539
  return {
519
- then: withAuth.then.bind(withAuth)
540
+ then: withAuth.then.bind(withAuth),
541
+ catch: withAuth.catch.bind(withAuth)
520
542
  };
521
543
  };
522
544
  this.invokeMutation = (mutationName, body) => {
523
545
  const abortController = new AbortController();
524
- const taskResult = this.getWithAuth().then(() => {
546
+ const taskResult = this.withRetry(() => {
525
547
  return dcFetch(addToken(`${this.endpointUrl}:executeMutation`, this.apiKey), {
526
548
  name: `projects/${this._project}/locations/${this._location}/services/${this._serviceName}/connectors/${this._connectorName}`,
527
549
  operationName: mutationName,
528
550
  variables: body
529
- }, abortController, this._accessToken);
551
+ }, abortController, this._accessToken, this._isUsingGen);
530
552
  });
531
553
  return {
532
554
  then: taskResult.then.bind(taskResult),
@@ -581,12 +603,12 @@ class RESTTransport {
581
603
  onTokenChanged(newToken) {
582
604
  this._accessToken = newToken;
583
605
  }
584
- getWithAuth() {
606
+ getWithAuth(forceToken = false) {
585
607
  let starterPromise = new Promise(resolve => resolve(this._accessToken));
586
608
  if (!this._authInitialized) {
587
609
  if (this.authProvider) {
588
610
  starterPromise = this.authProvider
589
- .getToken(/*forceToken=*/ false)
611
+ .getToken(/*forceToken=*/ forceToken)
590
612
  .then(data => {
591
613
  if (!data) {
592
614
  return null;
@@ -601,6 +623,30 @@ class RESTTransport {
601
623
  }
602
624
  return starterPromise;
603
625
  }
626
+ _setLastToken(lastToken) {
627
+ this._lastToken = lastToken;
628
+ }
629
+ withRetry(promiseFactory, retry = false) {
630
+ let isNewToken = false;
631
+ return this.getWithAuth(retry)
632
+ .then(res => {
633
+ isNewToken = this._lastToken !== res;
634
+ this._lastToken = res;
635
+ return res;
636
+ })
637
+ .then(promiseFactory)
638
+ .catch(err => {
639
+ // Only retry if the result is unauthorized and the last token isn't the same as the new one.
640
+ if ('code' in err &&
641
+ err.code === Code.UNAUTHORIZED &&
642
+ !retry &&
643
+ isNewToken) {
644
+ logDebug('Retrying due to unauthorized');
645
+ return this.withRetry(promiseFactory, true);
646
+ }
647
+ throw err;
648
+ });
649
+ }
604
650
  }
605
651
 
606
652
  /**
@@ -619,16 +665,26 @@ class RESTTransport {
619
665
  * See the License for the specific language governing permissions and
620
666
  * limitations under the License.
621
667
  */
622
- function mutationRef(dcInstance, queryName, variables) {
668
+ /**
669
+ *
670
+ * @param dcInstance Data Connect instance
671
+ * @param mutationName name of mutation
672
+ * @param variables variables to send with mutation
673
+ * @returns `MutationRef`
674
+ */
675
+ function mutationRef(dcInstance, mutationName, variables) {
623
676
  dcInstance.setInitialized();
624
677
  const ref = {
625
678
  dataConnect: dcInstance,
626
- name: queryName,
679
+ name: mutationName,
627
680
  refType: MUTATION_STR,
628
681
  variables: variables
629
682
  };
630
683
  return ref;
631
684
  }
685
+ /**
686
+ * @internal
687
+ */
632
688
  class MutationManager {
633
689
  constructor(_transport) {
634
690
  this._transport = _transport;
@@ -646,6 +702,11 @@ class MutationManager {
646
702
  return withRefPromise;
647
703
  }
648
704
  }
705
+ /**
706
+ * Execute Mutation
707
+ * @param mutationRef mutation to execute
708
+ * @returns `MutationRef`
709
+ */
649
710
  function executeMutation(mutationRef) {
650
711
  return mutationRef.dataConnect._mutationManager.executeMutation(mutationRef);
651
712
  }
@@ -680,6 +741,9 @@ function parseOptions(fullHost) {
680
741
  const port = Number(portAsString);
681
742
  return { host, port, sslEnabled: isSecure };
682
743
  }
744
+ /**
745
+ * Class representing Firebase Data Connect
746
+ */
683
747
  class DataConnect {
684
748
  constructor(app,
685
749
  // TODO(mtewani): Replace with _dataConnectOptions in the future
@@ -689,6 +753,7 @@ class DataConnect {
689
753
  this._authProvider = _authProvider;
690
754
  this.isEmulator = false;
691
755
  this.initialized = false;
756
+ this._isUsingGeneratedSdk = false;
692
757
  if (typeof process !== 'undefined' && process.env) {
693
758
  const host = process.env[FIREBASE_DATA_CONNECT_EMULATOR_HOST_VAR];
694
759
  if (host) {
@@ -698,6 +763,14 @@ class DataConnect {
698
763
  }
699
764
  }
700
765
  }
766
+ /*
767
+ @internal
768
+ */
769
+ _useGeneratedSdk() {
770
+ if (!this._isUsingGeneratedSdk) {
771
+ this._isUsingGeneratedSdk = true;
772
+ }
773
+ }
701
774
  _delete() {
702
775
  _removeServiceInstance(this.app, 'data-connect', JSON.stringify(this.getSettings()));
703
776
  return Promise.resolve();
@@ -719,7 +792,7 @@ class DataConnect {
719
792
  this._authTokenProvider = new FirebaseAuthProvider(this.app.name, this.app.options, this._authProvider);
720
793
  }
721
794
  this.initialized = true;
722
- this._transport = new this._transportClass(this.dataConnectOptions, this.app.options.apiKey, this._authTokenProvider);
795
+ this._transport = new this._transportClass(this.dataConnectOptions, this.app.options.apiKey, this._authTokenProvider, undefined, this._isUsingGeneratedSdk);
723
796
  if (this._transportOptions) {
724
797
  this._transport.useEmulator(this._transportOptions.host, this._transportOptions.port, this._transportOptions.sslEnabled);
725
798
  }
@@ -735,6 +808,13 @@ class DataConnect {
735
808
  this.isEmulator = true;
736
809
  }
737
810
  }
811
+ /**
812
+ * Connect to the DataConnect Emulator
813
+ * @param dc Data Connect instance
814
+ * @param host host of emulator server
815
+ * @param port port of emulator server
816
+ * @param sslEnabled use https
817
+ */
738
818
  function connectDataConnectEmulator(dc, host, port, sslEnabled = false) {
739
819
  dc.enableEmulator({ host, port, sslEnabled });
740
820
  }
@@ -749,7 +829,7 @@ function getDataConnect(appOrOptions, optionalOptions) {
749
829
  dcOptions = optionalOptions;
750
830
  app = appOrOptions;
751
831
  }
752
- if (!app) {
832
+ if (!app || Object.keys(app).length === 0) {
753
833
  app = getApp();
754
834
  }
755
835
  const provider = _getProvider(app, 'data-connect');
@@ -763,9 +843,7 @@ function getDataConnect(appOrOptions, optionalOptions) {
763
843
  return dcInstance;
764
844
  }
765
845
  }
766
- if (!dcOptions) {
767
- throw new DataConnectError(Code.INVALID_ARGUMENT, 'DC Option Required');
768
- }
846
+ validateDCOptions(dcOptions);
769
847
  logDebug('Creating new DataConnect instance');
770
848
  // Initialize with options.
771
849
  return provider.initialize({
@@ -773,6 +851,29 @@ function getDataConnect(appOrOptions, optionalOptions) {
773
851
  options: dcOptions
774
852
  });
775
853
  }
854
+ /**
855
+ *
856
+ * @param dcOptions
857
+ * @returns {void}
858
+ * @internal
859
+ */
860
+ function validateDCOptions(dcOptions) {
861
+ const fields = ['connector', 'location', 'service'];
862
+ if (!dcOptions) {
863
+ throw new DataConnectError(Code.INVALID_ARGUMENT, 'DC Option Required');
864
+ }
865
+ fields.forEach(field => {
866
+ if (dcOptions[field] === null || dcOptions[field] === undefined) {
867
+ throw new DataConnectError(Code.INVALID_ARGUMENT, `${field} Required`);
868
+ }
869
+ });
870
+ return true;
871
+ }
872
+ /**
873
+ * Delete DataConnect instance
874
+ * @param dataConnect DataConnect instance
875
+ * @returns
876
+ */
776
877
  function terminate(dataConnect) {
777
878
  return dataConnect._delete();
778
879
  // TODO(mtewani): Stop pending tasks
@@ -803,6 +904,9 @@ function registerDataConnect(variant) {
803
904
  if (settings) {
804
905
  newOpts = JSON.parse(settings);
805
906
  }
907
+ if (!app.options.projectId) {
908
+ throw new DataConnectError(Code.INVALID_ARGUMENT, 'Project ID must be provided. Did you pass in a proper projectId to initializeApp?');
909
+ }
806
910
  return new DataConnect(app, Object.assign(Object.assign({}, newOpts), { projectId: app.options.projectId }), authProvider);
807
911
  }, "PUBLIC" /* ComponentType.PUBLIC */).setMultipleInstances(true));
808
912
  registerVersion(name, version, variant);
@@ -826,9 +930,22 @@ function registerDataConnect(variant) {
826
930
  * See the License for the specific language governing permissions and
827
931
  * limitations under the License.
828
932
  */
933
+ /**
934
+ * Execute Query
935
+ * @param queryRef query to execute.
936
+ * @returns `QueryPromise`
937
+ */
829
938
  function executeQuery(queryRef) {
830
939
  return queryRef.dataConnect._queryManager.executeQuery(queryRef);
831
940
  }
941
+ /**
942
+ * Execute Query
943
+ * @param dcInstance Data Connect instance to use.
944
+ * @param queryName Query to execute
945
+ * @param variables Variables to execute with
946
+ * @param initialCache initial cache to use for client hydration
947
+ * @returns `QueryRef`
948
+ */
832
949
  function queryRef(dcInstance, queryName, variables, initialCache) {
833
950
  dcInstance.setInitialized();
834
951
  dcInstance._queryManager.track(queryName, variables, initialCache);
@@ -839,6 +956,11 @@ function queryRef(dcInstance, queryName, variables, initialCache) {
839
956
  variables: variables
840
957
  };
841
958
  }
959
+ /**
960
+ * Converts serialized ref to query ref
961
+ * @param serializedRef ref to convert to `QueryRef`
962
+ * @returns `QueryRef`
963
+ */
842
964
  function toQueryRef(serializedRef) {
843
965
  const { refInfo: { name, variables, connectorConfig } } = serializedRef;
844
966
  return queryRef(getDataConnect(connectorConfig), name, variables);
@@ -860,6 +982,57 @@ function toQueryRef(serializedRef) {
860
982
  * See the License for the specific language governing permissions and
861
983
  * limitations under the License.
862
984
  */
985
+ /**
986
+ * The generated SDK will allow the user to pass in either the variable or the data connect instance with the variable,
987
+ * and this function validates the variables and returns back the DataConnect instance and variables based on the arguments passed in.
988
+ * @param connectorConfig
989
+ * @param dcOrVars
990
+ * @param vars
991
+ * @param validateVars
992
+ * @returns {DataConnect} and {Variables} instance
993
+ * @internal
994
+ */
995
+ function validateArgs(connectorConfig, dcOrVars, vars, validateVars) {
996
+ let dcInstance;
997
+ let realVars;
998
+ if (dcOrVars && 'enableEmulator' in dcOrVars) {
999
+ dcInstance = dcOrVars;
1000
+ realVars = vars;
1001
+ }
1002
+ else {
1003
+ dcInstance = getDataConnect(connectorConfig);
1004
+ realVars = dcOrVars;
1005
+ }
1006
+ if (!dcInstance || (!realVars && validateVars)) {
1007
+ throw new DataConnectError(Code.INVALID_ARGUMENT, 'Variables required.');
1008
+ }
1009
+ return { dc: dcInstance, vars: realVars };
1010
+ }
1011
+
1012
+ /**
1013
+ * @license
1014
+ * Copyright 2024 Google LLC
1015
+ *
1016
+ * Licensed under the Apache License, Version 2.0 (the "License");
1017
+ * you may not use this file except in compliance with the License.
1018
+ * You may obtain a copy of the License at
1019
+ *
1020
+ * http://www.apache.org/licenses/LICENSE-2.0
1021
+ *
1022
+ * Unless required by applicable law or agreed to in writing, software
1023
+ * distributed under the License is distributed on an "AS IS" BASIS,
1024
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1025
+ * See the License for the specific language governing permissions and
1026
+ * limitations under the License.
1027
+ */
1028
+ /**
1029
+ * Subscribe to a `QueryRef`
1030
+ * @param queryRefOrSerializedResult query ref or serialized result.
1031
+ * @param observerOrOnNext observer object or next function.
1032
+ * @param onError Callback to call when error gets thrown.
1033
+ * @param onComplete Called when subscription completes.
1034
+ * @returns `SubscriptionOptions`
1035
+ */
863
1036
  function subscribe(queryRefOrSerializedResult, observerOrOnNext, onError, onComplete) {
864
1037
  let ref;
865
1038
  let initialCache;
@@ -898,5 +1071,5 @@ function subscribe(queryRefOrSerializedResult, observerOrOnNext, onError, onComp
898
1071
  */
899
1072
  registerDataConnect();
900
1073
 
901
- export { DataConnect, FIREBASE_DATA_CONNECT_EMULATOR_HOST_VAR, FirebaseAuthProvider, MUTATION_STR, MutationManager, QUERY_STR, SOURCE_CACHE, SOURCE_SERVER, connectDataConnectEmulator, executeMutation, executeQuery, getDataConnect, mutationRef, parseOptions, queryRef, setLogLevel, subscribe, terminate, toQueryRef };
1074
+ export { DataConnect, FIREBASE_DATA_CONNECT_EMULATOR_HOST_VAR, FirebaseAuthProvider, MUTATION_STR, MutationManager, QUERY_STR, SOURCE_CACHE, SOURCE_SERVER, connectDataConnectEmulator, executeMutation, executeQuery, getDataConnect, mutationRef, parseOptions, queryRef, setLogLevel, subscribe, terminate, toQueryRef, validateArgs, validateDCOptions };
902
1075
  //# sourceMappingURL=index.esm2017.js.map