@squidcloud/client 1.0.114 → 1.0.115

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/cjs/index.js CHANGED
@@ -32812,7 +32812,7 @@ var promise_pool_dist = __webpack_require__(3910);
32812
32812
  /** Two transactions cannot run in parallel - this mutex is used for blocking a second transaction. */
32813
32813
  const RUN_IN_TRANSACTION_MUTEX = 'dataManager_runInTransaction';
32814
32814
  class DataManager {
32815
- constructor(documentStore, mutationSender, socketManager, querySubscriptionManager, queryBuilderFactory, lockManager, destructManager, documentIdentityService) {
32815
+ constructor(documentStore, mutationSender, socketManager, querySubscriptionManager, queryBuilderFactory, lockManager, destructManager, documentIdentityService, querySender) {
32816
32816
  this.documentStore = documentStore;
32817
32817
  this.mutationSender = mutationSender;
32818
32818
  this.socketManager = socketManager;
@@ -32821,6 +32821,7 @@ class DataManager {
32821
32821
  this.lockManager = lockManager;
32822
32822
  this.destructManager = destructManager;
32823
32823
  this.documentIdentityService = documentIdentityService;
32824
+ this.querySender = querySender;
32824
32825
  this.docIdToLocalTimestamp = new Map();
32825
32826
  /**
32826
32827
  * During a batch, any update to a document that may trigger an update to a query is collected here and once the batch
@@ -32899,7 +32900,7 @@ class DataManager {
32899
32900
  * We should not send queries to the server before we know that all outgoing updates were
32900
32901
  * applied on the server.
32901
32902
  */
32902
- this.querySubscriptionManager.safeToSendQueriesToServer.next(isEmpty);
32903
+ this.querySender.safeToSendQueriesToServer.next(isEmpty);
32903
32904
  });
32904
32905
  }
32905
32906
  getProperties(squidDocId) {
@@ -32933,7 +32934,7 @@ class DataManager {
32933
32934
  async runInTransaction(fn, transactionId) {
32934
32935
  if (transactionId) {
32935
32936
  assert_assertTruthy(transactionId === this.currentTransactionId, 'Transaction already ended.');
32936
- return fn(transactionId).then((ignored) => Promise.resolve());
32937
+ return fn(transactionId).then(() => Promise.resolve());
32937
32938
  }
32938
32939
  if (this.lockManager.canGetLock(RUN_IN_TRANSACTION_MUTEX)) {
32939
32940
  this.lockManager.lockSync(RUN_IN_TRANSACTION_MUTEX);
@@ -33056,12 +33057,9 @@ class DataManager {
33056
33057
  this.handleIncomingMutations(notification.payload);
33057
33058
  });
33058
33059
  });
33059
- this.socketManager
33060
- .observeNotifications()
33061
- .pipe((0,external_rxjs_namespaceObject.filter)((notification) => notification.type === 'query'), map_map((n) => n))
33062
- .subscribe((notification) => {
33060
+ this.querySubscriptionManager.observeQueryResults().subscribe((queryResult) => {
33063
33061
  this.outgoingMutationsEmpty.pipe((0,external_rxjs_namespaceObject.filter)(Boolean), (0,external_rxjs_namespaceObject.take)(1)).subscribe(() => {
33064
- this.handleIncomingQuerySnapshots(notification.payload);
33062
+ this.handleIncomingQuerySnapshots(queryResult);
33065
33063
  });
33066
33064
  });
33067
33065
  }
@@ -33294,7 +33292,7 @@ class DataManager {
33294
33292
  async sendMutationsForIntegration(outgoingMutations, integrationId) {
33295
33293
  try {
33296
33294
  const { timestamp, idResolutionMap = {}, refreshList = [], } = await this.mutationSender.sendMutations(outgoingMutations.map((outgoingMutation) => outgoingMutation.mutation), integrationId);
33297
- await this.documentIdentityService.migrate(idResolutionMap);
33295
+ this.documentIdentityService.migrate(idResolutionMap);
33298
33296
  refreshList.forEach((docId) => {
33299
33297
  this.refreshDocIdToTimestamp.set(idResolutionMap[docId] || docId, timestamp);
33300
33298
  });
@@ -47030,16 +47028,16 @@ class GraphQLClientFactory {
47030
47028
  ;// CONCATENATED MODULE: ./src/mutation/mutation-sender.ts
47031
47029
 
47032
47030
  class MutationSender {
47033
- constructor(rpcManager, lockManager, querySubscriptionManager) {
47031
+ constructor(rpcManager, lockManager, querySender) {
47034
47032
  this.rpcManager = rpcManager;
47035
47033
  this.lockManager = lockManager;
47036
- this.querySubscriptionManager = querySubscriptionManager;
47034
+ this.querySender = querySender;
47037
47035
  }
47038
47036
  async sendMutations(mutations, integrationId) {
47039
47037
  const reducedMutations = reduceMutations(mutations);
47040
47038
  const mutexes = reducedMutations.map((mutation) => `sendMutation_${getSquidDocId(mutation.squidDocIdObj)}`);
47041
47039
  await this.lockManager.lock(...mutexes);
47042
- await this.querySubscriptionManager.waitForAllQueriesToFinish();
47040
+ await this.querySender.waitForAllQueriesToFinish();
47043
47041
  try {
47044
47042
  const request = {
47045
47043
  mutations: reducedMutations,
@@ -47608,36 +47606,33 @@ function getStateInPath(obj, path) {
47608
47606
  class SubscriptionData {
47609
47607
  }
47610
47608
 
47609
+ ;// CONCATENATED MODULE: ./src/query/query.types.ts
47610
+ var LimitUnderflowState;
47611
+ (function (LimitUnderflowState) {
47612
+ LimitUnderflowState[LimitUnderflowState["UNKNOWN"] = 0] = "UNKNOWN";
47613
+ LimitUnderflowState[LimitUnderflowState["DISABLED"] = 1] = "DISABLED";
47614
+ LimitUnderflowState[LimitUnderflowState["ENABLED"] = 2] = "ENABLED";
47615
+ })(LimitUnderflowState || (LimitUnderflowState = {}));
47616
+
47611
47617
  ;// CONCATENATED MODULE: ./src/query/query-subscription.manager.ts
47612
47618
 
47613
47619
 
47614
47620
 
47615
47621
 
47616
47622
 
47623
+
47617
47624
  // See limitUnderflowState below.
47618
47625
  // Exported only for tests
47619
47626
  const FETCH_BEYOND_LIMIT = 100;
47620
47627
  const LIMIT_UNDERFLOW_TRIGGER = 20;
47621
- var LimitUnderflowState;
47622
- (function (LimitUnderflowState) {
47623
- LimitUnderflowState[LimitUnderflowState["UNKNOWN"] = 0] = "UNKNOWN";
47624
- LimitUnderflowState[LimitUnderflowState["DISABLED"] = 1] = "DISABLED";
47625
- LimitUnderflowState[LimitUnderflowState["ENABLED"] = 2] = "ENABLED";
47626
- })(LimitUnderflowState || (LimitUnderflowState = {}));
47627
- /** The query mapping requires an app ID, but on the client it doesn't matter, so we use 'client' */
47628
- const APP_ID = 'client';
47629
47628
  class QuerySubscriptionManager {
47630
- constructor(rpcManager, clientIdService, documentStore, destructManager, documentIdentityService) {
47629
+ constructor(rpcManager, clientIdService, documentStore, destructManager, documentIdentityService, querySender) {
47631
47630
  this.rpcManager = rpcManager;
47632
47631
  this.clientIdService = clientIdService;
47633
47632
  this.documentStore = documentStore;
47634
47633
  this.destructManager = destructManager;
47635
47634
  this.documentIdentityService = documentIdentityService;
47636
- /**
47637
- * As long as there are mutations in flight we do not want to send queries because it causes race conditions,
47638
- * preventing parallel queries and mutations simplifies the mental model.
47639
- */
47640
- this.safeToSendQueriesToServer = new external_rxjs_namespaceObject.BehaviorSubject(true);
47635
+ this.querySender = querySender;
47641
47636
  /**
47642
47637
  * An observable used by the data manager, the query subscription manager (this class) identifies when a document no
47643
47638
  * longer has queries that it is part of their result, such document is considered orphan. The data manager will mark
@@ -47646,12 +47641,6 @@ class QuerySubscriptionManager {
47646
47641
  this.onOrphanDocuments = new external_rxjs_namespaceObject.Subject();
47647
47642
  /** All the currently running queries with their full state. */
47648
47643
  this.ongoingQueries = new Map();
47649
- /**
47650
- * The number of queries that are currently in-flight (about to be sent to the server or sent but did not get a
47651
- * response yet). This is used by the data manager to prevent it from sending mutations while there are in-flight
47652
- * queries.
47653
- */
47654
- this.inflightQueriesCount = new external_rxjs_namespaceObject.BehaviorSubject(0);
47655
47644
  /**
47656
47645
  * The two maps below maintain the relation between document ids we know about locally to clientRequestIds (queries).
47657
47646
  * This relation is used for determining whether a document can be safely removed.
@@ -47663,6 +47652,7 @@ class QuerySubscriptionManager {
47663
47652
  * queries)
47664
47653
  */
47665
47654
  this.queryMappingManager = new ClientQueryMappingManager();
47655
+ this.queryResultsSubject = new external_rxjs_namespaceObject.Subject();
47666
47656
  /**
47667
47657
  * In the case that a document ID changed on the server, this observable will notify us about the change, and we
47668
47658
  * will update the mapping.
@@ -47673,9 +47663,12 @@ class QuerySubscriptionManager {
47673
47663
  this.refreshOngoingQueries();
47674
47664
  });
47675
47665
  this.destructManager.onPreDestruct(() => {
47676
- this.predestruct();
47666
+ this.preDestruct();
47677
47667
  });
47678
47668
  }
47669
+ observeQueryResults() {
47670
+ return this.queryResultsSubject.asObservable();
47671
+ }
47679
47672
  /**
47680
47673
  * Returns true if the client knows about this clientRequestId. It may happen that it will return false in the case
47681
47674
  * that the client unsubscribed from a query but the server sent a mutation update for this clientRequestId.
@@ -47708,11 +47701,12 @@ class QuerySubscriptionManager {
47708
47701
  return;
47709
47702
  }
47710
47703
  ongoingQuery.gotInitialResponse = true;
47704
+ ongoingQuery.isInFlight = false;
47711
47705
  }
47712
47706
  /** Given a document, returns all the queries that should be notified with the new document properties. */
47713
47707
  findQueriesForDocument(doc, squidDocId) {
47714
47708
  const { collectionName, integrationId } = parseSquidDocId(squidDocId);
47715
- const mapping = this.queryMappingManager.getMapping(APP_ID, collectionName, integrationId);
47709
+ const mapping = this.queryMappingManager.getMapping(collectionName, integrationId);
47716
47710
  if (!mapping)
47717
47711
  return [];
47718
47712
  return findQueriesForDocumentSync(mapping, doc);
@@ -47836,10 +47830,7 @@ class QuerySubscriptionManager {
47836
47830
  * If the parent query has more or equal number results to the given limit - it may be that the sub-query
47837
47831
  * may need to see different result from what the parent sees.
47838
47832
  */
47839
- if (candidateParentQuery.dataSubject.value.length >= limit) {
47840
- return false;
47841
- }
47842
- return true;
47833
+ return candidateParentQuery.dataSubject.value.length < limit;
47843
47834
  }
47844
47835
  /**
47845
47836
  * Given an ongoing query, search for candidate ongoing queries that can serve as a parent.
@@ -47893,7 +47884,7 @@ class QuerySubscriptionManager {
47893
47884
  return this.allOngoingQueriesGotInitialResult(rootOngoingQuery);
47894
47885
  }), (0,external_rxjs_namespaceObject.startWith)(undefined), (0,external_rxjs_namespaceObject.pairwise)(), filter(([before, after]) => {
47895
47886
  return !lodash.isEqual(before, after);
47896
- }), map_map(([ignored, after]) => after),
47887
+ }), map_map(([_ignored, after]) => after),
47897
47888
  // This handles 'subscribe = false'
47898
47889
  subscribe ? (0,external_rxjs_namespaceObject.tap)() : (0,external_rxjs_namespaceObject.take)(1), (0,external_rxjs_namespaceObject.finalize)(() => {
47899
47890
  var _a;
@@ -47936,12 +47927,6 @@ class QuerySubscriptionManager {
47936
47927
  this.onOrphanDocuments.next(orphanDocument);
47937
47928
  }
47938
47929
  }
47939
- /** Will resolve once all the in-flight queries are done. */
47940
- async waitForAllQueriesToFinish() {
47941
- return (0,external_rxjs_namespaceObject.firstValueFrom)(this.inflightQueriesCount.pipe(filter((count) => count === 0))).then(() => {
47942
- return undefined;
47943
- });
47944
- }
47945
47930
  /** Register logic for cleaning up the query when it is unsubscribed. */
47946
47931
  registerQueryFinalizer(ongoingQuery) {
47947
47932
  const clientRequestId = ongoingQuery.clientRequestId;
@@ -47951,7 +47936,7 @@ class QuerySubscriptionManager {
47951
47936
  if (ongoingQuery.unsubscribeBlockerCount.value > 0) {
47952
47937
  await (0,external_rxjs_namespaceObject.firstValueFrom)((0,external_rxjs_namespaceObject.race)(this.destructManager.observeIsDestructing(), ongoingQuery.unsubscribeBlockerCount.pipe(filter((count) => count === 0))));
47953
47938
  }
47954
- this.queryMappingManager.removeQuery(clientRequestId, querySubscriptionId).then();
47939
+ this.queryMappingManager.removeQuery(querySubscriptionId).then();
47955
47940
  this.ongoingQueries.delete(clientRequestId);
47956
47941
  if (ongoingQuery.subscribe) {
47957
47942
  if (!ongoingQuery.isEmptyForJoin && ongoingQuery.activated) {
@@ -48168,9 +48153,7 @@ class QuerySubscriptionManager {
48168
48153
  supportedQuery.dataSubject.complete();
48169
48154
  }
48170
48155
  }
48171
- predestruct() {
48172
- this.safeToSendQueriesToServer.next(false);
48173
- this.safeToSendQueriesToServer.complete();
48156
+ preDestruct() {
48174
48157
  this.unsubscribe();
48175
48158
  }
48176
48159
  unsubscribe() {
@@ -48192,7 +48175,7 @@ class QuerySubscriptionManager {
48192
48175
  const query = ongoingQuery.query;
48193
48176
  const clientRequestId = ongoingQuery.clientRequestId;
48194
48177
  const querySubscriptionId = getQuerySubscriptionId(this.clientIdService.getClientId(), clientRequestId);
48195
- this.queryMappingManager.addQuery(APP_ID, query, querySubscriptionId);
48178
+ this.queryMappingManager.addQuery(query, querySubscriptionId);
48196
48179
  this.ongoingQueries.set(clientRequestId, ongoingQuery);
48197
48180
  const parentOngoingQuery = this.findValidParentOfOngoingQuery(ongoingQuery);
48198
48181
  if (parentOngoingQuery) {
@@ -48287,27 +48270,21 @@ class QuerySubscriptionManager {
48287
48270
  clientRequestId: ongoingQuery.clientRequestId,
48288
48271
  subscribe: ongoingQuery.subscribe,
48289
48272
  };
48290
- this.safeToSendQueriesToServer.pipe(filter(Boolean), (0,external_rxjs_namespaceObject.take)(1)).subscribe(() => {
48291
- if (ongoingQuery.done) {
48292
- return;
48293
- }
48294
- this.inflightQueriesCount.next(this.inflightQueriesCount.value + 1);
48295
- ongoingQuery.isInFlight = true;
48296
- this.rpcManager
48297
- .post('query/query', queryRequest)
48298
- .catch((e) => {
48299
- DebugLogger.debug('Query error', ongoingQuery.query, e);
48300
- ongoingQuery.dataSubject.error(e);
48301
- ongoingQuery.done = true;
48302
- ongoingQuery.queryRegistered.error('query failed');
48303
- })
48304
- .then(() => {
48305
- ongoingQuery.queryRegistered.next(true);
48306
- })
48307
- .finally(() => {
48308
- this.inflightQueriesCount.next(this.inflightQueriesCount.value - 1);
48309
- ongoingQuery.isInFlight = false;
48310
- });
48273
+ ongoingQuery.isInFlight = true;
48274
+ this.querySender
48275
+ .sendQuery(queryRequest)
48276
+ .then((queryResult) => {
48277
+ ongoingQuery.queryRegistered.next(true);
48278
+ this.queryResultsSubject.next(queryResult);
48279
+ })
48280
+ .catch((e) => {
48281
+ DebugLogger.debug('Query error', ongoingQuery.query, e);
48282
+ ongoingQuery.dataSubject.error(e);
48283
+ ongoingQuery.done = true;
48284
+ ongoingQuery.queryRegistered.error('query failed');
48285
+ })
48286
+ .finally(() => {
48287
+ ongoingQuery.isInFlight = false;
48311
48288
  });
48312
48289
  }
48313
48290
  /** naive way to refresh queries/subscriptions when we have a new client id */
@@ -48390,7 +48367,7 @@ class ClientQueryMappingManager {
48390
48367
  this.stateService = new StateService();
48391
48368
  this.querySubscriptionIdToQuery = {};
48392
48369
  }
48393
- addQuery(appId, query, querySubscriptionId) {
48370
+ addQuery(query, querySubscriptionId) {
48394
48371
  this.stateService.runInBatch(() => {
48395
48372
  let condCount = 0;
48396
48373
  const visitedEqualityFields = new Set();
@@ -48422,7 +48399,7 @@ class ClientQueryMappingManager {
48422
48399
  });
48423
48400
  this.querySubscriptionIdToQuery[querySubscriptionId] = query;
48424
48401
  }
48425
- async removeQuery(appId, querySubscriptionId) {
48402
+ async removeQuery(querySubscriptionId) {
48426
48403
  const query = this.querySubscriptionIdToQuery[querySubscriptionId];
48427
48404
  if (!query)
48428
48405
  return;
@@ -48448,7 +48425,7 @@ class ClientQueryMappingManager {
48448
48425
  });
48449
48426
  return query;
48450
48427
  }
48451
- getMapping(appId, collectionName, integrationId) {
48428
+ getMapping(collectionName, integrationId) {
48452
48429
  return this.stateService.getStateInPath([
48453
48430
  'queryMapping',
48454
48431
  collectionName,
@@ -48477,11 +48454,9 @@ class ClientQueryMappingManager {
48477
48454
 
48478
48455
 
48479
48456
  class RpcManager {
48480
- constructor(region, appId, socketManager, destructManager, headers = {}, authManager, clientIdService) {
48457
+ constructor(region, appId, destructManager, headers = {}, authManager, clientIdService) {
48481
48458
  this.region = region;
48482
48459
  this.appId = appId;
48483
- this.socketManager = socketManager;
48484
- this.destructManager = destructManager;
48485
48460
  this.authManager = authManager;
48486
48461
  this.clientIdService = clientIdService;
48487
48462
  this.staticHeaders = {};
@@ -48530,8 +48505,6 @@ class RpcManager {
48530
48505
  return this.staticHeaders;
48531
48506
  }
48532
48507
  async ready() {
48533
- // Waiting for socket connection to be established before sending an RPC request
48534
- await (0,external_rxjs_namespaceObject.firstValueFrom)((0,external_rxjs_namespaceObject.race)(this.socketManager.observeConnectionReady().pipe((0,external_rxjs_namespaceObject.filter)(Boolean)), this.destructManager.observeIsDestructing()));
48535
48508
  await this.authManager.waitForReadyState();
48536
48509
  }
48537
48510
  async post(path, message) {
@@ -48551,7 +48524,13 @@ class RpcManager {
48551
48524
  body: serialization_serializeObj(message),
48552
48525
  headers: headers,
48553
48526
  });
48554
- return await this.parseResponse(response);
48527
+ const parsedResponse = await this.parseResponse(response);
48528
+ DebugLogger.debug(`received response: ${JSON.stringify(parsedResponse)}`);
48529
+ return parsedResponse;
48530
+ }
48531
+ catch (e) {
48532
+ DebugLogger.debug(`received error: ${JSON.stringify(e)}`);
48533
+ throw e;
48555
48534
  }
48556
48535
  finally {
48557
48536
  this.onGoingRpcCounter.next(this.onGoingRpcCounter.value - 1);
@@ -48563,7 +48542,7 @@ class RpcManager {
48563
48542
  try {
48564
48543
  text = await response.text();
48565
48544
  try {
48566
- json = JSON.parse(text);
48545
+ json = deserializeObj(text);
48567
48546
  }
48568
48547
  catch (_a) { }
48569
48548
  }
@@ -48663,7 +48642,6 @@ class SocketManager {
48663
48642
  this.allMessagesObserver = new external_rxjs_namespaceObject.Subject();
48664
48643
  this.connectionReady = new external_rxjs_namespaceObject.BehaviorSubject(false);
48665
48644
  this.seenMessageIds = new Set();
48666
- this.firstConnection = true;
48667
48645
  this.destructSubject = new external_rxjs_namespaceObject.Subject();
48668
48646
  /**
48669
48647
  * On a client disconnecting, we wait for a bit to see if the client reconnects,
@@ -48737,11 +48715,7 @@ class SocketManager {
48737
48715
  }
48738
48716
  onConnectionReady() {
48739
48717
  this.connectionReady.next(true);
48740
- /** Catchup on messages if reconnecting */
48741
- if (!this.firstConnection) {
48742
- this.sendMessage({ type: 'catchup' });
48743
- }
48744
- this.firstConnection = false;
48718
+ this.sendMessage({ type: 'catchup' });
48745
48719
  }
48746
48720
  onMessage(messagesStr) {
48747
48721
  if (messagesStr === 'connectionReady') {
@@ -48790,6 +48764,85 @@ class SocketManager {
48790
48764
  }
48791
48765
  }
48792
48766
 
48767
+ ;// CONCATENATED MODULE: ./src/query/query-sender.ts
48768
+
48769
+
48770
+ class QuerySender {
48771
+ constructor(rpcManager, destructManager) {
48772
+ this.rpcManager = rpcManager;
48773
+ this.destructManager = destructManager;
48774
+ /**
48775
+ * A collection of query requests awaiting batch dispatch.
48776
+ */
48777
+ this.pendingQueryRequests = [];
48778
+ /**
48779
+ * As long as there are mutations in flight we do not want to send queries because it causes race conditions,
48780
+ * preventing parallel queries and mutations simplifies the mental model.
48781
+ */
48782
+ this.safeToSendQueriesToServer = new external_rxjs_namespaceObject.BehaviorSubject(true);
48783
+ /**
48784
+ * The number of queries that are currently in-flight (about to be sent to the server or sent but did not get a
48785
+ * response yet). This is used by the data manager to prevent it from sending mutations while there are in-flight
48786
+ * queries.
48787
+ */
48788
+ this.inflightQueriesCount = new external_rxjs_namespaceObject.BehaviorSubject(0);
48789
+ this.destructManager.onPreDestruct(() => {
48790
+ this.preDestruct();
48791
+ });
48792
+ }
48793
+ async sendQuery(queryRequest) {
48794
+ const responseSubject = new external_rxjs_namespaceObject.Subject();
48795
+ const responsePromise = (0,external_rxjs_namespaceObject.firstValueFrom)(responseSubject);
48796
+ this.pendingQueryRequests.push({ queryRequest, responseSubject });
48797
+ if (this.pendingQueryBatchTimeout) {
48798
+ clearTimeout(this.pendingQueryBatchTimeout);
48799
+ this.pendingQueryBatchTimeout = undefined;
48800
+ }
48801
+ this.pendingQueryBatchTimeout = setTimeout(() => {
48802
+ this.safeToSendQueriesToServer.pipe(filter(Boolean), (0,external_rxjs_namespaceObject.take)(1)).subscribe(() => {
48803
+ this.processQueryBatch();
48804
+ });
48805
+ }, 0);
48806
+ return responsePromise;
48807
+ }
48808
+ async processQueryBatch() {
48809
+ const queryRequestAndSubjects = this.pendingQueryRequests.splice(0);
48810
+ const pendingQueryRequests = queryRequestAndSubjects.map(({ queryRequest }) => queryRequest);
48811
+ const responseSubjects = queryRequestAndSubjects.map(({ responseSubject }) => responseSubject);
48812
+ this.inflightQueriesCount.next(this.inflightQueriesCount.value + pendingQueryRequests.length);
48813
+ try {
48814
+ const batchResponse = await this.rpcManager.post('query/batchQueries', pendingQueryRequests);
48815
+ for (const { queryRequest, responseSubject } of queryRequestAndSubjects) {
48816
+ const clientRequestId = queryRequest.clientRequestId;
48817
+ const error = batchResponse.errors[clientRequestId];
48818
+ const result = batchResponse.results[clientRequestId];
48819
+ if (error) {
48820
+ responseSubject.error(error);
48821
+ }
48822
+ else {
48823
+ responseSubject.next(result);
48824
+ }
48825
+ }
48826
+ }
48827
+ catch (e) {
48828
+ responseSubjects.forEach((responseSubject) => responseSubject.error(e));
48829
+ }
48830
+ finally {
48831
+ this.inflightQueriesCount.next(this.inflightQueriesCount.value - pendingQueryRequests.length);
48832
+ }
48833
+ }
48834
+ /** Will resolve once all the in-flight queries are done. */
48835
+ async waitForAllQueriesToFinish() {
48836
+ return (0,external_rxjs_namespaceObject.firstValueFrom)(this.inflightQueriesCount.pipe(filter((count) => count === 0))).then(() => {
48837
+ return undefined;
48838
+ });
48839
+ }
48840
+ preDestruct() {
48841
+ this.safeToSendQueriesToServer.next(false);
48842
+ this.safeToSendQueriesToServer.complete();
48843
+ }
48844
+ }
48845
+
48793
48846
  ;// CONCATENATED MODULE: ./src/squid.ts
48794
48847
 
48795
48848
 
@@ -48813,6 +48866,7 @@ class SocketManager {
48813
48866
 
48814
48867
 
48815
48868
 
48869
+
48816
48870
 
48817
48871
 
48818
48872
  /**
@@ -48997,18 +49051,19 @@ class Squid {
48997
49051
  this.clientIdService = new ClientIdService(this.destructManager);
48998
49052
  this.authManager = new AuthManager(this.destructManager, options.apiKey);
48999
49053
  this.socketManager = new SocketManager(this.clientIdService, options.region, appId, options.messageNotificationWrapper, this.destructManager, this.authManager);
49000
- this.rpcManager = new RpcManager(options.region, appId, this.socketManager, this.destructManager, httpHeaders, this.authManager, this.clientIdService);
49054
+ this.rpcManager = new RpcManager(options.region, appId, this.destructManager, httpHeaders, this.authManager, this.clientIdService);
49001
49055
  this.documentStore = new DocumentStore();
49002
49056
  this.lockManager = new LockManager();
49003
49057
  this.distributedLockManager = new DistributedLockManager(this.socketManager, this.destructManager);
49004
49058
  this.documentIdentityService = new DocumentIdentityService(this.documentStore, this.destructManager);
49005
49059
  this.documentReferenceFactory = new DocumentReferenceFactory(this.documentIdentityService);
49006
- this.querySubscriptionManager = new QuerySubscriptionManager(this.rpcManager, this.clientIdService, this.documentStore, this.destructManager, this.documentIdentityService);
49060
+ this.querySender = new QuerySender(this.rpcManager, this.destructManager);
49061
+ this.querySubscriptionManager = new QuerySubscriptionManager(this.rpcManager, this.clientIdService, this.documentStore, this.destructManager, this.documentIdentityService, this.querySender);
49007
49062
  this.localQueryManager = new LocalQueryManager(this.documentStore, this.documentReferenceFactory, this.querySubscriptionManager);
49008
- const mutationSender = new MutationSender(this.rpcManager, this.lockManager, this.querySubscriptionManager);
49063
+ const mutationSender = new MutationSender(this.rpcManager, this.lockManager, this.querySender);
49009
49064
  this.queryBuilderFactory = new QueryBuilderFactory(this.querySubscriptionManager, this.localQueryManager, this.documentReferenceFactory, this.documentIdentityService);
49010
49065
  this.collectionReferenceFactory = new CollectionReferenceFactory(this.documentReferenceFactory, this.queryBuilderFactory, this.querySubscriptionManager);
49011
- this.dataManager = new DataManager(this.documentStore, mutationSender, this.socketManager, this.querySubscriptionManager, this.queryBuilderFactory, this.lockManager, this.destructManager, this.documentIdentityService);
49066
+ this.dataManager = new DataManager(this.documentStore, mutationSender, this.socketManager, this.querySubscriptionManager, this.queryBuilderFactory, this.lockManager, this.destructManager, this.documentIdentityService, this.querySender);
49012
49067
  this.documentReferenceFactory.setDataManager(this.dataManager);
49013
49068
  this.backendFunctionManager = new BackendFunctionManager(this.clientIdService, this.rpcManager, this.socketManager);
49014
49069
  this.namedQueryManager = new NamedQueryManager(this.rpcManager, this.socketManager);
@@ -1,4 +1,5 @@
1
1
  import { DocTimestamp, LockManager, Mutation, SquidDocId, SquidDocument } from '@squidcloud/common';
2
+ import { QuerySender } from './query/query-sender';
2
3
  import { DestructManager } from './destruct.manager';
3
4
  import DocumentIdentityService from './document-identity.service';
4
5
  import { DocumentStore } from './document-store';
@@ -20,6 +21,7 @@ export declare class DataManager {
20
21
  private readonly lockManager;
21
22
  private readonly destructManager;
22
23
  private readonly documentIdentityService;
24
+ private readonly querySender;
23
25
  private readonly docIdToLocalTimestamp;
24
26
  private currentTransactionId;
25
27
  /**
@@ -88,7 +90,7 @@ export declare class DataManager {
88
90
  private readonly refreshDocIdToTimestamp;
89
91
  private deleteExpiredTimestampsInterval;
90
92
  private handleIncomingMessagesForTests;
91
- constructor(documentStore: DocumentStore, mutationSender: MutationSender, socketManager: SocketManager, querySubscriptionManager: QuerySubscriptionManager, queryBuilderFactory: QueryBuilderFactory, lockManager: LockManager, destructManager: DestructManager, documentIdentityService: DocumentIdentityService);
93
+ constructor(documentStore: DocumentStore, mutationSender: MutationSender, socketManager: SocketManager, querySubscriptionManager: QuerySubscriptionManager, queryBuilderFactory: QueryBuilderFactory, lockManager: LockManager, destructManager: DestructManager, documentIdentityService: DocumentIdentityService, querySender: QuerySender);
92
94
  getProperties(squidDocId: SquidDocId): SquidDocument | undefined;
93
95
  /** Whether a document has changes that are out of sync with the server. */
94
96
  isDirty(squidDocId: SquidDocId): boolean;
@@ -1,10 +1,10 @@
1
1
  import { IntegrationId, LockManager, MutateResponse, Mutation } from '@squidcloud/common';
2
- import { QuerySubscriptionManager } from '../query/query-subscription.manager';
3
2
  import { RpcManager } from '../rpc.manager';
3
+ import { QuerySender } from '../query/query-sender';
4
4
  export declare class MutationSender {
5
5
  private readonly rpcManager;
6
6
  private readonly lockManager;
7
- private readonly querySubscriptionManager;
8
- constructor(rpcManager: RpcManager, lockManager: LockManager, querySubscriptionManager: QuerySubscriptionManager);
7
+ private readonly querySender;
8
+ constructor(rpcManager: RpcManager, lockManager: LockManager, querySender: QuerySender);
9
9
  sendMutations(mutations: Array<Mutation>, integrationId: IntegrationId): Promise<MutateResponse>;
10
10
  }
@@ -0,0 +1,33 @@
1
+ import { RpcManager } from '../rpc.manager';
2
+ import { QueryRequest, QueryResultData } from '@squidcloud/common';
3
+ import { BehaviorSubject } from 'rxjs';
4
+ import { DestructManager } from '../destruct.manager';
5
+ export declare class QuerySender {
6
+ private readonly rpcManager;
7
+ private readonly destructManager;
8
+ /**
9
+ * A collection of query requests awaiting batch dispatch.
10
+ */
11
+ private readonly pendingQueryRequests;
12
+ /**
13
+ * A timeout identifier used to ensure that only the last query in rapid succession triggers the batch send process.
14
+ */
15
+ private pendingQueryBatchTimeout?;
16
+ /**
17
+ * As long as there are mutations in flight we do not want to send queries because it causes race conditions,
18
+ * preventing parallel queries and mutations simplifies the mental model.
19
+ */
20
+ readonly safeToSendQueriesToServer: BehaviorSubject<boolean>;
21
+ /**
22
+ * The number of queries that are currently in-flight (about to be sent to the server or sent but did not get a
23
+ * response yet). This is used by the data manager to prevent it from sending mutations while there are in-flight
24
+ * queries.
25
+ */
26
+ private readonly inflightQueriesCount;
27
+ constructor(rpcManager: RpcManager, destructManager: DestructManager);
28
+ sendQuery(queryRequest: QueryRequest): Promise<QueryResultData>;
29
+ private processQueryBatch;
30
+ /** Will resolve once all the in-flight queries are done. */
31
+ waitForAllQueriesToFinish(): Promise<void>;
32
+ private preDestruct;
33
+ }
@@ -1,70 +1,21 @@
1
- import { Alias, ClientRequestId, JoinCondition, Query, QuerySubscriptionId, SquidDocId, SquidDocument } from '@squidcloud/common';
2
- import { BehaviorSubject, Observable, ReplaySubject, Subject } from 'rxjs';
1
+ import { Alias, ClientRequestId, JoinCondition, Query, QueryResultData, QuerySubscriptionId, SquidDocId, SquidDocument } from '@squidcloud/common';
2
+ import { Observable, Subject } from 'rxjs';
3
3
  import { DestructManager } from '../destruct.manager';
4
4
  import DocumentIdentityService from '../document-identity.service';
5
5
  import { DocumentStore } from '../document-store';
6
6
  import { RpcManager } from '../rpc.manager';
7
7
  import { ClientIdService } from '../client-id.service';
8
+ import { OngoingQuery } from './query.types';
9
+ import { QuerySender } from './query-sender';
8
10
  export declare const FETCH_BEYOND_LIMIT = 100;
9
11
  export declare const LIMIT_UNDERFLOW_TRIGGER = 20;
10
- interface OngoingQuery {
11
- clientRequestId: ClientRequestId;
12
- query: Query;
13
- supportedQueries: Array<OngoingQuery>;
14
- supportingOngoingQuery?: OngoingQuery;
15
- gotInitialResponse: boolean;
16
- activated: boolean;
17
- joinCondition?: JoinCondition;
18
- alias: Alias;
19
- dataSubject: BehaviorSubject<Array<SquidDocument> | null>;
20
- queryRegistered: BehaviorSubject<boolean>;
21
- /**
22
- * In case that this query is a parent of another query (that is, the other query is a subset of this query), this
23
- * query should not be unsubscribed from the server until we registered the child query in the server.
24
- */
25
- unsubscribeBlockerCount: BehaviorSubject<number>;
26
- subscribe: boolean;
27
- /**
28
- * In case of joins, and if this ongoing query is the root, this field emits all the supported observables
29
- * for example A.join(B, {..some join condition..}).join(C, {...some join condition}.
30
- * This field will emit [A.subject.pipe(), B.subject.pipe(), C.subject.pipe()]. Any new supported queries will be
31
- * added here.
32
- */
33
- allObservables?: ReplaySubject<Array<Observable<DocsAndAlias>>>;
34
- isEmptyForJoin: boolean;
35
- done: boolean;
36
- isInFlight: boolean;
37
- forceFetchFromServer: boolean;
38
- /**
39
- * If there's a limit, we request `limit + FETCH_BEYOND_LIMIT` documents from the server.
40
- * If we got that many documents, that means there may be even more. In that case, if our result set goes below
41
- * `limit + LIMIT_UNDERFLOW_TRIGGER` documents (due to local or remote deletions), we need to resend the query to the
42
- * server to potentially get more documents.
43
- * If the number of documents is less than `limit + FETCH_BEYOND_LIMIT`, that means there are not that many documents
44
- * on the server, so we don't need to resend the query regardless of how small our result size is or becomes.
45
- */
46
- limitUnderflowState: LimitUnderflowState;
47
- }
48
- declare enum LimitUnderflowState {
49
- UNKNOWN = 0,
50
- DISABLED = 1,
51
- ENABLED = 2
52
- }
53
- interface DocsAndAlias {
54
- docs: Array<SquidDocument>;
55
- alias: Alias;
56
- }
57
12
  export declare class QuerySubscriptionManager {
58
13
  private readonly rpcManager;
59
14
  private readonly clientIdService;
60
15
  private readonly documentStore;
61
16
  private readonly destructManager;
62
17
  private readonly documentIdentityService;
63
- /**
64
- * As long as there are mutations in flight we do not want to send queries because it causes race conditions,
65
- * preventing parallel queries and mutations simplifies the mental model.
66
- */
67
- readonly safeToSendQueriesToServer: BehaviorSubject<boolean>;
18
+ private readonly querySender;
68
19
  /**
69
20
  * An observable used by the data manager, the query subscription manager (this class) identifies when a document no
70
21
  * longer has queries that it is part of their result, such document is considered orphan. The data manager will mark
@@ -73,12 +24,6 @@ export declare class QuerySubscriptionManager {
73
24
  onOrphanDocuments: Subject<string[]>;
74
25
  /** All the currently running queries with their full state. */
75
26
  private readonly ongoingQueries;
76
- /**
77
- * The number of queries that are currently in-flight (about to be sent to the server or sent but did not get a
78
- * response yet). This is used by the data manager to prevent it from sending mutations while there are in-flight
79
- * queries.
80
- */
81
- private readonly inflightQueriesCount;
82
27
  /**
83
28
  * The two maps below maintain the relation between document ids we know about locally to clientRequestIds (queries).
84
29
  * This relation is used for determining whether a document can be safely removed.
@@ -90,7 +35,9 @@ export declare class QuerySubscriptionManager {
90
35
  * queries)
91
36
  */
92
37
  private readonly queryMappingManager;
93
- constructor(rpcManager: RpcManager, clientIdService: ClientIdService, documentStore: DocumentStore, destructManager: DestructManager, documentIdentityService: DocumentIdentityService);
38
+ private readonly queryResultsSubject;
39
+ constructor(rpcManager: RpcManager, clientIdService: ClientIdService, documentStore: DocumentStore, destructManager: DestructManager, documentIdentityService: DocumentIdentityService, querySender: QuerySender);
40
+ observeQueryResults(): Observable<QueryResultData>;
94
41
  /**
95
42
  * Returns true if the client knows about this clientRequestId. It may happen that it will return false in the case
96
43
  * that the client unsubscribed from a query but the server sent a mutation update for this clientRequestId.
@@ -144,8 +91,6 @@ export declare class QuerySubscriptionManager {
144
91
  * Removes a query from the mapping and updates the orphan documents as needed.
145
92
  */
146
93
  private removeClientRequestIdMapping;
147
- /** Will resolve once all the in-flight queries are done. */
148
- waitForAllQueriesToFinish(): Promise<void>;
149
94
  /** Register logic for cleaning up the query when it is unsubscribed. */
150
95
  private registerQueryFinalizer;
151
96
  /** Creates a graph of ongoing queries and returns the root of the graph. */
@@ -157,7 +102,7 @@ export declare class QuerySubscriptionManager {
157
102
  private updateOngoingQueryWithNewDataFromSupportingQuery;
158
103
  private allOngoingQueriesGotInitialResult;
159
104
  private completeAllSupportedQueries;
160
- private predestruct;
105
+ private preDestruct;
161
106
  unsubscribe(): void;
162
107
  hasSubscription(clientRequestId: ClientRequestId): boolean;
163
108
  /** Sends the query request to the server and makes sure to unsubscribe once the subject completes. */
@@ -183,4 +128,3 @@ export declare class QuerySubscriptionManager {
183
128
  private refreshOngoingQueries;
184
129
  private migrateDocIds;
185
130
  }
186
- export {};
@@ -0,0 +1,49 @@
1
+ import { Alias, ClientRequestId, JoinCondition, Query, SquidDocument } from '@squidcloud/common';
2
+ import { BehaviorSubject, Observable, ReplaySubject } from 'rxjs';
3
+ export interface OngoingQuery {
4
+ clientRequestId: ClientRequestId;
5
+ query: Query;
6
+ supportedQueries: Array<OngoingQuery>;
7
+ supportingOngoingQuery?: OngoingQuery;
8
+ gotInitialResponse: boolean;
9
+ activated: boolean;
10
+ joinCondition?: JoinCondition;
11
+ alias: Alias;
12
+ dataSubject: BehaviorSubject<Array<SquidDocument> | null>;
13
+ queryRegistered: BehaviorSubject<boolean>;
14
+ /**
15
+ * In case that this query is a parent of another query (that is, the other query is a subset of this query), this
16
+ * query should not be unsubscribed from the server until we registered the child query in the server.
17
+ */
18
+ unsubscribeBlockerCount: BehaviorSubject<number>;
19
+ subscribe: boolean;
20
+ /**
21
+ * In case of joins, and if this ongoing query is the root, this field emits all the supported observables
22
+ * for example A.join(B, {...some join condition...}).join(C, {...some join condition}.
23
+ * This field will emit [A.subject.pipe(), B.subject.pipe(), C.subject.pipe()]. Any new supported queries will be
24
+ * added here.
25
+ */
26
+ allObservables?: ReplaySubject<Array<Observable<DocsAndAlias>>>;
27
+ isEmptyForJoin: boolean;
28
+ done: boolean;
29
+ isInFlight: boolean;
30
+ forceFetchFromServer: boolean;
31
+ /**
32
+ * If there's a limit, we request `limit + FETCH_BEYOND_LIMIT` documents from the server.
33
+ * If we got that many documents, that means there may be even more. In that case, if our result set goes below
34
+ * `limit + LIMIT_UNDERFLOW_TRIGGER` documents (due to local or remote deletions), we need to resend the query to the
35
+ * server to potentially get more documents.
36
+ * If the number of documents is less than `limit + FETCH_BEYOND_LIMIT`, that means there are not that many documents
37
+ * on the server, so we don't need to resend the query regardless of how small our result size is or becomes.
38
+ */
39
+ limitUnderflowState: LimitUnderflowState;
40
+ }
41
+ export declare enum LimitUnderflowState {
42
+ UNKNOWN = 0,
43
+ DISABLED = 1,
44
+ ENABLED = 2
45
+ }
46
+ export interface DocsAndAlias {
47
+ docs: Array<SquidDocument>;
48
+ alias: Alias;
49
+ }
@@ -2,17 +2,14 @@ import { SupportedSquidRegion } from '@squidcloud/common';
2
2
  import { AuthManager } from './auth.manager';
3
3
  import { ClientIdService } from './client-id.service';
4
4
  import { DestructManager } from './destruct.manager';
5
- import { SocketManager } from './socket.manager';
6
5
  export declare class RpcManager {
7
6
  private readonly region;
8
7
  private readonly appId;
9
- private readonly socketManager;
10
- private readonly destructManager;
11
8
  private readonly authManager;
12
9
  private readonly clientIdService;
13
10
  private readonly staticHeaders;
14
11
  private readonly onGoingRpcCounter;
15
- constructor(region: SupportedSquidRegion, appId: string, socketManager: SocketManager, destructManager: DestructManager, headers: Record<string, string> | undefined, authManager: AuthManager, clientIdService: ClientIdService);
12
+ constructor(region: SupportedSquidRegion, appId: string, destructManager: DestructManager, headers: Record<string, string> | undefined, authManager: AuthManager, clientIdService: ClientIdService);
16
13
  awaitAllSettled(): Promise<void>;
17
14
  setStaticHeader(key: string, value: string): void;
18
15
  deleteStaticHeader(key: string): void;
@@ -15,7 +15,6 @@ export declare class SocketManager {
15
15
  private readonly connectionReady;
16
16
  private readonly seenMessageIds;
17
17
  private socket;
18
- private firstConnection;
19
18
  private destructSubject;
20
19
  /**
21
20
  * On a client disconnecting, we wait for a bit to see if the client reconnects,
@@ -73,6 +73,7 @@ export declare class Squid {
73
73
  private readonly aiClientFactory;
74
74
  private readonly _connectionDetails;
75
75
  private readonly secretClient;
76
+ private readonly querySender;
76
77
  private static readonly squidInstancesMap;
77
78
  /**
78
79
  * Creates a new instance of Squid with the given options.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@squidcloud/client",
3
- "version": "1.0.114",
3
+ "version": "1.0.115",
4
4
  "description": "A typescript implementation of the Squid client",
5
5
  "main": "dist/cjs/index.js",
6
6
  "types": "dist/typescript-client/src/index.d.ts",