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