@firebase/data-connect 0.6.1-20260505164105 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,27 +14,63 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
- import { AbstractDataConnectTransport, DataConnectResponse, SubscribeObserver } from '../transport';
17
+ import { DataConnectOptions, TransportOptions } from '../../api/DataConnect';
18
+ import { AppCheckTokenProvider } from '../../core/AppCheckTokenProvider';
19
+ import { Code } from '../../core/error';
20
+ import { AuthTokenProvider } from '../../core/FirebaseAuthProvider';
21
+ import { AbstractDataConnectTransport, CallerSdkType, DataConnectResponse, SubscribeObserver } from '../transport';
18
22
  import { DataConnectStreamRequest } from './wire';
19
23
  /**
20
- * The base class for all {@link DataConnectStreamTransport | Stream Transport} implementations.
21
- * Handles management of logical streams (requests), authentication, data routing to query layer, etc.
24
+ * A promise returned to the user when invoking an operation via {@linkcode AbstractDataConnectStreamTransport.invokeQuery | invokeQuery}
25
+ * or {@linkcode AbstractDataConnectStreamTransport.invokeMutation | invokeMutation} and the functions
26
+ * that resolve/reject it.
27
+ * @internal
28
+ */
29
+ export interface InvokeOperationPromise<Data> {
30
+ responsePromise: Promise<DataConnectResponse<Data>>;
31
+ resolveFn: (response: DataConnectResponse<Data>) => void;
32
+ rejectFn: (reason: any) => void;
33
+ }
34
+ /**
35
+ * The base class for all Stream Transport implementations.
36
+ * Handles management of logical streams (requests), authentication, data routing to query layer,
37
+ * request optimizations, etc.
22
38
  * @internal
23
39
  */
24
40
  export declare abstract class AbstractDataConnectStreamTransport extends AbstractDataConnectTransport {
25
- /** Optional callback invoked when the stream closes gracefully. */
26
- onGracefulStreamClose?: () => void;
41
+ protected apiKey?: string | undefined;
42
+ protected appId?: (string | null) | undefined;
43
+ protected authProvider?: AuthTokenProvider | undefined;
44
+ protected appCheckProvider?: AppCheckTokenProvider | undefined;
45
+ protected _isUsingGen: boolean;
46
+ protected _callerSdkType: CallerSdkType;
47
+ /** Optional callback invoked when the stream closes (gracefully or fatally). */
48
+ onCloseCallback?: () => void;
27
49
  /** True if the physical stream connection is fully open and ready to transmit data. */
28
50
  abstract get streamIsReady(): boolean;
29
51
  /** Is the stream currently waiting to close connection? */
30
52
  get isPendingClose(): boolean;
31
- private pendingClose;
32
53
  /** True if the transport is unable to connect to the server */
33
54
  isUnableToConnect: boolean;
34
55
  /** True if there are active subscriptions on the stream */
35
56
  get hasActiveSubscriptions(): boolean;
36
57
  /** True if there are active execute or mutation requests on the stream */
37
58
  get hasActiveExecuteRequests(): boolean;
59
+ constructor(options: DataConnectOptions, apiKey?: string | undefined, appId?: (string | null) | undefined, authProvider?: AuthTokenProvider | undefined, appCheckProvider?: AppCheckTokenProvider | undefined, transportOptions?: TransportOptions | undefined, _isUsingGen?: boolean, _callerSdkType?: CallerSdkType);
60
+ /**
61
+ * Register event listeners for browser-specific events like online/offline and visibility changes.
62
+ */
63
+ private registerBrowserEventListeners;
64
+ /**
65
+ * Remove event listeners registered by {@linkcode AbstractDataConnectStreamTransport.registerBrowserEventListeners | registerBrowserEventListeners()}
66
+ * for browser-specific events like online/offline and visibility changes.
67
+ */
68
+ private cleanupBrowserEventListeners;
69
+ /**
70
+ * Disposes of the transport instance, cleaning up event listeners and timers,
71
+ * and closing the connection.
72
+ */
73
+ cleanupAndTerminate(code?: Code, reason?: string): Promise<void>;
38
74
  /**
39
75
  * Open a physical connection to the server.
40
76
  * @returns a promise which resolves when the connection is ready, or rejects if it fails to open.
@@ -42,13 +78,12 @@ export declare abstract class AbstractDataConnectStreamTransport extends Abstrac
42
78
  protected abstract openConnection(): Promise<void>;
43
79
  /**
44
80
  * Close the physical connection with the server. Handles no cleanup - simply closes the
45
- * implementation-specific connection.
81
+ * implementation-specific connection. On failure to close, the connection is still considered closed.
46
82
  * @returns a promise which resolves when the connection is closed, or rejects if it fails to close.
47
- * On failure to close, the connection is still considered closed.
48
83
  */
49
84
  protected abstract closeConnection(): Promise<void>;
50
85
  /**
51
- * Queue a message to be sent over the stream.
86
+ * Queue a {@linkcode DataConnectStreamRequest} to be sent over the stream.
52
87
  * @param requestBody The body of the message to be sent.
53
88
  * @throws DataConnectError if sending fails.
54
89
  */
@@ -59,79 +94,125 @@ export declare abstract class AbstractDataConnectStreamTransport extends Abstrac
59
94
  * @returns A promise that resolves when the stream is open and ready.
60
95
  */
61
96
  protected abstract ensureConnection(): Promise<void>;
62
- /** The request ID of the next message to be sent. Monotonically increasing sequence number. */
97
+ /** The Request ID of the next message to be sent. Monotonically increasing sequence number starting at {@linkcode FIRST_REQUEST_ID}. */
63
98
  private requestNumber;
64
99
  /**
65
- * Generates and returns the next request ID.
100
+ * Generates and returns the next Request ID. Starts at {@linkcode FIRST_REQUEST_ID} and increments
101
+ * for each request sent.
66
102
  */
67
103
  private nextRequestId;
68
104
  /**
69
- * Map of query/variables to their active execute/resume request bodies.
105
+ * Map of query/variables to their active {@linkcode ExecuteStreamRequest} or {@linkcode ResumeStreamRequest}
106
+ * request bodies. These requests are de-duplicated by query/variables so that there is only one active
107
+ * request for each query/variables combination.
108
+ */
109
+ private activeInvokeQueryRequests;
110
+ /**
111
+ * Map of query/variables to the promises returned to the user, for invokeQuery requests which are
112
+ * queued and waiting for active request to resolve.
70
113
  */
71
- private activeQueryExecuteRequests;
114
+ private queuedInvokeQueryRequests;
72
115
  /**
73
- * Map of mutation/variables to their active execute request bodies.
116
+ * Map of mutation/variables to their active {@linkcode ExecuteStreamRequest} request bodies. Mutations
117
+ * can have more than one active request at a time as they are not idempotent, and therefore should
118
+ * not be de-duplicated.
74
119
  */
75
- private activeMutationExecuteRequests;
120
+ private activeInvokeMutationRequests;
76
121
  /**
77
- * Map of query/variables to their active subscribe request bodies.
122
+ * Map of query/variables to their active {@linkcode SubscribeStreamRequest} request bodies. There
123
+ * may only be one active request for each query/variables combination.
78
124
  */
79
- private activeSubscribeRequests;
125
+ private activeInvokeSubscribeRequests;
80
126
  /**
81
- * Map of active execution RequestIds and their corresponding Promises and resolvers.
127
+ * Map of active {@linkcode ExecuteStreamRequest} RequestIds from {@linkcode invokeQuery} and {@linkcode invokeMutation},
128
+ * and their corresponding {@linkcode InvokeOperationPromise}.
82
129
  */
83
130
  private executeRequestPromises;
84
131
  /**
85
- * Map of active subscription RequestIds and their corresponding observers.
132
+ * Map of active {@linkcode ResumeStreamRequest} RequestIds from {@linkcode invokeQuery}, and their
133
+ * corresponding {@linkcode InvokeOperationPromise}.
134
+ */
135
+ private resumeRequestPromises;
136
+ /**
137
+ * Map of active {@linkcode invokeSubscribe} RequestIds and their corresponding {@linkcode SubscribeObserver}.
86
138
  */
87
139
  private subscribeObservers;
88
- /** current close timeout from setTimeout(), if any */
89
- private closeTimeout;
90
- /** has the close timeout finished? */
91
- private closeTimeoutFinished;
140
+ /**
141
+ * Map of subscribe RequestIds to deferred unsubscription requests. Used when a client unsubscribes
142
+ * while a resume request is actively pending.
143
+ */
144
+ private pendingCancellations;
145
+ /** current idle timeout, if any */
146
+ private idleTimeout;
92
147
  /** current auth uid. used to detect if a different user logs in */
93
148
  private authUid;
94
149
  /** Flag to ensure we wait for the initial auth state once per connection attempt. */
95
150
  private hasWaitedForInitialAuth;
96
151
  /**
97
- * Tracks a query execution request, storing the request body and creating and storing a promise that
98
- * will be resolved when the response is received.
99
- * @returns The reject function and the response promise.
152
+ * Tracks an {@linkcode invokeMutation} request, storing the request body and creating and storing a
153
+ * response promise that will be resolved when the response is received.
154
+ * @returns The tracked {@linkcode InvokeOperationPromise}.
100
155
  *
101
156
  * @remarks
102
157
  * This method returns a promise, but is synchronous.
103
158
  */
104
- private trackQueryExecuteRequest;
159
+ private trackInvokeMutationRequest;
105
160
  /**
106
- * Tracks a mutation execution request, storing the request body and creating and storing a promise
107
- * that will be resolved when the response is received.
108
- * @returns The reject function and the response promise.
109
- *
110
- * @remarks
111
- * This method returns a promise, but is synchronous.
112
- */
113
- private trackMutationExecuteRequest;
114
- /**
115
- * Tracks a subscribe request, storing the request body and the notification observer.
161
+ * Tracks an {@linkcode invokeSubscribe} request, storing the request body and the {@linkcode SubscribeObserver}.
116
162
  * @remarks
117
163
  * This method is synchronous.
118
164
  */
119
- private trackSubscribeRequest;
165
+ private trackInvokeSubscribeRequest;
120
166
  /**
121
167
  * Cleans up the query execute request tracking data structures, deleting the tracked request and
122
168
  * it's associated promise.
123
169
  */
124
- private cleanupQueryExecuteRequest;
170
+ private cleanupInvokeQueryRequest;
125
171
  /**
126
172
  * Cleans up the mutation execute request tracking data structures, deleting the tracked request and
127
173
  * it's associated promise.
128
174
  */
129
- private cleanupMutationExecuteRequest;
175
+ private cleanupInvokeMutationRequest;
130
176
  /**
131
177
  * Cleans up the subscribe request tracking data structures, deleting the tracked request and
132
178
  * it's associated promise.
133
179
  */
134
- private cleanupSubscribeRequest;
180
+ private cleanupInvokeSubscribeRequest;
181
+ /** Delay for next reconnection attempt in ms */
182
+ private reconnectDelayMs;
183
+ /** Timer for reconnection */
184
+ private reconnectTimer;
185
+ /** Number of consecutive reconnection attempts */
186
+ private reconnectAttempts;
187
+ /** Callback to remove online event listener */
188
+ private removeOnlineEventListener;
189
+ /** Callback to remove visibility change event listener */
190
+ private removeVisibilityChangeEventListener;
191
+ /**
192
+ * Short-circuit a reconnection attempt, if one is pending. Triggered when an online event is
193
+ * dispatched.
194
+ */
195
+ onOnlineEventListener: () => void;
196
+ /**
197
+ * Short-circuit a reconnection attempt, if one is pending. Triggered when a visibility change
198
+ * event is dispatched.
199
+ */
200
+ onVisibilityChangeEventListener: () => void;
201
+ /**
202
+ * Cancel reconnecting.
203
+ */
204
+ private cancelReconnect;
205
+ /**
206
+ * Starts the backoff timer for reconnection attempts. We use an exponential backoff with randomized
207
+ * jitter to prevent overwhelming the backend with connection attempts.
208
+ */
209
+ private startReconnectBackoff;
210
+ private attemptReconnect;
211
+ /**
212
+ * Retriggers all active requests on the stream connection - first subscribes, then query executions,
213
+ * and skip mutations. Used after a successful reconnection.
214
+ */
215
+ private retriggerActiveRequests;
135
216
  /**
136
217
  * Tracks if the next message to be sent is the first message of the stream.
137
218
  */
@@ -152,16 +233,12 @@ export declare abstract class AbstractDataConnectStreamTransport extends Abstrac
152
233
  */
153
234
  protected onConnectionReady(): void;
154
235
  /**
155
- * Attempt to close the connection. Will only close if there are no active requests preventing it
156
- * from doing so.
157
- */
158
- private attemptClose;
159
- /**
160
- * Begin closing the connection. Waits for and cleans up all active requests, and waits for
161
- * {@link IDLE_CONNECTION_TIMEOUT_MS}. This is a graceful close - it will be called when there are
162
- * no more active subscriptions, so there's no need to cleanup.
236
+ * Begin closing the connection. Waits for {@linkcode IDLE_CONNECTION_TIMEOUT_MS} without cleaning up
237
+ * any requests (meaning it will not close after the timeout unless the requests are closed first).
238
+ * This is a graceful close - it will be called when there are no more active subscriptions, so
239
+ * there's no need to cleanup.
163
240
  */
164
- private prepareToCloseGracefully;
241
+ private startIdleCloseTimeout;
165
242
  /**
166
243
  * Cancel closing the connection.
167
244
  */
@@ -170,7 +247,12 @@ export declare abstract class AbstractDataConnectStreamTransport extends Abstrac
170
247
  * Reject all active execute promises and notify all subscribe observers with the given error.
171
248
  * Clear active request tracking maps without cancelling or re-invoking any requests.
172
249
  */
173
- private rejectAllActiveRequests;
250
+ private rejectAllRequests;
251
+ /**
252
+ * Reject all mutation execute promises.
253
+ * Clear active request tracking maps without cancelling or re-invoking any requests.
254
+ */
255
+ private rejectAllMutationsOnReconnect;
174
256
  /**
175
257
  * Called by concrete implementations when the stream is successfully closed, gracefully or otherwise.
176
258
  */
@@ -192,7 +274,7 @@ export declare abstract class AbstractDataConnectStreamTransport extends Abstrac
192
274
  */
193
275
  private sendRequestMessage;
194
276
  /**
195
- * Helper to generate a consistent string key for the tracking maps.
277
+ * Helper to generate a consistent string key for the request tracking maps.
196
278
  */
197
279
  private getMapKey;
198
280
  /**
@@ -207,6 +289,22 @@ export declare abstract class AbstractDataConnectStreamTransport extends Abstrac
207
289
  * preserves the synchronous update of the tracking data structures before the method returns.
208
290
  */
209
291
  invokeQuery<Data, Variables>(queryName: string, variables?: Variables): Promise<DataConnectResponse<Data>>;
292
+ /**
293
+ * Queue a new query execute request to be executed after the currently active query execute
294
+ * request resolves, and track + return a promise associated with the queued request. If there is
295
+ * already a queued request for this mapKey, return the existing queued request's promise instead.
296
+ */
297
+ private queueInvokeQueryRequest;
298
+ /**
299
+ * Executes or resumes a query. Does not check for any active requests which may be overwritten by
300
+ * this request - this should be handled by the caller.
301
+ */
302
+ private executeOrResumeQuery;
303
+ /**
304
+ * When a query invoke request is fulfilled, clean up and trigger the next queued
305
+ * request if one exists.
306
+ */
307
+ private onInvokeQueryRequestFulfilled;
210
308
  /**
211
309
  * @inheritdoc
212
310
  * @remarks
@@ -232,12 +330,23 @@ export declare abstract class AbstractDataConnectStreamTransport extends Abstrac
232
330
  * preserves the synchronous update of the tracking data structures before the method returns.
233
331
  */
234
332
  invokeUnsubscribe<Variables>(queryName: string, variables: Variables): void;
333
+ /**
334
+ * Cancels a subscription, cleans up the request tracking data structures, and checks to see if we
335
+ * should close the stream due to inactivity.
336
+ */
337
+ private cancelSubscription;
235
338
  onAuthTokenChanged(newToken: string | null): void;
236
339
  /**
237
340
  * Handle a response message from the server. Called by the connection-specific implementation after
238
- * it's transformed a message from the server into a {@link DataConnectResponse}.
239
- * @param requestId the requestId associated with this response.
341
+ * it's transformed a message from the server into a {@linkcode DataConnectResponse}.
342
+ * @param requestId the Request ID associated with this response.
240
343
  * @param response the response from the server.
241
344
  */
242
345
  protected handleResponse<Data>(requestId: string, response: DataConnectResponse<Data>): Promise<void>;
346
+ /**
347
+ * Handles an invoke operation response, resolving or rejecting the promise returned to the user
348
+ * Does not handle any cleanup for requests - this should be handled by the caller or the promise's
349
+ * finally() block.
350
+ */
351
+ private handleInvokeOperationResponse;
243
352
  }
@@ -14,10 +14,6 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
- import { DataConnectOptions, TransportOptions } from '../../api/DataConnect';
18
- import { AppCheckTokenProvider } from '../../core/AppCheckTokenProvider';
19
- import { AuthTokenProvider } from '../../core/FirebaseAuthProvider';
20
- import { CallerSdkType } from '../transport';
21
17
  import { AbstractDataConnectStreamTransport } from './streamTransport';
22
18
  import { DataConnectStreamRequest } from './wire';
23
19
  /**
@@ -39,12 +35,6 @@ export declare const WEBSOCKET_CLOSE_CODE = 1000;
39
35
  * @internal
40
36
  */
41
37
  export declare class WebSocketTransport extends AbstractDataConnectStreamTransport {
42
- protected apiKey?: string | undefined;
43
- protected appId?: (string | null) | undefined;
44
- protected authProvider?: AuthTokenProvider | undefined;
45
- protected appCheckProvider?: AppCheckTokenProvider | undefined;
46
- protected _isUsingGen: boolean;
47
- protected _callerSdkType: CallerSdkType;
48
38
  get endpointUrl(): string;
49
39
  /** Decodes binary WebSocket responses to strings */
50
40
  private decoder;
@@ -61,7 +51,6 @@ export declare class WebSocketTransport extends AbstractDataConnectStreamTranspo
61
51
  * or already connected). Will be resolved or rejected when the connection is opened or fails to open.
62
52
  */
63
53
  private connectionAttempt;
64
- constructor(options: DataConnectOptions, apiKey?: string | undefined, appId?: (string | null) | undefined, authProvider?: AuthTokenProvider | undefined, appCheckProvider?: AppCheckTokenProvider | undefined, transportOptions?: TransportOptions | undefined, _isUsingGen?: boolean, _callerSdkType?: CallerSdkType);
65
54
  protected ensureConnection(): Promise<void>;
66
55
  protected openConnection(): Promise<void>;
67
56
  protected closeConnection(code?: number, reason?: string): Promise<void>;