@depup/firebase__data-connect 0.4.0-depup.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.
Files changed (86) hide show
  1. package/README.md +31 -0
  2. package/changes.json +10 -0
  3. package/dist/index.cjs.js +2069 -0
  4. package/dist/index.cjs.js.map +1 -0
  5. package/dist/index.esm.js +2039 -0
  6. package/dist/index.esm.js.map +1 -0
  7. package/dist/index.node.cjs.js +2084 -0
  8. package/dist/index.node.cjs.js.map +1 -0
  9. package/dist/internal.d.ts +748 -0
  10. package/dist/node-esm/index.node.esm.js +2054 -0
  11. package/dist/node-esm/index.node.esm.js.map +1 -0
  12. package/dist/node-esm/package.json +1 -0
  13. package/dist/node-esm/src/api/DataConnect.d.ts +159 -0
  14. package/dist/node-esm/src/api/Mutation.d.ts +61 -0
  15. package/dist/node-esm/src/api/Reference.d.ts +53 -0
  16. package/dist/node-esm/src/api/index.d.ts +25 -0
  17. package/dist/node-esm/src/api/query.d.ts +71 -0
  18. package/dist/node-esm/src/api.browser.d.ts +18 -0
  19. package/dist/node-esm/src/api.node.d.ts +18 -0
  20. package/dist/node-esm/src/cache/Cache.d.ts +53 -0
  21. package/dist/node-esm/src/cache/CacheProvider.d.ts +25 -0
  22. package/dist/node-esm/src/cache/EntityDataObject.d.ts +37 -0
  23. package/dist/node-esm/src/cache/EntityNode.d.ts +56 -0
  24. package/dist/node-esm/src/cache/ImpactedQueryRefsAccumulator.d.ts +23 -0
  25. package/dist/node-esm/src/cache/InMemoryCacheProvider.d.ts +30 -0
  26. package/dist/node-esm/src/cache/ResultTree.d.ts +42 -0
  27. package/dist/node-esm/src/cache/ResultTreeProcessor.d.ts +40 -0
  28. package/dist/node-esm/src/cache/cacheUtils.d.ts +20 -0
  29. package/dist/node-esm/src/core/AppCheckTokenProvider.d.ts +31 -0
  30. package/dist/node-esm/src/core/FirebaseAuthProvider.d.ts +36 -0
  31. package/dist/node-esm/src/core/error.d.ts +53 -0
  32. package/dist/node-esm/src/core/query/QueryManager.d.ts +47 -0
  33. package/dist/node-esm/src/core/query/queryOptions.d.ts +25 -0
  34. package/dist/node-esm/src/core/query/subscribe.d.ts +67 -0
  35. package/dist/node-esm/src/core/version.d.ts +23 -0
  36. package/dist/node-esm/src/index.d.ts +29 -0
  37. package/dist/node-esm/src/index.node.d.ts +18 -0
  38. package/dist/node-esm/src/logger.d.ts +20 -0
  39. package/dist/node-esm/src/network/fetch.d.ts +24 -0
  40. package/dist/node-esm/src/network/index.d.ts +17 -0
  41. package/dist/node-esm/src/network/transport/index.d.ts +81 -0
  42. package/dist/node-esm/src/network/transport/rest.d.ts +49 -0
  43. package/dist/node-esm/src/register.d.ts +1 -0
  44. package/dist/node-esm/src/util/encoder.d.ts +22 -0
  45. package/dist/node-esm/src/util/map.d.ts +17 -0
  46. package/dist/node-esm/src/util/url.d.ts +20 -0
  47. package/dist/node-esm/src/util/validateArgs.d.ts +33 -0
  48. package/dist/private.d.ts +655 -0
  49. package/dist/public.d.ts +350 -0
  50. package/dist/src/api/DataConnect.d.ts +159 -0
  51. package/dist/src/api/Mutation.d.ts +61 -0
  52. package/dist/src/api/Reference.d.ts +53 -0
  53. package/dist/src/api/index.d.ts +25 -0
  54. package/dist/src/api/query.d.ts +71 -0
  55. package/dist/src/api.browser.d.ts +18 -0
  56. package/dist/src/api.node.d.ts +18 -0
  57. package/dist/src/cache/Cache.d.ts +53 -0
  58. package/dist/src/cache/CacheProvider.d.ts +25 -0
  59. package/dist/src/cache/EntityDataObject.d.ts +37 -0
  60. package/dist/src/cache/EntityNode.d.ts +56 -0
  61. package/dist/src/cache/ImpactedQueryRefsAccumulator.d.ts +23 -0
  62. package/dist/src/cache/InMemoryCacheProvider.d.ts +30 -0
  63. package/dist/src/cache/ResultTree.d.ts +42 -0
  64. package/dist/src/cache/ResultTreeProcessor.d.ts +40 -0
  65. package/dist/src/cache/cacheUtils.d.ts +20 -0
  66. package/dist/src/core/AppCheckTokenProvider.d.ts +31 -0
  67. package/dist/src/core/FirebaseAuthProvider.d.ts +36 -0
  68. package/dist/src/core/error.d.ts +53 -0
  69. package/dist/src/core/query/QueryManager.d.ts +47 -0
  70. package/dist/src/core/query/queryOptions.d.ts +25 -0
  71. package/dist/src/core/query/subscribe.d.ts +67 -0
  72. package/dist/src/core/version.d.ts +23 -0
  73. package/dist/src/index.d.ts +29 -0
  74. package/dist/src/index.node.d.ts +18 -0
  75. package/dist/src/logger.d.ts +20 -0
  76. package/dist/src/network/fetch.d.ts +24 -0
  77. package/dist/src/network/index.d.ts +17 -0
  78. package/dist/src/network/transport/index.d.ts +81 -0
  79. package/dist/src/network/transport/rest.d.ts +49 -0
  80. package/dist/src/register.d.ts +1 -0
  81. package/dist/src/tsdoc-metadata.json +11 -0
  82. package/dist/src/util/encoder.d.ts +22 -0
  83. package/dist/src/util/map.d.ts +17 -0
  84. package/dist/src/util/url.d.ts +20 -0
  85. package/dist/src/util/validateArgs.d.ts +33 -0
  86. package/package.json +99 -0
@@ -0,0 +1,2039 @@
1
+ import { _isFirebaseServerApp, _removeServiceInstance, getApp, _getProvider, _registerComponent, registerVersion, SDK_VERSION as SDK_VERSION$1 } from '@firebase/app';
2
+ import { Component } from '@firebase/component';
3
+ import { FirebaseError, generateSHA256Hash, isCloudWorkstation, pingServer, updateEmulatorBanner } from '@firebase/util';
4
+ import { Logger } from '@firebase/logger';
5
+
6
+ const name = "@firebase/data-connect";
7
+ const version = "0.4.0";
8
+
9
+ /**
10
+ * @license
11
+ * Copyright 2024 Google LLC
12
+ *
13
+ * Licensed under the Apache License, Version 2.0 (the "License");
14
+ * you may not use this file except in compliance with the License.
15
+ * You may obtain a copy of the License at
16
+ *
17
+ * http://www.apache.org/licenses/LICENSE-2.0
18
+ *
19
+ * Unless required by applicable law or agreed to in writing, software
20
+ * distributed under the License is distributed on an "AS IS" BASIS,
21
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22
+ * See the License for the specific language governing permissions and
23
+ * limitations under the License.
24
+ */
25
+ /** The semver (www.semver.org) version of the SDK. */
26
+ let SDK_VERSION = '';
27
+ /**
28
+ * SDK_VERSION should be set before any database instance is created
29
+ * @internal
30
+ */
31
+ function setSDKVersion(version) {
32
+ SDK_VERSION = version;
33
+ }
34
+
35
+ /**
36
+ * @license
37
+ * Copyright 2024 Google LLC
38
+ *
39
+ * Licensed under the Apache License, Version 2.0 (the "License");
40
+ * you may not use this file except in compliance with the License.
41
+ * You may obtain a copy of the License at
42
+ *
43
+ * http://www.apache.org/licenses/LICENSE-2.0
44
+ *
45
+ * Unless required by applicable law or agreed to in writing, software
46
+ * distributed under the License is distributed on an "AS IS" BASIS,
47
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
48
+ * See the License for the specific language governing permissions and
49
+ * limitations under the License.
50
+ */
51
+ const Code = {
52
+ OTHER: 'other',
53
+ ALREADY_INITIALIZED: 'already-initialized',
54
+ NOT_INITIALIZED: 'not-initialized',
55
+ NOT_SUPPORTED: 'not-supported',
56
+ INVALID_ARGUMENT: 'invalid-argument',
57
+ PARTIAL_ERROR: 'partial-error',
58
+ UNAUTHORIZED: 'unauthorized'
59
+ };
60
+ /** An error returned by a DataConnect operation. */
61
+ class DataConnectError extends FirebaseError {
62
+ constructor(code, message) {
63
+ super(code, message);
64
+ /** @internal */
65
+ this.name = 'DataConnectError';
66
+ // Ensure the instanceof operator works as expected on subclasses of Error.
67
+ // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#custom_error_types
68
+ // and https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#support-for-newtarget
69
+ Object.setPrototypeOf(this, DataConnectError.prototype);
70
+ }
71
+ /** @internal */
72
+ toString() {
73
+ return `${this.name}[code=${this.code}]: ${this.message}`;
74
+ }
75
+ }
76
+ /** An error returned by a DataConnect operation. */
77
+ class DataConnectOperationError extends DataConnectError {
78
+ /** @hideconstructor */
79
+ constructor(message, response) {
80
+ super(Code.PARTIAL_ERROR, message);
81
+ /** @internal */
82
+ this.name = 'DataConnectOperationError';
83
+ this.response = response;
84
+ }
85
+ }
86
+
87
+ /**
88
+ * @license
89
+ * Copyright 2025 Google LLC
90
+ *
91
+ * Licensed under the Apache License, Version 2.0 (the "License");
92
+ * you may not use this file except in compliance with the License.
93
+ * You may obtain a copy of the License at
94
+ *
95
+ * http://www.apache.org/licenses/LICENSE-2.0
96
+ *
97
+ * Unless required by applicable law or agreed to in writing, software
98
+ * distributed under the License is distributed on an "AS IS" BASIS,
99
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
100
+ * See the License for the specific language governing permissions and
101
+ * limitations under the License.
102
+ */
103
+ class EntityDataObject {
104
+ getServerValue(key) {
105
+ return this.serverValues[key];
106
+ }
107
+ constructor(globalID) {
108
+ this.globalID = globalID;
109
+ this.serverValues = {};
110
+ this.referencedFrom = new Set();
111
+ }
112
+ getServerValues() {
113
+ return this.serverValues;
114
+ }
115
+ toJSON() {
116
+ return {
117
+ globalID: this.globalID,
118
+ map: this.serverValues,
119
+ referencedFrom: Array.from(this.referencedFrom)
120
+ };
121
+ }
122
+ static fromJSON(json) {
123
+ const edo = new EntityDataObject(json.globalID);
124
+ edo.serverValues = json.map;
125
+ edo.referencedFrom = new Set(json.referencedFrom);
126
+ return edo;
127
+ }
128
+ updateServerValue(key, value, requestedFrom) {
129
+ this.serverValues[key] = value;
130
+ this.referencedFrom.add(requestedFrom);
131
+ return Array.from(this.referencedFrom);
132
+ }
133
+ }
134
+
135
+ /**
136
+ * @license
137
+ * Copyright 2025 Google LLC
138
+ *
139
+ * Licensed under the Apache License, Version 2.0 (the "License");
140
+ * you may not use this file except in compliance with the License.
141
+ * You may obtain a copy of the License at
142
+ *
143
+ * http://www.apache.org/licenses/LICENSE-2.0
144
+ *
145
+ * Unless required by applicable law or agreed to in writing, software
146
+ * distributed under the License is distributed on an "AS IS" BASIS,
147
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
148
+ * See the License for the specific language governing permissions and
149
+ * limitations under the License.
150
+ */
151
+ class InMemoryCacheProvider {
152
+ constructor(_keyId) {
153
+ this._keyId = _keyId;
154
+ this.edos = new Map();
155
+ this.resultTrees = new Map();
156
+ }
157
+ async setResultTree(queryId, rt) {
158
+ this.resultTrees.set(queryId, rt);
159
+ }
160
+ async getResultTree(queryId) {
161
+ return this.resultTrees.get(queryId);
162
+ }
163
+ async updateEntityData(entityData) {
164
+ this.edos.set(entityData.globalID, entityData);
165
+ }
166
+ async getEntityData(globalId) {
167
+ if (!this.edos.has(globalId)) {
168
+ this.edos.set(globalId, new EntityDataObject(globalId));
169
+ }
170
+ // Because of the above, we can guarantee that there will be an EDO at the globalId.
171
+ return this.edos.get(globalId);
172
+ }
173
+ close() {
174
+ // No-op
175
+ return Promise.resolve();
176
+ }
177
+ }
178
+
179
+ /**
180
+ * @license
181
+ * Copyright 2025 Google LLC
182
+ *
183
+ * Licensed under the Apache License, Version 2.0 (the "License");
184
+ * you may not use this file except in compliance with the License.
185
+ * You may obtain a copy of the License at
186
+ *
187
+ * http://www.apache.org/licenses/LICENSE-2.0
188
+ *
189
+ * Unless required by applicable law or agreed to in writing, software
190
+ * distributed under the License is distributed on an "AS IS" BASIS,
191
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
192
+ * See the License for the specific language governing permissions and
193
+ * limitations under the License.
194
+ */
195
+ const GLOBAL_ID_KEY = '_id';
196
+ const OBJECT_LISTS_KEY = '_objectLists';
197
+ const REFERENCES_KEY = '_references';
198
+ const SCALARS_KEY = '_scalars';
199
+ const ENTITY_DATA_KEYS_KEY = '_entity_data_keys';
200
+ class EntityNode {
201
+ constructor() {
202
+ this.scalars = {};
203
+ this.references = {};
204
+ this.objectLists = {};
205
+ this.entityDataKeys = new Set();
206
+ }
207
+ async loadData(queryId, values, entityIds, acc, cacheProvider) {
208
+ if (values === undefined) {
209
+ return;
210
+ }
211
+ if (typeof values !== 'object' || Array.isArray(values)) {
212
+ throw new DataConnectError(Code.INVALID_ARGUMENT, 'EntityNode initialized with non-object value');
213
+ }
214
+ if (values === null) {
215
+ return;
216
+ }
217
+ if (typeof values === 'object' &&
218
+ entityIds &&
219
+ entityIds[GLOBAL_ID_KEY] &&
220
+ typeof entityIds[GLOBAL_ID_KEY] === 'string') {
221
+ this.globalId = entityIds[GLOBAL_ID_KEY];
222
+ this.entityData = await cacheProvider.getEntityData(this.globalId);
223
+ }
224
+ for (const key in values) {
225
+ if (values.hasOwnProperty(key)) {
226
+ if (typeof values[key] === 'object') {
227
+ if (Array.isArray(values[key])) {
228
+ const ids = entityIds && entityIds[key];
229
+ const objArray = [];
230
+ const scalarArray = [];
231
+ for (const [index, value] of values[key].entries()) {
232
+ if (typeof value === 'object') {
233
+ if (Array.isArray(value)) ;
234
+ else {
235
+ const entityNode = new EntityNode();
236
+ await entityNode.loadData(queryId, value, ids && ids[index], acc, cacheProvider);
237
+ objArray.push(entityNode);
238
+ }
239
+ }
240
+ else {
241
+ scalarArray.push(value);
242
+ }
243
+ }
244
+ if (scalarArray.length > 0 && objArray.length > 0) {
245
+ this.scalars[key] = values[key];
246
+ }
247
+ else if (scalarArray.length > 0) {
248
+ if (this.entityData) {
249
+ const impactedRefs = this.entityData.updateServerValue(key, scalarArray, queryId);
250
+ this.entityDataKeys.add(key);
251
+ acc.add(impactedRefs);
252
+ }
253
+ else {
254
+ this.scalars[key] = scalarArray;
255
+ }
256
+ }
257
+ else if (objArray.length > 0) {
258
+ this.objectLists[key] = objArray;
259
+ }
260
+ else {
261
+ this.scalars[key] = [];
262
+ }
263
+ }
264
+ else {
265
+ if (values[key] === null) {
266
+ this.scalars[key] = null;
267
+ continue;
268
+ }
269
+ const entityNode = new EntityNode();
270
+ // TODO: Load Data might need to be pushed into ResultTreeProcessor instead.
271
+ await entityNode.loadData(queryId, values[key], entityIds && entityIds[key], acc, cacheProvider);
272
+ this.references[key] = entityNode;
273
+ }
274
+ }
275
+ else {
276
+ if (this.entityData) {
277
+ const impactedRefs = this.entityData.updateServerValue(key, values[key], queryId);
278
+ this.entityDataKeys.add(key);
279
+ acc.add(impactedRefs);
280
+ }
281
+ else {
282
+ this.scalars[key] = values[key];
283
+ }
284
+ }
285
+ }
286
+ }
287
+ if (this.entityData) {
288
+ await cacheProvider.updateEntityData(this.entityData);
289
+ }
290
+ }
291
+ toJSON(mode) {
292
+ const resultObject = {};
293
+ if (mode === EncodingMode.hydrated) {
294
+ if (this.entityData) {
295
+ for (const key of this.entityDataKeys) {
296
+ resultObject[key] = this.entityData.getServerValue(key);
297
+ }
298
+ }
299
+ if (this.scalars) {
300
+ Object.assign(resultObject, this.scalars);
301
+ }
302
+ if (this.references) {
303
+ for (const key in this.references) {
304
+ if (this.references.hasOwnProperty(key)) {
305
+ resultObject[key] = this.references[key].toJSON(mode);
306
+ }
307
+ }
308
+ }
309
+ if (this.objectLists) {
310
+ for (const key in this.objectLists) {
311
+ if (this.objectLists.hasOwnProperty(key)) {
312
+ resultObject[key] = this.objectLists[key].map(obj => obj.toJSON(mode));
313
+ }
314
+ }
315
+ }
316
+ return resultObject;
317
+ }
318
+ else {
319
+ // Get JSON representation of dehydrated list
320
+ if (this.entityData) {
321
+ resultObject[GLOBAL_ID_KEY] = this.entityData.globalID;
322
+ }
323
+ resultObject[ENTITY_DATA_KEYS_KEY] = Array.from(this.entityDataKeys);
324
+ if (this.scalars) {
325
+ resultObject[SCALARS_KEY] = this.scalars;
326
+ }
327
+ if (this.references) {
328
+ const references = {};
329
+ for (const key in this.references) {
330
+ if (this.references.hasOwnProperty(key)) {
331
+ references[key] = this.references[key].toJSON(mode);
332
+ }
333
+ }
334
+ resultObject[REFERENCES_KEY] = references;
335
+ }
336
+ if (this.objectLists) {
337
+ const objectLists = {};
338
+ for (const key in this.objectLists) {
339
+ if (this.objectLists.hasOwnProperty(key)) {
340
+ objectLists[key] = this.objectLists[key].map(obj => obj.toJSON(mode));
341
+ }
342
+ }
343
+ resultObject[OBJECT_LISTS_KEY] = objectLists;
344
+ }
345
+ }
346
+ return resultObject;
347
+ }
348
+ static fromJson(obj) {
349
+ const sdo = new EntityNode();
350
+ if (obj.backingData) {
351
+ sdo.entityData = EntityDataObject.fromJSON(obj.backingData);
352
+ }
353
+ sdo.globalId = obj.globalID;
354
+ sdo.scalars = obj.scalars;
355
+ if (obj.references) {
356
+ const references = {};
357
+ for (const key in obj.references) {
358
+ if (obj.references.hasOwnProperty(key)) {
359
+ references[key] = EntityNode.fromJson(obj.references[key]);
360
+ }
361
+ }
362
+ sdo.references = references;
363
+ }
364
+ if (obj.objectLists) {
365
+ const objectLists = {};
366
+ for (const key in obj.objectLists) {
367
+ if (obj.objectLists.hasOwnProperty(key)) {
368
+ objectLists[key] = obj.objectLists[key].map(obj => EntityNode.fromJson(obj));
369
+ }
370
+ }
371
+ sdo.objectLists = objectLists;
372
+ }
373
+ return sdo;
374
+ }
375
+ }
376
+ // Helpful for storing in persistent cache, which is not available yet.
377
+ var EncodingMode;
378
+ (function (EncodingMode) {
379
+ EncodingMode[EncodingMode["hydrated"] = 0] = "hydrated";
380
+ EncodingMode[EncodingMode["dehydrated"] = 1] = "dehydrated";
381
+ })(EncodingMode || (EncodingMode = {}));
382
+
383
+ /**
384
+ * @license
385
+ * Copyright 2025 Google LLC
386
+ *
387
+ * Licensed under the Apache License, Version 2.0 (the "License");
388
+ * you may not use this file except in compliance with the License.
389
+ * You may obtain a copy of the License at
390
+ *
391
+ * http://www.apache.org/licenses/LICENSE-2.0
392
+ *
393
+ * Unless required by applicable law or agreed to in writing, software
394
+ * distributed under the License is distributed on an "AS IS" BASIS,
395
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
396
+ * See the License for the specific language governing permissions and
397
+ * limitations under the License.
398
+ */
399
+ class ResultTree {
400
+ /**
401
+ * Create a {@link ResultTree} from a dehydrated JSON object.
402
+ * @param value The dehydrated JSON object.
403
+ * @returns The {@link ResultTree}.
404
+ */
405
+ static fromJson(value) {
406
+ return new ResultTree(EntityNode.fromJson(value.rootStub), value.maxAge, value.cachedAt, value.lastAccessed);
407
+ }
408
+ constructor(rootStub, maxAge = 0, cachedAt, _lastAccessed) {
409
+ this.rootStub = rootStub;
410
+ this.maxAge = maxAge;
411
+ this.cachedAt = cachedAt;
412
+ this._lastAccessed = _lastAccessed;
413
+ }
414
+ isStale() {
415
+ return (Date.now() - new Date(this.cachedAt.getTime()).getTime() >
416
+ this.maxAge * 1000);
417
+ }
418
+ updateMaxAge(maxAgeInSeconds) {
419
+ this.maxAge = maxAgeInSeconds;
420
+ }
421
+ updateAccessed() {
422
+ this._lastAccessed = new Date();
423
+ }
424
+ get lastAccessed() {
425
+ return this._lastAccessed;
426
+ }
427
+ getRootStub() {
428
+ return this.rootStub;
429
+ }
430
+ }
431
+
432
+ /**
433
+ * @license
434
+ * Copyright 2025 Google LLC
435
+ *
436
+ * Licensed under the Apache License, Version 2.0 (the "License");
437
+ * you may not use this file except in compliance with the License.
438
+ * You may obtain a copy of the License at
439
+ *
440
+ * http://www.apache.org/licenses/LICENSE-2.0
441
+ *
442
+ * Unless required by applicable law or agreed to in writing, software
443
+ * distributed under the License is distributed on an "AS IS" BASIS,
444
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
445
+ * See the License for the specific language governing permissions and
446
+ * limitations under the License.
447
+ */
448
+ class ImpactedQueryRefsAccumulator {
449
+ constructor(queryId) {
450
+ this.queryId = queryId;
451
+ this.impacted = new Set();
452
+ }
453
+ add(impacted) {
454
+ impacted
455
+ .filter(ref => ref !== this.queryId)
456
+ .forEach(ref => this.impacted.add(ref));
457
+ }
458
+ consumeEvents() {
459
+ const events = Array.from(this.impacted);
460
+ this.impacted.clear();
461
+ return events;
462
+ }
463
+ }
464
+
465
+ /**
466
+ * @license
467
+ * Copyright 2025 Google LLC
468
+ *
469
+ * Licensed under the Apache License, Version 2.0 (the "License");
470
+ * you may not use this file except in compliance with the License.
471
+ * You may obtain a copy of the License at
472
+ *
473
+ * http://www.apache.org/licenses/LICENSE-2.0
474
+ *
475
+ * Unless required by applicable law or agreed to in writing, software
476
+ * distributed under the License is distributed on an "AS IS" BASIS,
477
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
478
+ * See the License for the specific language governing permissions and
479
+ * limitations under the License.
480
+ */
481
+ class ResultTreeProcessor {
482
+ /**
483
+ * Hydrate the EntityNode into a JSON object so that it can be returned to the user.
484
+ * @param rootStubObject
485
+ * @returns {string}
486
+ */
487
+ hydrateResults(rootStubObject) {
488
+ return rootStubObject.toJSON(EncodingMode.hydrated);
489
+ }
490
+ /**
491
+ * Dehydrate results so that they can be stored in the cache.
492
+ * @param json
493
+ * @param entityIds
494
+ * @param cacheProvider
495
+ * @param queryId
496
+ * @returns {Promise<DehydratedResults>}
497
+ */
498
+ async dehydrateResults(json, entityIds, cacheProvider, queryId) {
499
+ const acc = new ImpactedQueryRefsAccumulator(queryId);
500
+ const entityNode = new EntityNode();
501
+ await entityNode.loadData(queryId, json, entityIds, acc, cacheProvider);
502
+ return {
503
+ entityNode,
504
+ impacted: acc.consumeEvents()
505
+ };
506
+ }
507
+ }
508
+
509
+ /**
510
+ * @license
511
+ * Copyright 2025 Google LLC
512
+ *
513
+ * Licensed under the Apache License, Version 2.0 (the "License");
514
+ * you may not use this file except in compliance with the License.
515
+ * You may obtain a copy of the License at
516
+ *
517
+ * http://www.apache.org/licenses/LICENSE-2.0
518
+ *
519
+ * Unless required by applicable law or agreed to in writing, software
520
+ * distributed under the License is distributed on an "AS IS" BASIS,
521
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
522
+ * See the License for the specific language governing permissions and
523
+ * limitations under the License.
524
+ */
525
+ class DataConnectCache {
526
+ constructor(authProvider, projectId, connectorConfig, host, cacheSettings) {
527
+ this.authProvider = authProvider;
528
+ this.projectId = projectId;
529
+ this.connectorConfig = connectorConfig;
530
+ this.host = host;
531
+ this.cacheSettings = cacheSettings;
532
+ this.cacheProvider = null;
533
+ this.uid = null;
534
+ this.authProvider.addTokenChangeListener(async (_) => {
535
+ const newUid = this.authProvider.getAuth().getUid();
536
+ // We should only close if the token changes and so does the new UID
537
+ if (this.uid !== newUid) {
538
+ this.cacheProvider?.close();
539
+ this.uid = newUid;
540
+ const identifier = await this.getIdentifier(this.uid);
541
+ this.cacheProvider = this.initializeNewProviders(identifier);
542
+ }
543
+ });
544
+ }
545
+ async initialize() {
546
+ if (!this.cacheProvider) {
547
+ const identifier = await this.getIdentifier(this.uid);
548
+ this.cacheProvider = this.initializeNewProviders(identifier);
549
+ }
550
+ }
551
+ async getIdentifier(uid) {
552
+ const identifier = `${'memory' // TODO: replace this with indexeddb when persistence is available.
553
+ }-${this.projectId}-${this.connectorConfig.service}-${this.connectorConfig.connector}-${this.connectorConfig.location}-${uid}-${this.host}`;
554
+ const sha256 = await generateSHA256Hash(identifier);
555
+ return sha256;
556
+ }
557
+ initializeNewProviders(identifier) {
558
+ return this.cacheSettings.cacheProvider.initialize(identifier);
559
+ }
560
+ async containsResultTree(queryId) {
561
+ await this.initialize();
562
+ const resultTree = await this.cacheProvider.getResultTree(queryId);
563
+ return resultTree !== undefined;
564
+ }
565
+ async getResultTree(queryId) {
566
+ await this.initialize();
567
+ return this.cacheProvider.getResultTree(queryId);
568
+ }
569
+ async getResultJSON(queryId) {
570
+ await this.initialize();
571
+ const processor = new ResultTreeProcessor();
572
+ const cacheProvider = this.cacheProvider;
573
+ const resultTree = await cacheProvider.getResultTree(queryId);
574
+ if (!resultTree) {
575
+ throw new DataConnectError(Code.INVALID_ARGUMENT, `${queryId} not found in cache. Call "update()" first.`);
576
+ }
577
+ return processor.hydrateResults(resultTree.getRootStub());
578
+ }
579
+ async update(queryId, serverValues, entityIds) {
580
+ await this.initialize();
581
+ const processor = new ResultTreeProcessor();
582
+ const cacheProvider = this.cacheProvider;
583
+ const { entityNode: stubDataObject, impacted } = await processor.dehydrateResults(serverValues, entityIds, cacheProvider, queryId);
584
+ const now = new Date();
585
+ await cacheProvider.setResultTree(queryId, new ResultTree(stubDataObject, serverValues.maxAge || this.cacheSettings.maxAgeSeconds, now, now));
586
+ return impacted;
587
+ }
588
+ }
589
+ class MemoryStub {
590
+ constructor() {
591
+ this.type = 'MEMORY';
592
+ }
593
+ /**
594
+ * @internal
595
+ */
596
+ initialize(cacheId) {
597
+ return new InMemoryCacheProvider(cacheId);
598
+ }
599
+ }
600
+
601
+ /**
602
+ * @license
603
+ * Copyright 2024 Google LLC
604
+ *
605
+ * Licensed under the Apache License, Version 2.0 (the "License");
606
+ * you may not use this file except in compliance with the License.
607
+ * You may obtain a copy of the License at
608
+ *
609
+ * http://www.apache.org/licenses/LICENSE-2.0
610
+ *
611
+ * Unless required by applicable law or agreed to in writing, software
612
+ * distributed under the License is distributed on an "AS IS" BASIS,
613
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
614
+ * See the License for the specific language governing permissions and
615
+ * limitations under the License.
616
+ */
617
+ /**
618
+ * @internal
619
+ * Abstraction around AppCheck's token fetching capabilities.
620
+ */
621
+ class AppCheckTokenProvider {
622
+ constructor(app, appCheckProvider) {
623
+ this.appCheckProvider = appCheckProvider;
624
+ if (_isFirebaseServerApp(app) && app.settings.appCheckToken) {
625
+ this.serverAppAppCheckToken = app.settings.appCheckToken;
626
+ }
627
+ this.appCheck = appCheckProvider?.getImmediate({ optional: true });
628
+ if (!this.appCheck) {
629
+ void appCheckProvider
630
+ ?.get()
631
+ .then(appCheck => (this.appCheck = appCheck))
632
+ .catch();
633
+ }
634
+ }
635
+ getToken() {
636
+ if (this.serverAppAppCheckToken) {
637
+ return Promise.resolve({ token: this.serverAppAppCheckToken });
638
+ }
639
+ if (!this.appCheck) {
640
+ return new Promise((resolve, reject) => {
641
+ // Support delayed initialization of FirebaseAppCheck. This allows our
642
+ // customers to initialize the RTDB SDK before initializing Firebase
643
+ // AppCheck and ensures that all requests are authenticated if a token
644
+ // becomes available before the timoeout below expires.
645
+ setTimeout(() => {
646
+ if (this.appCheck) {
647
+ this.getToken().then(resolve, reject);
648
+ }
649
+ else {
650
+ resolve(null);
651
+ }
652
+ }, 0);
653
+ });
654
+ }
655
+ return this.appCheck.getToken();
656
+ }
657
+ addTokenChangeListener(listener) {
658
+ void this.appCheckProvider
659
+ ?.get()
660
+ .then(appCheck => appCheck.addTokenListener(listener));
661
+ }
662
+ }
663
+
664
+ /**
665
+ * @license
666
+ * Copyright 2024 Google LLC
667
+ *
668
+ * Licensed under the Apache License, Version 2.0 (the "License");
669
+ * you may not use this file except in compliance with the License.
670
+ * You may obtain a copy of the License at
671
+ *
672
+ * http://www.apache.org/licenses/LICENSE-2.0
673
+ *
674
+ * Unless required by applicable law or agreed to in writing, software
675
+ * distributed under the License is distributed on an "AS IS" BASIS,
676
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
677
+ * See the License for the specific language governing permissions and
678
+ * limitations under the License.
679
+ */
680
+ const logger = new Logger('@firebase/data-connect');
681
+ function setLogLevel(logLevel) {
682
+ logger.setLogLevel(logLevel);
683
+ }
684
+ function logDebug(msg) {
685
+ logger.debug(`DataConnect (${SDK_VERSION}): ${msg}`);
686
+ }
687
+ function logError(msg) {
688
+ logger.error(`DataConnect (${SDK_VERSION}): ${msg}`);
689
+ }
690
+
691
+ /**
692
+ * @license
693
+ * Copyright 2024 Google LLC
694
+ *
695
+ * Licensed under the Apache License, Version 2.0 (the "License");
696
+ * you may not use this file except in compliance with the License.
697
+ * You may obtain a copy of the License at
698
+ *
699
+ * http://www.apache.org/licenses/LICENSE-2.0
700
+ *
701
+ * Unless required by applicable law or agreed to in writing, software
702
+ * distributed under the License is distributed on an "AS IS" BASIS,
703
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
704
+ * See the License for the specific language governing permissions and
705
+ * limitations under the License.
706
+ */
707
+ // @internal
708
+ class FirebaseAuthProvider {
709
+ constructor(_appName, _options, _authProvider) {
710
+ this._appName = _appName;
711
+ this._options = _options;
712
+ this._authProvider = _authProvider;
713
+ this._auth = _authProvider.getImmediate({ optional: true });
714
+ if (!this._auth) {
715
+ _authProvider.onInit(auth => (this._auth = auth));
716
+ }
717
+ }
718
+ getAuth() {
719
+ return this._auth;
720
+ }
721
+ getToken(forceRefresh) {
722
+ if (!this._auth) {
723
+ return new Promise((resolve, reject) => {
724
+ setTimeout(() => {
725
+ if (this._auth) {
726
+ this.getToken(forceRefresh).then(resolve, reject);
727
+ }
728
+ else {
729
+ resolve(null);
730
+ }
731
+ }, 0);
732
+ });
733
+ }
734
+ return this._auth.getToken(forceRefresh).catch(error => {
735
+ if (error && error.code === 'auth/token-not-initialized') {
736
+ logDebug('Got auth/token-not-initialized error. Treating as null token.');
737
+ return null;
738
+ }
739
+ else {
740
+ logError('Error received when attempting to retrieve token: ' +
741
+ JSON.stringify(error));
742
+ return Promise.reject(error);
743
+ }
744
+ });
745
+ }
746
+ addTokenChangeListener(listener) {
747
+ this._auth?.addAuthTokenListener(listener);
748
+ }
749
+ removeTokenChangeListener(listener) {
750
+ this._authProvider
751
+ .get()
752
+ .then(auth => auth.removeAuthTokenListener(listener))
753
+ .catch(err => logError(err));
754
+ }
755
+ }
756
+
757
+ /**
758
+ * @license
759
+ * Copyright 2024 Google LLC
760
+ *
761
+ * Licensed under the Apache License, Version 2.0 (the "License");
762
+ * you may not use this file except in compliance with the License.
763
+ * You may obtain a copy of the License at
764
+ *
765
+ * http://www.apache.org/licenses/LICENSE-2.0
766
+ *
767
+ * Unless required by applicable law or agreed to in writing, software
768
+ * distributed under the License is distributed on an "AS IS" BASIS,
769
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
770
+ * See the License for the specific language governing permissions and
771
+ * limitations under the License.
772
+ */
773
+ const QUERY_STR = 'query';
774
+ const MUTATION_STR = 'mutation';
775
+ const SOURCE_SERVER = 'SERVER';
776
+ const SOURCE_CACHE = 'CACHE';
777
+
778
+ /**
779
+ * @license
780
+ * Copyright 2026 Google LLC
781
+ *
782
+ * Licensed under the Apache License, Version 2.0 (the "License");
783
+ * you may not use this file except in compliance with the License.
784
+ * You may obtain a copy of the License at
785
+ *
786
+ * http://www.apache.org/licenses/LICENSE-2.0
787
+ *
788
+ * Unless required by applicable law or agreed to in writing, software
789
+ * distributed under the License is distributed on an "AS IS" BASIS,
790
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
791
+ * See the License for the specific language governing permissions and
792
+ * limitations under the License.
793
+ */
794
+ function parseEntityIds(result) {
795
+ // Iterate through extensions.dataConnect
796
+ const dataConnectExtensions = result.extensions?.dataConnect;
797
+ const dataCopy = Object.assign(result);
798
+ if (!dataConnectExtensions) {
799
+ return dataCopy;
800
+ }
801
+ const ret = {};
802
+ for (const extension of dataConnectExtensions) {
803
+ const { path } = extension;
804
+ populatePath(path, ret, extension);
805
+ }
806
+ return ret;
807
+ }
808
+ // mutates the object to update the path
809
+ function populatePath(path, toUpdate, extension) {
810
+ let curObj = toUpdate;
811
+ for (const slice of path) {
812
+ if (typeof curObj[slice] !== 'object') {
813
+ curObj[slice] = {};
814
+ }
815
+ curObj = curObj[slice];
816
+ }
817
+ if ('entityId' in extension && extension.entityId) {
818
+ curObj['_id'] = extension.entityId;
819
+ }
820
+ else if ('entityIds' in extension) {
821
+ const entityArr = extension.entityIds;
822
+ for (let i = 0; i < entityArr.length; i++) {
823
+ const entityId = entityArr[i];
824
+ if (typeof curObj[i] === 'undefined') {
825
+ curObj[i] = {};
826
+ }
827
+ curObj[i]._id = entityId;
828
+ }
829
+ }
830
+ }
831
+
832
+ /**
833
+ * @license
834
+ * Copyright 2024 Google LLC
835
+ *
836
+ * Licensed under the Apache License, Version 2.0 (the "License");
837
+ * you may not use this file except in compliance with the License.
838
+ * You may obtain a copy of the License at
839
+ *
840
+ * http://www.apache.org/licenses/LICENSE-2.0
841
+ *
842
+ * Unless required by applicable law or agreed to in writing, software
843
+ * distributed under the License is distributed on an "AS IS" BASIS,
844
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
845
+ * See the License for the specific language governing permissions and
846
+ * limitations under the License.
847
+ */
848
+ let encoderImpl;
849
+ let decoderImpl;
850
+ function setEncoder(encoder) {
851
+ encoderImpl = encoder;
852
+ }
853
+ function setDecoder(decoder) {
854
+ decoderImpl = decoder;
855
+ }
856
+ function sortKeysForObj(o) {
857
+ return Object.keys(o)
858
+ .sort()
859
+ .reduce((accumulator, currentKey) => {
860
+ accumulator[currentKey] = o[currentKey];
861
+ return accumulator;
862
+ }, {});
863
+ }
864
+ setEncoder((o) => JSON.stringify(sortKeysForObj(o)));
865
+ setDecoder(s => sortKeysForObj(JSON.parse(s)));
866
+
867
+ /**
868
+ * @license
869
+ * Copyright 2024 Google LLC
870
+ *
871
+ * Licensed under the Apache License, Version 2.0 (the "License");
872
+ * you may not use this file except in compliance with the License.
873
+ * You may obtain a copy of the License at
874
+ *
875
+ * http://www.apache.org/licenses/LICENSE-2.0
876
+ *
877
+ * Unless required by applicable law or agreed to in writing, software
878
+ * distributed under the License is distributed on an "AS IS" BASIS,
879
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
880
+ * See the License for the specific language governing permissions and
881
+ * limitations under the License.
882
+ */
883
+ function getRefSerializer(queryRef, data, source, fetchTime) {
884
+ return function toJSON() {
885
+ return {
886
+ data,
887
+ refInfo: {
888
+ name: queryRef.name,
889
+ variables: queryRef.variables,
890
+ connectorConfig: {
891
+ projectId: queryRef.dataConnect.app.options.projectId,
892
+ ...queryRef.dataConnect.getSettings()
893
+ }
894
+ },
895
+ fetchTime,
896
+ source
897
+ };
898
+ };
899
+ }
900
+ class QueryManager {
901
+ async preferCacheResults(queryRef, allowStale = false) {
902
+ let cacheResult;
903
+ try {
904
+ cacheResult = await this.fetchCacheResults(queryRef, allowStale);
905
+ }
906
+ catch (e) {
907
+ // Ignore the error and try to fetch from the server.
908
+ }
909
+ if (cacheResult) {
910
+ return cacheResult;
911
+ }
912
+ return this.fetchServerResults(queryRef);
913
+ }
914
+ constructor(transport, dc, cache) {
915
+ this.transport = transport;
916
+ this.dc = dc;
917
+ this.cache = cache;
918
+ this.callbacks = new Map();
919
+ this.subscriptionCache = new Map();
920
+ this.queue = [];
921
+ }
922
+ async waitForQueuedWrites() {
923
+ for (const promise of this.queue) {
924
+ await promise;
925
+ }
926
+ this.queue = [];
927
+ }
928
+ updateSSR(updatedData) {
929
+ this.queue.push(this.updateCache(updatedData).then(async (result) => this.publishCacheResultsToSubscribers(result, updatedData.fetchTime)));
930
+ }
931
+ async updateCache(result, extensions) {
932
+ await this.waitForQueuedWrites();
933
+ if (this.cache) {
934
+ const entityIds = parseEntityIds(result);
935
+ const updatedMaxAge = getMaxAgeFromExtensions(extensions);
936
+ if (updatedMaxAge !== undefined) {
937
+ this.cache.cacheSettings.maxAgeSeconds = updatedMaxAge;
938
+ }
939
+ return this.cache.update(encoderImpl({
940
+ name: result.ref.name,
941
+ variables: result.ref.variables,
942
+ refType: QUERY_STR
943
+ }), result.data, entityIds);
944
+ }
945
+ else {
946
+ const key = encoderImpl({
947
+ name: result.ref.name,
948
+ variables: result.ref.variables,
949
+ refType: QUERY_STR
950
+ });
951
+ this.subscriptionCache.set(key, result);
952
+ return [key];
953
+ }
954
+ }
955
+ addSubscription(queryRef, onResultCallback, onCompleteCallback, onErrorCallback, initialCache) {
956
+ const key = encoderImpl({
957
+ name: queryRef.name,
958
+ variables: queryRef.variables,
959
+ refType: QUERY_STR
960
+ });
961
+ const unsubscribe = () => {
962
+ if (this.callbacks.has(key)) {
963
+ const callbackList = this.callbacks.get(key);
964
+ this.callbacks.set(key, callbackList.filter(callback => callback !== subscription));
965
+ onCompleteCallback?.();
966
+ }
967
+ };
968
+ const subscription = {
969
+ userCallback: onResultCallback,
970
+ errCallback: onErrorCallback,
971
+ unsubscribe
972
+ };
973
+ if (initialCache) {
974
+ this.updateSSR(initialCache);
975
+ }
976
+ const promise = this.preferCacheResults(queryRef, /*allowStale=*/ true);
977
+ // We want to ignore the error and let subscriptions handle it
978
+ promise.then(undefined, err => { });
979
+ if (!this.callbacks.has(key)) {
980
+ this.callbacks.set(key, []);
981
+ }
982
+ this.callbacks
983
+ .get(key)
984
+ .push(subscription);
985
+ return unsubscribe;
986
+ }
987
+ async fetchServerResults(queryRef) {
988
+ await this.waitForQueuedWrites();
989
+ const key = encoderImpl({
990
+ name: queryRef.name,
991
+ variables: queryRef.variables,
992
+ refType: QUERY_STR
993
+ });
994
+ try {
995
+ const result = await this.transport.invokeQuery(queryRef.name, queryRef.variables);
996
+ const fetchTime = Date.now().toString();
997
+ const originalExtensions = result.extensions;
998
+ const queryResult = {
999
+ ...result,
1000
+ ref: queryRef,
1001
+ source: SOURCE_SERVER,
1002
+ fetchTime,
1003
+ data: result.data,
1004
+ extensions: getDataConnectExtensionsWithoutMaxAge(originalExtensions),
1005
+ toJSON: getRefSerializer(queryRef, result.data, SOURCE_SERVER, fetchTime)
1006
+ };
1007
+ let updatedKeys = [];
1008
+ updatedKeys = await this.updateCache(queryResult, originalExtensions?.dataConnect);
1009
+ this.publishDataToSubscribers(key, queryResult);
1010
+ if (this.cache) {
1011
+ await this.publishCacheResultsToSubscribers(updatedKeys, fetchTime);
1012
+ }
1013
+ else {
1014
+ this.subscriptionCache.set(key, queryResult);
1015
+ }
1016
+ return queryResult;
1017
+ }
1018
+ catch (e) {
1019
+ this.publishErrorToSubscribers(key, e);
1020
+ throw e;
1021
+ }
1022
+ }
1023
+ async fetchCacheResults(queryRef, allowStale = false) {
1024
+ await this.waitForQueuedWrites();
1025
+ let result;
1026
+ if (!this.cache) {
1027
+ result = await this.getFromSubscriberCache(queryRef);
1028
+ }
1029
+ else {
1030
+ result = await this.getFromResultTreeCache(queryRef, allowStale);
1031
+ }
1032
+ if (!result) {
1033
+ throw new DataConnectError(Code.OTHER, 'No cache entry found for query: ' + queryRef.name);
1034
+ }
1035
+ const fetchTime = Date.now().toString();
1036
+ const queryResult = {
1037
+ ...result,
1038
+ ref: queryRef,
1039
+ source: SOURCE_CACHE,
1040
+ fetchTime,
1041
+ data: result.data,
1042
+ extensions: result.extensions,
1043
+ toJSON: getRefSerializer(queryRef, result.data, SOURCE_CACHE, fetchTime)
1044
+ };
1045
+ if (this.cache) {
1046
+ const key = encoderImpl({
1047
+ name: queryRef.name,
1048
+ variables: queryRef.variables,
1049
+ refType: QUERY_STR
1050
+ });
1051
+ await this.publishCacheResultsToSubscribers([key], fetchTime);
1052
+ }
1053
+ else {
1054
+ const key = encoderImpl({
1055
+ name: queryRef.name,
1056
+ variables: queryRef.variables,
1057
+ refType: QUERY_STR
1058
+ });
1059
+ this.subscriptionCache.set(key, queryResult);
1060
+ this.publishDataToSubscribers(key, queryResult);
1061
+ }
1062
+ return queryResult;
1063
+ }
1064
+ publishErrorToSubscribers(key, err) {
1065
+ this.callbacks.get(key)?.forEach(subscription => {
1066
+ if (subscription.errCallback) {
1067
+ subscription.errCallback(err);
1068
+ }
1069
+ });
1070
+ }
1071
+ async getFromResultTreeCache(queryRef, allowStale = false) {
1072
+ const key = encoderImpl({
1073
+ name: queryRef.name,
1074
+ variables: queryRef.variables,
1075
+ refType: QUERY_STR
1076
+ });
1077
+ if (!this.cache || !(await this.cache.containsResultTree(key))) {
1078
+ return null;
1079
+ }
1080
+ const cacheResult = (await this.cache.getResultJSON(key));
1081
+ const resultTree = await this.cache.getResultTree(key);
1082
+ if (!allowStale && resultTree.isStale()) {
1083
+ return null;
1084
+ }
1085
+ const result = {
1086
+ source: SOURCE_CACHE,
1087
+ ref: queryRef,
1088
+ data: cacheResult,
1089
+ toJSON: getRefSerializer(queryRef, cacheResult, SOURCE_CACHE, resultTree.cachedAt.toString()),
1090
+ fetchTime: resultTree.cachedAt.toString()
1091
+ };
1092
+ (await this.cache.getResultTree(key)).updateAccessed();
1093
+ return result;
1094
+ }
1095
+ async getFromSubscriberCache(queryRef) {
1096
+ const key = encoderImpl({
1097
+ name: queryRef.name,
1098
+ variables: queryRef.variables,
1099
+ refType: QUERY_STR
1100
+ });
1101
+ if (!this.subscriptionCache.has(key)) {
1102
+ return;
1103
+ }
1104
+ const result = this.subscriptionCache.get(key);
1105
+ result.source = SOURCE_CACHE;
1106
+ result.toJSON = getRefSerializer(result.ref, result.data, SOURCE_CACHE, result.fetchTime);
1107
+ return result;
1108
+ }
1109
+ publishDataToSubscribers(key, queryResult) {
1110
+ if (!this.callbacks.has(key)) {
1111
+ return;
1112
+ }
1113
+ const subscribers = this.callbacks.get(key);
1114
+ subscribers.forEach(callback => {
1115
+ callback.userCallback(queryResult);
1116
+ });
1117
+ }
1118
+ async publishCacheResultsToSubscribers(impactedQueries, fetchTime) {
1119
+ if (!this.cache) {
1120
+ return;
1121
+ }
1122
+ for (const query of impactedQueries) {
1123
+ const callbacks = this.callbacks.get(query);
1124
+ if (!callbacks) {
1125
+ continue;
1126
+ }
1127
+ const newJson = (await this.cache.getResultTree(query))
1128
+ .getRootStub()
1129
+ .toJSON(EncodingMode.hydrated);
1130
+ const { name, variables } = decoderImpl(query);
1131
+ const queryRef = {
1132
+ dataConnect: this.dc,
1133
+ refType: QUERY_STR,
1134
+ name,
1135
+ variables
1136
+ };
1137
+ this.publishDataToSubscribers(query, {
1138
+ data: newJson,
1139
+ fetchTime,
1140
+ ref: queryRef,
1141
+ source: SOURCE_CACHE,
1142
+ toJSON: getRefSerializer(queryRef, newJson, SOURCE_CACHE, fetchTime)
1143
+ });
1144
+ }
1145
+ }
1146
+ enableEmulator(host, port) {
1147
+ this.transport.useEmulator(host, port);
1148
+ }
1149
+ }
1150
+ function getMaxAgeFromExtensions(extensions) {
1151
+ if (!extensions) {
1152
+ return;
1153
+ }
1154
+ for (const extension of extensions) {
1155
+ if ('maxAge' in extension &&
1156
+ extension.maxAge !== undefined &&
1157
+ extension.maxAge !== null) {
1158
+ if (extension.maxAge.endsWith('s')) {
1159
+ return Number(extension.maxAge.substring(0, extension.maxAge.length - 1));
1160
+ }
1161
+ }
1162
+ }
1163
+ }
1164
+ function getDataConnectExtensionsWithoutMaxAge(extensions) {
1165
+ return {
1166
+ dataConnect: extensions.dataConnect?.filter(extension => 'entityId' in extension || 'entityIds' in extension)
1167
+ };
1168
+ }
1169
+
1170
+ /**
1171
+ * @license
1172
+ * Copyright 2024 Google LLC
1173
+ *
1174
+ * Licensed under the Apache License, Version 2.0 (the "License");
1175
+ * you may not use this file except in compliance with the License.
1176
+ * You may obtain a copy of the License at
1177
+ *
1178
+ * http://www.apache.org/licenses/LICENSE-2.0
1179
+ *
1180
+ * Unless required by applicable law or agreed to in writing, software
1181
+ * distributed under the License is distributed on an "AS IS" BASIS,
1182
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1183
+ * See the License for the specific language governing permissions and
1184
+ * limitations under the License.
1185
+ */
1186
+ const CallerSdkTypeEnum = {
1187
+ Base: 'Base', // Core JS SDK
1188
+ Generated: 'Generated', // Generated JS SDK
1189
+ TanstackReactCore: 'TanstackReactCore', // Tanstack non-generated React SDK
1190
+ GeneratedReact: 'GeneratedReact', // Tanstack non-generated Angular SDK
1191
+ TanstackAngularCore: 'TanstackAngularCore', // Tanstack non-generated Angular SDK
1192
+ GeneratedAngular: 'GeneratedAngular' // Generated Angular SDK
1193
+ };
1194
+
1195
+ /**
1196
+ * @license
1197
+ * Copyright 2024 Google LLC
1198
+ *
1199
+ * Licensed under the Apache License, Version 2.0 (the "License");
1200
+ * you may not use this file except in compliance with the License.
1201
+ * You may obtain a copy of the License at
1202
+ *
1203
+ * http://www.apache.org/licenses/LICENSE-2.0
1204
+ *
1205
+ * Unless required by applicable law or agreed to in writing, software
1206
+ * distributed under the License is distributed on an "AS IS" BASIS,
1207
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1208
+ * See the License for the specific language governing permissions and
1209
+ * limitations under the License.
1210
+ */
1211
+ const PROD_HOST = 'firebasedataconnect.googleapis.com';
1212
+ function urlBuilder(projectConfig, transportOptions) {
1213
+ const { connector, location, projectId: project, service } = projectConfig;
1214
+ const { host, sslEnabled, port } = transportOptions;
1215
+ const protocol = sslEnabled ? 'https' : 'http';
1216
+ const realHost = host || PROD_HOST;
1217
+ let baseUrl = `${protocol}://${realHost}`;
1218
+ if (typeof port === 'number') {
1219
+ baseUrl += `:${port}`;
1220
+ }
1221
+ else if (typeof port !== 'undefined') {
1222
+ logError('Port type is of an invalid type');
1223
+ throw new DataConnectError(Code.INVALID_ARGUMENT, 'Incorrect type for port passed in!');
1224
+ }
1225
+ return `${baseUrl}/v1/projects/${project}/locations/${location}/services/${service}/connectors/${connector}`;
1226
+ }
1227
+ function addToken(url, apiKey) {
1228
+ if (!apiKey) {
1229
+ return url;
1230
+ }
1231
+ const newUrl = new URL(url);
1232
+ newUrl.searchParams.append('key', apiKey);
1233
+ return newUrl.toString();
1234
+ }
1235
+
1236
+ /**
1237
+ * @license
1238
+ * Copyright 2024 Google LLC
1239
+ *
1240
+ * Licensed under the Apache License, Version 2.0 (the "License");
1241
+ * you may not use this file except in compliance with the License.
1242
+ * You may obtain a copy of the License at
1243
+ *
1244
+ * http://www.apache.org/licenses/LICENSE-2.0
1245
+ *
1246
+ * Unless required by applicable law or agreed to in writing, software
1247
+ * distributed under the License is distributed on an "AS IS" BASIS,
1248
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1249
+ * See the License for the specific language governing permissions and
1250
+ * limitations under the License.
1251
+ */
1252
+ let connectFetch = globalThis.fetch;
1253
+ function getGoogApiClientValue(_isUsingGen, _callerSdkType) {
1254
+ let str = 'gl-js/ fire/' + SDK_VERSION;
1255
+ if (_callerSdkType !== CallerSdkTypeEnum.Base &&
1256
+ _callerSdkType !== CallerSdkTypeEnum.Generated) {
1257
+ str += ' js/' + _callerSdkType.toLowerCase();
1258
+ }
1259
+ else if (_isUsingGen || _callerSdkType === CallerSdkTypeEnum.Generated) {
1260
+ str += ' js/gen';
1261
+ }
1262
+ return str;
1263
+ }
1264
+ async function dcFetch(url, body, { signal }, appId, accessToken, appCheckToken, _isUsingGen, _callerSdkType, _isUsingEmulator) {
1265
+ if (!connectFetch) {
1266
+ throw new DataConnectError(Code.OTHER, 'No Fetch Implementation detected!');
1267
+ }
1268
+ const headers = {
1269
+ 'Content-Type': 'application/json',
1270
+ 'X-Goog-Api-Client': getGoogApiClientValue(_isUsingGen, _callerSdkType)
1271
+ };
1272
+ if (accessToken) {
1273
+ headers['X-Firebase-Auth-Token'] = accessToken;
1274
+ }
1275
+ if (appId) {
1276
+ headers['x-firebase-gmpid'] = appId;
1277
+ }
1278
+ if (appCheckToken) {
1279
+ headers['X-Firebase-AppCheck'] = appCheckToken;
1280
+ }
1281
+ const bodyStr = JSON.stringify(body);
1282
+ const fetchOptions = {
1283
+ body: bodyStr,
1284
+ method: 'POST',
1285
+ headers,
1286
+ signal
1287
+ };
1288
+ if (isCloudWorkstation(url) && _isUsingEmulator) {
1289
+ fetchOptions.credentials = 'include';
1290
+ }
1291
+ let response;
1292
+ try {
1293
+ response = await connectFetch(url, fetchOptions);
1294
+ }
1295
+ catch (err) {
1296
+ throw new DataConnectError(Code.OTHER, 'Failed to fetch: ' + JSON.stringify(err));
1297
+ }
1298
+ let jsonResponse;
1299
+ try {
1300
+ jsonResponse = await response.json();
1301
+ }
1302
+ catch (e) {
1303
+ throw new DataConnectError(Code.OTHER, JSON.stringify(e));
1304
+ }
1305
+ const message = getErrorMessage(jsonResponse);
1306
+ if (response.status >= 400) {
1307
+ logError('Error while performing request: ' + JSON.stringify(jsonResponse));
1308
+ if (response.status === 401) {
1309
+ throw new DataConnectError(Code.UNAUTHORIZED, message);
1310
+ }
1311
+ throw new DataConnectError(Code.OTHER, message);
1312
+ }
1313
+ if (jsonResponse.errors && jsonResponse.errors.length) {
1314
+ const stringified = JSON.stringify(jsonResponse.errors);
1315
+ const failureResponse = {
1316
+ errors: jsonResponse.errors,
1317
+ data: jsonResponse.data
1318
+ };
1319
+ throw new DataConnectOperationError('DataConnect error while performing request: ' + stringified, failureResponse);
1320
+ }
1321
+ if (!jsonResponse.extensions) {
1322
+ jsonResponse.extensions = {
1323
+ dataConnect: []
1324
+ };
1325
+ }
1326
+ return jsonResponse;
1327
+ }
1328
+ function getErrorMessage(obj) {
1329
+ if ('message' in obj && obj.message) {
1330
+ return obj.message;
1331
+ }
1332
+ return JSON.stringify(obj);
1333
+ }
1334
+
1335
+ /**
1336
+ * @license
1337
+ * Copyright 2024 Google LLC
1338
+ *
1339
+ * Licensed under the Apache License, Version 2.0 (the "License");
1340
+ * you may not use this file except in compliance with the License.
1341
+ * You may obtain a copy of the License at
1342
+ *
1343
+ * http://www.apache.org/licenses/LICENSE-2.0
1344
+ *
1345
+ * Unless required by applicable law or agreed to in writing, software
1346
+ * distributed under the License is distributed on an "AS IS" BASIS,
1347
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1348
+ * See the License for the specific language governing permissions and
1349
+ * limitations under the License.
1350
+ */
1351
+ class RESTTransport {
1352
+ constructor(options, apiKey, appId, authProvider, appCheckProvider, transportOptions, _isUsingGen = false, _callerSdkType = CallerSdkTypeEnum.Base) {
1353
+ this.apiKey = apiKey;
1354
+ this.appId = appId;
1355
+ this.authProvider = authProvider;
1356
+ this.appCheckProvider = appCheckProvider;
1357
+ this._isUsingGen = _isUsingGen;
1358
+ this._callerSdkType = _callerSdkType;
1359
+ this._host = '';
1360
+ this._location = 'l';
1361
+ this._connectorName = '';
1362
+ this._secure = true;
1363
+ this._project = 'p';
1364
+ this._accessToken = null;
1365
+ this._appCheckToken = null;
1366
+ this._lastToken = null;
1367
+ this._isUsingEmulator = false;
1368
+ // TODO(mtewani): Update U to include shape of body defined in line 13.
1369
+ this.invokeQuery = (queryName, body) => {
1370
+ const abortController = new AbortController();
1371
+ // TODO(mtewani): Update to proper value
1372
+ const withAuth = this.withRetry(() => dcFetch(addToken(`${this.endpointUrl}:executeQuery`, this.apiKey), {
1373
+ name: `projects/${this._project}/locations/${this._location}/services/${this._serviceName}/connectors/${this._connectorName}`,
1374
+ operationName: queryName,
1375
+ variables: body
1376
+ }, abortController, this.appId, this._accessToken, this._appCheckToken, this._isUsingGen, this._callerSdkType, this._isUsingEmulator));
1377
+ return withAuth;
1378
+ };
1379
+ this.invokeMutation = (mutationName, body) => {
1380
+ const abortController = new AbortController();
1381
+ const taskResult = this.withRetry(() => {
1382
+ return dcFetch(addToken(`${this.endpointUrl}:executeMutation`, this.apiKey), {
1383
+ name: `projects/${this._project}/locations/${this._location}/services/${this._serviceName}/connectors/${this._connectorName}`,
1384
+ operationName: mutationName,
1385
+ variables: body
1386
+ }, abortController, this.appId, this._accessToken, this._appCheckToken, this._isUsingGen, this._callerSdkType, this._isUsingEmulator);
1387
+ });
1388
+ return taskResult;
1389
+ };
1390
+ if (transportOptions) {
1391
+ if (typeof transportOptions.port === 'number') {
1392
+ this._port = transportOptions.port;
1393
+ }
1394
+ if (typeof transportOptions.sslEnabled !== 'undefined') {
1395
+ this._secure = transportOptions.sslEnabled;
1396
+ }
1397
+ this._host = transportOptions.host;
1398
+ }
1399
+ const { location, projectId: project, connector, service } = options;
1400
+ if (location) {
1401
+ this._location = location;
1402
+ }
1403
+ if (project) {
1404
+ this._project = project;
1405
+ }
1406
+ this._serviceName = service;
1407
+ if (!connector) {
1408
+ throw new DataConnectError(Code.INVALID_ARGUMENT, 'Connector Name required!');
1409
+ }
1410
+ this._connectorName = connector;
1411
+ this.authProvider?.addTokenChangeListener(token => {
1412
+ logDebug(`New Token Available: ${token}`);
1413
+ this._accessToken = token;
1414
+ });
1415
+ this.appCheckProvider?.addTokenChangeListener(result => {
1416
+ const { token } = result;
1417
+ logDebug(`New App Check Token Available: ${token}`);
1418
+ this._appCheckToken = token;
1419
+ });
1420
+ }
1421
+ get endpointUrl() {
1422
+ return urlBuilder({
1423
+ connector: this._connectorName,
1424
+ location: this._location,
1425
+ projectId: this._project,
1426
+ service: this._serviceName
1427
+ }, { host: this._host, sslEnabled: this._secure, port: this._port });
1428
+ }
1429
+ useEmulator(host, port, isSecure) {
1430
+ this._host = host;
1431
+ this._isUsingEmulator = true;
1432
+ if (typeof port === 'number') {
1433
+ this._port = port;
1434
+ }
1435
+ if (typeof isSecure !== 'undefined') {
1436
+ this._secure = isSecure;
1437
+ }
1438
+ }
1439
+ onTokenChanged(newToken) {
1440
+ this._accessToken = newToken;
1441
+ }
1442
+ async getWithAuth(forceToken = false) {
1443
+ let starterPromise = new Promise(resolve => resolve(this._accessToken));
1444
+ if (this.appCheckProvider) {
1445
+ const appCheckToken = await this.appCheckProvider.getToken();
1446
+ if (appCheckToken) {
1447
+ this._appCheckToken = appCheckToken.token;
1448
+ }
1449
+ }
1450
+ if (this.authProvider) {
1451
+ starterPromise = this.authProvider
1452
+ .getToken(/*forceToken=*/ forceToken)
1453
+ .then(data => {
1454
+ if (!data) {
1455
+ return null;
1456
+ }
1457
+ this._accessToken = data.accessToken;
1458
+ return this._accessToken;
1459
+ });
1460
+ }
1461
+ else {
1462
+ starterPromise = new Promise(resolve => resolve(''));
1463
+ }
1464
+ return starterPromise;
1465
+ }
1466
+ _setLastToken(lastToken) {
1467
+ this._lastToken = lastToken;
1468
+ }
1469
+ withRetry(promiseFactory, retry = false) {
1470
+ let isNewToken = false;
1471
+ return this.getWithAuth(retry)
1472
+ .then(res => {
1473
+ isNewToken = this._lastToken !== res;
1474
+ this._lastToken = res;
1475
+ return res;
1476
+ })
1477
+ .then(promiseFactory)
1478
+ .catch(err => {
1479
+ // Only retry if the result is unauthorized and the last token isn't the same as the new one.
1480
+ if ('code' in err &&
1481
+ err.code === Code.UNAUTHORIZED &&
1482
+ !retry &&
1483
+ isNewToken) {
1484
+ logDebug('Retrying due to unauthorized');
1485
+ return this.withRetry(promiseFactory, true);
1486
+ }
1487
+ throw err;
1488
+ });
1489
+ }
1490
+ _setCallerSdkType(callerSdkType) {
1491
+ this._callerSdkType = callerSdkType;
1492
+ }
1493
+ }
1494
+
1495
+ /**
1496
+ * @license
1497
+ * Copyright 2024 Google LLC
1498
+ *
1499
+ * Licensed under the Apache License, Version 2.0 (the "License");
1500
+ * you may not use this file except in compliance with the License.
1501
+ * You may obtain a copy of the License at
1502
+ *
1503
+ * http://www.apache.org/licenses/LICENSE-2.0
1504
+ *
1505
+ * Unless required by applicable law or agreed to in writing, software
1506
+ * distributed under the License is distributed on an "AS IS" BASIS,
1507
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1508
+ * See the License for the specific language governing permissions and
1509
+ * limitations under the License.
1510
+ */
1511
+ /**
1512
+ *
1513
+ * @param dcInstance Data Connect instance
1514
+ * @param mutationName name of mutation
1515
+ * @param variables variables to send with mutation
1516
+ * @returns `MutationRef`
1517
+ */
1518
+ function mutationRef(dcInstance, mutationName, variables) {
1519
+ dcInstance.setInitialized();
1520
+ const ref = {
1521
+ dataConnect: dcInstance,
1522
+ name: mutationName,
1523
+ refType: MUTATION_STR,
1524
+ variables: variables
1525
+ };
1526
+ return ref;
1527
+ }
1528
+ /**
1529
+ * @internal
1530
+ */
1531
+ class MutationManager {
1532
+ constructor(_transport) {
1533
+ this._transport = _transport;
1534
+ this._inflight = [];
1535
+ }
1536
+ executeMutation(mutationRef) {
1537
+ const result = this._transport.invokeMutation(mutationRef.name, mutationRef.variables);
1538
+ const withRefPromise = result.then(res => {
1539
+ const obj = {
1540
+ ...res, // Double check that the result is result.data, not just result
1541
+ source: SOURCE_SERVER,
1542
+ ref: mutationRef,
1543
+ fetchTime: Date.now().toLocaleString()
1544
+ };
1545
+ return obj;
1546
+ });
1547
+ this._inflight.push(result);
1548
+ const removePromise = () => (this._inflight = this._inflight.filter(promise => promise !== result));
1549
+ result.then(removePromise, removePromise);
1550
+ return withRefPromise;
1551
+ }
1552
+ }
1553
+ /**
1554
+ * Execute Mutation
1555
+ * @param mutationRef mutation to execute
1556
+ * @returns `MutationRef`
1557
+ */
1558
+ function executeMutation(mutationRef) {
1559
+ return mutationRef.dataConnect._mutationManager.executeMutation(mutationRef);
1560
+ }
1561
+
1562
+ /**
1563
+ * @license
1564
+ * Copyright 2024 Google LLC
1565
+ *
1566
+ * Licensed under the Apache License, Version 2.0 (the "License");
1567
+ * you may not use this file except in compliance with the License.
1568
+ * You may obtain a copy of the License at
1569
+ *
1570
+ * http://www.apache.org/licenses/LICENSE-2.0
1571
+ *
1572
+ * Unless required by applicable law or agreed to in writing, software
1573
+ * distributed under the License is distributed on an "AS IS" BASIS,
1574
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1575
+ * See the License for the specific language governing permissions and
1576
+ * limitations under the License.
1577
+ */
1578
+ const FIREBASE_DATA_CONNECT_EMULATOR_HOST_VAR = 'FIREBASE_DATA_CONNECT_EMULATOR_HOST';
1579
+ /**
1580
+ *
1581
+ * @param fullHost
1582
+ * @returns TransportOptions
1583
+ * @internal
1584
+ */
1585
+ function parseOptions(fullHost) {
1586
+ const [protocol, hostName] = fullHost.split('://');
1587
+ const isSecure = protocol === 'https';
1588
+ const [host, portAsString] = hostName.split(':');
1589
+ const port = Number(portAsString);
1590
+ return { host, port, sslEnabled: isSecure };
1591
+ }
1592
+ /**
1593
+ * Class representing Firebase Data Connect
1594
+ */
1595
+ class DataConnect {
1596
+ // @internal
1597
+ constructor(app,
1598
+ // TODO(mtewani): Replace with _dataConnectOptions in the future
1599
+ dataConnectOptions, _authProvider, _appCheckProvider) {
1600
+ this.app = app;
1601
+ this.dataConnectOptions = dataConnectOptions;
1602
+ this._authProvider = _authProvider;
1603
+ this._appCheckProvider = _appCheckProvider;
1604
+ this.isEmulator = false;
1605
+ this._initialized = false;
1606
+ this._isUsingGeneratedSdk = false;
1607
+ this._callerSdkType = CallerSdkTypeEnum.Base;
1608
+ if (typeof process !== 'undefined' && process.env) {
1609
+ const host = process.env[FIREBASE_DATA_CONNECT_EMULATOR_HOST_VAR];
1610
+ if (host) {
1611
+ logDebug('Found custom host. Using emulator');
1612
+ this.isEmulator = true;
1613
+ this._transportOptions = parseOptions(host);
1614
+ }
1615
+ }
1616
+ }
1617
+ /**
1618
+ * @internal
1619
+ */
1620
+ getCache() {
1621
+ return this.cache;
1622
+ }
1623
+ // @internal
1624
+ _useGeneratedSdk() {
1625
+ if (!this._isUsingGeneratedSdk) {
1626
+ this._isUsingGeneratedSdk = true;
1627
+ }
1628
+ }
1629
+ _setCallerSdkType(callerSdkType) {
1630
+ this._callerSdkType = callerSdkType;
1631
+ if (this._initialized) {
1632
+ this._transport._setCallerSdkType(callerSdkType);
1633
+ }
1634
+ }
1635
+ _delete() {
1636
+ _removeServiceInstance(this.app, 'data-connect', JSON.stringify(this.getSettings()));
1637
+ return Promise.resolve();
1638
+ }
1639
+ // @internal
1640
+ getSettings() {
1641
+ const copy = JSON.parse(JSON.stringify(this.dataConnectOptions));
1642
+ delete copy.projectId;
1643
+ return copy;
1644
+ }
1645
+ /**
1646
+ * @internal
1647
+ */
1648
+ setCacheSettings(cacheSettings) {
1649
+ this._cacheSettings = cacheSettings;
1650
+ }
1651
+ // @internal
1652
+ setInitialized() {
1653
+ if (this._initialized) {
1654
+ return;
1655
+ }
1656
+ if (this._transportClass === undefined) {
1657
+ logDebug('transportClass not provided. Defaulting to RESTTransport.');
1658
+ this._transportClass = RESTTransport;
1659
+ }
1660
+ this._authTokenProvider = new FirebaseAuthProvider(this.app.name, this.app.options, this._authProvider);
1661
+ const connectorConfig = {
1662
+ connector: this.dataConnectOptions.connector,
1663
+ service: this.dataConnectOptions.service,
1664
+ location: this.dataConnectOptions.location
1665
+ };
1666
+ if (this._cacheSettings) {
1667
+ this.cache = new DataConnectCache(this._authTokenProvider, this.app.options.projectId, connectorConfig, this._transportOptions?.host || PROD_HOST, this._cacheSettings);
1668
+ }
1669
+ if (this._appCheckProvider) {
1670
+ this._appCheckTokenProvider = new AppCheckTokenProvider(this.app, this._appCheckProvider);
1671
+ }
1672
+ this._transport = new this._transportClass(this.dataConnectOptions, this.app.options.apiKey, this.app.options.appId, this._authTokenProvider, this._appCheckTokenProvider, undefined, this._isUsingGeneratedSdk, this._callerSdkType);
1673
+ if (this._transportOptions) {
1674
+ this._transport.useEmulator(this._transportOptions.host, this._transportOptions.port, this._transportOptions.sslEnabled);
1675
+ }
1676
+ this._queryManager = new QueryManager(this._transport, this, this.cache);
1677
+ this._mutationManager = new MutationManager(this._transport);
1678
+ this._initialized = true;
1679
+ }
1680
+ // @internal
1681
+ enableEmulator(transportOptions) {
1682
+ if (this._transportOptions &&
1683
+ this._initialized &&
1684
+ !areTransportOptionsEqual(this._transportOptions, transportOptions)) {
1685
+ logError('enableEmulator called after initialization');
1686
+ throw new DataConnectError(Code.ALREADY_INITIALIZED, 'DataConnect instance already initialized!');
1687
+ }
1688
+ this._transportOptions = transportOptions;
1689
+ this.isEmulator = true;
1690
+ }
1691
+ }
1692
+ /**
1693
+ * @internal
1694
+ * @param transportOptions1
1695
+ * @param transportOptions2
1696
+ * @returns
1697
+ */
1698
+ function areTransportOptionsEqual(transportOptions1, transportOptions2) {
1699
+ return (transportOptions1.host === transportOptions2.host &&
1700
+ transportOptions1.port === transportOptions2.port &&
1701
+ transportOptions1.sslEnabled === transportOptions2.sslEnabled);
1702
+ }
1703
+ /**
1704
+ * Connect to the DataConnect Emulator
1705
+ * @param dc Data Connect instance
1706
+ * @param host host of emulator server
1707
+ * @param port port of emulator server
1708
+ * @param sslEnabled use https
1709
+ */
1710
+ function connectDataConnectEmulator(dc, host, port, sslEnabled = false) {
1711
+ // Workaround to get cookies in Firebase Studio
1712
+ if (isCloudWorkstation(host)) {
1713
+ void pingServer(`https://${host}${port ? `:${port}` : ''}`);
1714
+ updateEmulatorBanner('Data Connect', true);
1715
+ }
1716
+ dc.enableEmulator({ host, port, sslEnabled });
1717
+ }
1718
+ function getDataConnect(appOrConnectorConfig, settingsOrConnectorConfig, settings) {
1719
+ let app;
1720
+ let connectorConfig;
1721
+ let realSettings;
1722
+ if ('location' in appOrConnectorConfig) {
1723
+ connectorConfig = appOrConnectorConfig;
1724
+ app = getApp();
1725
+ realSettings = settingsOrConnectorConfig;
1726
+ }
1727
+ else {
1728
+ app = appOrConnectorConfig;
1729
+ connectorConfig = settingsOrConnectorConfig;
1730
+ realSettings = settings;
1731
+ }
1732
+ if (!app || Object.keys(app).length === 0) {
1733
+ app = getApp();
1734
+ }
1735
+ // Options to store in Firebase Component Provider.
1736
+ const serializedOptions = {
1737
+ ...connectorConfig,
1738
+ projectId: app.options.projectId
1739
+ };
1740
+ // We should sort the keys before initialization.
1741
+ const sortedSerialized = Object.fromEntries(Object.entries(serializedOptions).sort());
1742
+ const provider = _getProvider(app, 'data-connect');
1743
+ const identifier = JSON.stringify(sortedSerialized);
1744
+ if (provider.isInitialized(identifier)) {
1745
+ const dcInstance = provider.getImmediate({ identifier });
1746
+ const options = provider.getOptions(identifier);
1747
+ const optionsValid = Object.keys(options).length > 0;
1748
+ if (optionsValid) {
1749
+ logDebug('Re-using cached instance');
1750
+ return dcInstance;
1751
+ }
1752
+ }
1753
+ validateDCOptions(connectorConfig);
1754
+ logDebug('Creating new DataConnect instance');
1755
+ // Initialize with options.
1756
+ const dataConnect = provider.initialize({
1757
+ instanceIdentifier: identifier,
1758
+ options: Object.fromEntries(Object.entries({
1759
+ ...sortedSerialized
1760
+ }).sort())
1761
+ });
1762
+ if (realSettings?.cacheSettings) {
1763
+ dataConnect.setCacheSettings(realSettings.cacheSettings);
1764
+ }
1765
+ return dataConnect;
1766
+ }
1767
+ /**
1768
+ *
1769
+ * @param dcOptions
1770
+ * @returns {void}
1771
+ * @internal
1772
+ */
1773
+ function validateDCOptions(dcOptions) {
1774
+ const fields = ['connector', 'location', 'service'];
1775
+ if (!dcOptions) {
1776
+ throw new DataConnectError(Code.INVALID_ARGUMENT, 'DC Option Required');
1777
+ }
1778
+ fields.forEach(field => {
1779
+ if (dcOptions[field] === null ||
1780
+ dcOptions[field] === undefined) {
1781
+ throw new DataConnectError(Code.INVALID_ARGUMENT, `${field} Required`);
1782
+ }
1783
+ });
1784
+ return true;
1785
+ }
1786
+ /**
1787
+ * Delete DataConnect instance
1788
+ * @param dataConnect DataConnect instance
1789
+ * @returns
1790
+ */
1791
+ function terminate(dataConnect) {
1792
+ return dataConnect._delete();
1793
+ // TODO(mtewani): Stop pending tasks
1794
+ }
1795
+ const StorageType = {
1796
+ MEMORY: 'MEMORY'
1797
+ };
1798
+ function makeMemoryCacheProvider() {
1799
+ return new MemoryStub();
1800
+ }
1801
+
1802
+ /**
1803
+ * @license
1804
+ * Copyright 2024 Google LLC
1805
+ *
1806
+ * Licensed under the Apache License, Version 2.0 (the "License");
1807
+ * you may not use this file except in compliance with the License.
1808
+ * You may obtain a copy of the License at
1809
+ *
1810
+ * http://www.apache.org/licenses/LICENSE-2.0
1811
+ *
1812
+ * Unless required by applicable law or agreed to in writing, software
1813
+ * distributed under the License is distributed on an "AS IS" BASIS,
1814
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1815
+ * See the License for the specific language governing permissions and
1816
+ * limitations under the License.
1817
+ */
1818
+ function registerDataConnect(variant) {
1819
+ setSDKVersion(SDK_VERSION$1);
1820
+ _registerComponent(new Component('data-connect', (container, { instanceIdentifier: connectorConfigStr, options }) => {
1821
+ const app = container.getProvider('app').getImmediate();
1822
+ const authProvider = container.getProvider('auth-internal');
1823
+ const appCheckProvider = container.getProvider('app-check-internal');
1824
+ let newOpts = options;
1825
+ if (connectorConfigStr) {
1826
+ newOpts = {
1827
+ ...JSON.parse(connectorConfigStr),
1828
+ ...newOpts
1829
+ };
1830
+ }
1831
+ if (!app.options.projectId) {
1832
+ throw new DataConnectError(Code.INVALID_ARGUMENT, 'Project ID must be provided. Did you pass in a proper projectId to initializeApp?');
1833
+ }
1834
+ return new DataConnect(app, { ...newOpts, projectId: app.options.projectId }, authProvider, appCheckProvider);
1835
+ }, "PUBLIC" /* ComponentType.PUBLIC */).setMultipleInstances(true));
1836
+ registerVersion(name, version, variant);
1837
+ // BUILD_TARGET will be replaced by values like esm, cjs, etc during the compilation
1838
+ registerVersion(name, version, 'esm2020');
1839
+ }
1840
+
1841
+ /**
1842
+ * @license
1843
+ * Copyright 2025 Google LLC
1844
+ *
1845
+ * Licensed under the Apache License, Version 2.0 (the "License");
1846
+ * you may not use this file except in compliance with the License.
1847
+ * You may obtain a copy of the License at
1848
+ *
1849
+ * http://www.apache.org/licenses/LICENSE-2.0
1850
+ *
1851
+ * Unless required by applicable law or agreed to in writing, software
1852
+ * distributed under the License is distributed on an "AS IS" BASIS,
1853
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1854
+ * See the License for the specific language governing permissions and
1855
+ * limitations under the License.
1856
+ */
1857
+ const QueryFetchPolicy = {
1858
+ PREFER_CACHE: 'PREFER_CACHE',
1859
+ CACHE_ONLY: 'CACHE_ONLY',
1860
+ SERVER_ONLY: 'SERVER_ONLY'
1861
+ };
1862
+
1863
+ /**
1864
+ * @license
1865
+ * Copyright 2024 Google LLC
1866
+ *
1867
+ * Licensed under the Apache License, Version 2.0 (the "License");
1868
+ * you may not use this file except in compliance with the License.
1869
+ * You may obtain a copy of the License at
1870
+ *
1871
+ * http://www.apache.org/licenses/LICENSE-2.0
1872
+ *
1873
+ * Unless required by applicable law or agreed to in writing, software
1874
+ * distributed under the License is distributed on an "AS IS" BASIS,
1875
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1876
+ * See the License for the specific language governing permissions and
1877
+ * limitations under the License.
1878
+ */
1879
+ /**
1880
+ * Execute Query
1881
+ * @param queryRef query to execute.
1882
+ * @returns `QueryPromise`
1883
+ */
1884
+ function executeQuery(queryRef, options) {
1885
+ if (queryRef.refType !== QUERY_STR) {
1886
+ return Promise.reject(new DataConnectError(Code.INVALID_ARGUMENT, `ExecuteQuery can only execute query operations`));
1887
+ }
1888
+ const queryManager = queryRef.dataConnect._queryManager;
1889
+ const fetchPolicy = options?.fetchPolicy ?? QueryFetchPolicy.PREFER_CACHE;
1890
+ switch (fetchPolicy) {
1891
+ case QueryFetchPolicy.SERVER_ONLY:
1892
+ return queryManager.fetchServerResults(queryRef);
1893
+ case QueryFetchPolicy.CACHE_ONLY:
1894
+ return queryManager.fetchCacheResults(queryRef, true);
1895
+ case QueryFetchPolicy.PREFER_CACHE:
1896
+ return queryManager.preferCacheResults(queryRef, false);
1897
+ default:
1898
+ throw new DataConnectError(Code.INVALID_ARGUMENT, `Invalid fetch policy: ${fetchPolicy}`);
1899
+ }
1900
+ }
1901
+ /**
1902
+ * Execute Query
1903
+ * @param dcInstance Data Connect instance to use.
1904
+ * @param queryName Query to execute
1905
+ * @param variables Variables to execute with
1906
+ * @param initialCache initial cache to use for client hydration
1907
+ * @returns `QueryRef`
1908
+ */
1909
+ function queryRef(dcInstance, queryName, variables, initialCache) {
1910
+ dcInstance.setInitialized();
1911
+ if (initialCache !== undefined) {
1912
+ dcInstance._queryManager.updateSSR(initialCache);
1913
+ }
1914
+ return {
1915
+ dataConnect: dcInstance,
1916
+ refType: QUERY_STR,
1917
+ name: queryName,
1918
+ variables: variables
1919
+ };
1920
+ }
1921
+ /**
1922
+ * Converts serialized ref to query ref
1923
+ * @param serializedRef ref to convert to `QueryRef`
1924
+ * @returns `QueryRef`
1925
+ */
1926
+ function toQueryRef(serializedRef) {
1927
+ const { refInfo: { name, variables, connectorConfig } } = serializedRef;
1928
+ return queryRef(getDataConnect(connectorConfig), name, variables);
1929
+ }
1930
+
1931
+ /**
1932
+ * @license
1933
+ * Copyright 2024 Google LLC
1934
+ *
1935
+ * Licensed under the Apache License, Version 2.0 (the "License");
1936
+ * you may not use this file except in compliance with the License.
1937
+ * You may obtain a copy of the License at
1938
+ *
1939
+ * http://www.apache.org/licenses/LICENSE-2.0
1940
+ *
1941
+ * Unless required by applicable law or agreed to in writing, software
1942
+ * distributed under the License is distributed on an "AS IS" BASIS,
1943
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1944
+ * See the License for the specific language governing permissions and
1945
+ * limitations under the License.
1946
+ */
1947
+ /**
1948
+ * The generated SDK will allow the user to pass in either the variable or the data connect instance with the variable,
1949
+ * and this function validates the variables and returns back the DataConnect instance and variables based on the arguments passed in.
1950
+ * @param connectorConfig
1951
+ * @param dcOrVars
1952
+ * @param vars
1953
+ * @param validateVars
1954
+ * @returns {DataConnect} and {Variables} instance
1955
+ * @internal
1956
+ */
1957
+ function validateArgs(connectorConfig, dcOrVars, vars, validateVars) {
1958
+ let dcInstance;
1959
+ let realVars;
1960
+ if (dcOrVars && 'enableEmulator' in dcOrVars) {
1961
+ dcInstance = dcOrVars;
1962
+ realVars = vars;
1963
+ }
1964
+ else {
1965
+ dcInstance = getDataConnect(connectorConfig);
1966
+ realVars = dcOrVars;
1967
+ }
1968
+ if (!dcInstance || (!realVars && validateVars)) {
1969
+ throw new DataConnectError(Code.INVALID_ARGUMENT, 'Variables required.');
1970
+ }
1971
+ return { dc: dcInstance, vars: realVars };
1972
+ }
1973
+
1974
+ /**
1975
+ * @license
1976
+ * Copyright 2025 Google LLC
1977
+ *
1978
+ * Licensed under the Apache License, Version 2.0 (the "License");
1979
+ * you may not use this file except in compliance with the License.
1980
+ * You may obtain a copy of the License at
1981
+ *
1982
+ * http://www.apache.org/licenses/LICENSE-2.0
1983
+ *
1984
+ * Unless required by applicable law or agreed to in writing, software
1985
+ * distributed under the License is distributed on an "AS IS" BASIS,
1986
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1987
+ * See the License for the specific language governing permissions and
1988
+ * limitations under the License.
1989
+ */
1990
+ /**
1991
+ * Subscribe to a `QueryRef`
1992
+ * @param queryRefOrSerializedResult query ref or serialized result.
1993
+ * @param observerOrOnNext observer object or next function.
1994
+ * @param onError Callback to call when error gets thrown.
1995
+ * @param onComplete Called when subscription completes.
1996
+ * @returns `SubscriptionOptions`
1997
+ */
1998
+ function subscribe(queryRefOrSerializedResult, observerOrOnNext, onError, onComplete) {
1999
+ let ref;
2000
+ let initialCache;
2001
+ if ('refInfo' in queryRefOrSerializedResult) {
2002
+ const serializedRef = queryRefOrSerializedResult;
2003
+ const { data, source, fetchTime } = serializedRef;
2004
+ ref = toQueryRef(serializedRef);
2005
+ initialCache = {
2006
+ data,
2007
+ source,
2008
+ fetchTime,
2009
+ ref,
2010
+ toJSON: getRefSerializer(ref, data, source, fetchTime)
2011
+ };
2012
+ }
2013
+ else {
2014
+ ref = queryRefOrSerializedResult;
2015
+ }
2016
+ let onResult = undefined;
2017
+ if (typeof observerOrOnNext === 'function') {
2018
+ onResult = observerOrOnNext;
2019
+ }
2020
+ else {
2021
+ onResult = observerOrOnNext.onNext;
2022
+ onError = observerOrOnNext.onErr;
2023
+ onComplete = observerOrOnNext.onComplete;
2024
+ }
2025
+ if (!onResult) {
2026
+ throw new DataConnectError(Code.INVALID_ARGUMENT, 'Must provide onNext');
2027
+ }
2028
+ return ref.dataConnect._queryManager.addSubscription(ref, onResult, onComplete, onError, initialCache);
2029
+ }
2030
+
2031
+ /**
2032
+ * Firebase Data Connect
2033
+ *
2034
+ * @packageDocumentation
2035
+ */
2036
+ registerDataConnect();
2037
+
2038
+ export { CallerSdkTypeEnum, Code, DataConnect, DataConnectError, DataConnectOperationError, MUTATION_STR, MutationManager, QUERY_STR, QueryFetchPolicy, SOURCE_CACHE, SOURCE_SERVER, StorageType, areTransportOptionsEqual, connectDataConnectEmulator, executeMutation, executeQuery, getDataConnect, makeMemoryCacheProvider, mutationRef, parseOptions, queryRef, setLogLevel, subscribe, terminate, toQueryRef, validateArgs, validateDCOptions };
2039
+ //# sourceMappingURL=index.esm.js.map