@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
@@ -25,7 +25,8 @@ const Code = {
25
25
  NOT_INITIALIZED: 'not-initialized',
26
26
  NOT_SUPPORTED: 'not-supported',
27
27
  INVALID_ARGUMENT: 'invalid-argument',
28
- PARTIAL_ERROR: 'partial-error'
28
+ PARTIAL_ERROR: 'partial-error',
29
+ UNAUTHORIZED: 'unauthorized'
29
30
  };
30
31
  /** An error returned by a DataConnect operation. */
31
32
  class DataConnectError extends FirebaseError {
@@ -122,12 +123,20 @@ let connectFetch = globalThis.fetch;
122
123
  function initializeFetch(fetchImpl) {
123
124
  connectFetch = fetchImpl;
124
125
  }
125
- function dcFetch(url, body, { signal }, accessToken) {
126
+ function getGoogApiClientValue(_isUsingGen) {
127
+ let str = 'gl-js/ fire/' + SDK_VERSION;
128
+ if (_isUsingGen) {
129
+ str += ' web/gen';
130
+ }
131
+ return str;
132
+ }
133
+ function dcFetch(url, body, { signal }, accessToken, _isUsingGen) {
126
134
  if (!connectFetch) {
127
135
  throw new DataConnectError(Code.OTHER, 'No Fetch Implementation detected!');
128
136
  }
129
137
  const headers = {
130
- 'Content-Type': 'application/json'
138
+ 'Content-Type': 'application/json',
139
+ 'X-Goog-Api-Client': getGoogApiClientValue(_isUsingGen)
131
140
  };
132
141
  if (accessToken) {
133
142
  headers['X-Firebase-Auth-Token'] = accessToken;
@@ -139,8 +148,9 @@ function dcFetch(url, body, { signal }, accessToken) {
139
148
  method: 'POST',
140
149
  headers,
141
150
  signal
142
- }).catch(err => {
143
- throw new DataConnectError(Code.OTHER, "Failed to fetch: " + JSON.stringify(err));
151
+ })
152
+ .catch(err => {
153
+ throw new DataConnectError(Code.OTHER, 'Failed to fetch: ' + JSON.stringify(err));
144
154
  })
145
155
  .then(async (response) => {
146
156
  let jsonResponse = null;
@@ -150,9 +160,13 @@ function dcFetch(url, body, { signal }, accessToken) {
150
160
  catch (e) {
151
161
  throw new DataConnectError(Code.OTHER, JSON.stringify(e));
152
162
  }
163
+ const message = getMessage(jsonResponse);
153
164
  if (response.status >= 400) {
154
165
  logError('Error while performing request: ' + JSON.stringify(jsonResponse));
155
- throw new DataConnectError(Code.OTHER, JSON.stringify(jsonResponse));
166
+ if (response.status === 401) {
167
+ throw new DataConnectError(Code.UNAUTHORIZED, message);
168
+ }
169
+ throw new DataConnectError(Code.OTHER, message);
156
170
  }
157
171
  return jsonResponse;
158
172
  })
@@ -164,10 +178,16 @@ function dcFetch(url, body, { signal }, accessToken) {
164
178
  }
165
179
  return res;
166
180
  });
181
+ }
182
+ function getMessage(obj) {
183
+ if ('message' in obj) {
184
+ return obj.message;
185
+ }
186
+ return JSON.stringify(obj);
167
187
  }
168
188
 
169
189
  const name = "@firebase/data-connect";
170
- const version = "0.0.2-dataconnect-preview.877f8b7d0";
190
+ const version = "0.0.3-dataconnect-preview.d986d4bf2";
171
191
 
172
192
  /**
173
193
  * @license
@@ -227,7 +247,8 @@ class FirebaseAuthProvider {
227
247
  removeTokenChangeListener(listener) {
228
248
  this._authProvider
229
249
  .get()
230
- .then(auth => auth.removeAuthTokenListener(listener));
250
+ .then(auth => auth.removeAuthTokenListener(listener))
251
+ .catch(err => logError(err));
231
252
  }
232
253
  }
233
254
 
@@ -495,10 +516,11 @@ function addToken(url, apiKey) {
495
516
  * limitations under the License.
496
517
  */
497
518
  class RESTTransport {
498
- constructor(options, apiKey, authProvider, transportOptions) {
519
+ constructor(options, apiKey, authProvider, transportOptions, _isUsingGen = false) {
499
520
  var _a;
500
521
  this.apiKey = apiKey;
501
522
  this.authProvider = authProvider;
523
+ this._isUsingGen = _isUsingGen;
502
524
  this._host = '';
503
525
  this._location = 'l';
504
526
  this._connectorName = '';
@@ -506,30 +528,30 @@ class RESTTransport {
506
528
  this._project = 'p';
507
529
  this._accessToken = null;
508
530
  this._authInitialized = false;
531
+ this._lastToken = null;
509
532
  // TODO(mtewani): Update U to include shape of body defined in line 13.
510
533
  this.invokeQuery = (queryName, body) => {
511
534
  const abortController = new AbortController();
512
535
  // TODO(mtewani): Update to proper value
513
- const withAuth = this.getWithAuth().then(() => {
514
- return dcFetch(addToken(`${this.endpointUrl}:executeQuery`, this.apiKey), {
515
- name: `projects/${this._project}/locations/${this._location}/services/${this._serviceName}/connectors/${this._connectorName}`,
516
- operationName: queryName,
517
- variables: body
518
- }, // TODO(mtewani): This is a patch, fix this.
519
- abortController, this._accessToken);
520
- });
536
+ const withAuth = this.withRetry(() => dcFetch(addToken(`${this.endpointUrl}:executeQuery`, this.apiKey), {
537
+ name: `projects/${this._project}/locations/${this._location}/services/${this._serviceName}/connectors/${this._connectorName}`,
538
+ operationName: queryName,
539
+ variables: body
540
+ }, // TODO(mtewani): This is a patch, fix this.
541
+ abortController, this._accessToken, this._isUsingGen));
521
542
  return {
522
- then: withAuth.then.bind(withAuth)
543
+ then: withAuth.then.bind(withAuth),
544
+ catch: withAuth.catch.bind(withAuth)
523
545
  };
524
546
  };
525
547
  this.invokeMutation = (mutationName, body) => {
526
548
  const abortController = new AbortController();
527
- const taskResult = this.getWithAuth().then(() => {
549
+ const taskResult = this.withRetry(() => {
528
550
  return dcFetch(addToken(`${this.endpointUrl}:executeMutation`, this.apiKey), {
529
551
  name: `projects/${this._project}/locations/${this._location}/services/${this._serviceName}/connectors/${this._connectorName}`,
530
552
  operationName: mutationName,
531
553
  variables: body
532
- }, abortController, this._accessToken);
554
+ }, abortController, this._accessToken, this._isUsingGen);
533
555
  });
534
556
  return {
535
557
  then: taskResult.then.bind(taskResult),
@@ -584,12 +606,12 @@ class RESTTransport {
584
606
  onTokenChanged(newToken) {
585
607
  this._accessToken = newToken;
586
608
  }
587
- getWithAuth() {
609
+ getWithAuth(forceToken = false) {
588
610
  let starterPromise = new Promise(resolve => resolve(this._accessToken));
589
611
  if (!this._authInitialized) {
590
612
  if (this.authProvider) {
591
613
  starterPromise = this.authProvider
592
- .getToken(/*forceToken=*/ false)
614
+ .getToken(/*forceToken=*/ forceToken)
593
615
  .then(data => {
594
616
  if (!data) {
595
617
  return null;
@@ -604,6 +626,30 @@ class RESTTransport {
604
626
  }
605
627
  return starterPromise;
606
628
  }
629
+ _setLastToken(lastToken) {
630
+ this._lastToken = lastToken;
631
+ }
632
+ withRetry(promiseFactory, retry = false) {
633
+ let isNewToken = false;
634
+ return this.getWithAuth(retry)
635
+ .then(res => {
636
+ isNewToken = this._lastToken !== res;
637
+ this._lastToken = res;
638
+ return res;
639
+ })
640
+ .then(promiseFactory)
641
+ .catch(err => {
642
+ // Only retry if the result is unauthorized and the last token isn't the same as the new one.
643
+ if ('code' in err &&
644
+ err.code === Code.UNAUTHORIZED &&
645
+ !retry &&
646
+ isNewToken) {
647
+ logDebug('Retrying due to unauthorized');
648
+ return this.withRetry(promiseFactory, true);
649
+ }
650
+ throw err;
651
+ });
652
+ }
607
653
  }
608
654
 
609
655
  /**
@@ -622,16 +668,26 @@ class RESTTransport {
622
668
  * See the License for the specific language governing permissions and
623
669
  * limitations under the License.
624
670
  */
625
- function mutationRef(dcInstance, queryName, variables) {
671
+ /**
672
+ *
673
+ * @param dcInstance Data Connect instance
674
+ * @param mutationName name of mutation
675
+ * @param variables variables to send with mutation
676
+ * @returns `MutationRef`
677
+ */
678
+ function mutationRef(dcInstance, mutationName, variables) {
626
679
  dcInstance.setInitialized();
627
680
  const ref = {
628
681
  dataConnect: dcInstance,
629
- name: queryName,
682
+ name: mutationName,
630
683
  refType: MUTATION_STR,
631
684
  variables: variables
632
685
  };
633
686
  return ref;
634
687
  }
688
+ /**
689
+ * @internal
690
+ */
635
691
  class MutationManager {
636
692
  constructor(_transport) {
637
693
  this._transport = _transport;
@@ -649,6 +705,11 @@ class MutationManager {
649
705
  return withRefPromise;
650
706
  }
651
707
  }
708
+ /**
709
+ * Execute Mutation
710
+ * @param mutationRef mutation to execute
711
+ * @returns `MutationRef`
712
+ */
652
713
  function executeMutation(mutationRef) {
653
714
  return mutationRef.dataConnect._mutationManager.executeMutation(mutationRef);
654
715
  }
@@ -683,6 +744,9 @@ function parseOptions(fullHost) {
683
744
  const port = Number(portAsString);
684
745
  return { host, port, sslEnabled: isSecure };
685
746
  }
747
+ /**
748
+ * Class representing Firebase Data Connect
749
+ */
686
750
  class DataConnect {
687
751
  constructor(app,
688
752
  // TODO(mtewani): Replace with _dataConnectOptions in the future
@@ -692,6 +756,7 @@ class DataConnect {
692
756
  this._authProvider = _authProvider;
693
757
  this.isEmulator = false;
694
758
  this.initialized = false;
759
+ this._isUsingGeneratedSdk = false;
695
760
  if (typeof process !== 'undefined' && process.env) {
696
761
  const host = process.env[FIREBASE_DATA_CONNECT_EMULATOR_HOST_VAR];
697
762
  if (host) {
@@ -701,6 +766,14 @@ class DataConnect {
701
766
  }
702
767
  }
703
768
  }
769
+ /*
770
+ @internal
771
+ */
772
+ _useGeneratedSdk() {
773
+ if (!this._isUsingGeneratedSdk) {
774
+ this._isUsingGeneratedSdk = true;
775
+ }
776
+ }
704
777
  _delete() {
705
778
  _removeServiceInstance(this.app, 'data-connect', JSON.stringify(this.getSettings()));
706
779
  return Promise.resolve();
@@ -722,7 +795,7 @@ class DataConnect {
722
795
  this._authTokenProvider = new FirebaseAuthProvider(this.app.name, this.app.options, this._authProvider);
723
796
  }
724
797
  this.initialized = true;
725
- this._transport = new this._transportClass(this.dataConnectOptions, this.app.options.apiKey, this._authTokenProvider);
798
+ this._transport = new this._transportClass(this.dataConnectOptions, this.app.options.apiKey, this._authTokenProvider, undefined, this._isUsingGeneratedSdk);
726
799
  if (this._transportOptions) {
727
800
  this._transport.useEmulator(this._transportOptions.host, this._transportOptions.port, this._transportOptions.sslEnabled);
728
801
  }
@@ -738,6 +811,13 @@ class DataConnect {
738
811
  this.isEmulator = true;
739
812
  }
740
813
  }
814
+ /**
815
+ * Connect to the DataConnect Emulator
816
+ * @param dc Data Connect instance
817
+ * @param host host of emulator server
818
+ * @param port port of emulator server
819
+ * @param sslEnabled use https
820
+ */
741
821
  function connectDataConnectEmulator(dc, host, port, sslEnabled = false) {
742
822
  dc.enableEmulator({ host, port, sslEnabled });
743
823
  }
@@ -752,7 +832,7 @@ function getDataConnect(appOrOptions, optionalOptions) {
752
832
  dcOptions = optionalOptions;
753
833
  app = appOrOptions;
754
834
  }
755
- if (!app) {
835
+ if (!app || Object.keys(app).length === 0) {
756
836
  app = getApp();
757
837
  }
758
838
  const provider = _getProvider(app, 'data-connect');
@@ -766,9 +846,7 @@ function getDataConnect(appOrOptions, optionalOptions) {
766
846
  return dcInstance;
767
847
  }
768
848
  }
769
- if (!dcOptions) {
770
- throw new DataConnectError(Code.INVALID_ARGUMENT, 'DC Option Required');
771
- }
849
+ validateDCOptions(dcOptions);
772
850
  logDebug('Creating new DataConnect instance');
773
851
  // Initialize with options.
774
852
  return provider.initialize({
@@ -776,6 +854,29 @@ function getDataConnect(appOrOptions, optionalOptions) {
776
854
  options: dcOptions
777
855
  });
778
856
  }
857
+ /**
858
+ *
859
+ * @param dcOptions
860
+ * @returns {void}
861
+ * @internal
862
+ */
863
+ function validateDCOptions(dcOptions) {
864
+ const fields = ['connector', 'location', 'service'];
865
+ if (!dcOptions) {
866
+ throw new DataConnectError(Code.INVALID_ARGUMENT, 'DC Option Required');
867
+ }
868
+ fields.forEach(field => {
869
+ if (dcOptions[field] === null || dcOptions[field] === undefined) {
870
+ throw new DataConnectError(Code.INVALID_ARGUMENT, `${field} Required`);
871
+ }
872
+ });
873
+ return true;
874
+ }
875
+ /**
876
+ * Delete DataConnect instance
877
+ * @param dataConnect DataConnect instance
878
+ * @returns
879
+ */
779
880
  function terminate(dataConnect) {
780
881
  return dataConnect._delete();
781
882
  // TODO(mtewani): Stop pending tasks
@@ -806,6 +907,9 @@ function registerDataConnect(variant) {
806
907
  if (settings) {
807
908
  newOpts = JSON.parse(settings);
808
909
  }
910
+ if (!app.options.projectId) {
911
+ throw new DataConnectError(Code.INVALID_ARGUMENT, 'Project ID must be provided. Did you pass in a proper projectId to initializeApp?');
912
+ }
809
913
  return new DataConnect(app, Object.assign(Object.assign({}, newOpts), { projectId: app.options.projectId }), authProvider);
810
914
  }, "PUBLIC" /* ComponentType.PUBLIC */).setMultipleInstances(true));
811
915
  registerVersion(name, version, variant);
@@ -829,9 +933,22 @@ function registerDataConnect(variant) {
829
933
  * See the License for the specific language governing permissions and
830
934
  * limitations under the License.
831
935
  */
936
+ /**
937
+ * Execute Query
938
+ * @param queryRef query to execute.
939
+ * @returns `QueryPromise`
940
+ */
832
941
  function executeQuery(queryRef) {
833
942
  return queryRef.dataConnect._queryManager.executeQuery(queryRef);
834
943
  }
944
+ /**
945
+ * Execute Query
946
+ * @param dcInstance Data Connect instance to use.
947
+ * @param queryName Query to execute
948
+ * @param variables Variables to execute with
949
+ * @param initialCache initial cache to use for client hydration
950
+ * @returns `QueryRef`
951
+ */
835
952
  function queryRef(dcInstance, queryName, variables, initialCache) {
836
953
  dcInstance.setInitialized();
837
954
  dcInstance._queryManager.track(queryName, variables, initialCache);
@@ -842,6 +959,11 @@ function queryRef(dcInstance, queryName, variables, initialCache) {
842
959
  variables: variables
843
960
  };
844
961
  }
962
+ /**
963
+ * Converts serialized ref to query ref
964
+ * @param serializedRef ref to convert to `QueryRef`
965
+ * @returns `QueryRef`
966
+ */
845
967
  function toQueryRef(serializedRef) {
846
968
  const { refInfo: { name, variables, connectorConfig } } = serializedRef;
847
969
  return queryRef(getDataConnect(connectorConfig), name, variables);
@@ -863,6 +985,57 @@ function toQueryRef(serializedRef) {
863
985
  * See the License for the specific language governing permissions and
864
986
  * limitations under the License.
865
987
  */
988
+ /**
989
+ * The generated SDK will allow the user to pass in either the variable or the data connect instance with the variable,
990
+ * and this function validates the variables and returns back the DataConnect instance and variables based on the arguments passed in.
991
+ * @param connectorConfig
992
+ * @param dcOrVars
993
+ * @param vars
994
+ * @param validateVars
995
+ * @returns {DataConnect} and {Variables} instance
996
+ * @internal
997
+ */
998
+ function validateArgs(connectorConfig, dcOrVars, vars, validateVars) {
999
+ let dcInstance;
1000
+ let realVars;
1001
+ if (dcOrVars && 'enableEmulator' in dcOrVars) {
1002
+ dcInstance = dcOrVars;
1003
+ realVars = vars;
1004
+ }
1005
+ else {
1006
+ dcInstance = getDataConnect(connectorConfig);
1007
+ realVars = dcOrVars;
1008
+ }
1009
+ if (!dcInstance || (!realVars && validateVars)) {
1010
+ throw new DataConnectError(Code.INVALID_ARGUMENT, 'Variables required.');
1011
+ }
1012
+ return { dc: dcInstance, vars: realVars };
1013
+ }
1014
+
1015
+ /**
1016
+ * @license
1017
+ * Copyright 2024 Google LLC
1018
+ *
1019
+ * Licensed under the Apache License, Version 2.0 (the "License");
1020
+ * you may not use this file except in compliance with the License.
1021
+ * You may obtain a copy of the License at
1022
+ *
1023
+ * http://www.apache.org/licenses/LICENSE-2.0
1024
+ *
1025
+ * Unless required by applicable law or agreed to in writing, software
1026
+ * distributed under the License is distributed on an "AS IS" BASIS,
1027
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1028
+ * See the License for the specific language governing permissions and
1029
+ * limitations under the License.
1030
+ */
1031
+ /**
1032
+ * Subscribe to a `QueryRef`
1033
+ * @param queryRefOrSerializedResult query ref or serialized result.
1034
+ * @param observerOrOnNext observer object or next function.
1035
+ * @param onError Callback to call when error gets thrown.
1036
+ * @param onComplete Called when subscription completes.
1037
+ * @returns `SubscriptionOptions`
1038
+ */
866
1039
  function subscribe(queryRefOrSerializedResult, observerOrOnNext, onError, onComplete) {
867
1040
  let ref;
868
1041
  let initialCache;
@@ -913,5 +1086,5 @@ function subscribe(queryRefOrSerializedResult, observerOrOnNext, onError, onComp
913
1086
  initializeFetch(fetch);
914
1087
  registerDataConnect('node');
915
1088
 
916
- 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 };
1089
+ 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 };
917
1090
  //# sourceMappingURL=index.node.esm.js.map