@firebase/data-connect 0.3.12 → 0.4.0-canary.78384d32c
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.
- package/dist/index.cjs.js +1012 -210
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +1011 -212
- package/dist/index.esm.js.map +1 -1
- package/dist/index.node.cjs.js +960 -158
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/internal.d.ts +311 -25
- package/dist/node-esm/index.node.esm.js +959 -160
- package/dist/node-esm/index.node.esm.js.map +1 -1
- package/dist/node-esm/src/api/DataConnect.d.ts +44 -3
- package/dist/node-esm/src/api/Reference.d.ts +2 -0
- package/dist/node-esm/src/api/index.d.ts +2 -1
- package/dist/node-esm/src/api/query.d.ts +9 -26
- package/dist/node-esm/src/api.browser.d.ts +2 -18
- package/dist/node-esm/src/api.node.d.ts +2 -1
- package/dist/node-esm/src/cache/Cache.d.ts +53 -0
- package/dist/node-esm/src/cache/CacheProvider.d.ts +25 -0
- package/dist/node-esm/src/cache/EntityDataObject.d.ts +37 -0
- package/dist/node-esm/src/cache/EntityNode.d.ts +56 -0
- package/dist/node-esm/src/cache/ImpactedQueryRefsAccumulator.d.ts +23 -0
- package/dist/node-esm/src/cache/InMemoryCacheProvider.d.ts +30 -0
- package/dist/node-esm/src/cache/ResultTree.d.ts +42 -0
- package/dist/node-esm/src/cache/ResultTreeProcessor.d.ts +40 -0
- package/dist/node-esm/src/cache/cacheUtils.d.ts +20 -0
- package/dist/node-esm/src/core/FirebaseAuthProvider.d.ts +3 -1
- package/dist/node-esm/src/core/query/QueryManager.d.ts +47 -0
- package/dist/node-esm/src/core/query/queryOptions.d.ts +25 -0
- package/dist/node-esm/src/core/query/subscribe.d.ts +67 -0
- package/dist/node-esm/src/network/fetch.d.ts +2 -5
- package/dist/node-esm/src/network/index.d.ts +1 -1
- package/dist/node-esm/src/network/transport/index.d.ts +37 -8
- package/dist/node-esm/src/network/transport/rest.d.ts +5 -17
- package/dist/node-esm/src/util/encoder.d.ts +4 -1
- package/dist/node-esm/src/util/url.d.ts +1 -0
- package/dist/private.d.ts +278 -17
- package/dist/public.d.ts +77 -3
- package/dist/src/api/DataConnect.d.ts +44 -3
- package/dist/src/api/Reference.d.ts +2 -0
- package/dist/src/api/index.d.ts +2 -1
- package/dist/src/api/query.d.ts +9 -26
- package/dist/src/api.browser.d.ts +2 -18
- package/dist/src/api.node.d.ts +2 -1
- package/dist/src/cache/Cache.d.ts +53 -0
- package/dist/src/cache/CacheProvider.d.ts +25 -0
- package/dist/src/cache/EntityDataObject.d.ts +37 -0
- package/dist/src/cache/EntityNode.d.ts +56 -0
- package/dist/src/cache/ImpactedQueryRefsAccumulator.d.ts +23 -0
- package/dist/src/cache/InMemoryCacheProvider.d.ts +30 -0
- package/dist/src/cache/ResultTree.d.ts +42 -0
- package/dist/src/cache/ResultTreeProcessor.d.ts +40 -0
- package/dist/src/cache/cacheUtils.d.ts +20 -0
- package/dist/src/core/FirebaseAuthProvider.d.ts +3 -1
- package/dist/src/core/query/QueryManager.d.ts +47 -0
- package/dist/src/core/query/queryOptions.d.ts +25 -0
- package/dist/src/core/query/subscribe.d.ts +67 -0
- package/dist/src/network/fetch.d.ts +2 -5
- package/dist/src/network/index.d.ts +1 -1
- package/dist/src/network/transport/index.d.ts +37 -8
- package/dist/src/network/transport/rest.d.ts +5 -17
- package/dist/src/util/encoder.d.ts +4 -1
- package/dist/src/util/url.d.ts +1 -0
- package/package.json +7 -7
- package/dist/node-esm/src/core/QueryManager.d.ts +0 -45
- package/dist/src/core/QueryManager.d.ts +0 -45
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FirebaseError, isCloudWorkstation, pingServer, updateEmulatorBanner } from '@firebase/util';
|
|
1
|
+
import { FirebaseError, isCloudWorkstation, generateSHA256Hash, pingServer, updateEmulatorBanner } from '@firebase/util';
|
|
2
2
|
import { Logger } from '@firebase/logger';
|
|
3
3
|
import { _isFirebaseServerApp, _removeServiceInstance, getApp, _getProvider, _registerComponent, registerVersion, SDK_VERSION as SDK_VERSION$1 } from '@firebase/app';
|
|
4
4
|
import { Component } from '@firebase/component';
|
|
@@ -164,7 +164,7 @@ function getGoogApiClientValue(_isUsingGen, _callerSdkType) {
|
|
|
164
164
|
}
|
|
165
165
|
return str;
|
|
166
166
|
}
|
|
167
|
-
function dcFetch(url, body, { signal }, appId, accessToken, appCheckToken, _isUsingGen, _callerSdkType, _isUsingEmulator) {
|
|
167
|
+
async function dcFetch(url, body, { signal }, appId, accessToken, appCheckToken, _isUsingGen, _callerSdkType, _isUsingEmulator) {
|
|
168
168
|
if (!connectFetch) {
|
|
169
169
|
throw new DataConnectError(Code.OTHER, 'No Fetch Implementation detected!');
|
|
170
170
|
}
|
|
@@ -191,41 +191,44 @@ function dcFetch(url, body, { signal }, appId, accessToken, appCheckToken, _isUs
|
|
|
191
191
|
if (isCloudWorkstation(url) && _isUsingEmulator) {
|
|
192
192
|
fetchOptions.credentials = 'include';
|
|
193
193
|
}
|
|
194
|
-
|
|
195
|
-
|
|
194
|
+
let response;
|
|
195
|
+
try {
|
|
196
|
+
response = await connectFetch(url, fetchOptions);
|
|
197
|
+
}
|
|
198
|
+
catch (err) {
|
|
196
199
|
throw new DataConnectError(Code.OTHER, 'Failed to fetch: ' + JSON.stringify(err));
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
throw new DataConnectError(Code.UNAUTHORIZED, message);
|
|
211
|
-
}
|
|
212
|
-
throw new DataConnectError(Code.OTHER, message);
|
|
213
|
-
}
|
|
214
|
-
return jsonResponse;
|
|
215
|
-
})
|
|
216
|
-
.then(res => {
|
|
217
|
-
if (res.errors && res.errors.length) {
|
|
218
|
-
const stringified = JSON.stringify(res.errors);
|
|
219
|
-
const response = {
|
|
220
|
-
errors: res.errors,
|
|
221
|
-
data: res.data
|
|
222
|
-
};
|
|
223
|
-
throw new DataConnectOperationError('DataConnect error while performing request: ' + stringified, response);
|
|
200
|
+
}
|
|
201
|
+
let jsonResponse;
|
|
202
|
+
try {
|
|
203
|
+
jsonResponse = await response.json();
|
|
204
|
+
}
|
|
205
|
+
catch (e) {
|
|
206
|
+
throw new DataConnectError(Code.OTHER, JSON.stringify(e));
|
|
207
|
+
}
|
|
208
|
+
const message = getErrorMessage(jsonResponse);
|
|
209
|
+
if (response.status >= 400) {
|
|
210
|
+
logError('Error while performing request: ' + JSON.stringify(jsonResponse));
|
|
211
|
+
if (response.status === 401) {
|
|
212
|
+
throw new DataConnectError(Code.UNAUTHORIZED, message);
|
|
224
213
|
}
|
|
225
|
-
|
|
226
|
-
}
|
|
214
|
+
throw new DataConnectError(Code.OTHER, message);
|
|
215
|
+
}
|
|
216
|
+
if (jsonResponse.errors && jsonResponse.errors.length) {
|
|
217
|
+
const stringified = JSON.stringify(jsonResponse.errors);
|
|
218
|
+
const failureResponse = {
|
|
219
|
+
errors: jsonResponse.errors,
|
|
220
|
+
data: jsonResponse.data
|
|
221
|
+
};
|
|
222
|
+
throw new DataConnectOperationError('DataConnect error while performing request: ' + stringified, failureResponse);
|
|
223
|
+
}
|
|
224
|
+
if (!jsonResponse.extensions) {
|
|
225
|
+
jsonResponse.extensions = {
|
|
226
|
+
dataConnect: []
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
return jsonResponse;
|
|
227
230
|
}
|
|
228
|
-
function
|
|
231
|
+
function getErrorMessage(obj) {
|
|
229
232
|
if ('message' in obj && obj.message) {
|
|
230
233
|
return obj.message;
|
|
231
234
|
}
|
|
@@ -233,7 +236,521 @@ function getMessage(obj) {
|
|
|
233
236
|
}
|
|
234
237
|
|
|
235
238
|
const name = "@firebase/data-connect";
|
|
236
|
-
const version = "0.
|
|
239
|
+
const version = "0.4.0-canary.78384d32c";
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* @license
|
|
243
|
+
* Copyright 2025 Google LLC
|
|
244
|
+
*
|
|
245
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
246
|
+
* you may not use this file except in compliance with the License.
|
|
247
|
+
* You may obtain a copy of the License at
|
|
248
|
+
*
|
|
249
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
250
|
+
*
|
|
251
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
252
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
253
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
254
|
+
* See the License for the specific language governing permissions and
|
|
255
|
+
* limitations under the License.
|
|
256
|
+
*/
|
|
257
|
+
class EntityDataObject {
|
|
258
|
+
getServerValue(key) {
|
|
259
|
+
return this.serverValues[key];
|
|
260
|
+
}
|
|
261
|
+
constructor(globalID) {
|
|
262
|
+
this.globalID = globalID;
|
|
263
|
+
this.serverValues = {};
|
|
264
|
+
this.referencedFrom = new Set();
|
|
265
|
+
}
|
|
266
|
+
getServerValues() {
|
|
267
|
+
return this.serverValues;
|
|
268
|
+
}
|
|
269
|
+
toJSON() {
|
|
270
|
+
return {
|
|
271
|
+
globalID: this.globalID,
|
|
272
|
+
map: this.serverValues,
|
|
273
|
+
referencedFrom: Array.from(this.referencedFrom)
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
static fromJSON(json) {
|
|
277
|
+
const edo = new EntityDataObject(json.globalID);
|
|
278
|
+
edo.serverValues = json.map;
|
|
279
|
+
edo.referencedFrom = new Set(json.referencedFrom);
|
|
280
|
+
return edo;
|
|
281
|
+
}
|
|
282
|
+
updateServerValue(key, value, requestedFrom) {
|
|
283
|
+
this.serverValues[key] = value;
|
|
284
|
+
this.referencedFrom.add(requestedFrom);
|
|
285
|
+
return Array.from(this.referencedFrom);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* @license
|
|
291
|
+
* Copyright 2025 Google LLC
|
|
292
|
+
*
|
|
293
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
294
|
+
* you may not use this file except in compliance with the License.
|
|
295
|
+
* You may obtain a copy of the License at
|
|
296
|
+
*
|
|
297
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
298
|
+
*
|
|
299
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
300
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
301
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
302
|
+
* See the License for the specific language governing permissions and
|
|
303
|
+
* limitations under the License.
|
|
304
|
+
*/
|
|
305
|
+
class InMemoryCacheProvider {
|
|
306
|
+
constructor(_keyId) {
|
|
307
|
+
this._keyId = _keyId;
|
|
308
|
+
this.edos = new Map();
|
|
309
|
+
this.resultTrees = new Map();
|
|
310
|
+
}
|
|
311
|
+
async setResultTree(queryId, rt) {
|
|
312
|
+
this.resultTrees.set(queryId, rt);
|
|
313
|
+
}
|
|
314
|
+
async getResultTree(queryId) {
|
|
315
|
+
return this.resultTrees.get(queryId);
|
|
316
|
+
}
|
|
317
|
+
async updateEntityData(entityData) {
|
|
318
|
+
this.edos.set(entityData.globalID, entityData);
|
|
319
|
+
}
|
|
320
|
+
async getEntityData(globalId) {
|
|
321
|
+
if (!this.edos.has(globalId)) {
|
|
322
|
+
this.edos.set(globalId, new EntityDataObject(globalId));
|
|
323
|
+
}
|
|
324
|
+
// Because of the above, we can guarantee that there will be an EDO at the globalId.
|
|
325
|
+
return this.edos.get(globalId);
|
|
326
|
+
}
|
|
327
|
+
close() {
|
|
328
|
+
// No-op
|
|
329
|
+
return Promise.resolve();
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* @license
|
|
335
|
+
* Copyright 2025 Google LLC
|
|
336
|
+
*
|
|
337
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
338
|
+
* you may not use this file except in compliance with the License.
|
|
339
|
+
* You may obtain a copy of the License at
|
|
340
|
+
*
|
|
341
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
342
|
+
*
|
|
343
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
344
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
345
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
346
|
+
* See the License for the specific language governing permissions and
|
|
347
|
+
* limitations under the License.
|
|
348
|
+
*/
|
|
349
|
+
const GLOBAL_ID_KEY = '_id';
|
|
350
|
+
const OBJECT_LISTS_KEY = '_objectLists';
|
|
351
|
+
const REFERENCES_KEY = '_references';
|
|
352
|
+
const SCALARS_KEY = '_scalars';
|
|
353
|
+
const ENTITY_DATA_KEYS_KEY = '_entity_data_keys';
|
|
354
|
+
class EntityNode {
|
|
355
|
+
constructor() {
|
|
356
|
+
this.scalars = {};
|
|
357
|
+
this.references = {};
|
|
358
|
+
this.objectLists = {};
|
|
359
|
+
this.entityDataKeys = new Set();
|
|
360
|
+
}
|
|
361
|
+
async loadData(queryId, values, entityIds, acc, cacheProvider) {
|
|
362
|
+
if (values === undefined) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
if (typeof values !== 'object' || Array.isArray(values)) {
|
|
366
|
+
throw new DataConnectError(Code.INVALID_ARGUMENT, 'EntityNode initialized with non-object value');
|
|
367
|
+
}
|
|
368
|
+
if (values === null) {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
if (typeof values === 'object' &&
|
|
372
|
+
entityIds &&
|
|
373
|
+
entityIds[GLOBAL_ID_KEY] &&
|
|
374
|
+
typeof entityIds[GLOBAL_ID_KEY] === 'string') {
|
|
375
|
+
this.globalId = entityIds[GLOBAL_ID_KEY];
|
|
376
|
+
this.entityData = await cacheProvider.getEntityData(this.globalId);
|
|
377
|
+
}
|
|
378
|
+
for (const key in values) {
|
|
379
|
+
if (values.hasOwnProperty(key)) {
|
|
380
|
+
if (typeof values[key] === 'object') {
|
|
381
|
+
if (Array.isArray(values[key])) {
|
|
382
|
+
const ids = entityIds && entityIds[key];
|
|
383
|
+
const objArray = [];
|
|
384
|
+
const scalarArray = [];
|
|
385
|
+
for (const [index, value] of values[key].entries()) {
|
|
386
|
+
if (typeof value === 'object') {
|
|
387
|
+
if (Array.isArray(value)) ;
|
|
388
|
+
else {
|
|
389
|
+
const entityNode = new EntityNode();
|
|
390
|
+
await entityNode.loadData(queryId, value, ids && ids[index], acc, cacheProvider);
|
|
391
|
+
objArray.push(entityNode);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
else {
|
|
395
|
+
scalarArray.push(value);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
if (scalarArray.length > 0 && objArray.length > 0) {
|
|
399
|
+
this.scalars[key] = values[key];
|
|
400
|
+
}
|
|
401
|
+
else if (scalarArray.length > 0) {
|
|
402
|
+
if (this.entityData) {
|
|
403
|
+
const impactedRefs = this.entityData.updateServerValue(key, scalarArray, queryId);
|
|
404
|
+
this.entityDataKeys.add(key);
|
|
405
|
+
acc.add(impactedRefs);
|
|
406
|
+
}
|
|
407
|
+
else {
|
|
408
|
+
this.scalars[key] = scalarArray;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
else if (objArray.length > 0) {
|
|
412
|
+
this.objectLists[key] = objArray;
|
|
413
|
+
}
|
|
414
|
+
else {
|
|
415
|
+
this.scalars[key] = [];
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
if (values[key] === null) {
|
|
420
|
+
this.scalars[key] = null;
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
const entityNode = new EntityNode();
|
|
424
|
+
// TODO: Load Data might need to be pushed into ResultTreeProcessor instead.
|
|
425
|
+
await entityNode.loadData(queryId, values[key], entityIds && entityIds[key], acc, cacheProvider);
|
|
426
|
+
this.references[key] = entityNode;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
else {
|
|
430
|
+
if (this.entityData) {
|
|
431
|
+
const impactedRefs = this.entityData.updateServerValue(key, values[key], queryId);
|
|
432
|
+
this.entityDataKeys.add(key);
|
|
433
|
+
acc.add(impactedRefs);
|
|
434
|
+
}
|
|
435
|
+
else {
|
|
436
|
+
this.scalars[key] = values[key];
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
if (this.entityData) {
|
|
442
|
+
await cacheProvider.updateEntityData(this.entityData);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
toJSON(mode) {
|
|
446
|
+
const resultObject = {};
|
|
447
|
+
if (mode === EncodingMode.hydrated) {
|
|
448
|
+
if (this.entityData) {
|
|
449
|
+
for (const key of this.entityDataKeys) {
|
|
450
|
+
resultObject[key] = this.entityData.getServerValue(key);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
if (this.scalars) {
|
|
454
|
+
Object.assign(resultObject, this.scalars);
|
|
455
|
+
}
|
|
456
|
+
if (this.references) {
|
|
457
|
+
for (const key in this.references) {
|
|
458
|
+
if (this.references.hasOwnProperty(key)) {
|
|
459
|
+
resultObject[key] = this.references[key].toJSON(mode);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
if (this.objectLists) {
|
|
464
|
+
for (const key in this.objectLists) {
|
|
465
|
+
if (this.objectLists.hasOwnProperty(key)) {
|
|
466
|
+
resultObject[key] = this.objectLists[key].map(obj => obj.toJSON(mode));
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
return resultObject;
|
|
471
|
+
}
|
|
472
|
+
else {
|
|
473
|
+
// Get JSON representation of dehydrated list
|
|
474
|
+
if (this.entityData) {
|
|
475
|
+
resultObject[GLOBAL_ID_KEY] = this.entityData.globalID;
|
|
476
|
+
}
|
|
477
|
+
resultObject[ENTITY_DATA_KEYS_KEY] = Array.from(this.entityDataKeys);
|
|
478
|
+
if (this.scalars) {
|
|
479
|
+
resultObject[SCALARS_KEY] = this.scalars;
|
|
480
|
+
}
|
|
481
|
+
if (this.references) {
|
|
482
|
+
const references = {};
|
|
483
|
+
for (const key in this.references) {
|
|
484
|
+
if (this.references.hasOwnProperty(key)) {
|
|
485
|
+
references[key] = this.references[key].toJSON(mode);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
resultObject[REFERENCES_KEY] = references;
|
|
489
|
+
}
|
|
490
|
+
if (this.objectLists) {
|
|
491
|
+
const objectLists = {};
|
|
492
|
+
for (const key in this.objectLists) {
|
|
493
|
+
if (this.objectLists.hasOwnProperty(key)) {
|
|
494
|
+
objectLists[key] = this.objectLists[key].map(obj => obj.toJSON(mode));
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
resultObject[OBJECT_LISTS_KEY] = objectLists;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
return resultObject;
|
|
501
|
+
}
|
|
502
|
+
static fromJson(obj) {
|
|
503
|
+
const sdo = new EntityNode();
|
|
504
|
+
if (obj.backingData) {
|
|
505
|
+
sdo.entityData = EntityDataObject.fromJSON(obj.backingData);
|
|
506
|
+
}
|
|
507
|
+
sdo.globalId = obj.globalID;
|
|
508
|
+
sdo.scalars = obj.scalars;
|
|
509
|
+
if (obj.references) {
|
|
510
|
+
const references = {};
|
|
511
|
+
for (const key in obj.references) {
|
|
512
|
+
if (obj.references.hasOwnProperty(key)) {
|
|
513
|
+
references[key] = EntityNode.fromJson(obj.references[key]);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
sdo.references = references;
|
|
517
|
+
}
|
|
518
|
+
if (obj.objectLists) {
|
|
519
|
+
const objectLists = {};
|
|
520
|
+
for (const key in obj.objectLists) {
|
|
521
|
+
if (obj.objectLists.hasOwnProperty(key)) {
|
|
522
|
+
objectLists[key] = obj.objectLists[key].map(obj => EntityNode.fromJson(obj));
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
sdo.objectLists = objectLists;
|
|
526
|
+
}
|
|
527
|
+
return sdo;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
// Helpful for storing in persistent cache, which is not available yet.
|
|
531
|
+
var EncodingMode;
|
|
532
|
+
(function (EncodingMode) {
|
|
533
|
+
EncodingMode[EncodingMode["hydrated"] = 0] = "hydrated";
|
|
534
|
+
EncodingMode[EncodingMode["dehydrated"] = 1] = "dehydrated";
|
|
535
|
+
})(EncodingMode || (EncodingMode = {}));
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* @license
|
|
539
|
+
* Copyright 2025 Google LLC
|
|
540
|
+
*
|
|
541
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
542
|
+
* you may not use this file except in compliance with the License.
|
|
543
|
+
* You may obtain a copy of the License at
|
|
544
|
+
*
|
|
545
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
546
|
+
*
|
|
547
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
548
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
549
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
550
|
+
* See the License for the specific language governing permissions and
|
|
551
|
+
* limitations under the License.
|
|
552
|
+
*/
|
|
553
|
+
class ResultTree {
|
|
554
|
+
/**
|
|
555
|
+
* Create a {@link ResultTree} from a dehydrated JSON object.
|
|
556
|
+
* @param value The dehydrated JSON object.
|
|
557
|
+
* @returns The {@link ResultTree}.
|
|
558
|
+
*/
|
|
559
|
+
static fromJson(value) {
|
|
560
|
+
return new ResultTree(EntityNode.fromJson(value.rootStub), value.maxAge, value.cachedAt, value.lastAccessed);
|
|
561
|
+
}
|
|
562
|
+
constructor(rootStub, maxAge = 0, cachedAt, _lastAccessed) {
|
|
563
|
+
this.rootStub = rootStub;
|
|
564
|
+
this.maxAge = maxAge;
|
|
565
|
+
this.cachedAt = cachedAt;
|
|
566
|
+
this._lastAccessed = _lastAccessed;
|
|
567
|
+
}
|
|
568
|
+
isStale() {
|
|
569
|
+
return (Date.now() - new Date(this.cachedAt.getTime()).getTime() >
|
|
570
|
+
this.maxAge * 1000);
|
|
571
|
+
}
|
|
572
|
+
updateMaxAge(maxAgeInSeconds) {
|
|
573
|
+
this.maxAge = maxAgeInSeconds;
|
|
574
|
+
}
|
|
575
|
+
updateAccessed() {
|
|
576
|
+
this._lastAccessed = new Date();
|
|
577
|
+
}
|
|
578
|
+
get lastAccessed() {
|
|
579
|
+
return this._lastAccessed;
|
|
580
|
+
}
|
|
581
|
+
getRootStub() {
|
|
582
|
+
return this.rootStub;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* @license
|
|
588
|
+
* Copyright 2025 Google LLC
|
|
589
|
+
*
|
|
590
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
591
|
+
* you may not use this file except in compliance with the License.
|
|
592
|
+
* You may obtain a copy of the License at
|
|
593
|
+
*
|
|
594
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
595
|
+
*
|
|
596
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
597
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
598
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
599
|
+
* See the License for the specific language governing permissions and
|
|
600
|
+
* limitations under the License.
|
|
601
|
+
*/
|
|
602
|
+
class ImpactedQueryRefsAccumulator {
|
|
603
|
+
constructor(queryId) {
|
|
604
|
+
this.queryId = queryId;
|
|
605
|
+
this.impacted = new Set();
|
|
606
|
+
}
|
|
607
|
+
add(impacted) {
|
|
608
|
+
impacted
|
|
609
|
+
.filter(ref => ref !== this.queryId)
|
|
610
|
+
.forEach(ref => this.impacted.add(ref));
|
|
611
|
+
}
|
|
612
|
+
consumeEvents() {
|
|
613
|
+
const events = Array.from(this.impacted);
|
|
614
|
+
this.impacted.clear();
|
|
615
|
+
return events;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* @license
|
|
621
|
+
* Copyright 2025 Google LLC
|
|
622
|
+
*
|
|
623
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
624
|
+
* you may not use this file except in compliance with the License.
|
|
625
|
+
* You may obtain a copy of the License at
|
|
626
|
+
*
|
|
627
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
628
|
+
*
|
|
629
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
630
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
631
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
632
|
+
* See the License for the specific language governing permissions and
|
|
633
|
+
* limitations under the License.
|
|
634
|
+
*/
|
|
635
|
+
class ResultTreeProcessor {
|
|
636
|
+
/**
|
|
637
|
+
* Hydrate the EntityNode into a JSON object so that it can be returned to the user.
|
|
638
|
+
* @param rootStubObject
|
|
639
|
+
* @returns {string}
|
|
640
|
+
*/
|
|
641
|
+
hydrateResults(rootStubObject) {
|
|
642
|
+
return rootStubObject.toJSON(EncodingMode.hydrated);
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Dehydrate results so that they can be stored in the cache.
|
|
646
|
+
* @param json
|
|
647
|
+
* @param entityIds
|
|
648
|
+
* @param cacheProvider
|
|
649
|
+
* @param queryId
|
|
650
|
+
* @returns {Promise<DehydratedResults>}
|
|
651
|
+
*/
|
|
652
|
+
async dehydrateResults(json, entityIds, cacheProvider, queryId) {
|
|
653
|
+
const acc = new ImpactedQueryRefsAccumulator(queryId);
|
|
654
|
+
const entityNode = new EntityNode();
|
|
655
|
+
await entityNode.loadData(queryId, json, entityIds, acc, cacheProvider);
|
|
656
|
+
return {
|
|
657
|
+
entityNode,
|
|
658
|
+
impacted: acc.consumeEvents()
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* @license
|
|
665
|
+
* Copyright 2025 Google LLC
|
|
666
|
+
*
|
|
667
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
668
|
+
* you may not use this file except in compliance with the License.
|
|
669
|
+
* You may obtain a copy of the License at
|
|
670
|
+
*
|
|
671
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
672
|
+
*
|
|
673
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
674
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
675
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
676
|
+
* See the License for the specific language governing permissions and
|
|
677
|
+
* limitations under the License.
|
|
678
|
+
*/
|
|
679
|
+
class DataConnectCache {
|
|
680
|
+
constructor(authProvider, projectId, connectorConfig, host, cacheSettings) {
|
|
681
|
+
this.authProvider = authProvider;
|
|
682
|
+
this.projectId = projectId;
|
|
683
|
+
this.connectorConfig = connectorConfig;
|
|
684
|
+
this.host = host;
|
|
685
|
+
this.cacheSettings = cacheSettings;
|
|
686
|
+
this.cacheProvider = null;
|
|
687
|
+
this.uid = null;
|
|
688
|
+
this.authProvider.addTokenChangeListener(async (_) => {
|
|
689
|
+
const newUid = this.authProvider.getAuth().getUid();
|
|
690
|
+
// We should only close if the token changes and so does the new UID
|
|
691
|
+
if (this.uid !== newUid) {
|
|
692
|
+
this.cacheProvider?.close();
|
|
693
|
+
this.uid = newUid;
|
|
694
|
+
const identifier = await this.getIdentifier(this.uid);
|
|
695
|
+
this.cacheProvider = this.initializeNewProviders(identifier);
|
|
696
|
+
}
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
async initialize() {
|
|
700
|
+
if (!this.cacheProvider) {
|
|
701
|
+
const identifier = await this.getIdentifier(this.uid);
|
|
702
|
+
this.cacheProvider = this.initializeNewProviders(identifier);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
async getIdentifier(uid) {
|
|
706
|
+
const identifier = `${'memory' // TODO: replace this with indexeddb when persistence is available.
|
|
707
|
+
}-${this.projectId}-${this.connectorConfig.service}-${this.connectorConfig.connector}-${this.connectorConfig.location}-${uid}-${this.host}`;
|
|
708
|
+
const sha256 = await generateSHA256Hash(identifier);
|
|
709
|
+
return sha256;
|
|
710
|
+
}
|
|
711
|
+
initializeNewProviders(identifier) {
|
|
712
|
+
return this.cacheSettings.cacheProvider.initialize(identifier);
|
|
713
|
+
}
|
|
714
|
+
async containsResultTree(queryId) {
|
|
715
|
+
await this.initialize();
|
|
716
|
+
const resultTree = await this.cacheProvider.getResultTree(queryId);
|
|
717
|
+
return resultTree !== undefined;
|
|
718
|
+
}
|
|
719
|
+
async getResultTree(queryId) {
|
|
720
|
+
await this.initialize();
|
|
721
|
+
return this.cacheProvider.getResultTree(queryId);
|
|
722
|
+
}
|
|
723
|
+
async getResultJSON(queryId) {
|
|
724
|
+
await this.initialize();
|
|
725
|
+
const processor = new ResultTreeProcessor();
|
|
726
|
+
const cacheProvider = this.cacheProvider;
|
|
727
|
+
const resultTree = await cacheProvider.getResultTree(queryId);
|
|
728
|
+
if (!resultTree) {
|
|
729
|
+
throw new DataConnectError(Code.INVALID_ARGUMENT, `${queryId} not found in cache. Call "update()" first.`);
|
|
730
|
+
}
|
|
731
|
+
return processor.hydrateResults(resultTree.getRootStub());
|
|
732
|
+
}
|
|
733
|
+
async update(queryId, serverValues, entityIds) {
|
|
734
|
+
await this.initialize();
|
|
735
|
+
const processor = new ResultTreeProcessor();
|
|
736
|
+
const cacheProvider = this.cacheProvider;
|
|
737
|
+
const { entityNode: stubDataObject, impacted } = await processor.dehydrateResults(serverValues, entityIds, cacheProvider, queryId);
|
|
738
|
+
const now = new Date();
|
|
739
|
+
await cacheProvider.setResultTree(queryId, new ResultTree(stubDataObject, serverValues.maxAge || this.cacheSettings.maxAgeSeconds, now, now));
|
|
740
|
+
return impacted;
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
class MemoryStub {
|
|
744
|
+
constructor() {
|
|
745
|
+
this.type = 'MEMORY';
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* @internal
|
|
749
|
+
*/
|
|
750
|
+
initialize(cacheId) {
|
|
751
|
+
return new InMemoryCacheProvider(cacheId);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
237
754
|
|
|
238
755
|
/**
|
|
239
756
|
* @license
|
|
@@ -325,6 +842,9 @@ class FirebaseAuthProvider {
|
|
|
325
842
|
_authProvider.onInit(auth => (this._auth = auth));
|
|
326
843
|
}
|
|
327
844
|
}
|
|
845
|
+
getAuth() {
|
|
846
|
+
return this._auth;
|
|
847
|
+
}
|
|
328
848
|
getToken(forceRefresh) {
|
|
329
849
|
if (!this._auth) {
|
|
330
850
|
return new Promise((resolve, reject) => {
|
|
@@ -384,7 +904,7 @@ const SOURCE_CACHE = 'CACHE';
|
|
|
384
904
|
|
|
385
905
|
/**
|
|
386
906
|
* @license
|
|
387
|
-
* Copyright
|
|
907
|
+
* Copyright 2026 Google LLC
|
|
388
908
|
*
|
|
389
909
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
390
910
|
* you may not use this file except in compliance with the License.
|
|
@@ -398,11 +918,43 @@ const SOURCE_CACHE = 'CACHE';
|
|
|
398
918
|
* See the License for the specific language governing permissions and
|
|
399
919
|
* limitations under the License.
|
|
400
920
|
*/
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
921
|
+
function parseEntityIds(result) {
|
|
922
|
+
// Iterate through extensions.dataConnect
|
|
923
|
+
const dataConnectExtensions = result.extensions?.dataConnect;
|
|
924
|
+
const dataCopy = Object.assign(result);
|
|
925
|
+
if (!dataConnectExtensions) {
|
|
926
|
+
return dataCopy;
|
|
927
|
+
}
|
|
928
|
+
const ret = {};
|
|
929
|
+
for (const extension of dataConnectExtensions) {
|
|
930
|
+
const { path } = extension;
|
|
931
|
+
populatePath(path, ret, extension);
|
|
932
|
+
}
|
|
933
|
+
return ret;
|
|
934
|
+
}
|
|
935
|
+
// mutates the object to update the path
|
|
936
|
+
function populatePath(path, toUpdate, extension) {
|
|
937
|
+
let curObj = toUpdate;
|
|
938
|
+
for (const slice of path) {
|
|
939
|
+
if (typeof curObj[slice] !== 'object') {
|
|
940
|
+
curObj[slice] = {};
|
|
941
|
+
}
|
|
942
|
+
curObj = curObj[slice];
|
|
943
|
+
}
|
|
944
|
+
if ('entityId' in extension && extension.entityId) {
|
|
945
|
+
curObj['_id'] = extension.entityId;
|
|
946
|
+
}
|
|
947
|
+
else if ('entityIds' in extension) {
|
|
948
|
+
const entityArr = extension.entityIds;
|
|
949
|
+
for (let i = 0; i < entityArr.length; i++) {
|
|
950
|
+
const entityId = entityArr[i];
|
|
951
|
+
if (typeof curObj[i] === 'undefined') {
|
|
952
|
+
curObj[i] = {};
|
|
953
|
+
}
|
|
954
|
+
curObj[i]._id = entityId;
|
|
955
|
+
}
|
|
956
|
+
}
|
|
404
957
|
}
|
|
405
|
-
setEncoder(o => JSON.stringify(o));
|
|
406
958
|
|
|
407
959
|
/**
|
|
408
960
|
* @license
|
|
@@ -420,11 +972,24 @@ setEncoder(o => JSON.stringify(o));
|
|
|
420
972
|
* See the License for the specific language governing permissions and
|
|
421
973
|
* limitations under the License.
|
|
422
974
|
*/
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
975
|
+
let encoderImpl;
|
|
976
|
+
let decoderImpl;
|
|
977
|
+
function setEncoder(encoder) {
|
|
978
|
+
encoderImpl = encoder;
|
|
979
|
+
}
|
|
980
|
+
function setDecoder(decoder) {
|
|
981
|
+
decoderImpl = decoder;
|
|
982
|
+
}
|
|
983
|
+
function sortKeysForObj(o) {
|
|
984
|
+
return Object.keys(o)
|
|
985
|
+
.sort()
|
|
986
|
+
.reduce((accumulator, currentKey) => {
|
|
987
|
+
accumulator[currentKey] = o[currentKey];
|
|
988
|
+
return accumulator;
|
|
989
|
+
}, {});
|
|
427
990
|
}
|
|
991
|
+
setEncoder((o) => JSON.stringify(sortKeysForObj(o)));
|
|
992
|
+
setDecoder(s => sortKeysForObj(JSON.parse(s)));
|
|
428
993
|
|
|
429
994
|
/**
|
|
430
995
|
* @license
|
|
@@ -442,7 +1007,7 @@ function setIfNotExists(map, key, val) {
|
|
|
442
1007
|
* See the License for the specific language governing permissions and
|
|
443
1008
|
* limitations under the License.
|
|
444
1009
|
*/
|
|
445
|
-
function getRefSerializer(queryRef, data, source) {
|
|
1010
|
+
function getRefSerializer(queryRef, data, source, fetchTime) {
|
|
446
1011
|
return function toJSON() {
|
|
447
1012
|
return {
|
|
448
1013
|
data,
|
|
@@ -454,32 +1019,65 @@ function getRefSerializer(queryRef, data, source) {
|
|
|
454
1019
|
...queryRef.dataConnect.getSettings()
|
|
455
1020
|
}
|
|
456
1021
|
},
|
|
457
|
-
fetchTime
|
|
1022
|
+
fetchTime,
|
|
458
1023
|
source
|
|
459
1024
|
};
|
|
460
1025
|
};
|
|
461
1026
|
}
|
|
462
1027
|
class QueryManager {
|
|
463
|
-
|
|
1028
|
+
async preferCacheResults(queryRef, allowStale = false) {
|
|
1029
|
+
let cacheResult;
|
|
1030
|
+
try {
|
|
1031
|
+
cacheResult = await this.fetchCacheResults(queryRef, allowStale);
|
|
1032
|
+
}
|
|
1033
|
+
catch (e) {
|
|
1034
|
+
// Ignore the error and try to fetch from the server.
|
|
1035
|
+
}
|
|
1036
|
+
if (cacheResult) {
|
|
1037
|
+
return cacheResult;
|
|
1038
|
+
}
|
|
1039
|
+
return this.fetchServerResults(queryRef);
|
|
1040
|
+
}
|
|
1041
|
+
constructor(transport, dc, cache) {
|
|
464
1042
|
this.transport = transport;
|
|
465
|
-
this.
|
|
1043
|
+
this.dc = dc;
|
|
1044
|
+
this.cache = cache;
|
|
1045
|
+
this.callbacks = new Map();
|
|
1046
|
+
this.subscriptionCache = new Map();
|
|
1047
|
+
this.queue = [];
|
|
466
1048
|
}
|
|
467
|
-
|
|
468
|
-
const
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
1049
|
+
async waitForQueuedWrites() {
|
|
1050
|
+
for (const promise of this.queue) {
|
|
1051
|
+
await promise;
|
|
1052
|
+
}
|
|
1053
|
+
this.queue = [];
|
|
1054
|
+
}
|
|
1055
|
+
updateSSR(updatedData) {
|
|
1056
|
+
this.queue.push(this.updateCache(updatedData).then(async (result) => this.publishCacheResultsToSubscribers(result, updatedData.fetchTime)));
|
|
1057
|
+
}
|
|
1058
|
+
async updateCache(result, extensions) {
|
|
1059
|
+
await this.waitForQueuedWrites();
|
|
1060
|
+
if (this.cache) {
|
|
1061
|
+
const entityIds = parseEntityIds(result);
|
|
1062
|
+
const updatedMaxAge = getMaxAgeFromExtensions(extensions);
|
|
1063
|
+
if (updatedMaxAge !== undefined) {
|
|
1064
|
+
this.cache.cacheSettings.maxAgeSeconds = updatedMaxAge;
|
|
1065
|
+
}
|
|
1066
|
+
return this.cache.update(encoderImpl({
|
|
1067
|
+
name: result.ref.name,
|
|
1068
|
+
variables: result.ref.variables,
|
|
1069
|
+
refType: QUERY_STR
|
|
1070
|
+
}), result.data, entityIds);
|
|
1071
|
+
}
|
|
1072
|
+
else {
|
|
1073
|
+
const key = encoderImpl({
|
|
1074
|
+
name: result.ref.name,
|
|
1075
|
+
variables: result.ref.variables,
|
|
1076
|
+
refType: QUERY_STR
|
|
1077
|
+
});
|
|
1078
|
+
this.subscriptionCache.set(key, result);
|
|
1079
|
+
return [key];
|
|
1080
|
+
}
|
|
483
1081
|
}
|
|
484
1082
|
addSubscription(queryRef, onResultCallback, onCompleteCallback, onErrorCallback, initialCache) {
|
|
485
1083
|
const key = encoderImpl({
|
|
@@ -487,99 +1085,213 @@ class QueryManager {
|
|
|
487
1085
|
variables: queryRef.variables,
|
|
488
1086
|
refType: QUERY_STR
|
|
489
1087
|
});
|
|
490
|
-
const trackedQuery = this._queries.get(key);
|
|
491
|
-
const subscription = {
|
|
492
|
-
userCallback: onResultCallback,
|
|
493
|
-
onCompleteCallback,
|
|
494
|
-
errCallback: onErrorCallback
|
|
495
|
-
};
|
|
496
1088
|
const unsubscribe = () => {
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
if (initialCache && trackedQuery.currentCache !== initialCache) {
|
|
502
|
-
logDebug('Initial cache found. Comparing dates.');
|
|
503
|
-
if (!trackedQuery.currentCache ||
|
|
504
|
-
(trackedQuery.currentCache &&
|
|
505
|
-
compareDates(trackedQuery.currentCache.fetchTime, initialCache.fetchTime))) {
|
|
506
|
-
trackedQuery.currentCache = initialCache;
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
if (trackedQuery.currentCache !== null) {
|
|
510
|
-
const cachedData = trackedQuery.currentCache.data;
|
|
511
|
-
onResultCallback({
|
|
512
|
-
data: cachedData,
|
|
513
|
-
source: SOURCE_CACHE,
|
|
514
|
-
ref: queryRef,
|
|
515
|
-
toJSON: getRefSerializer(queryRef, trackedQuery.currentCache.data, SOURCE_CACHE),
|
|
516
|
-
fetchTime: trackedQuery.currentCache.fetchTime
|
|
517
|
-
});
|
|
518
|
-
if (trackedQuery.lastError !== null && onErrorCallback) {
|
|
519
|
-
onErrorCallback(undefined);
|
|
1089
|
+
if (this.callbacks.has(key)) {
|
|
1090
|
+
const callbackList = this.callbacks.get(key);
|
|
1091
|
+
this.callbacks.set(key, callbackList.filter(callback => callback !== subscription));
|
|
1092
|
+
onCompleteCallback?.();
|
|
520
1093
|
}
|
|
521
|
-
}
|
|
522
|
-
|
|
1094
|
+
};
|
|
1095
|
+
const subscription = {
|
|
523
1096
|
userCallback: onResultCallback,
|
|
524
1097
|
errCallback: onErrorCallback,
|
|
525
1098
|
unsubscribe
|
|
526
|
-
}
|
|
527
|
-
if (
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
1099
|
+
};
|
|
1100
|
+
if (initialCache) {
|
|
1101
|
+
this.updateSSR(initialCache);
|
|
1102
|
+
}
|
|
1103
|
+
const promise = this.preferCacheResults(queryRef, /*allowStale=*/ true);
|
|
1104
|
+
// We want to ignore the error and let subscriptions handle it
|
|
1105
|
+
promise.then(undefined, err => { });
|
|
1106
|
+
if (!this.callbacks.has(key)) {
|
|
1107
|
+
this.callbacks.set(key, []);
|
|
532
1108
|
}
|
|
1109
|
+
this.callbacks
|
|
1110
|
+
.get(key)
|
|
1111
|
+
.push(subscription);
|
|
533
1112
|
return unsubscribe;
|
|
534
1113
|
}
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
throw new DataConnectError(Code.INVALID_ARGUMENT, `ExecuteQuery can only execute query operation`);
|
|
538
|
-
}
|
|
1114
|
+
async fetchServerResults(queryRef) {
|
|
1115
|
+
await this.waitForQueuedWrites();
|
|
539
1116
|
const key = encoderImpl({
|
|
540
1117
|
name: queryRef.name,
|
|
541
1118
|
variables: queryRef.variables,
|
|
542
1119
|
refType: QUERY_STR
|
|
543
1120
|
});
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
const
|
|
548
|
-
const
|
|
549
|
-
...
|
|
550
|
-
source: SOURCE_SERVER,
|
|
1121
|
+
try {
|
|
1122
|
+
const result = await this.transport.invokeQuery(queryRef.name, queryRef.variables);
|
|
1123
|
+
const fetchTime = Date.now().toString();
|
|
1124
|
+
const originalExtensions = result.extensions;
|
|
1125
|
+
const queryResult = {
|
|
1126
|
+
...result,
|
|
551
1127
|
ref: queryRef,
|
|
552
|
-
|
|
553
|
-
fetchTime
|
|
1128
|
+
source: SOURCE_SERVER,
|
|
1129
|
+
fetchTime,
|
|
1130
|
+
data: result.data,
|
|
1131
|
+
extensions: getDataConnectExtensionsWithoutMaxAge(originalExtensions),
|
|
1132
|
+
toJSON: getRefSerializer(queryRef, result.data, SOURCE_SERVER, fetchTime)
|
|
554
1133
|
};
|
|
555
|
-
|
|
556
|
-
|
|
1134
|
+
let updatedKeys = [];
|
|
1135
|
+
updatedKeys = await this.updateCache(queryResult, originalExtensions?.dataConnect);
|
|
1136
|
+
this.publishDataToSubscribers(key, queryResult);
|
|
1137
|
+
if (this.cache) {
|
|
1138
|
+
await this.publishCacheResultsToSubscribers(updatedKeys, fetchTime);
|
|
1139
|
+
}
|
|
1140
|
+
else {
|
|
1141
|
+
this.subscriptionCache.set(key, queryResult);
|
|
1142
|
+
}
|
|
1143
|
+
return queryResult;
|
|
1144
|
+
}
|
|
1145
|
+
catch (e) {
|
|
1146
|
+
this.publishErrorToSubscribers(key, e);
|
|
1147
|
+
throw e;
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
async fetchCacheResults(queryRef, allowStale = false) {
|
|
1151
|
+
await this.waitForQueuedWrites();
|
|
1152
|
+
let result;
|
|
1153
|
+
if (!this.cache) {
|
|
1154
|
+
result = await this.getFromSubscriberCache(queryRef);
|
|
1155
|
+
}
|
|
1156
|
+
else {
|
|
1157
|
+
result = await this.getFromResultTreeCache(queryRef, allowStale);
|
|
1158
|
+
}
|
|
1159
|
+
if (!result) {
|
|
1160
|
+
throw new DataConnectError(Code.OTHER, 'No cache entry found for query: ' + queryRef.name);
|
|
1161
|
+
}
|
|
1162
|
+
const fetchTime = Date.now().toString();
|
|
1163
|
+
const queryResult = {
|
|
1164
|
+
...result,
|
|
1165
|
+
ref: queryRef,
|
|
1166
|
+
source: SOURCE_CACHE,
|
|
1167
|
+
fetchTime,
|
|
1168
|
+
data: result.data,
|
|
1169
|
+
extensions: result.extensions,
|
|
1170
|
+
toJSON: getRefSerializer(queryRef, result.data, SOURCE_CACHE, fetchTime)
|
|
1171
|
+
};
|
|
1172
|
+
if (this.cache) {
|
|
1173
|
+
const key = encoderImpl({
|
|
1174
|
+
name: queryRef.name,
|
|
1175
|
+
variables: queryRef.variables,
|
|
1176
|
+
refType: QUERY_STR
|
|
557
1177
|
});
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
trackedQuery.lastError = err;
|
|
566
|
-
trackedQuery.subscriptions.forEach(subscription => {
|
|
567
|
-
if (subscription.errCallback) {
|
|
568
|
-
subscription.errCallback(err);
|
|
569
|
-
}
|
|
1178
|
+
await this.publishCacheResultsToSubscribers([key], fetchTime);
|
|
1179
|
+
}
|
|
1180
|
+
else {
|
|
1181
|
+
const key = encoderImpl({
|
|
1182
|
+
name: queryRef.name,
|
|
1183
|
+
variables: queryRef.variables,
|
|
1184
|
+
refType: QUERY_STR
|
|
570
1185
|
});
|
|
571
|
-
|
|
1186
|
+
this.subscriptionCache.set(key, queryResult);
|
|
1187
|
+
this.publishDataToSubscribers(key, queryResult);
|
|
1188
|
+
}
|
|
1189
|
+
return queryResult;
|
|
1190
|
+
}
|
|
1191
|
+
publishErrorToSubscribers(key, err) {
|
|
1192
|
+
this.callbacks.get(key)?.forEach(subscription => {
|
|
1193
|
+
if (subscription.errCallback) {
|
|
1194
|
+
subscription.errCallback(err);
|
|
1195
|
+
}
|
|
1196
|
+
});
|
|
1197
|
+
}
|
|
1198
|
+
async getFromResultTreeCache(queryRef, allowStale = false) {
|
|
1199
|
+
const key = encoderImpl({
|
|
1200
|
+
name: queryRef.name,
|
|
1201
|
+
variables: queryRef.variables,
|
|
1202
|
+
refType: QUERY_STR
|
|
1203
|
+
});
|
|
1204
|
+
if (!this.cache || !(await this.cache.containsResultTree(key))) {
|
|
1205
|
+
return null;
|
|
1206
|
+
}
|
|
1207
|
+
const cacheResult = (await this.cache.getResultJSON(key));
|
|
1208
|
+
const resultTree = await this.cache.getResultTree(key);
|
|
1209
|
+
if (!allowStale && resultTree.isStale()) {
|
|
1210
|
+
return null;
|
|
1211
|
+
}
|
|
1212
|
+
const result = {
|
|
1213
|
+
source: SOURCE_CACHE,
|
|
1214
|
+
ref: queryRef,
|
|
1215
|
+
data: cacheResult,
|
|
1216
|
+
toJSON: getRefSerializer(queryRef, cacheResult, SOURCE_CACHE, resultTree.cachedAt.toString()),
|
|
1217
|
+
fetchTime: resultTree.cachedAt.toString()
|
|
1218
|
+
};
|
|
1219
|
+
(await this.cache.getResultTree(key)).updateAccessed();
|
|
1220
|
+
return result;
|
|
1221
|
+
}
|
|
1222
|
+
async getFromSubscriberCache(queryRef) {
|
|
1223
|
+
const key = encoderImpl({
|
|
1224
|
+
name: queryRef.name,
|
|
1225
|
+
variables: queryRef.variables,
|
|
1226
|
+
refType: QUERY_STR
|
|
1227
|
+
});
|
|
1228
|
+
if (!this.subscriptionCache.has(key)) {
|
|
1229
|
+
return;
|
|
1230
|
+
}
|
|
1231
|
+
const result = this.subscriptionCache.get(key);
|
|
1232
|
+
result.source = SOURCE_CACHE;
|
|
1233
|
+
result.toJSON = getRefSerializer(result.ref, result.data, SOURCE_CACHE, result.fetchTime);
|
|
1234
|
+
return result;
|
|
1235
|
+
}
|
|
1236
|
+
publishDataToSubscribers(key, queryResult) {
|
|
1237
|
+
if (!this.callbacks.has(key)) {
|
|
1238
|
+
return;
|
|
1239
|
+
}
|
|
1240
|
+
const subscribers = this.callbacks.get(key);
|
|
1241
|
+
subscribers.forEach(callback => {
|
|
1242
|
+
callback.userCallback(queryResult);
|
|
572
1243
|
});
|
|
573
|
-
|
|
1244
|
+
}
|
|
1245
|
+
async publishCacheResultsToSubscribers(impactedQueries, fetchTime) {
|
|
1246
|
+
if (!this.cache) {
|
|
1247
|
+
return;
|
|
1248
|
+
}
|
|
1249
|
+
for (const query of impactedQueries) {
|
|
1250
|
+
const callbacks = this.callbacks.get(query);
|
|
1251
|
+
if (!callbacks) {
|
|
1252
|
+
continue;
|
|
1253
|
+
}
|
|
1254
|
+
const newJson = (await this.cache.getResultTree(query))
|
|
1255
|
+
.getRootStub()
|
|
1256
|
+
.toJSON(EncodingMode.hydrated);
|
|
1257
|
+
const { name, variables } = decoderImpl(query);
|
|
1258
|
+
const queryRef = {
|
|
1259
|
+
dataConnect: this.dc,
|
|
1260
|
+
refType: QUERY_STR,
|
|
1261
|
+
name,
|
|
1262
|
+
variables
|
|
1263
|
+
};
|
|
1264
|
+
this.publishDataToSubscribers(query, {
|
|
1265
|
+
data: newJson,
|
|
1266
|
+
fetchTime,
|
|
1267
|
+
ref: queryRef,
|
|
1268
|
+
source: SOURCE_CACHE,
|
|
1269
|
+
toJSON: getRefSerializer(queryRef, newJson, SOURCE_CACHE, fetchTime)
|
|
1270
|
+
});
|
|
1271
|
+
}
|
|
574
1272
|
}
|
|
575
1273
|
enableEmulator(host, port) {
|
|
576
1274
|
this.transport.useEmulator(host, port);
|
|
577
1275
|
}
|
|
578
1276
|
}
|
|
579
|
-
function
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
1277
|
+
function getMaxAgeFromExtensions(extensions) {
|
|
1278
|
+
if (!extensions) {
|
|
1279
|
+
return;
|
|
1280
|
+
}
|
|
1281
|
+
for (const extension of extensions) {
|
|
1282
|
+
if ('maxAge' in extension &&
|
|
1283
|
+
extension.maxAge !== undefined &&
|
|
1284
|
+
extension.maxAge !== null) {
|
|
1285
|
+
if (extension.maxAge.endsWith('s')) {
|
|
1286
|
+
return Number(extension.maxAge.substring(0, extension.maxAge.length - 1));
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
function getDataConnectExtensionsWithoutMaxAge(extensions) {
|
|
1292
|
+
return {
|
|
1293
|
+
dataConnect: extensions.dataConnect?.filter(extension => 'entityId' in extension || 'entityIds' in extension)
|
|
1294
|
+
};
|
|
583
1295
|
}
|
|
584
1296
|
|
|
585
1297
|
/**
|
|
@@ -598,11 +1310,12 @@ function compareDates(str1, str2) {
|
|
|
598
1310
|
* See the License for the specific language governing permissions and
|
|
599
1311
|
* limitations under the License.
|
|
600
1312
|
*/
|
|
1313
|
+
const PROD_HOST = 'firebasedataconnect.googleapis.com';
|
|
601
1314
|
function urlBuilder(projectConfig, transportOptions) {
|
|
602
1315
|
const { connector, location, projectId: project, service } = projectConfig;
|
|
603
1316
|
const { host, sslEnabled, port } = transportOptions;
|
|
604
1317
|
const protocol = sslEnabled ? 'https' : 'http';
|
|
605
|
-
const realHost = host ||
|
|
1318
|
+
const realHost = host || PROD_HOST;
|
|
606
1319
|
let baseUrl = `${protocol}://${realHost}`;
|
|
607
1320
|
if (typeof port === 'number') {
|
|
608
1321
|
baseUrl += `:${port}`;
|
|
@@ -732,7 +1445,10 @@ class RESTTransport {
|
|
|
732
1445
|
async getWithAuth(forceToken = false) {
|
|
733
1446
|
let starterPromise = new Promise(resolve => resolve(this._accessToken));
|
|
734
1447
|
if (this.appCheckProvider) {
|
|
735
|
-
|
|
1448
|
+
const appCheckToken = await this.appCheckProvider.getToken();
|
|
1449
|
+
if (appCheckToken) {
|
|
1450
|
+
this._appCheckToken = appCheckToken.token;
|
|
1451
|
+
}
|
|
736
1452
|
}
|
|
737
1453
|
if (this.authProvider) {
|
|
738
1454
|
starterPromise = this.authProvider
|
|
@@ -901,6 +1617,12 @@ class DataConnect {
|
|
|
901
1617
|
}
|
|
902
1618
|
}
|
|
903
1619
|
}
|
|
1620
|
+
/**
|
|
1621
|
+
* @internal
|
|
1622
|
+
*/
|
|
1623
|
+
getCache() {
|
|
1624
|
+
return this.cache;
|
|
1625
|
+
}
|
|
904
1626
|
// @internal
|
|
905
1627
|
_useGeneratedSdk() {
|
|
906
1628
|
if (!this._isUsingGeneratedSdk) {
|
|
@@ -923,6 +1645,12 @@ class DataConnect {
|
|
|
923
1645
|
delete copy.projectId;
|
|
924
1646
|
return copy;
|
|
925
1647
|
}
|
|
1648
|
+
/**
|
|
1649
|
+
* @internal
|
|
1650
|
+
*/
|
|
1651
|
+
setCacheSettings(cacheSettings) {
|
|
1652
|
+
this._cacheSettings = cacheSettings;
|
|
1653
|
+
}
|
|
926
1654
|
// @internal
|
|
927
1655
|
setInitialized() {
|
|
928
1656
|
if (this._initialized) {
|
|
@@ -932,19 +1660,25 @@ class DataConnect {
|
|
|
932
1660
|
logDebug('transportClass not provided. Defaulting to RESTTransport.');
|
|
933
1661
|
this._transportClass = RESTTransport;
|
|
934
1662
|
}
|
|
935
|
-
|
|
936
|
-
|
|
1663
|
+
this._authTokenProvider = new FirebaseAuthProvider(this.app.name, this.app.options, this._authProvider);
|
|
1664
|
+
const connectorConfig = {
|
|
1665
|
+
connector: this.dataConnectOptions.connector,
|
|
1666
|
+
service: this.dataConnectOptions.service,
|
|
1667
|
+
location: this.dataConnectOptions.location
|
|
1668
|
+
};
|
|
1669
|
+
if (this._cacheSettings) {
|
|
1670
|
+
this.cache = new DataConnectCache(this._authTokenProvider, this.app.options.projectId, connectorConfig, this._transportOptions?.host || PROD_HOST, this._cacheSettings);
|
|
937
1671
|
}
|
|
938
1672
|
if (this._appCheckProvider) {
|
|
939
1673
|
this._appCheckTokenProvider = new AppCheckTokenProvider(this.app, this._appCheckProvider);
|
|
940
1674
|
}
|
|
941
|
-
this._initialized = true;
|
|
942
1675
|
this._transport = new this._transportClass(this.dataConnectOptions, this.app.options.apiKey, this.app.options.appId, this._authTokenProvider, this._appCheckTokenProvider, undefined, this._isUsingGeneratedSdk, this._callerSdkType);
|
|
943
1676
|
if (this._transportOptions) {
|
|
944
1677
|
this._transport.useEmulator(this._transportOptions.host, this._transportOptions.port, this._transportOptions.sslEnabled);
|
|
945
1678
|
}
|
|
946
|
-
this._queryManager = new QueryManager(this._transport);
|
|
1679
|
+
this._queryManager = new QueryManager(this._transport, this, this.cache);
|
|
947
1680
|
this._mutationManager = new MutationManager(this._transport);
|
|
1681
|
+
this._initialized = true;
|
|
948
1682
|
}
|
|
949
1683
|
// @internal
|
|
950
1684
|
enableEmulator(transportOptions) {
|
|
@@ -984,22 +1718,32 @@ function connectDataConnectEmulator(dc, host, port, sslEnabled = false) {
|
|
|
984
1718
|
}
|
|
985
1719
|
dc.enableEmulator({ host, port, sslEnabled });
|
|
986
1720
|
}
|
|
987
|
-
function getDataConnect(
|
|
1721
|
+
function getDataConnect(appOrConnectorConfig, settingsOrConnectorConfig, settings) {
|
|
988
1722
|
let app;
|
|
989
|
-
let
|
|
990
|
-
|
|
991
|
-
|
|
1723
|
+
let connectorConfig;
|
|
1724
|
+
let realSettings;
|
|
1725
|
+
if ('location' in appOrConnectorConfig) {
|
|
1726
|
+
connectorConfig = appOrConnectorConfig;
|
|
992
1727
|
app = getApp();
|
|
1728
|
+
realSettings = settingsOrConnectorConfig;
|
|
993
1729
|
}
|
|
994
1730
|
else {
|
|
995
|
-
|
|
996
|
-
|
|
1731
|
+
app = appOrConnectorConfig;
|
|
1732
|
+
connectorConfig = settingsOrConnectorConfig;
|
|
1733
|
+
realSettings = settings;
|
|
997
1734
|
}
|
|
998
1735
|
if (!app || Object.keys(app).length === 0) {
|
|
999
1736
|
app = getApp();
|
|
1000
1737
|
}
|
|
1738
|
+
// Options to store in Firebase Component Provider.
|
|
1739
|
+
const serializedOptions = {
|
|
1740
|
+
...connectorConfig,
|
|
1741
|
+
projectId: app.options.projectId
|
|
1742
|
+
};
|
|
1743
|
+
// We should sort the keys before initialization.
|
|
1744
|
+
const sortedSerialized = Object.fromEntries(Object.entries(serializedOptions).sort());
|
|
1001
1745
|
const provider = _getProvider(app, 'data-connect');
|
|
1002
|
-
const identifier = JSON.stringify(
|
|
1746
|
+
const identifier = JSON.stringify(sortedSerialized);
|
|
1003
1747
|
if (provider.isInitialized(identifier)) {
|
|
1004
1748
|
const dcInstance = provider.getImmediate({ identifier });
|
|
1005
1749
|
const options = provider.getOptions(identifier);
|
|
@@ -1009,13 +1753,19 @@ function getDataConnect(appOrOptions, optionalOptions) {
|
|
|
1009
1753
|
return dcInstance;
|
|
1010
1754
|
}
|
|
1011
1755
|
}
|
|
1012
|
-
validateDCOptions(
|
|
1756
|
+
validateDCOptions(connectorConfig);
|
|
1013
1757
|
logDebug('Creating new DataConnect instance');
|
|
1014
1758
|
// Initialize with options.
|
|
1015
|
-
|
|
1759
|
+
const dataConnect = provider.initialize({
|
|
1016
1760
|
instanceIdentifier: identifier,
|
|
1017
|
-
options:
|
|
1761
|
+
options: Object.fromEntries(Object.entries({
|
|
1762
|
+
...sortedSerialized
|
|
1763
|
+
}).sort())
|
|
1018
1764
|
});
|
|
1765
|
+
if (realSettings?.cacheSettings) {
|
|
1766
|
+
dataConnect.setCacheSettings(realSettings.cacheSettings);
|
|
1767
|
+
}
|
|
1768
|
+
return dataConnect;
|
|
1019
1769
|
}
|
|
1020
1770
|
/**
|
|
1021
1771
|
*
|
|
@@ -1045,6 +1795,12 @@ function terminate(dataConnect) {
|
|
|
1045
1795
|
return dataConnect._delete();
|
|
1046
1796
|
// TODO(mtewani): Stop pending tasks
|
|
1047
1797
|
}
|
|
1798
|
+
const StorageType = {
|
|
1799
|
+
MEMORY: 'MEMORY'
|
|
1800
|
+
};
|
|
1801
|
+
function makeMemoryCacheProvider() {
|
|
1802
|
+
return new MemoryStub();
|
|
1803
|
+
}
|
|
1048
1804
|
|
|
1049
1805
|
/**
|
|
1050
1806
|
* @license
|
|
@@ -1064,13 +1820,16 @@ function terminate(dataConnect) {
|
|
|
1064
1820
|
*/
|
|
1065
1821
|
function registerDataConnect(variant) {
|
|
1066
1822
|
setSDKVersion(SDK_VERSION$1);
|
|
1067
|
-
_registerComponent(new Component('data-connect', (container, { instanceIdentifier:
|
|
1823
|
+
_registerComponent(new Component('data-connect', (container, { instanceIdentifier: connectorConfigStr, options }) => {
|
|
1068
1824
|
const app = container.getProvider('app').getImmediate();
|
|
1069
1825
|
const authProvider = container.getProvider('auth-internal');
|
|
1070
1826
|
const appCheckProvider = container.getProvider('app-check-internal');
|
|
1071
1827
|
let newOpts = options;
|
|
1072
|
-
if (
|
|
1073
|
-
newOpts =
|
|
1828
|
+
if (connectorConfigStr) {
|
|
1829
|
+
newOpts = {
|
|
1830
|
+
...JSON.parse(connectorConfigStr),
|
|
1831
|
+
...newOpts
|
|
1832
|
+
};
|
|
1074
1833
|
}
|
|
1075
1834
|
if (!app.options.projectId) {
|
|
1076
1835
|
throw new DataConnectError(Code.INVALID_ARGUMENT, 'Project ID must be provided. Did you pass in a proper projectId to initializeApp?');
|
|
@@ -1082,6 +1841,28 @@ function registerDataConnect(variant) {
|
|
|
1082
1841
|
registerVersion(name, version, 'esm2020');
|
|
1083
1842
|
}
|
|
1084
1843
|
|
|
1844
|
+
/**
|
|
1845
|
+
* @license
|
|
1846
|
+
* Copyright 2025 Google LLC
|
|
1847
|
+
*
|
|
1848
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1849
|
+
* you may not use this file except in compliance with the License.
|
|
1850
|
+
* You may obtain a copy of the License at
|
|
1851
|
+
*
|
|
1852
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1853
|
+
*
|
|
1854
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
1855
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
1856
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1857
|
+
* See the License for the specific language governing permissions and
|
|
1858
|
+
* limitations under the License.
|
|
1859
|
+
*/
|
|
1860
|
+
const QueryFetchPolicy = {
|
|
1861
|
+
PREFER_CACHE: 'PREFER_CACHE',
|
|
1862
|
+
CACHE_ONLY: 'CACHE_ONLY',
|
|
1863
|
+
SERVER_ONLY: 'SERVER_ONLY'
|
|
1864
|
+
};
|
|
1865
|
+
|
|
1085
1866
|
/**
|
|
1086
1867
|
* @license
|
|
1087
1868
|
* Copyright 2024 Google LLC
|
|
@@ -1103,8 +1884,22 @@ function registerDataConnect(variant) {
|
|
|
1103
1884
|
* @param queryRef query to execute.
|
|
1104
1885
|
* @returns `QueryPromise`
|
|
1105
1886
|
*/
|
|
1106
|
-
function executeQuery(queryRef) {
|
|
1107
|
-
|
|
1887
|
+
function executeQuery(queryRef, options) {
|
|
1888
|
+
if (queryRef.refType !== QUERY_STR) {
|
|
1889
|
+
return Promise.reject(new DataConnectError(Code.INVALID_ARGUMENT, `ExecuteQuery can only execute query operations`));
|
|
1890
|
+
}
|
|
1891
|
+
const queryManager = queryRef.dataConnect._queryManager;
|
|
1892
|
+
const fetchPolicy = options?.fetchPolicy ?? QueryFetchPolicy.PREFER_CACHE;
|
|
1893
|
+
switch (fetchPolicy) {
|
|
1894
|
+
case QueryFetchPolicy.SERVER_ONLY:
|
|
1895
|
+
return queryManager.fetchServerResults(queryRef);
|
|
1896
|
+
case QueryFetchPolicy.CACHE_ONLY:
|
|
1897
|
+
return queryManager.fetchCacheResults(queryRef, true);
|
|
1898
|
+
case QueryFetchPolicy.PREFER_CACHE:
|
|
1899
|
+
return queryManager.preferCacheResults(queryRef, false);
|
|
1900
|
+
default:
|
|
1901
|
+
throw new DataConnectError(Code.INVALID_ARGUMENT, `Invalid fetch policy: ${fetchPolicy}`);
|
|
1902
|
+
}
|
|
1108
1903
|
}
|
|
1109
1904
|
/**
|
|
1110
1905
|
* Execute Query
|
|
@@ -1116,7 +1911,9 @@ function executeQuery(queryRef) {
|
|
|
1116
1911
|
*/
|
|
1117
1912
|
function queryRef(dcInstance, queryName, variables, initialCache) {
|
|
1118
1913
|
dcInstance.setInitialized();
|
|
1119
|
-
|
|
1914
|
+
if (initialCache !== undefined) {
|
|
1915
|
+
dcInstance._queryManager.updateSSR(initialCache);
|
|
1916
|
+
}
|
|
1120
1917
|
return {
|
|
1121
1918
|
dataConnect: dcInstance,
|
|
1122
1919
|
refType: QUERY_STR,
|
|
@@ -1179,7 +1976,7 @@ function validateArgs(connectorConfig, dcOrVars, vars, validateVars) {
|
|
|
1179
1976
|
|
|
1180
1977
|
/**
|
|
1181
1978
|
* @license
|
|
1182
|
-
* Copyright
|
|
1979
|
+
* Copyright 2025 Google LLC
|
|
1183
1980
|
*
|
|
1184
1981
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1185
1982
|
* you may not use this file except in compliance with the License.
|
|
@@ -1207,12 +2004,14 @@ function subscribe(queryRefOrSerializedResult, observerOrOnNext, onError, onComp
|
|
|
1207
2004
|
if ('refInfo' in queryRefOrSerializedResult) {
|
|
1208
2005
|
const serializedRef = queryRefOrSerializedResult;
|
|
1209
2006
|
const { data, source, fetchTime } = serializedRef;
|
|
2007
|
+
ref = toQueryRef(serializedRef);
|
|
1210
2008
|
initialCache = {
|
|
1211
2009
|
data,
|
|
1212
2010
|
source,
|
|
1213
|
-
fetchTime
|
|
2011
|
+
fetchTime,
|
|
2012
|
+
ref,
|
|
2013
|
+
toJSON: getRefSerializer(ref, data, source, fetchTime)
|
|
1214
2014
|
};
|
|
1215
|
-
ref = toQueryRef(serializedRef);
|
|
1216
2015
|
}
|
|
1217
2016
|
else {
|
|
1218
2017
|
ref = queryRefOrSerializedResult;
|
|
@@ -1251,5 +2050,5 @@ function subscribe(queryRefOrSerializedResult, observerOrOnNext, onError, onComp
|
|
|
1251
2050
|
initializeFetch(fetch);
|
|
1252
2051
|
registerDataConnect('node');
|
|
1253
2052
|
|
|
1254
|
-
export { CallerSdkTypeEnum, Code, DataConnect, DataConnectError, DataConnectOperationError, MUTATION_STR, MutationManager, QUERY_STR, SOURCE_CACHE, SOURCE_SERVER, areTransportOptionsEqual, connectDataConnectEmulator, executeMutation, executeQuery, getDataConnect, mutationRef, parseOptions, queryRef, setLogLevel, subscribe, terminate, toQueryRef, validateArgs, validateDCOptions };
|
|
2053
|
+
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 };
|
|
1255
2054
|
//# sourceMappingURL=index.node.esm.js.map
|