Skip to content

Commit 882fb8d

Browse files
authored
Merge 3c747e2 into 7ca7f4e
2 parents 7ca7f4e + 3c747e2 commit 882fb8d

File tree

11 files changed

+189
-61
lines changed

11 files changed

+189
-61
lines changed

.changeset/giant-months-guess.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@firebase/firestore": patch
3+
---
4+
5+
Release Bundles for Next SDK

common/api-review/firestore-exp.api.md

+28
Original file line numberDiff line numberDiff line change
@@ -278,8 +278,33 @@ export function limit(limit: number): QueryConstraint;
278278
// @public
279279
export function limitToLast(limit: number): QueryConstraint;
280280

281+
// @public
282+
export function loadBundle(firestore: FirebaseFirestore, bundleData: ReadableStream<Uint8Array> | ArrayBuffer | string): LoadBundleTask;
283+
284+
// @public
285+
export class LoadBundleTask implements PromiseLike<LoadBundleTaskProgress> {
286+
catch<R>(onRejected: (a: Error) => R | PromiseLike<R>): Promise<R | LoadBundleTaskProgress>;
287+
_completeWith(progress: LoadBundleTaskProgress): void;
288+
_failWith(error: FirestoreError): void;
289+
onProgress(next?: (progress: LoadBundleTaskProgress) => unknown, error?: (err: Error) => unknown, complete?: () => void): void;
290+
then<T, R>(onFulfilled?: (a: LoadBundleTaskProgress) => T | PromiseLike<T>, onRejected?: (a: Error) => R | PromiseLike<R>): Promise<T | R>;
291+
_updateProgress(progress: LoadBundleTaskProgress): void;
292+
}
293+
294+
// @public
295+
export interface LoadBundleTaskProgress {
296+
bytesLoaded: number;
297+
documentsLoaded: number;
298+
taskState: TaskState;
299+
totalBytes: number;
300+
totalDocuments: number;
301+
}
302+
281303
export { LogLevel }
282304

305+
// @public
306+
export function namedQuery(firestore: FirebaseFirestore, name: string): Promise<Query | null>;
307+
283308
// @public
284309
export function onSnapshot<T>(reference: DocumentReference<T>, observer: {
285310
next?: (snapshot: DocumentSnapshot<T>) => void;
@@ -463,6 +488,9 @@ export function startAt(snapshot: DocumentSnapshot_2<unknown>): QueryConstraint;
463488
// @public
464489
export function startAt(...fieldValues: unknown[]): QueryConstraint;
465490

491+
// @public
492+
export type TaskState = 'Error' | 'Running' | 'Success';
493+
466494
// @public
467495
export function terminate(firestore: FirebaseFirestore): Promise<void>;
468496

packages/firestore/exp/index.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,17 @@ export {
3232
disableNetwork,
3333
enableNetwork,
3434
terminate,
35-
useFirestoreEmulator
35+
useFirestoreEmulator,
36+
loadBundle,
37+
namedQuery
3638
} from '../src/exp/database';
3739

40+
export {
41+
LoadBundleTask,
42+
LoadBundleTaskProgress,
43+
TaskState
44+
} from '../src/exp/bundle';
45+
3846
export { Settings, PersistenceSettings } from '../src/exp/settings';
3947

4048
export {

packages/firestore/export.ts

+31-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@
1515
* limitations under the License.
1616
*/
1717

18+
import { Firestore, Query } from './src/api/database';
19+
import { LoadBundleTask } from './src/exp/bundle';
20+
import {
21+
loadBundle as expLoadBundle,
22+
namedQuery as expNamedQuery
23+
} from './src/exp/database';
24+
1825
export { Blob } from './src/api/blob';
1926
export {
2027
CollectionReference,
@@ -36,4 +43,27 @@ export { FieldPath } from './src/api/field_path';
3643
export { FieldValue } from './src/api/field_value';
3744
export { Timestamp } from './src/api/timestamp';
3845
export { FirebaseFirestore as ExpFirebaseFirestore } from './src/exp/database';
39-
export { loadBundle, namedQuery } from './src/api/bundle';
46+
47+
export function loadBundle(
48+
this: Firestore,
49+
data: ArrayBuffer | ReadableStream<Uint8Array> | string
50+
): LoadBundleTask {
51+
return expLoadBundle(this._delegate, data);
52+
}
53+
54+
export function namedQuery(
55+
this: Firestore,
56+
queryName: string
57+
): Promise<Query | null> {
58+
return expNamedQuery(this._delegate, queryName).then(expQuery => {
59+
if (!expQuery) {
60+
return null;
61+
}
62+
return new Query(
63+
this,
64+
// We can pass `expQuery` here directly since named queries don't have a UserDataConverter.
65+
// Otherwise, we would have to create a new ExpQuery and pass the old UserDataConverter.
66+
expQuery
67+
);
68+
});
69+
}

packages/firestore/index.bundle.ts

+2-12
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,8 @@ import { Firestore, loadBundle, namedQuery } from './export';
2121
* Prototype patches bundle loading to Firestore.
2222
*/
2323
export function registerBundle(instance: typeof Firestore): void {
24-
instance.prototype.loadBundle = function (
25-
this: Firestore,
26-
data: ArrayBuffer | ReadableStream<Uint8Array> | string
27-
) {
28-
return loadBundle(this, data);
29-
};
30-
instance.prototype.namedQuery = function (
31-
this: Firestore,
32-
queryName: string
33-
) {
34-
return namedQuery(this, queryName);
35-
};
24+
instance.prototype.loadBundle = loadBundle;
25+
instance.prototype.namedQuery = namedQuery;
3626
}
3727

3828
registerBundle(Firestore);

packages/firestore/src/api/database.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import {
4646
} from '@firebase/firestore-types';
4747

4848
import { DatabaseId } from '../core/database_info';
49+
import { LoadBundleTask } from '../exp/bundle';
4950
import { Bytes } from '../exp/bytes';
5051
import {
5152
clearIndexedDbPersistence,
@@ -122,7 +123,6 @@ import {
122123
import { setLogLevel as setClientLogLevel } from '../util/log';
123124

124125
import { Blob } from './blob';
125-
import { LoadBundleTask } from './bundle';
126126
import { Compat } from './compat';
127127
import {
128128
CompleteFn,

packages/firestore/src/core/firestore_client.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@
1717

1818
import { GetOptions } from '@firebase/firestore-types';
1919

20-
import { LoadBundleTask } from '../api/bundle';
2120
import {
2221
CredentialChangeListener,
2322
CredentialsProvider
2423
} from '../api/credentials';
2524
import { User } from '../auth/user';
25+
import { LoadBundleTask } from '../exp/bundle';
2626
import { LocalStore } from '../local/local_store';
2727
import {
2828
localStoreExecuteQuery,

packages/firestore/src/core/sync_engine_impl.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { LoadBundleTask } from '../api/bundle';
1918
import { User } from '../auth/user';
19+
import { LoadBundleTask } from '../exp/bundle';
2020
import { ignoreIfPrimaryLeaseLoss, LocalStore } from '../local/local_store';
2121
import {
2222
localStoreAcknowledgeBatch,

packages/firestore/src/api/bundle.ts renamed to packages/firestore/src/exp/bundle.ts

+57-42
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,42 @@
1515
* limitations under the License.
1616
*/
1717

18-
import {
19-
LoadBundleTask as ApiLoadBundleTask,
20-
LoadBundleTaskProgress
21-
} from '@firebase/firestore-types';
22-
23-
import { ensureFirestoreConfigured } from '../../src/exp/database';
24-
import { Query as ExpQuery } from '../../src/exp/reference';
25-
import {
26-
firestoreClientGetNamedQuery,
27-
firestoreClientLoadBundle
28-
} from '../core/firestore_client';
18+
import { PartialObserver } from '../api/observer';
2919
import { debugAssert } from '../util/assert';
3020
import { FirestoreError } from '../util/error';
3121
import { Deferred } from '../util/promise';
3222

33-
import { Query, Firestore } from './database';
34-
import { PartialObserver } from './observer';
23+
/**
24+
* Represents the state of bundle loading tasks.
25+
*
26+
* Both 'Error' and 'Success' are sinking state: task will abort or complete and there will
27+
* be no more updates after they are reported.
28+
*/
29+
export type TaskState = 'Error' | 'Running' | 'Success';
3530

36-
export class LoadBundleTask
37-
implements ApiLoadBundleTask, PromiseLike<LoadBundleTaskProgress> {
31+
/**
32+
* Represents a progress update or a final state from loading bundles.
33+
*/
34+
export interface LoadBundleTaskProgress {
35+
/** How many documents have been loaded. */
36+
documentsLoaded: number;
37+
/** How many documents are in the bundle being loaded. */
38+
totalDocuments: number;
39+
/** How many bytes have been loaded. */
40+
bytesLoaded: number;
41+
/** How many bytes are in the bundle being loaded. */
42+
totalBytes: number;
43+
/** Current task state. */
44+
taskState: TaskState;
45+
}
46+
47+
/**
48+
* Represents the task of loading a Firestore bundle. It provides progress of bundle
49+
* loading, as well as task completion and error events.
50+
*
51+
* The API is compatible with `Promise<LoadBundleTaskProgress>`.
52+
*/
53+
export class LoadBundleTask implements PromiseLike<LoadBundleTaskProgress> {
3854
private _progressObserver: PartialObserver<LoadBundleTaskProgress> = {};
3955
private _taskCompletionResolver = new Deferred<LoadBundleTaskProgress>();
4056

@@ -46,6 +62,17 @@ export class LoadBundleTask
4662
documentsLoaded: 0
4763
};
4864

65+
/**
66+
* Registers functions to listen to bundle loading progress events.
67+
* @param next
68+
* Called when there is a progress update from bundle loading. Typically `next` calls occur
69+
* each time a Firestore document is loaded from the bundle.
70+
* @param error
71+
* Called when an error occurs during bundle loading. The task aborts after reporting the
72+
* error, and there should be no more updates after this.
73+
* @param complete
74+
* Called when the loading task is complete.
75+
*/
4976
onProgress(
5077
next?: (progress: LoadBundleTaskProgress) => unknown,
5178
error?: (err: Error) => unknown,
@@ -58,12 +85,27 @@ export class LoadBundleTask
5885
};
5986
}
6087

88+
/**
89+
* Implements the `Promise<LoadBundleTaskProgress>.catch` interface.
90+
*
91+
* @param onRejected
92+
* Called when an error occurs during bundle loading.
93+
*/
6194
catch<R>(
6295
onRejected: (a: Error) => R | PromiseLike<R>
6396
): Promise<R | LoadBundleTaskProgress> {
6497
return this._taskCompletionResolver.promise.catch(onRejected);
6598
}
6699

100+
/**
101+
* Implements the `Promise<LoadBundleTaskProgress>.then` interface.
102+
*
103+
* @param onFulfilled
104+
* Called on the completion of the loading task with a final `LoadBundleTaskProgress` update.
105+
* The update will always have its `taskState` set to `"Success"`.
106+
* @param onRejected
107+
* Called when an error occurs during bundle loading.
108+
*/
67109
then<T, R>(
68110
onFulfilled?: (a: LoadBundleTaskProgress) => T | PromiseLike<T>,
69111
onRejected?: (a: Error) => R | PromiseLike<R>
@@ -122,30 +164,3 @@ export class LoadBundleTask
122164
}
123165
}
124166
}
125-
126-
export function loadBundle(
127-
db: Firestore,
128-
bundleData: ArrayBuffer | ReadableStream<Uint8Array> | string
129-
): LoadBundleTask {
130-
const resultTask = new LoadBundleTask();
131-
firestoreClientLoadBundle(
132-
ensureFirestoreConfigured(db._delegate),
133-
db._databaseId,
134-
bundleData,
135-
resultTask
136-
);
137-
return resultTask;
138-
}
139-
140-
export function namedQuery(db: Firestore, name: string): Promise<Query | null> {
141-
return firestoreClientGetNamedQuery(
142-
ensureFirestoreConfigured(db._delegate),
143-
name
144-
).then(namedQuery => {
145-
if (!namedQuery) {
146-
return null;
147-
}
148-
149-
return new Query(db, new ExpQuery(db._delegate, null, namedQuery.query));
150-
});
151-
}

packages/firestore/src/exp/database.ts

+53-1
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,15 @@ import {
3232
FirestoreClient,
3333
firestoreClientDisableNetwork,
3434
firestoreClientEnableNetwork,
35+
firestoreClientGetNamedQuery,
36+
firestoreClientLoadBundle,
3537
firestoreClientWaitForPendingWrites,
3638
setOfflineComponentProvider,
3739
setOnlineComponentProvider
3840
} from '../core/firestore_client';
3941
import { makeDatabaseInfo } from '../lite/components';
4042
import { FirebaseFirestore as LiteFirestore } from '../lite/database';
43+
import { Query } from '../lite/reference';
4144
import {
4245
indexedDbClearPersistence,
4346
indexedDbStoragePrefix
@@ -51,8 +54,8 @@ import { Code, FirestoreError } from '../util/error';
5154
import { cast } from '../util/input_validation';
5255
import { Deferred } from '../util/promise';
5356

57+
import { LoadBundleTask } from './bundle';
5458
import { PersistenceSettings, Settings } from './settings';
55-
5659
export { useFirestoreEmulator } from '../lite/database';
5760

5861
/** DOMException error code constants. */
@@ -460,6 +463,55 @@ export function terminate(firestore: FirebaseFirestore): Promise<void> {
460463
return firestore._delete();
461464
}
462465

466+
/**
467+
* Loads a Firestore bundle into the local cache.
468+
*
469+
* @param firestore - The `Firestore` instance to load bundles for for.
470+
* @param bundleData - An object representing the bundle to be loaded. Valid objects are
471+
* `ArrayBuffer`, `ReadableStream<Uint8Array>` or `string`.
472+
*
473+
* @return
474+
* A `LoadBundleTask` object, which notifies callers with progress updates, and completion
475+
* or error events. It can be used as a `Promise<LoadBundleTaskProgress>`.
476+
*/
477+
export function loadBundle(
478+
firestore: FirebaseFirestore,
479+
bundleData: ReadableStream<Uint8Array> | ArrayBuffer | string
480+
): LoadBundleTask {
481+
firestore = cast(firestore, FirebaseFirestore);
482+
const client = ensureFirestoreConfigured(firestore);
483+
const resultTask = new LoadBundleTask();
484+
firestoreClientLoadBundle(
485+
client,
486+
firestore._databaseId,
487+
bundleData,
488+
resultTask
489+
);
490+
return resultTask;
491+
}
492+
493+
/**
494+
* Reads a Firestore `Query` from local cache, identified by the given name.
495+
*
496+
* The named queries are packaged into bundles on the server side (along
497+
* with resulting documents), and loaded to local cache using `loadBundle`. Once in local
498+
* cache, use this method to extract a `Query` by name.
499+
*/
500+
export function namedQuery(
501+
firestore: FirebaseFirestore,
502+
name: string
503+
): Promise<Query | null> {
504+
firestore = cast(firestore, FirebaseFirestore);
505+
const client = ensureFirestoreConfigured(firestore);
506+
return firestoreClientGetNamedQuery(client, name).then(namedQuery => {
507+
if (!namedQuery) {
508+
return null;
509+
}
510+
511+
return new Query(firestore, null, namedQuery.query);
512+
});
513+
}
514+
463515
function verifyNotInitialized(firestore: FirebaseFirestore): void {
464516
if (firestore._initialized || firestore._terminated) {
465517
throw new FirestoreError(

0 commit comments

Comments
 (0)