From e11f1feb8332dd790e4bd4abdff712706749586a Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Wed, 25 Nov 2020 06:46:13 -0500 Subject: [PATCH 01/16] Add bundles to d.ts and rearrange bundles source code for building it as a separate module. --- packages/firebase/index.d.ts | 35 +++ packages/firestore-types/index.d.ts | 33 +++ packages/firestore/exp-types/index.d.ts | 37 +++ packages/firestore/index.bundle.ts | 39 +++ packages/firestore/src/api/bundle.ts | 81 +++--- packages/firestore/src/api/database.ts | 54 ++-- packages/firestore/src/core/bundle.ts | 190 ++++++++++--- packages/firestore/src/core/bundle_types.ts | 45 +++ .../firestore/src/core/firestore_client.ts | 46 +--- packages/firestore/src/core/sync_engine.ts | 87 +----- packages/firestore/src/local/bundle_cache.ts | 2 +- .../src/local/indexeddb_bundle_cache.ts | 2 +- .../firestore/src/local/local_serializer.ts | 2 +- packages/firestore/src/local/local_store.ts | 232 +--------------- .../firestore/src/local/local_store_bundle.ts | 258 ++++++++++++++++++ .../src/local/memory_bundle_cache.ts | 2 +- .../test/integration/util/firebase_export.ts | 2 + .../test/unit/local/bundle_cache.test.ts | 2 +- .../test/unit/local/local_store.test.ts | 15 +- .../test/unit/local/test_bundle_cache.ts | 2 +- .../test/unit/specs/spec_test_runner.ts | 2 +- 21 files changed, 680 insertions(+), 488 deletions(-) create mode 100644 packages/firestore/index.bundle.ts create mode 100644 packages/firestore/src/core/bundle_types.ts create mode 100644 packages/firestore/src/local/local_store_bundle.ts diff --git a/packages/firebase/index.d.ts b/packages/firebase/index.d.ts index b922fcff0e5..8274fe81492 100644 --- a/packages/firebase/index.d.ts +++ b/packages/firebase/index.d.ts @@ -15,6 +15,8 @@ * limitations under the License. */ +import { DocumentData, LoadBundleTask, Query } from '@firebase/firestore-types'; + /** * firebase is a global namespace from which all Firebase * services are accessed. @@ -8289,12 +8291,45 @@ declare namespace firebase.firestore { */ terminate(): Promise; + loadBundle( + bundleData: ArrayBuffer | ReadableStream | string + ): LoadBundleTask; + + namedQuery(name: string): Promise | null>; + /** * @hidden */ INTERNAL: { delete: () => Promise }; } + export interface LoadBundleTask { + onProgress( + next?: (progress: LoadBundleTaskProgress) => any, + error?: (error: Error) => any, + complete?: () => void + ): void; + + then( + onFulfilled?: (a: LoadBundleTaskProgress) => T | PromiseLike, + onRejected?: (a: Error) => R | PromiseLike + ): Promise; + + catch( + onRejected: (a: Error) => R | PromiseLike + ): Promise; + } + + export interface LoadBundleTaskProgress { + documentsLoaded: number; + totalDocuments: number; + bytesLoaded: number; + totalBytes: number; + taskState: TaskState; + } + + export type TaskState = 'Error' | 'Running' | 'Success'; + /** * An immutable object representing a geo point in Firestore. The geo point * is represented as latitude/longitude pair. diff --git a/packages/firestore-types/index.d.ts b/packages/firestore-types/index.d.ts index 2c94ea2842f..b76b5143488 100644 --- a/packages/firestore-types/index.d.ts +++ b/packages/firestore-types/index.d.ts @@ -96,9 +96,42 @@ export class FirebaseFirestore { terminate(): Promise; + loadBundle( + bundleData: ArrayBuffer | ReadableStream | string + ): LoadBundleTask; + + namedQuery(name: string): Promise | null>; + INTERNAL: { delete: () => Promise }; } +export interface LoadBundleTask { + onProgress( + next?: (progress: LoadBundleTaskProgress) => any, + error?: (error: Error) => any, + complete?: () => void + ): void; + + then( + onFulfilled?: (a: LoadBundleTaskProgress) => T | PromiseLike, + onRejected?: (a: Error) => R | PromiseLike + ): Promise; + + catch( + onRejected: (a: Error) => R | PromiseLike + ): Promise; +} + +export interface LoadBundleTaskProgress { + documentsLoaded: number; + totalDocuments: number; + bytesLoaded: number; + totalBytes: number; + taskState: TaskState; +} + +export type TaskState = 'Error' | 'Running' | 'Success'; + export class GeoPoint { constructor(latitude: number, longitude: number); diff --git a/packages/firestore/exp-types/index.d.ts b/packages/firestore/exp-types/index.d.ts index 5c1bb7f2b68..19994191aab 100644 --- a/packages/firestore/exp-types/index.d.ts +++ b/packages/firestore/exp-types/index.d.ts @@ -516,6 +516,43 @@ export function snapshotEqual( right: DocumentSnapshot | QuerySnapshot ): boolean; +export interface LoadBundleTask { + onProgress( + next?: (progress: LoadBundleTaskProgress) => any, + error?: (error: Error) => any, + complete?: () => void + ): void; + + then( + onFulfilled?: (a: LoadBundleTaskProgress) => T | PromiseLike, + onRejected?: (a: Error) => R | PromiseLike + ): Promise; + + catch( + onRejected: (a: Error) => R | PromiseLike + ): Promise; +} + +export interface LoadBundleTaskProgress { + documentsLoaded: number; + totalDocuments: number; + bytesLoaded: number; + totalBytes: number; + taskState: TaskState; +} + +export type TaskState = 'Error' | 'Running' | 'Success'; + +export function loadBundle( + firestore: FirebaseFirestore, + bundleData: ArrayBuffer | ReadableStream | string +): LoadBundleTask; + +export function namedQuery( + firestore: FirebaseFirestore, + name: string +): Promise | null>; + export type FirestoreErrorCode = | 'cancelled' | 'unknown' diff --git a/packages/firestore/index.bundle.ts b/packages/firestore/index.bundle.ts new file mode 100644 index 00000000000..48e2712fe90 --- /dev/null +++ b/packages/firestore/index.bundle.ts @@ -0,0 +1,39 @@ +/** + * @license + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Firestore } from './src/api/database'; +import { loadBundle, namedQuery } from './src/api/bundle'; + +/** + * Registers the memory-only Firestore build with the components framework. + */ +export function registerBundle(instance: typeof Firestore): void { + instance.prototype.loadBundle = function ( + data: ArrayBuffer | ReadableStream | string + ) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return loadBundle(this as any, data); + }; + instance.prototype.namedQuery = function (queryName: string) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return namedQuery(this as any, queryName); + }; + + //TODO: add loadBundle and namedQuery to the firestore namespace +} + +registerBundle(Firestore); diff --git a/packages/firestore/src/api/bundle.ts b/packages/firestore/src/api/bundle.ts index 3f73589aced..e985b560eac 100644 --- a/packages/firestore/src/api/bundle.ts +++ b/packages/firestore/src/api/bundle.ts @@ -15,46 +15,24 @@ * limitations under the License. */ +import { LoadBundleTaskProgress } from '@firebase/firestore-types'; import { Deferred } from '../util/promise'; import { PartialObserver } from './observer'; import { debugAssert } from '../util/assert'; import { FirestoreError } from '../util/error'; - -export interface ApiLoadBundleTask { - onProgress( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - next?: (progress: ApiLoadBundleTaskProgress) => any, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - error?: (error: Error) => any, - complete?: () => void - ): void; - - then( - onFulfilled?: (a: ApiLoadBundleTaskProgress) => T | PromiseLike, - onRejected?: (a: Error) => R | PromiseLike - ): Promise; - - catch( - onRejected: (a: Error) => R | PromiseLike - ): Promise; -} - -export interface ApiLoadBundleTaskProgress { - documentsLoaded: number; - totalDocuments: number; - bytesLoaded: number; - totalBytes: number; - taskState: TaskState; -} - -export type TaskState = 'Error' | 'Running' | 'Success'; +import { ensureFirestoreConfigured, Query, Firestore } from './database'; +import { + firestoreClientGetNamedQuery, + firestoreClientLoadBundle +} from '../core/bundle'; +import { Query as ExpQuery } from '../../exp/src/api/reference'; export class LoadBundleTask - implements ApiLoadBundleTask, PromiseLike { - private _progressObserver: PartialObserver = {}; - private _taskCompletionResolver = new Deferred(); + implements LoadBundleTask, PromiseLike { + private _progressObserver: PartialObserver = {}; + private _taskCompletionResolver = new Deferred(); - private _lastProgress: ApiLoadBundleTaskProgress = { + private _lastProgress: LoadBundleTaskProgress = { taskState: 'Running', totalBytes: 0, totalDocuments: 0, @@ -63,7 +41,7 @@ export class LoadBundleTask }; onProgress( - next?: (progress: ApiLoadBundleTaskProgress) => unknown, + next?: (progress: LoadBundleTaskProgress) => unknown, error?: (err: Error) => unknown, complete?: () => void ): void { @@ -76,12 +54,12 @@ export class LoadBundleTask catch( onRejected: (a: Error) => R | PromiseLike - ): Promise { + ): Promise { return this._taskCompletionResolver.promise.catch(onRejected); } then( - onFulfilled?: (a: ApiLoadBundleTaskProgress) => T | PromiseLike, + onFulfilled?: (a: LoadBundleTaskProgress) => T | PromiseLike, onRejected?: (a: Error) => R | PromiseLike ): Promise { return this._taskCompletionResolver.promise.then(onFulfilled, onRejected); @@ -91,7 +69,7 @@ export class LoadBundleTask * Notifies all observers that bundle loading has completed, with a provided * `LoadBundleTaskProgress` object. */ - _completeWith(progress: ApiLoadBundleTaskProgress): void { + _completeWith(progress: LoadBundleTaskProgress): void { debugAssert( progress.taskState === 'Success', 'Task is not completed with Success.' @@ -126,7 +104,7 @@ export class LoadBundleTask * Notifies a progress update of loading a bundle. * @param progress - The new progress. */ - _updateProgress(progress: ApiLoadBundleTaskProgress): void { + _updateProgress(progress: LoadBundleTaskProgress): void { debugAssert( this._lastProgress.taskState === 'Running', 'Cannot update progress on a completed or failed task' @@ -138,3 +116,30 @@ export class LoadBundleTask } } } + +export function loadBundle( + db: Firestore, + bundleData: ArrayBuffer | ReadableStream | string +): LoadBundleTask { + const resultTask = new LoadBundleTask(); + // eslint-disable-next-line @typescript-eslint/no-floating-promises + firestoreClientLoadBundle( + ensureFirestoreConfigured(db._delegate), + bundleData, + resultTask + ); + return resultTask; +} + +export function namedQuery(db: Firestore, name: string): Promise { + return firestoreClientGetNamedQuery( + ensureFirestoreConfigured(db._delegate), + name + ).then(namedQuery => { + if (!namedQuery) { + return null; + } + + return new Query(db, new ExpQuery(db._delegate, null, namedQuery.query)); + }); +} diff --git a/packages/firestore/src/api/database.ts b/packages/firestore/src/api/database.ts index 215e4d119c4..009c593659b 100644 --- a/packages/firestore/src/api/database.ts +++ b/packages/firestore/src/api/database.ts @@ -18,11 +18,7 @@ import { FirebaseApp } from '@firebase/app-types'; import { _FirebaseApp, FirebaseService } from '@firebase/app-types/private'; import { DatabaseId } from '../core/database_info'; -import { - FirestoreClient, - firestoreClientGetNamedQuery, - firestoreClientLoadBundle -} from '../core/firestore_client'; +import { FirestoreClient } from '../core/firestore_client'; import { DocumentKey } from '../model/document_key'; import { FieldPath, ResourcePath } from '../model/path'; import { debugAssert } from '../util/assert'; @@ -94,7 +90,7 @@ import { } from '../../exp/src/api/reference'; import { LRU_COLLECTION_DISABLED } from '../local/lru_garbage_collector'; import { Compat } from '../compat/compat'; -import { ApiLoadBundleTask, LoadBundleTask } from './bundle'; +import { LoadBundleTask } from './bundle'; import { makeDatabaseInfo } from '../../lite/src/api/database'; import { DEFAULT_HOST } from '../../lite/src/api/components'; import { WriteBatch as ExpWriteBatch } from '../../exp/src/api/write_batch'; @@ -107,6 +103,7 @@ import { CollectionReference as PublicCollectionReference, DocumentChange as PublicDocumentChange, DocumentChangeType as PublicDocumentChangeType, + DocumentData, DocumentData as PublicDocumentData, DocumentReference as PublicDocumentReference, DocumentSnapshot as PublicDocumentSnapshot, @@ -368,6 +365,22 @@ export class Firestore ) ); } + + loadBundle( + bundleData: ArrayBuffer | ReadableStream | string + ): LoadBundleTask { + throw new FirestoreError( + Code.FAILED_PRECONDITION, + '"loadBundle()" does not exist, have you imported "firebase/firestore/bundle"?' + ); + } + + namedQuery(name: string): Promise | null> { + throw new FirestoreError( + Code.FAILED_PRECONDITION, + '"namedQuery()" does not exist, have you imported "firebase/firestore/bundle"?' + ); + } } export function ensureFirestoreConfigured( @@ -404,35 +417,6 @@ export function setLogLevel(level: PublicLogLevel): void { setClientLogLevel(level); } -export function loadBundle( - db: Firestore, - bundleData: ArrayBuffer | ReadableStream | string -): ApiLoadBundleTask { - const resultTask = new LoadBundleTask(); - // eslint-disable-next-line @typescript-eslint/no-floating-promises - firestoreClientLoadBundle( - ensureFirestoreConfigured(db._delegate), - bundleData, - resultTask - ); - return resultTask; -} - -export function namedQuery( - db: Firestore, - name: string -): Promise { - return firestoreClientGetNamedQuery( - ensureFirestoreConfigured(db._delegate), - name - ).then(namedQuery => { - if (!namedQuery) { - return null; - } - return new Query(db, new ExpQuery(db._delegate, null, namedQuery.query)); - }); -} - /** * A reference to a transaction. */ diff --git a/packages/firestore/src/core/bundle.ts b/packages/firestore/src/core/bundle.ts index 4dc59427a6a..c78bab2c7a9 100644 --- a/packages/firestore/src/core/bundle.ts +++ b/packages/firestore/src/core/bundle.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Query } from './query'; +import { LoadBundleTaskProgress } from '@firebase/firestore-types'; import { SnapshotVersion } from './snapshot_version'; import { fromDocument, @@ -31,43 +31,37 @@ import { import * as api from '../protos/firestore_proto_api'; import { DocumentKey } from '../model/document_key'; import { MaybeDocument, NoDocument } from '../model/document'; -import { debugAssert } from '../util/assert'; -import { - applyBundleDocuments, - LocalStore, - saveNamedQuery -} from '../local/local_store'; -import { SizedBundleElement } from '../util/bundle_reader'; +import { debugAssert, debugCast } from '../util/assert'; +import { LocalStore } from '../local/local_store'; +import { SizedBundleElement, BundleReader } from '../util/bundle_reader'; import { documentKeySet, DocumentKeySet, MaybeDocumentMap } from '../model/collections'; -import { ApiLoadBundleTaskProgress } from '../api/bundle'; - -/** - * Represents a Firestore bundle saved by the SDK in its local storage. - */ -export interface Bundle { - readonly id: string; - readonly version: number; - /** - * Set to the snapshot version of the bundle if created by the Server SDKs. - * Otherwise set to SnapshotVersion.MIN. - */ - readonly createTime: SnapshotVersion; -} - -/** - * Represents a Query saved by the SDK in its local storage. - */ -export interface NamedQuery { - readonly name: string; - readonly query: Query; - /** The time at which the results for this query were read. */ - readonly readTime: SnapshotVersion; -} - +import { + FirestoreClient, + getLocalStore, + getSyncEngine +} from './firestore_client'; +import { LoadBundleTask } from '../api/bundle'; +import { newSerializer, newTextEncoder } from '../platform/serializer'; +import { toByteStreamReader } from '../platform/byte_stream_reader'; +import { + emitNewSnapsAndNotifyLocalStore, + SyncEngine, + SyncEngineImpl +} from './sync_engine'; +import { logWarn } from '../util/log'; +import { LOG_TAG } from '../../lite/src/api/components'; +import { + applyBundleDocuments, + getNamedQuery, + hasNewerBundle, + saveBundle, + saveNamedQuery +} from '../local/local_store_bundle'; +import { NamedQuery } from './bundle_types'; /** * Represents a bundled document, including the metadata and the document * itself, if it exists. @@ -119,9 +113,9 @@ export class BundleConverter { * Returns a `LoadBundleTaskProgress` representing the initial progress of * loading a bundle. */ -export function bundleInitialProgress( +function bundleInitialProgress( metadata: ProtoBundleMetadata -): ApiLoadBundleTaskProgress { +): LoadBundleTaskProgress { return { taskState: 'Running', documentsLoaded: 0, @@ -135,9 +129,9 @@ export function bundleInitialProgress( * Returns a `LoadBundleTaskProgress` representing the progress that the loading * has succeeded. */ -export function bundleSuccessProgress( +function bundleSuccessProgress( metadata: ProtoBundleMetadata -): ApiLoadBundleTaskProgress { +): LoadBundleTaskProgress { return { taskState: 'Success', documentsLoaded: metadata.totalDocuments!, @@ -147,9 +141,9 @@ export function bundleSuccessProgress( }; } -export class BundleLoadResult { +class BundleLoadResult { constructor( - readonly progress: ApiLoadBundleTaskProgress, + readonly progress: LoadBundleTaskProgress, readonly changedDocs: MaybeDocumentMap ) {} } @@ -158,9 +152,9 @@ export class BundleLoadResult { * A class to process the elements from a bundle, load them into local * storage and provide progress update while loading. */ -export class BundleLoader { +class BundleLoader { /** The current progress of loading */ - private progress: ApiLoadBundleTaskProgress; + private progress: LoadBundleTaskProgress; /** Batched queries to be saved into storage */ private queries: ProtoNamedQuery[] = []; /** Batched documents to be saved into storage */ @@ -180,9 +174,7 @@ export class BundleLoader { * Returns a new progress if adding the element leads to a new progress, * otherwise returns null. */ - addSizedElement( - element: SizedBundleElement - ): ApiLoadBundleTaskProgress | null { + addSizedElement(element: SizedBundleElement): LoadBundleTaskProgress | null { debugAssert(!element.isBundleMetadata(), 'Unexpected bundle metadata.'); this.progress.bytesLoaded += element.byteLength; @@ -265,3 +257,113 @@ export class BundleLoader { return new BundleLoadResult({ ...this.progress }, changedDocuments); } } + +export async function firestoreClientLoadBundle( + client: FirestoreClient, + data: ReadableStream | ArrayBuffer | string, + resultTask: LoadBundleTask +): Promise { + const reader = createBundleReader( + data, + newSerializer((await client.getConfiguration()).databaseInfo.databaseId) + ); + client.asyncQueue.enqueueAndForget(async () => { + syncEngineLoadBundle(await getSyncEngine(client), reader, resultTask); + }); +} + +export function firestoreClientGetNamedQuery( + client: FirestoreClient, + queryName: string +): Promise { + return client.asyncQueue.enqueue(async () => + getNamedQuery(await getLocalStore(client), queryName) + ); +} + +function createBundleReader( + data: ReadableStream | ArrayBuffer | string, + serializer: JsonProtoSerializer +): BundleReader { + let content: ReadableStream | ArrayBuffer; + if (typeof data === 'string') { + content = newTextEncoder().encode(data); + } else { + content = data; + } + return new BundleReader(toByteStreamReader(content), serializer); +} + +/** + * Loads a Firestore bundle into the SDK. The returned promise resolves when + * the bundle finished loading. + * + * @param bundleReader Bundle to load into the SDK. + * @param task LoadBundleTask used to update the loading progress to public API. + */ +export function syncEngineLoadBundle( + syncEngine: SyncEngine, + bundleReader: BundleReader, + task: LoadBundleTask +): void { + const syncEngineImpl = debugCast(syncEngine, SyncEngineImpl); + + // eslint-disable-next-line @typescript-eslint/no-floating-promises + loadBundleImpl(syncEngineImpl, bundleReader, task).then(() => { + syncEngineImpl.sharedClientState.notifyBundleLoaded(); + }); +} + +async function loadBundleImpl( + syncEngine: SyncEngineImpl, + reader: BundleReader, + task: LoadBundleTask +): Promise { + try { + const metadata = await reader.getMetadata(); + const skip = await hasNewerBundle(syncEngine.localStore, metadata); + if (skip) { + await reader.close(); + task._completeWith(bundleSuccessProgress(metadata)); + return; + } + + task._updateProgress(bundleInitialProgress(metadata)); + + const loader = new BundleLoader( + metadata, + syncEngine.localStore, + reader.serializer + ); + let element = await reader.nextElement(); + while (element) { + debugAssert( + !element.payload.metadata, + 'Unexpected BundleMetadata element.' + ); + const progress = await loader.addSizedElement(element); + if (progress) { + task._updateProgress(progress); + } + + element = await reader.nextElement(); + } + + const result = await loader.complete(); + // TODO(b/160876443): This currently raises snapshots with + // `fromCache=false` if users already listen to some queries and bundles + // has newer version. + await emitNewSnapsAndNotifyLocalStore( + syncEngine, + result.changedDocs, + /* remoteEvent */ undefined + ); + + // Save metadata, so loading the same bundle will skip. + await saveBundle(syncEngine.localStore, metadata); + task._completeWith(result.progress); + } catch (e) { + logWarn(LOG_TAG, `Loading bundle failed with ${e}`); + task._failWith(e); + } +} diff --git a/packages/firestore/src/core/bundle_types.ts b/packages/firestore/src/core/bundle_types.ts new file mode 100644 index 00000000000..a4e7bf9bc19 --- /dev/null +++ b/packages/firestore/src/core/bundle_types.ts @@ -0,0 +1,45 @@ +/** + * @license + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This is a separate file because rollup (or typescript) treat it as circular +// dependency when it was in `./bundle.ts`, which breaks bundles module build. + +import { Query } from './query'; +import { SnapshotVersion } from './snapshot_version'; + +/** + * Represents a Firestore bundle saved by the SDK in its local storage. + */ +export interface Bundle { + readonly id: string; + readonly version: number; + /** + * Set to the snapshot version of the bundle if created by the Server SDKs. + * Otherwise set to SnapshotVersion.MIN. + */ + readonly createTime: SnapshotVersion; +} + +/** + * Represents a Query saved by the SDK in its local storage. + */ +export interface NamedQuery { + readonly name: string; + readonly query: Query; + /** The time at which the results for this query were read. */ + readonly readTime: SnapshotVersion; +} diff --git a/packages/firestore/src/core/firestore_client.ts b/packages/firestore/src/core/firestore_client.ts index 43912db19fc..e0e162d7b6a 100644 --- a/packages/firestore/src/core/firestore_client.ts +++ b/packages/firestore/src/core/firestore_client.ts @@ -23,7 +23,6 @@ import { } from '../api/credentials'; import { User } from '../auth/user'; import { - getNamedQuery, executeQuery, handleUserChange, LocalStore, @@ -55,7 +54,6 @@ import { registerPendingWritesCallback, SyncEngine, syncEngineListen, - syncEngineLoadBundle, syncEngineUnlisten, syncEngineWrite } from './sync_engine'; @@ -77,12 +75,6 @@ import { logDebug } from '../util/log'; import { AutoId } from '../util/misc'; import { Persistence } from '../local/persistence'; import { Datastore } from '../remote/datastore'; -import { BundleReader } from '../util/bundle_reader'; -import { LoadBundleTask } from '../api/bundle'; -import { newSerializer, newTextEncoder } from '../platform/serializer'; -import { toByteStreamReader } from '../platform/byte_stream_reader'; -import { NamedQuery } from './bundle'; -import { JsonProtoSerializer } from '../remote/serializer'; const LOG_TAG = 'FirestoreClient'; export const MAX_CONCURRENT_LIMBO_RESOLUTIONS = 100; @@ -276,7 +268,7 @@ function getRemoteStore(client: FirestoreClient): Promise { return ensureOnlineComponents(client).then(c => c.remoteStore); } -function getSyncEngine(client: FirestoreClient): Promise { +export function getSyncEngine(client: FirestoreClient): Promise { return ensureOnlineComponents(client).then(c => c.syncEngine); } @@ -656,39 +648,3 @@ function executeQueryViaSnapshotListener( }); return eventManagerListen(eventManager, listener); } - -export async function firestoreClientLoadBundle( - client: FirestoreClient, - data: ReadableStream | ArrayBuffer | string, - resultTask: LoadBundleTask -): Promise { - const reader = createBundleReader( - data, - newSerializer((await client.getConfiguration()).databaseInfo.databaseId) - ); - client.asyncQueue.enqueueAndForget(async () => { - syncEngineLoadBundle(await getSyncEngine(client), reader, resultTask); - }); -} - -export function firestoreClientGetNamedQuery( - client: FirestoreClient, - queryName: string -): Promise { - return client.asyncQueue.enqueue(async () => - getNamedQuery(await getLocalStore(client), queryName) - ); -} - -function createBundleReader( - data: ReadableStream | ArrayBuffer | string, - serializer: JsonProtoSerializer -): BundleReader { - let content: ReadableStream | ArrayBuffer; - if (typeof data === 'string') { - content = newTextEncoder().encode(data); - } else { - content = data; - } - return new BundleReader(toByteStreamReader(content), serializer); -} diff --git a/packages/firestore/src/core/sync_engine.ts b/packages/firestore/src/core/sync_engine.ts index f613cf011bf..289256db59d 100644 --- a/packages/firestore/src/core/sync_engine.ts +++ b/packages/firestore/src/core/sync_engine.ts @@ -17,13 +17,11 @@ import { User } from '../auth/user'; import { - hasNewerBundle, applyRemoteEventToLocalCache, getNewDocumentChanges, getCachedTarget, ignoreIfPrimaryLeaseLoss, LocalStore, - saveBundle, getActiveClientsFromPersistence, lookupMutationDocuments, removeCachedMutationBatchMetadata, @@ -60,7 +58,7 @@ import { } from '../remote/remote_store'; import { debugAssert, debugCast, fail, hardAssert } from '../util/assert'; import { Code, FirestoreError } from '../util/error'; -import { logDebug, logWarn } from '../util/log'; +import { logDebug } from '../util/log'; import { primitiveComparator } from '../util/misc'; import { ObjectMap } from '../util/obj_map'; import { Deferred } from '../util/promise'; @@ -98,13 +96,6 @@ import { } from './view'; import { ViewSnapshot } from './view_snapshot'; import { wrapInUserErrorIfRecoverable } from '../util/async_queue'; -import { BundleReader } from '../util/bundle_reader'; -import { - BundleLoader, - bundleInitialProgress, - bundleSuccessProgress -} from './bundle'; -import { LoadBundleTask } from '../api/bundle'; import { EventManager, eventManagerOnOnlineStateChange, @@ -209,7 +200,7 @@ export interface SyncEngine { * This is useful to implement optional features (like bundles) in free * functions, such that they are tree-shakeable. */ -class SyncEngineImpl implements SyncEngine { +export class SyncEngineImpl implements SyncEngine { syncEngineListener: SyncEngineListener = {}; /** @@ -1532,77 +1523,3 @@ export function ensureWriteCallbacks(syncEngine: SyncEngine): SyncEngineImpl { ); return syncEngineImpl; } - -/** - * Loads a Firestore bundle into the SDK. The returned promise resolves when - * the bundle finished loading. - * - * @param bundleReader - Bundle to load into the SDK. - * @param task - LoadBundleTask used to update the loading progress to public API. - */ -export function syncEngineLoadBundle( - syncEngine: SyncEngine, - bundleReader: BundleReader, - task: LoadBundleTask -): void { - const syncEngineImpl = debugCast(syncEngine, SyncEngineImpl); - - // eslint-disable-next-line @typescript-eslint/no-floating-promises - loadBundleImpl(syncEngineImpl, bundleReader, task).then(() => { - syncEngineImpl.sharedClientState.notifyBundleLoaded(); - }); -} - -async function loadBundleImpl( - syncEngine: SyncEngineImpl, - reader: BundleReader, - task: LoadBundleTask -): Promise { - try { - const metadata = await reader.getMetadata(); - const skip = await hasNewerBundle(syncEngine.localStore, metadata); - if (skip) { - await reader.close(); - task._completeWith(bundleSuccessProgress(metadata)); - return; - } - - task._updateProgress(bundleInitialProgress(metadata)); - - const loader = new BundleLoader( - metadata, - syncEngine.localStore, - reader.serializer - ); - let element = await reader.nextElement(); - while (element) { - debugAssert( - !element.payload.metadata, - 'Unexpected BundleMetadata element.' - ); - const progress = await loader.addSizedElement(element); - if (progress) { - task._updateProgress(progress); - } - - element = await reader.nextElement(); - } - - const result = await loader.complete(); - // TODO(b/160876443): This currently raises snapshots with - // `fromCache=false` if users already listen to some queries and bundles - // has newer version. - await emitNewSnapsAndNotifyLocalStore( - syncEngine, - result.changedDocs, - /* remoteEvent */ undefined - ); - - // Save metadata, so loading the same bundle will skip. - await saveBundle(syncEngine.localStore, metadata); - task._completeWith(result.progress); - } catch (e) { - logWarn(LOG_TAG, `Loading bundle failed with ${e}`); - task._failWith(e); - } -} diff --git a/packages/firestore/src/local/bundle_cache.ts b/packages/firestore/src/local/bundle_cache.ts index c9dadb86e9b..004d91c3053 100644 --- a/packages/firestore/src/local/bundle_cache.ts +++ b/packages/firestore/src/local/bundle_cache.ts @@ -21,7 +21,7 @@ import { NamedQuery as ProtoNamedQuery, BundleMetadata as ProtoBundleMetadata } from '../protos/firestore_bundle_proto'; -import { Bundle, NamedQuery } from '../core/bundle'; +import { Bundle, NamedQuery } from '../core/bundle_types'; /** * Provides interfaces to save and read Firestore bundles. diff --git a/packages/firestore/src/local/indexeddb_bundle_cache.ts b/packages/firestore/src/local/indexeddb_bundle_cache.ts index 341ff75e8c5..b713444761a 100644 --- a/packages/firestore/src/local/indexeddb_bundle_cache.ts +++ b/packages/firestore/src/local/indexeddb_bundle_cache.ts @@ -37,7 +37,7 @@ import { toDbBundle, toDbNamedQuery } from './local_serializer'; -import { Bundle, NamedQuery } from '../core/bundle'; +import { Bundle, NamedQuery } from '../core/bundle_types'; export class IndexedDbBundleCache implements BundleCache { constructor(private serializer: LocalSerializer) {} diff --git a/packages/firestore/src/local/local_serializer.ts b/packages/firestore/src/local/local_serializer.ts index f4b147af8cd..dd07ac0f8d9 100644 --- a/packages/firestore/src/local/local_serializer.ts +++ b/packages/firestore/src/local/local_serializer.ts @@ -55,7 +55,7 @@ import { DbUnknownDocument } from './indexeddb_schema'; import { TargetData, TargetPurpose } from './target_data'; -import { Bundle, NamedQuery } from '../core/bundle'; +import { Bundle, NamedQuery } from '../core/bundle_types'; import { LimitType, Query, queryWithLimit } from '../core/query'; import { BundleMetadata as ProtoBundleMetadata, diff --git a/packages/firestore/src/local/local_store.ts b/packages/firestore/src/local/local_store.ts index 76db4f85921..8c332d75f33 100644 --- a/packages/firestore/src/local/local_store.ts +++ b/packages/firestore/src/local/local_store.ts @@ -17,7 +17,7 @@ import { Timestamp } from '../api/timestamp'; import { User } from '../auth/user'; -import { newQueryForPath, Query, queryToTarget } from '../core/query'; +import { Query, queryToTarget } from '../core/query'; import { SnapshotVersion } from '../core/snapshot_version'; import { canonifyTarget, Target, targetEquals } from '../core/target'; import { BatchId, TargetId } from '../core/types'; @@ -25,7 +25,6 @@ import { DocumentKeySet, documentKeySet, DocumentMap, - documentVersionMap, DocumentVersionMap, maybeDocumentMap, MaybeDocumentMap @@ -76,16 +75,8 @@ import { import { IndexedDbTargetCache } from './indexeddb_target_cache'; import { extractFieldMask } from '../model/object_value'; import { isIndexedDbTransactionError } from './simple_db'; -import { - NamedQuery as ProtoNamedQuery, - BundleMetadata as ProtoBundleMetadata -} from '../protos/firestore_bundle_proto'; -import { BundleConverter, BundledDocuments, NamedQuery } from '../core/bundle'; import { BundleCache } from './bundle_cache'; -import { fromVersion, JsonProtoSerializer } from '../remote/serializer'; -import { fromBundledQuery } from './local_serializer'; -import { ByteString } from '../util/byte_string'; -import { ResourcePath } from '../model/path'; +import { JsonProtoSerializer } from '../remote/serializer'; const LOG_TAG = 'LocalStore'; @@ -183,7 +174,7 @@ export interface LocalStore { * This is useful to implement optional features (like bundles) in free * functions, such that they are tree-shakeable. */ -class LocalStoreImpl implements LocalStore { +export class LocalStoreImpl implements LocalStore { /** * The set of all mutations that have been sent but not yet been applied to * the backend. @@ -667,7 +658,7 @@ export function applyRemoteEventToLocalCache( * Note: this function will use `documentVersions` if it is defined; * when it is not defined, resorts to `globalVersion`. */ -function populateDocumentChangeBuffer( +export function populateDocumentChangeBuffer( txn: PersistenceTransaction, documentBuffer: RemoteDocumentChangeBuffer, documents: MaybeDocumentMap, @@ -1274,218 +1265,3 @@ export async function ignoreIfPrimaryLeaseLoss( throw err; } } - -/** - * Creates a new target using the given bundle name, which will be used to - * hold the keys of all documents from the bundle in query-document mappings. - * This ensures that the loaded documents do not get garbage collected - * right away. - */ -function umbrellaTarget(bundleName: string): Target { - // It is OK that the path used for the query is not valid, because this will - // not be read and queried. - return queryToTarget( - newQueryForPath(ResourcePath.fromString(`__bundle__/docs/${bundleName}`)) - ); -} - -/** - * Applies the documents from a bundle to the "ground-state" (remote) - * documents. - * - * LocalDocuments are re-calculated if there are remaining mutations in the - * queue. - */ -export async function applyBundleDocuments( - localStore: LocalStore, - documents: BundledDocuments, - bundleName: string -): Promise { - const localStoreImpl = debugCast(localStore, LocalStoreImpl); - const bundleConverter = new BundleConverter(localStoreImpl.serializer); - let documentKeys = documentKeySet(); - let documentMap = maybeDocumentMap(); - let versionMap = documentVersionMap(); - for (const bundleDoc of documents) { - const documentKey = bundleConverter.toDocumentKey(bundleDoc.metadata.name!); - if (bundleDoc.document) { - documentKeys = documentKeys.add(documentKey); - } - documentMap = documentMap.insert( - documentKey, - bundleConverter.toMaybeDocument(bundleDoc) - ); - versionMap = versionMap.insert( - documentKey, - bundleConverter.toSnapshotVersion(bundleDoc.metadata.readTime!) - ); - } - - const documentBuffer = localStoreImpl.remoteDocuments.newChangeBuffer({ - trackRemovals: true // Make sure document removals show up in `getNewDocumentChanges()` - }); - - // Allocates a target to hold all document keys from the bundle, such that - // they will not get garbage collected right away. - const umbrellaTargetData = await allocateTarget( - localStoreImpl, - umbrellaTarget(bundleName) - ); - return localStoreImpl.persistence.runTransaction( - 'Apply bundle documents', - 'readwrite', - txn => { - return populateDocumentChangeBuffer( - txn, - documentBuffer, - documentMap, - SnapshotVersion.min(), - versionMap - ) - .next(changedDocs => { - documentBuffer.apply(txn); - return changedDocs; - }) - .next(changedDocs => { - return localStoreImpl.targetCache - .removeMatchingKeysForTargetId(txn, umbrellaTargetData.targetId) - .next(() => - localStoreImpl.targetCache.addMatchingKeys( - txn, - documentKeys, - umbrellaTargetData.targetId - ) - ) - .next(() => - localStoreImpl.localDocuments.getLocalViewOfDocuments( - txn, - changedDocs - ) - ); - }); - } - ); -} - -/** - * Returns a promise of a boolean to indicate if the given bundle has already - * been loaded and the create time is newer than the current loading bundle. - */ -export function hasNewerBundle( - localStore: LocalStore, - bundleMetadata: ProtoBundleMetadata -): Promise { - const localStoreImpl = debugCast(localStore, LocalStoreImpl); - const bundleConverter = new BundleConverter(localStoreImpl.serializer); - const currentReadTime = bundleConverter.toSnapshotVersion( - bundleMetadata.createTime! - ); - return localStoreImpl.persistence - .runTransaction('hasNewerBundle', 'readonly', transaction => { - return localStoreImpl.bundleCache.getBundleMetadata( - transaction, - bundleMetadata.id! - ); - }) - .then(cached => { - return !!cached && cached.createTime!.compareTo(currentReadTime) >= 0; - }); -} - -/** - * Saves the given `BundleMetadata` to local persistence. - */ -export function saveBundle( - localStore: LocalStore, - bundleMetadata: ProtoBundleMetadata -): Promise { - const localStoreImpl = debugCast(localStore, LocalStoreImpl); - return localStoreImpl.persistence.runTransaction( - 'Save bundle', - 'readwrite', - transaction => { - return localStoreImpl.bundleCache.saveBundleMetadata( - transaction, - bundleMetadata - ); - } - ); -} - -/** - * Returns a promise of a `NamedQuery` associated with given query name. Promise - * resolves to undefined if no persisted data can be found. - */ -export function getNamedQuery( - localStore: LocalStore, - queryName: string -): Promise { - const localStoreImpl = debugCast(localStore, LocalStoreImpl); - return localStoreImpl.persistence.runTransaction( - 'Get named query', - 'readonly', - transaction => - localStoreImpl.bundleCache.getNamedQuery(transaction, queryName) - ); -} - -/** - * Saves the given `NamedQuery` to local persistence. - */ -export async function saveNamedQuery( - localStore: LocalStore, - query: ProtoNamedQuery, - documents: DocumentKeySet = documentKeySet() -): Promise { - // Allocate a target for the named query such that it can be resumed - // from associated read time if users use it to listen. - // NOTE: this also means if no corresponding target exists, the new target - // will remain active and will not get collected, unless users happen to - // unlisten the query somehow. - const allocated = await allocateTarget( - localStore, - queryToTarget(fromBundledQuery(query.bundledQuery!)) - ); - - const localStoreImpl = debugCast(localStore, LocalStoreImpl); - return localStoreImpl.persistence.runTransaction( - 'Save named query', - 'readwrite', - transaction => { - const readTime = fromVersion(query.readTime!); - // Simply save the query itself if it is older than what the SDK already - // has. - if (allocated.snapshotVersion.compareTo(readTime) >= 0) { - return localStoreImpl.bundleCache.saveNamedQuery(transaction, query); - } - - // Update existing target data because the query from the bundle is newer. - const newTargetData = allocated.withResumeToken( - ByteString.EMPTY_BYTE_STRING, - readTime - ); - localStoreImpl.targetDataByTarget = localStoreImpl.targetDataByTarget.insert( - newTargetData.targetId, - newTargetData - ); - return localStoreImpl.targetCache - .updateTargetData(transaction, newTargetData) - .next(() => - localStoreImpl.targetCache.removeMatchingKeysForTargetId( - transaction, - allocated.targetId - ) - ) - .next(() => - localStoreImpl.targetCache.addMatchingKeys( - transaction, - documents, - allocated.targetId - ) - ) - .next(() => - localStoreImpl.bundleCache.saveNamedQuery(transaction, query) - ); - } - ); -} diff --git a/packages/firestore/src/local/local_store_bundle.ts b/packages/firestore/src/local/local_store_bundle.ts new file mode 100644 index 00000000000..71cae7e00b8 --- /dev/null +++ b/packages/firestore/src/local/local_store_bundle.ts @@ -0,0 +1,258 @@ +/** + * @license + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { newQueryForPath, queryToTarget } from '../core/query'; +import { SnapshotVersion } from '../core/snapshot_version'; +import { Target } from '../core/target'; +import { + DocumentKeySet, + documentKeySet, + documentVersionMap, + maybeDocumentMap, + MaybeDocumentMap +} from '../model/collections'; +import { debugCast } from '../util/assert'; + +import * as bundleProto from '../protos/firestore_bundle_proto'; +import { BundleConverter, BundledDocuments } from '../core/bundle'; +import { NamedQuery } from '../core/bundle_types'; +import { fromVersion } from '../remote/serializer'; +import { fromBundledQuery } from './local_serializer'; +import { ByteString } from '../util/byte_string'; +import { ResourcePath } from '../model/path'; +import { + LocalStore, + LocalStoreImpl, + allocateTarget, + populateDocumentChangeBuffer +} from './local_store'; + +/** + * Applies the documents from a bundle to the "ground-state" (remote) + * documents. + * + * LocalDocuments are re-calculated if there are remaining mutations in the + * queue. + */ +export async function applyBundleDocuments( + localStore: LocalStore, + documents: BundledDocuments, + bundleName: string +): Promise { + const localStoreImpl = debugCast(localStore, LocalStoreImpl); + const bundleConverter = new BundleConverter(localStoreImpl.serializer); + let documentKeys = documentKeySet(); + let documentMap = maybeDocumentMap(); + let versionMap = documentVersionMap(); + for (const bundleDoc of documents) { + const documentKey = bundleConverter.toDocumentKey(bundleDoc.metadata.name!); + if (bundleDoc.document) { + documentKeys = documentKeys.add(documentKey); + } + documentMap = documentMap.insert( + documentKey, + bundleConverter.toMaybeDocument(bundleDoc) + ); + versionMap = versionMap.insert( + documentKey, + bundleConverter.toSnapshotVersion(bundleDoc.metadata.readTime!) + ); + } + + const documentBuffer = localStoreImpl.remoteDocuments.newChangeBuffer({ + trackRemovals: true // Make sure document removals show up in `getNewDocumentChanges()` + }); + + // Allocates a target to hold all document keys from the bundle, such that + // they will not get garbage collected right away. + const umbrellaTargetData = await allocateTarget( + localStoreImpl, + umbrellaTarget(bundleName) + ); + return localStoreImpl.persistence.runTransaction( + 'Apply bundle documents', + 'readwrite', + txn => { + return populateDocumentChangeBuffer( + txn, + documentBuffer, + documentMap, + SnapshotVersion.min(), + versionMap + ) + .next(changedDocs => { + documentBuffer.apply(txn); + return changedDocs; + }) + .next(changedDocs => { + return localStoreImpl.targetCache + .removeMatchingKeysForTargetId(txn, umbrellaTargetData.targetId) + .next(() => + localStoreImpl.targetCache.addMatchingKeys( + txn, + documentKeys, + umbrellaTargetData.targetId + ) + ) + .next(() => + localStoreImpl.localDocuments.getLocalViewOfDocuments( + txn, + changedDocs + ) + ); + }); + } + ); +} + +/** + * Returns a promise of a boolean to indicate if the given bundle has already + * been loaded and the create time is newer than the current loading bundle. + */ +export function hasNewerBundle( + localStore: LocalStore, + bundleMetadata: bundleProto.BundleMetadata +): Promise { + const localStoreImpl = debugCast(localStore, LocalStoreImpl); + const bundleConverter = new BundleConverter(localStoreImpl.serializer); + const currentReadTime = bundleConverter.toSnapshotVersion( + bundleMetadata.createTime! + ); + return localStoreImpl.persistence + .runTransaction('hasNewerBundle', 'readonly', transaction => { + return localStoreImpl.bundleCache.getBundleMetadata( + transaction, + bundleMetadata.id! + ); + }) + .then(cached => { + return !!cached && cached.createTime!.compareTo(currentReadTime) >= 0; + }); +} + +/** + * Saves the given `BundleMetadata` to local persistence. + * @param bundleMetadata + */ +export function saveBundle( + localStore: LocalStore, + bundleMetadata: bundleProto.BundleMetadata +): Promise { + const localStoreImpl = debugCast(localStore, LocalStoreImpl); + return localStoreImpl.persistence.runTransaction( + 'Save bundle', + 'readwrite', + transaction => { + return localStoreImpl.bundleCache.saveBundleMetadata( + transaction, + bundleMetadata + ); + } + ); +} + +/** + * Returns a promise of a `NamedQuery` associated with given query name. Promise + * resolves to undefined if no persisted data can be found. + */ +export function getNamedQuery( + localStore: LocalStore, + queryName: string +): Promise { + const localStoreImpl = debugCast(localStore, LocalStoreImpl); + return localStoreImpl.persistence.runTransaction( + 'Get named query', + 'readonly', + transaction => + localStoreImpl.bundleCache.getNamedQuery(transaction, queryName) + ); +} + +/** + * Saves the given `NamedQuery` to local persistence. + */ +export async function saveNamedQuery( + localStore: LocalStore, + query: bundleProto.NamedQuery, + documents: DocumentKeySet = documentKeySet() +): Promise { + // Allocate a target for the named query such that it can be resumed + // from associated read time if users use it to listen. + // NOTE: this also means if no corresponding target exists, the new target + // will remain active and will not get collected, unless users happen to + // unlisten the query somehow. + const allocated = await allocateTarget( + localStore, + queryToTarget(fromBundledQuery(query.bundledQuery!)) + ); + + const localStoreImpl = debugCast(localStore, LocalStoreImpl); + return localStoreImpl.persistence.runTransaction( + 'Save named query', + 'readwrite', + transaction => { + const readTime = fromVersion(query.readTime!); + // Simply save the query itself if it is older than what the SDK already + // has. + if (allocated.snapshotVersion.compareTo(readTime) >= 0) { + return localStoreImpl.bundleCache.saveNamedQuery(transaction, query); + } + + // Update existing target data because the query from the bundle is newer. + const newTargetData = allocated.withResumeToken( + ByteString.EMPTY_BYTE_STRING, + readTime + ); + localStoreImpl.targetDataByTarget = localStoreImpl.targetDataByTarget.insert( + newTargetData.targetId, + newTargetData + ); + return localStoreImpl.targetCache + .updateTargetData(transaction, newTargetData) + .next(() => + localStoreImpl.targetCache.removeMatchingKeysForTargetId( + transaction, + allocated.targetId + ) + ) + .next(() => + localStoreImpl.targetCache.addMatchingKeys( + transaction, + documents, + allocated.targetId + ) + ) + .next(() => + localStoreImpl.bundleCache.saveNamedQuery(transaction, query) + ); + } + ); +} + +/** + * Creates a new target using the given bundle name, which will be used to + * hold the keys of all documents from the bundle in query-document mappings. + * This ensures that the loaded documents do not get garbage collected + * right away. + */ +function umbrellaTarget(bundleName: string): Target { + // It is OK that the path used for the query is not valid, because this will + // not be read and queried. + return queryToTarget( + newQueryForPath(ResourcePath.fromString(`__bundle__/docs/${bundleName}`)) + ); +} diff --git a/packages/firestore/src/local/memory_bundle_cache.ts b/packages/firestore/src/local/memory_bundle_cache.ts index f45d2359af4..a145fe75d66 100644 --- a/packages/firestore/src/local/memory_bundle_cache.ts +++ b/packages/firestore/src/local/memory_bundle_cache.ts @@ -22,7 +22,7 @@ import { BundleMetadata as ProtoBundleMetadata } from '../protos/firestore_bundle_proto'; import { BundleCache } from './bundle_cache'; -import { Bundle, NamedQuery } from '../core/bundle'; +import { Bundle, NamedQuery } from '../core/bundle_types'; import { fromBundleMetadata, fromProtoNamedQuery, diff --git a/packages/firestore/test/integration/util/firebase_export.ts b/packages/firestore/test/integration/util/firebase_export.ts index e841232d12b..ed46e1c61c1 100644 --- a/packages/firestore/test/integration/util/firebase_export.ts +++ b/packages/firestore/test/integration/util/firebase_export.ts @@ -28,9 +28,11 @@ import { FieldValue } from '../../../src/compat/field_value'; import { FieldPath } from '../../../src/api/field_path'; import { Timestamp } from '../../../src/api/timestamp'; import { Blob } from '../../../src/api/blob'; +null; import { GeoPoint } from '../../../src/api/geo_point'; import { FirebaseApp } from '@firebase/app-types'; import { Firestore } from '../../../src/api/database'; +import '../../../index.bundle'; // TODO(dimond): Right now we create a new app and Firestore instance for // every test and never clean them up. We may need to revisit. diff --git a/packages/firestore/test/unit/local/bundle_cache.test.ts b/packages/firestore/test/unit/local/bundle_cache.test.ts index dfc84ee6b90..32d67ee730c 100644 --- a/packages/firestore/test/unit/local/bundle_cache.test.ts +++ b/packages/firestore/test/unit/local/bundle_cache.test.ts @@ -35,7 +35,7 @@ import { testIndexedDbPersistence, testMemoryEagerPersistence } from './persistence_test_helpers'; -import { NamedQuery } from '../../../src/core/bundle'; +import { NamedQuery } from '../../../src/core/bundle_types'; import { toQueryTarget } from '../../../src/remote/serializer'; describe('MemoryBundleCache', () => { diff --git a/packages/firestore/test/unit/local/local_store.test.ts b/packages/firestore/test/unit/local/local_store.test.ts index b379a403161..b3b5a601cce 100644 --- a/packages/firestore/test/unit/local/local_store.test.ts +++ b/packages/firestore/test/unit/local/local_store.test.ts @@ -34,15 +34,10 @@ import { SnapshotVersion } from '../../../src/core/snapshot_version'; import { IndexFreeQueryEngine } from '../../../src/local/index_free_query_engine'; import { IndexedDbPersistence } from '../../../src/local/indexeddb_persistence'; import { - applyBundleDocuments, - getNamedQuery, - hasNewerBundle, applyRemoteEventToLocalCache, LocalStore, LocalWriteResult, newLocalStore, - saveBundle, - saveNamedQuery, synchronizeLastDocumentChangeReadTime, notifyLocalViewChanges, acknowledgeBatch, @@ -55,6 +50,13 @@ import { getHighestUnacknowledgedBatchId, rejectBatch } from '../../../src/local/local_store'; +import { + applyBundleDocuments, + getNamedQuery, + hasNewerBundle, + saveBundle, + saveNamedQuery +} from '../../../src/local/local_store_bundle'; import { LocalViewChanges } from '../../../src/local/local_view_changes'; import { Persistence } from '../../../src/local/persistence'; import { SimpleQueryEngine } from '../../../src/local/simple_query_engine'; @@ -114,7 +116,8 @@ import { CountingQueryEngine, QueryEngineType } from './counting_query_engine'; import * as persistenceHelpers from './persistence_test_helpers'; import { JSON_SERIALIZER } from './persistence_test_helpers'; import { ByteString } from '../../../src/util/byte_string'; -import { BundledDocuments, NamedQuery } from '../../../src/core/bundle'; +import { BundledDocuments } from '../../../src/core/bundle'; +import { NamedQuery } from '../../../src/core/bundle_types'; import { BundleMetadata as ProtoBundleMetadata } from '../../../src/protos/firestore_bundle_proto'; export interface LocalStoreComponents { diff --git a/packages/firestore/test/unit/local/test_bundle_cache.ts b/packages/firestore/test/unit/local/test_bundle_cache.ts index 2ee7badc315..c2e3934b27f 100644 --- a/packages/firestore/test/unit/local/test_bundle_cache.ts +++ b/packages/firestore/test/unit/local/test_bundle_cache.ts @@ -17,7 +17,7 @@ import { Persistence } from '../../../src/local/persistence'; import { BundleCache } from '../../../src/local/bundle_cache'; -import { Bundle, NamedQuery } from '../../../src/core/bundle'; +import { Bundle, NamedQuery } from '../../../src/core/bundle_types'; import { NamedQuery as ProtoNamedQuery, BundleMetadata as ProtoBundleMetadata diff --git a/packages/firestore/test/unit/specs/spec_test_runner.ts b/packages/firestore/test/unit/specs/spec_test_runner.ts index 099489d6373..d48edeaa190 100644 --- a/packages/firestore/test/unit/specs/spec_test_runner.ts +++ b/packages/firestore/test/unit/specs/spec_test_runner.ts @@ -42,7 +42,6 @@ import { } from '../../../src/core/query'; import { SnapshotVersion } from '../../../src/core/snapshot_version'; import { - syncEngineLoadBundle, activeLimboDocumentResolutions, enqueuedLimboDocumentResolutions, registerPendingWritesCallback, @@ -156,6 +155,7 @@ import { import { toByteStreamReader } from '../../../src/platform/byte_stream_reader'; import { logWarn } from '../../../src/util/log'; import { newTextEncoder } from '../../../src/platform/serializer'; +import { syncEngineLoadBundle } from '../../../src/core/bundle'; const ARBITRARY_SEQUENCE_NUMBER = 2; From 79b1ed24283a92f41f7435ace4af45d3e16cf82f Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Thu, 26 Nov 2020 09:40:36 -0500 Subject: [PATCH 02/16] Address comments minus revert refactoring. --- packages/firestore/src/api/bundle.ts | 11 ++++--- packages/firestore/src/core/bundle.ts | 32 +++++++++---------- .../firestore/src/local/local_store_bundle.ts | 11 ++++--- .../test/integration/util/firebase_export.ts | 2 +- 4 files changed, 31 insertions(+), 25 deletions(-) diff --git a/packages/firestore/src/api/bundle.ts b/packages/firestore/src/api/bundle.ts index e985b560eac..e60c3d7ac5f 100644 --- a/packages/firestore/src/api/bundle.ts +++ b/packages/firestore/src/api/bundle.ts @@ -15,20 +15,23 @@ * limitations under the License. */ -import { LoadBundleTaskProgress } from '@firebase/firestore-types'; +import { + LoadBundleTask as ApiLoadBundleTask, + LoadBundleTaskProgress +} from '@firebase/firestore-types'; import { Deferred } from '../util/promise'; import { PartialObserver } from './observer'; import { debugAssert } from '../util/assert'; import { FirestoreError } from '../util/error'; import { ensureFirestoreConfigured, Query, Firestore } from './database'; +import { Query as ExpQuery } from '../../exp/src/api/reference'; import { firestoreClientGetNamedQuery, firestoreClientLoadBundle } from '../core/bundle'; -import { Query as ExpQuery } from '../../exp/src/api/reference'; export class LoadBundleTask - implements LoadBundleTask, PromiseLike { + implements ApiLoadBundleTask, PromiseLike { private _progressObserver: PartialObserver = {}; private _taskCompletionResolver = new Deferred(); @@ -122,9 +125,9 @@ export function loadBundle( bundleData: ArrayBuffer | ReadableStream | string ): LoadBundleTask { const resultTask = new LoadBundleTask(); - // eslint-disable-next-line @typescript-eslint/no-floating-promises firestoreClientLoadBundle( ensureFirestoreConfigured(db._delegate), + db._databaseId, bundleData, resultTask ); diff --git a/packages/firestore/src/core/bundle.ts b/packages/firestore/src/core/bundle.ts index c78bab2c7a9..5a088e3d407 100644 --- a/packages/firestore/src/core/bundle.ts +++ b/packages/firestore/src/core/bundle.ts @@ -24,29 +24,22 @@ import { JsonProtoSerializer } from '../remote/serializer'; import { - NamedQuery as ProtoNamedQuery, + BundledDocumentMetadata as ProtoBundledDocumentMetadata, BundleMetadata as ProtoBundleMetadata, - BundledDocumentMetadata as ProtoBundledDocumentMetadata + NamedQuery as ProtoNamedQuery } from '../protos/firestore_bundle_proto'; import * as api from '../protos/firestore_proto_api'; import { DocumentKey } from '../model/document_key'; import { MaybeDocument, NoDocument } from '../model/document'; import { debugAssert, debugCast } from '../util/assert'; import { LocalStore } from '../local/local_store'; -import { SizedBundleElement, BundleReader } from '../util/bundle_reader'; +import { BundleReader, SizedBundleElement } from '../util/bundle_reader'; import { documentKeySet, DocumentKeySet, MaybeDocumentMap } from '../model/collections'; -import { - FirestoreClient, - getLocalStore, - getSyncEngine -} from './firestore_client'; import { LoadBundleTask } from '../api/bundle'; -import { newSerializer, newTextEncoder } from '../platform/serializer'; -import { toByteStreamReader } from '../platform/byte_stream_reader'; import { emitNewSnapsAndNotifyLocalStore, SyncEngine, @@ -61,7 +54,16 @@ import { saveBundle, saveNamedQuery } from '../local/local_store_bundle'; +import { newSerializer, newTextEncoder } from '../platform/serializer'; +import { toByteStreamReader } from '../platform/byte_stream_reader'; +import { DatabaseId } from './database_info'; import { NamedQuery } from './bundle_types'; +import { + FirestoreClient, + getLocalStore, + getSyncEngine +} from './firestore_client'; + /** * Represents a bundled document, including the metadata and the document * itself, if it exists. @@ -258,15 +260,13 @@ class BundleLoader { } } -export async function firestoreClientLoadBundle( +export function firestoreClientLoadBundle( client: FirestoreClient, + databaseId: DatabaseId, data: ReadableStream | ArrayBuffer | string, resultTask: LoadBundleTask -): Promise { - const reader = createBundleReader( - data, - newSerializer((await client.getConfiguration()).databaseInfo.databaseId) - ); +): void { + const reader = createBundleReader(data, newSerializer(databaseId)); client.asyncQueue.enqueueAndForget(async () => { syncEngineLoadBundle(await getSyncEngine(client), reader, resultTask); }); diff --git a/packages/firestore/src/local/local_store_bundle.ts b/packages/firestore/src/local/local_store_bundle.ts index 71cae7e00b8..d237352a2af 100644 --- a/packages/firestore/src/local/local_store_bundle.ts +++ b/packages/firestore/src/local/local_store_bundle.ts @@ -27,7 +27,10 @@ import { } from '../model/collections'; import { debugCast } from '../util/assert'; -import * as bundleProto from '../protos/firestore_bundle_proto'; +import { + BundleMetadata, + NamedQuery as ProtoNamedQuery +} from '../protos/firestore_bundle_proto'; import { BundleConverter, BundledDocuments } from '../core/bundle'; import { NamedQuery } from '../core/bundle_types'; import { fromVersion } from '../remote/serializer'; @@ -125,7 +128,7 @@ export async function applyBundleDocuments( */ export function hasNewerBundle( localStore: LocalStore, - bundleMetadata: bundleProto.BundleMetadata + bundleMetadata: BundleMetadata ): Promise { const localStoreImpl = debugCast(localStore, LocalStoreImpl); const bundleConverter = new BundleConverter(localStoreImpl.serializer); @@ -150,7 +153,7 @@ export function hasNewerBundle( */ export function saveBundle( localStore: LocalStore, - bundleMetadata: bundleProto.BundleMetadata + bundleMetadata: BundleMetadata ): Promise { const localStoreImpl = debugCast(localStore, LocalStoreImpl); return localStoreImpl.persistence.runTransaction( @@ -187,7 +190,7 @@ export function getNamedQuery( */ export async function saveNamedQuery( localStore: LocalStore, - query: bundleProto.NamedQuery, + query: ProtoNamedQuery, documents: DocumentKeySet = documentKeySet() ): Promise { // Allocate a target for the named query such that it can be resumed diff --git a/packages/firestore/test/integration/util/firebase_export.ts b/packages/firestore/test/integration/util/firebase_export.ts index ed46e1c61c1..5da44345c22 100644 --- a/packages/firestore/test/integration/util/firebase_export.ts +++ b/packages/firestore/test/integration/util/firebase_export.ts @@ -28,10 +28,10 @@ import { FieldValue } from '../../../src/compat/field_value'; import { FieldPath } from '../../../src/api/field_path'; import { Timestamp } from '../../../src/api/timestamp'; import { Blob } from '../../../src/api/blob'; -null; import { GeoPoint } from '../../../src/api/geo_point'; import { FirebaseApp } from '@firebase/app-types'; import { Firestore } from '../../../src/api/database'; +// Import to trigger prototype patching of bundle loading. import '../../../index.bundle'; // TODO(dimond): Right now we create a new app and Firestore instance for From 6000ea1d2f22980dafaac462128bd8b54c8e307c Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Fri, 27 Nov 2020 19:45:33 -0500 Subject: [PATCH 03/16] Address comments and restore some code structure. --- packages/firestore/src/api/bundle.ts | 2 +- packages/firestore/src/core/bundle.ts | 68 +++++++------------ packages/firestore/src/core/bundle_types.ts | 45 ------------ .../firestore/src/core/firestore_client.ts | 43 +++++++++++- packages/firestore/src/local/bundle_cache.ts | 2 +- .../src/local/indexeddb_bundle_cache.ts | 2 +- .../firestore/src/local/local_serializer.ts | 2 +- .../firestore/src/local/local_store_bundle.ts | 3 +- .../src/local/memory_bundle_cache.ts | 2 +- .../test/unit/local/bundle_cache.test.ts | 2 +- .../test/unit/local/local_store.test.ts | 3 +- .../test/unit/local/test_bundle_cache.ts | 2 +- 12 files changed, 75 insertions(+), 101 deletions(-) delete mode 100644 packages/firestore/src/core/bundle_types.ts diff --git a/packages/firestore/src/api/bundle.ts b/packages/firestore/src/api/bundle.ts index e60c3d7ac5f..a5240d8a584 100644 --- a/packages/firestore/src/api/bundle.ts +++ b/packages/firestore/src/api/bundle.ts @@ -28,7 +28,7 @@ import { Query as ExpQuery } from '../../exp/src/api/reference'; import { firestoreClientGetNamedQuery, firestoreClientLoadBundle -} from '../core/bundle'; +} from '../core/firestore_client'; export class LoadBundleTask implements ApiLoadBundleTask, PromiseLike { diff --git a/packages/firestore/src/core/bundle.ts b/packages/firestore/src/core/bundle.ts index 5a088e3d407..61735939a2d 100644 --- a/packages/firestore/src/core/bundle.ts +++ b/packages/firestore/src/core/bundle.ts @@ -16,6 +16,7 @@ */ import { LoadBundleTaskProgress } from '@firebase/firestore-types'; +import { Query } from './query'; import { SnapshotVersion } from './snapshot_version'; import { fromDocument, @@ -49,20 +50,33 @@ import { logWarn } from '../util/log'; import { LOG_TAG } from '../../lite/src/api/components'; import { applyBundleDocuments, - getNamedQuery, hasNewerBundle, saveBundle, saveNamedQuery } from '../local/local_store_bundle'; -import { newSerializer, newTextEncoder } from '../platform/serializer'; -import { toByteStreamReader } from '../platform/byte_stream_reader'; -import { DatabaseId } from './database_info'; -import { NamedQuery } from './bundle_types'; -import { - FirestoreClient, - getLocalStore, - getSyncEngine -} from './firestore_client'; + +/** + * Represents a Firestore bundle saved by the SDK in its local storage. + */ +export interface Bundle { + readonly id: string; + readonly version: number; + /** + * Set to the snapshot version of the bundle if created by the Server SDKs. + * Otherwise set to SnapshotVersion.MIN. + */ + readonly createTime: SnapshotVersion; +} + +/** + * Represents a Query saved by the SDK in its local storage. + */ +export interface NamedQuery { + readonly name: string; + readonly query: Query; + /** The time at which the results for this query were read. */ + readonly readTime: SnapshotVersion; +} /** * Represents a bundled document, including the metadata and the document @@ -260,40 +274,6 @@ class BundleLoader { } } -export function firestoreClientLoadBundle( - client: FirestoreClient, - databaseId: DatabaseId, - data: ReadableStream | ArrayBuffer | string, - resultTask: LoadBundleTask -): void { - const reader = createBundleReader(data, newSerializer(databaseId)); - client.asyncQueue.enqueueAndForget(async () => { - syncEngineLoadBundle(await getSyncEngine(client), reader, resultTask); - }); -} - -export function firestoreClientGetNamedQuery( - client: FirestoreClient, - queryName: string -): Promise { - return client.asyncQueue.enqueue(async () => - getNamedQuery(await getLocalStore(client), queryName) - ); -} - -function createBundleReader( - data: ReadableStream | ArrayBuffer | string, - serializer: JsonProtoSerializer -): BundleReader { - let content: ReadableStream | ArrayBuffer; - if (typeof data === 'string') { - content = newTextEncoder().encode(data); - } else { - content = data; - } - return new BundleReader(toByteStreamReader(content), serializer); -} - /** * Loads a Firestore bundle into the SDK. The returned promise resolves when * the bundle finished loading. diff --git a/packages/firestore/src/core/bundle_types.ts b/packages/firestore/src/core/bundle_types.ts deleted file mode 100644 index a4e7bf9bc19..00000000000 --- a/packages/firestore/src/core/bundle_types.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** - * @license - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// This is a separate file because rollup (or typescript) treat it as circular -// dependency when it was in `./bundle.ts`, which breaks bundles module build. - -import { Query } from './query'; -import { SnapshotVersion } from './snapshot_version'; - -/** - * Represents a Firestore bundle saved by the SDK in its local storage. - */ -export interface Bundle { - readonly id: string; - readonly version: number; - /** - * Set to the snapshot version of the bundle if created by the Server SDKs. - * Otherwise set to SnapshotVersion.MIN. - */ - readonly createTime: SnapshotVersion; -} - -/** - * Represents a Query saved by the SDK in its local storage. - */ -export interface NamedQuery { - readonly name: string; - readonly query: Query; - /** The time at which the results for this query were read. */ - readonly readTime: SnapshotVersion; -} diff --git a/packages/firestore/src/core/firestore_client.ts b/packages/firestore/src/core/firestore_client.ts index e0e162d7b6a..c09abec6c87 100644 --- a/packages/firestore/src/core/firestore_client.ts +++ b/packages/firestore/src/core/firestore_client.ts @@ -58,7 +58,7 @@ import { syncEngineWrite } from './sync_engine'; import { View } from './view'; -import { DatabaseInfo } from './database_info'; +import { DatabaseId, DatabaseInfo } from './database_info'; import { newQueryForPath, Query } from './query'; import { Transaction } from './transaction'; import { ViewSnapshot } from './view_snapshot'; @@ -75,6 +75,13 @@ import { logDebug } from '../util/log'; import { AutoId } from '../util/misc'; import { Persistence } from '../local/persistence'; import { Datastore } from '../remote/datastore'; +import { LoadBundleTask } from '../api/bundle'; +import { newSerializer, newTextEncoder } from '../platform/serializer'; +import { getNamedQuery } from '../local/local_store_bundle'; +import { JsonProtoSerializer } from '../remote/serializer'; +import { BundleReader } from '../util/bundle_reader'; +import { toByteStreamReader } from '../platform/byte_stream_reader'; +import { NamedQuery, syncEngineLoadBundle } from './bundle'; const LOG_TAG = 'FirestoreClient'; export const MAX_CONCURRENT_LIMBO_RESOLUTIONS = 100; @@ -648,3 +655,37 @@ function executeQueryViaSnapshotListener( }); return eventManagerListen(eventManager, listener); } + +export function firestoreClientLoadBundle( + client: FirestoreClient, + databaseId: DatabaseId, + data: ReadableStream | ArrayBuffer | string, + resultTask: LoadBundleTask +): void { + const reader = createBundleReader(data, newSerializer(databaseId)); + client.asyncQueue.enqueueAndForget(async () => { + syncEngineLoadBundle(await getSyncEngine(client), reader, resultTask); + }); +} + +export function firestoreClientGetNamedQuery( + client: FirestoreClient, + queryName: string +): Promise { + return client.asyncQueue.enqueue(async () => + getNamedQuery(await getLocalStore(client), queryName) + ); +} + +function createBundleReader( + data: ReadableStream | ArrayBuffer | string, + serializer: JsonProtoSerializer +): BundleReader { + let content: ReadableStream | ArrayBuffer; + if (typeof data === 'string') { + content = newTextEncoder().encode(data); + } else { + content = data; + } + return new BundleReader(toByteStreamReader(content), serializer); +} diff --git a/packages/firestore/src/local/bundle_cache.ts b/packages/firestore/src/local/bundle_cache.ts index 004d91c3053..c9dadb86e9b 100644 --- a/packages/firestore/src/local/bundle_cache.ts +++ b/packages/firestore/src/local/bundle_cache.ts @@ -21,7 +21,7 @@ import { NamedQuery as ProtoNamedQuery, BundleMetadata as ProtoBundleMetadata } from '../protos/firestore_bundle_proto'; -import { Bundle, NamedQuery } from '../core/bundle_types'; +import { Bundle, NamedQuery } from '../core/bundle'; /** * Provides interfaces to save and read Firestore bundles. diff --git a/packages/firestore/src/local/indexeddb_bundle_cache.ts b/packages/firestore/src/local/indexeddb_bundle_cache.ts index b713444761a..341ff75e8c5 100644 --- a/packages/firestore/src/local/indexeddb_bundle_cache.ts +++ b/packages/firestore/src/local/indexeddb_bundle_cache.ts @@ -37,7 +37,7 @@ import { toDbBundle, toDbNamedQuery } from './local_serializer'; -import { Bundle, NamedQuery } from '../core/bundle_types'; +import { Bundle, NamedQuery } from '../core/bundle'; export class IndexedDbBundleCache implements BundleCache { constructor(private serializer: LocalSerializer) {} diff --git a/packages/firestore/src/local/local_serializer.ts b/packages/firestore/src/local/local_serializer.ts index dd07ac0f8d9..9465fa12c4d 100644 --- a/packages/firestore/src/local/local_serializer.ts +++ b/packages/firestore/src/local/local_serializer.ts @@ -55,13 +55,13 @@ import { DbUnknownDocument } from './indexeddb_schema'; import { TargetData, TargetPurpose } from './target_data'; -import { Bundle, NamedQuery } from '../core/bundle_types'; import { LimitType, Query, queryWithLimit } from '../core/query'; import { BundleMetadata as ProtoBundleMetadata, NamedQuery as ProtoNamedQuery, BundledQuery as ProtoBundledQuery } from '../protos/firestore_bundle_proto'; +import { Bundle, NamedQuery } from '../core/bundle'; /** Serializer for values stored in the LocalStore. */ export class LocalSerializer { diff --git a/packages/firestore/src/local/local_store_bundle.ts b/packages/firestore/src/local/local_store_bundle.ts index d237352a2af..75ee10b1d6f 100644 --- a/packages/firestore/src/local/local_store_bundle.ts +++ b/packages/firestore/src/local/local_store_bundle.ts @@ -31,8 +31,7 @@ import { BundleMetadata, NamedQuery as ProtoNamedQuery } from '../protos/firestore_bundle_proto'; -import { BundleConverter, BundledDocuments } from '../core/bundle'; -import { NamedQuery } from '../core/bundle_types'; +import { BundleConverter, BundledDocuments, NamedQuery } from '../core/bundle'; import { fromVersion } from '../remote/serializer'; import { fromBundledQuery } from './local_serializer'; import { ByteString } from '../util/byte_string'; diff --git a/packages/firestore/src/local/memory_bundle_cache.ts b/packages/firestore/src/local/memory_bundle_cache.ts index a145fe75d66..24d65e02f6e 100644 --- a/packages/firestore/src/local/memory_bundle_cache.ts +++ b/packages/firestore/src/local/memory_bundle_cache.ts @@ -22,12 +22,12 @@ import { BundleMetadata as ProtoBundleMetadata } from '../protos/firestore_bundle_proto'; import { BundleCache } from './bundle_cache'; -import { Bundle, NamedQuery } from '../core/bundle_types'; import { fromBundleMetadata, fromProtoNamedQuery, LocalSerializer } from './local_serializer'; +import { Bundle, NamedQuery } from '../core/bundle'; export class MemoryBundleCache implements BundleCache { private bundles = new Map(); diff --git a/packages/firestore/test/unit/local/bundle_cache.test.ts b/packages/firestore/test/unit/local/bundle_cache.test.ts index 32d67ee730c..1125fe26dc6 100644 --- a/packages/firestore/test/unit/local/bundle_cache.test.ts +++ b/packages/firestore/test/unit/local/bundle_cache.test.ts @@ -35,8 +35,8 @@ import { testIndexedDbPersistence, testMemoryEagerPersistence } from './persistence_test_helpers'; -import { NamedQuery } from '../../../src/core/bundle_types'; import { toQueryTarget } from '../../../src/remote/serializer'; +import { NamedQuery } from '../../../src/core/bundle'; describe('MemoryBundleCache', () => { let cache: TestBundleCache; diff --git a/packages/firestore/test/unit/local/local_store.test.ts b/packages/firestore/test/unit/local/local_store.test.ts index b3b5a601cce..4c6a1f6e3db 100644 --- a/packages/firestore/test/unit/local/local_store.test.ts +++ b/packages/firestore/test/unit/local/local_store.test.ts @@ -116,8 +116,7 @@ import { CountingQueryEngine, QueryEngineType } from './counting_query_engine'; import * as persistenceHelpers from './persistence_test_helpers'; import { JSON_SERIALIZER } from './persistence_test_helpers'; import { ByteString } from '../../../src/util/byte_string'; -import { BundledDocuments } from '../../../src/core/bundle'; -import { NamedQuery } from '../../../src/core/bundle_types'; +import { BundledDocuments, NamedQuery } from '../../../src/core/bundle'; import { BundleMetadata as ProtoBundleMetadata } from '../../../src/protos/firestore_bundle_proto'; export interface LocalStoreComponents { diff --git a/packages/firestore/test/unit/local/test_bundle_cache.ts b/packages/firestore/test/unit/local/test_bundle_cache.ts index c2e3934b27f..49d734dada8 100644 --- a/packages/firestore/test/unit/local/test_bundle_cache.ts +++ b/packages/firestore/test/unit/local/test_bundle_cache.ts @@ -17,11 +17,11 @@ import { Persistence } from '../../../src/local/persistence'; import { BundleCache } from '../../../src/local/bundle_cache'; -import { Bundle, NamedQuery } from '../../../src/core/bundle_types'; import { NamedQuery as ProtoNamedQuery, BundleMetadata as ProtoBundleMetadata } from '../../../src/protos/firestore_bundle_proto'; +import { Bundle, NamedQuery } from '../../../src/core/bundle'; /** * A wrapper around a BundleCache that automatically creates a From 326bb02471ee2715479c00bafc2ce2cb4d69a98c Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Fri, 27 Nov 2020 20:38:07 -0500 Subject: [PATCH 04/16] Keep bundle_types.ts, otherwise minified test breaks. --- packages/firestore/src/core/bundle.ts | 24 ---------- packages/firestore/src/core/bundle_types.ts | 45 +++++++++++++++++++ .../firestore/src/core/firestore_client.ts | 3 +- packages/firestore/src/local/bundle_cache.ts | 2 +- .../src/local/indexeddb_bundle_cache.ts | 2 +- .../firestore/src/local/local_serializer.ts | 2 +- .../firestore/src/local/local_store_bundle.ts | 3 +- .../src/local/memory_bundle_cache.ts | 2 +- .../test/unit/local/bundle_cache.test.ts | 2 +- .../test/unit/local/local_store.test.ts | 3 +- .../test/unit/local/test_bundle_cache.ts | 2 +- 11 files changed, 57 insertions(+), 33 deletions(-) create mode 100644 packages/firestore/src/core/bundle_types.ts diff --git a/packages/firestore/src/core/bundle.ts b/packages/firestore/src/core/bundle.ts index 61735939a2d..abc2b953e50 100644 --- a/packages/firestore/src/core/bundle.ts +++ b/packages/firestore/src/core/bundle.ts @@ -16,7 +16,6 @@ */ import { LoadBundleTaskProgress } from '@firebase/firestore-types'; -import { Query } from './query'; import { SnapshotVersion } from './snapshot_version'; import { fromDocument, @@ -55,29 +54,6 @@ import { saveNamedQuery } from '../local/local_store_bundle'; -/** - * Represents a Firestore bundle saved by the SDK in its local storage. - */ -export interface Bundle { - readonly id: string; - readonly version: number; - /** - * Set to the snapshot version of the bundle if created by the Server SDKs. - * Otherwise set to SnapshotVersion.MIN. - */ - readonly createTime: SnapshotVersion; -} - -/** - * Represents a Query saved by the SDK in its local storage. - */ -export interface NamedQuery { - readonly name: string; - readonly query: Query; - /** The time at which the results for this query were read. */ - readonly readTime: SnapshotVersion; -} - /** * Represents a bundled document, including the metadata and the document * itself, if it exists. diff --git a/packages/firestore/src/core/bundle_types.ts b/packages/firestore/src/core/bundle_types.ts new file mode 100644 index 00000000000..a4e7bf9bc19 --- /dev/null +++ b/packages/firestore/src/core/bundle_types.ts @@ -0,0 +1,45 @@ +/** + * @license + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This is a separate file because rollup (or typescript) treat it as circular +// dependency when it was in `./bundle.ts`, which breaks bundles module build. + +import { Query } from './query'; +import { SnapshotVersion } from './snapshot_version'; + +/** + * Represents a Firestore bundle saved by the SDK in its local storage. + */ +export interface Bundle { + readonly id: string; + readonly version: number; + /** + * Set to the snapshot version of the bundle if created by the Server SDKs. + * Otherwise set to SnapshotVersion.MIN. + */ + readonly createTime: SnapshotVersion; +} + +/** + * Represents a Query saved by the SDK in its local storage. + */ +export interface NamedQuery { + readonly name: string; + readonly query: Query; + /** The time at which the results for this query were read. */ + readonly readTime: SnapshotVersion; +} diff --git a/packages/firestore/src/core/firestore_client.ts b/packages/firestore/src/core/firestore_client.ts index c09abec6c87..ca5f6ed982c 100644 --- a/packages/firestore/src/core/firestore_client.ts +++ b/packages/firestore/src/core/firestore_client.ts @@ -81,7 +81,8 @@ import { getNamedQuery } from '../local/local_store_bundle'; import { JsonProtoSerializer } from '../remote/serializer'; import { BundleReader } from '../util/bundle_reader'; import { toByteStreamReader } from '../platform/byte_stream_reader'; -import { NamedQuery, syncEngineLoadBundle } from './bundle'; +import { syncEngineLoadBundle } from './bundle'; +import { NamedQuery } from './bundle_types'; const LOG_TAG = 'FirestoreClient'; export const MAX_CONCURRENT_LIMBO_RESOLUTIONS = 100; diff --git a/packages/firestore/src/local/bundle_cache.ts b/packages/firestore/src/local/bundle_cache.ts index c9dadb86e9b..004d91c3053 100644 --- a/packages/firestore/src/local/bundle_cache.ts +++ b/packages/firestore/src/local/bundle_cache.ts @@ -21,7 +21,7 @@ import { NamedQuery as ProtoNamedQuery, BundleMetadata as ProtoBundleMetadata } from '../protos/firestore_bundle_proto'; -import { Bundle, NamedQuery } from '../core/bundle'; +import { Bundle, NamedQuery } from '../core/bundle_types'; /** * Provides interfaces to save and read Firestore bundles. diff --git a/packages/firestore/src/local/indexeddb_bundle_cache.ts b/packages/firestore/src/local/indexeddb_bundle_cache.ts index 341ff75e8c5..b713444761a 100644 --- a/packages/firestore/src/local/indexeddb_bundle_cache.ts +++ b/packages/firestore/src/local/indexeddb_bundle_cache.ts @@ -37,7 +37,7 @@ import { toDbBundle, toDbNamedQuery } from './local_serializer'; -import { Bundle, NamedQuery } from '../core/bundle'; +import { Bundle, NamedQuery } from '../core/bundle_types'; export class IndexedDbBundleCache implements BundleCache { constructor(private serializer: LocalSerializer) {} diff --git a/packages/firestore/src/local/local_serializer.ts b/packages/firestore/src/local/local_serializer.ts index 9465fa12c4d..bb1d49525c8 100644 --- a/packages/firestore/src/local/local_serializer.ts +++ b/packages/firestore/src/local/local_serializer.ts @@ -61,7 +61,7 @@ import { NamedQuery as ProtoNamedQuery, BundledQuery as ProtoBundledQuery } from '../protos/firestore_bundle_proto'; -import { Bundle, NamedQuery } from '../core/bundle'; +import { Bundle, NamedQuery } from '../core/bundle_types'; /** Serializer for values stored in the LocalStore. */ export class LocalSerializer { diff --git a/packages/firestore/src/local/local_store_bundle.ts b/packages/firestore/src/local/local_store_bundle.ts index 75ee10b1d6f..64a4c608087 100644 --- a/packages/firestore/src/local/local_store_bundle.ts +++ b/packages/firestore/src/local/local_store_bundle.ts @@ -31,7 +31,7 @@ import { BundleMetadata, NamedQuery as ProtoNamedQuery } from '../protos/firestore_bundle_proto'; -import { BundleConverter, BundledDocuments, NamedQuery } from '../core/bundle'; +import { BundleConverter, BundledDocuments } from '../core/bundle'; import { fromVersion } from '../remote/serializer'; import { fromBundledQuery } from './local_serializer'; import { ByteString } from '../util/byte_string'; @@ -42,6 +42,7 @@ import { allocateTarget, populateDocumentChangeBuffer } from './local_store'; +import { NamedQuery } from '../core/bundle_types'; /** * Applies the documents from a bundle to the "ground-state" (remote) diff --git a/packages/firestore/src/local/memory_bundle_cache.ts b/packages/firestore/src/local/memory_bundle_cache.ts index 24d65e02f6e..e7b08144fee 100644 --- a/packages/firestore/src/local/memory_bundle_cache.ts +++ b/packages/firestore/src/local/memory_bundle_cache.ts @@ -27,7 +27,7 @@ import { fromProtoNamedQuery, LocalSerializer } from './local_serializer'; -import { Bundle, NamedQuery } from '../core/bundle'; +import { Bundle, NamedQuery } from '../core/bundle_types'; export class MemoryBundleCache implements BundleCache { private bundles = new Map(); diff --git a/packages/firestore/test/unit/local/bundle_cache.test.ts b/packages/firestore/test/unit/local/bundle_cache.test.ts index 1125fe26dc6..4c906a13453 100644 --- a/packages/firestore/test/unit/local/bundle_cache.test.ts +++ b/packages/firestore/test/unit/local/bundle_cache.test.ts @@ -36,7 +36,7 @@ import { testMemoryEagerPersistence } from './persistence_test_helpers'; import { toQueryTarget } from '../../../src/remote/serializer'; -import { NamedQuery } from '../../../src/core/bundle'; +import { NamedQuery } from '../../../src/core/bundle_types'; describe('MemoryBundleCache', () => { let cache: TestBundleCache; diff --git a/packages/firestore/test/unit/local/local_store.test.ts b/packages/firestore/test/unit/local/local_store.test.ts index 4c6a1f6e3db..91558811f2c 100644 --- a/packages/firestore/test/unit/local/local_store.test.ts +++ b/packages/firestore/test/unit/local/local_store.test.ts @@ -116,8 +116,9 @@ import { CountingQueryEngine, QueryEngineType } from './counting_query_engine'; import * as persistenceHelpers from './persistence_test_helpers'; import { JSON_SERIALIZER } from './persistence_test_helpers'; import { ByteString } from '../../../src/util/byte_string'; -import { BundledDocuments, NamedQuery } from '../../../src/core/bundle'; +import { BundledDocuments } from '../../../src/core/bundle'; import { BundleMetadata as ProtoBundleMetadata } from '../../../src/protos/firestore_bundle_proto'; +import { NamedQuery } from '../../../src/core/bundle_types'; export interface LocalStoreComponents { queryEngine: CountingQueryEngine; diff --git a/packages/firestore/test/unit/local/test_bundle_cache.ts b/packages/firestore/test/unit/local/test_bundle_cache.ts index 49d734dada8..0e9a4a879bc 100644 --- a/packages/firestore/test/unit/local/test_bundle_cache.ts +++ b/packages/firestore/test/unit/local/test_bundle_cache.ts @@ -21,7 +21,7 @@ import { NamedQuery as ProtoNamedQuery, BundleMetadata as ProtoBundleMetadata } from '../../../src/protos/firestore_bundle_proto'; -import { Bundle, NamedQuery } from '../../../src/core/bundle'; +import { Bundle, NamedQuery } from '../../../src/core/bundle_types'; /** * A wrapper around a BundleCache that automatically creates a From 210fbf486612b826c423005a6c16efa382c6a7f9 Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Wed, 25 Nov 2020 10:09:06 -0500 Subject: [PATCH 05/16] Checkin for comparison --- config/webpack.test.js | 2 +- integration/firestore/firebase_export.ts | 1 + .../firestore/firebase_export_memory.ts | 1 + packages/firestore/bundle/package.json | 10 + packages/firestore/memory/package.json | 12 +- packages/firestore/package.json | 22 +- packages/firestore/rollup.config.browser.js | 20 +- packages/firestore/rollup.config.node.js | 32 +- packages/firestore/rollup.config.rn.js | 10 +- packages/firestore/rollup.shared.js | 2 +- .../test/integration/api/bundle.test.ts | 482 +++++++++--------- 11 files changed, 307 insertions(+), 287 deletions(-) create mode 100644 packages/firestore/bundle/package.json diff --git a/config/webpack.test.js b/config/webpack.test.js index 887efd2ebec..cfd5e4f06cf 100644 --- a/config/webpack.test.js +++ b/config/webpack.test.js @@ -95,7 +95,7 @@ module.exports = { }, resolve: { modules: ['node_modules', path.resolve(__dirname, '../../node_modules')], - mainFields: ['browser', 'module', 'main'], + mainFields: ['esm2017', 'browser', 'module', 'main'], extensions: ['.js', '.ts'], symlinks: false }, diff --git a/integration/firestore/firebase_export.ts b/integration/firestore/firebase_export.ts index 4eeb1c7bfa9..317a53fac29 100644 --- a/integration/firestore/firebase_export.ts +++ b/integration/firestore/firebase_export.ts @@ -17,6 +17,7 @@ import firebase from '@firebase/app'; import '@firebase/firestore'; +import '@firebase/firestore/bundle'; import { FirebaseApp } from '@firebase/app-types'; import { Settings, FirebaseFirestore } from '@firebase/firestore-types'; diff --git a/integration/firestore/firebase_export_memory.ts b/integration/firestore/firebase_export_memory.ts index 441c778ef5a..ae08246a745 100644 --- a/integration/firestore/firebase_export_memory.ts +++ b/integration/firestore/firebase_export_memory.ts @@ -17,6 +17,7 @@ import firebase from '@firebase/app'; import '@firebase/firestore/memory'; +import '@firebase/firestore/bundle'; import { FirebaseApp } from '@firebase/app-types'; import { Settings, FirebaseFirestore } from '@firebase/firestore-types'; diff --git a/packages/firestore/bundle/package.json b/packages/firestore/bundle/package.json new file mode 100644 index 00000000000..df3c12d9f3f --- /dev/null +++ b/packages/firestore/bundle/package.json @@ -0,0 +1,10 @@ +{ + "name": "@firebase/firestore/bundle", + "description": "Firestore bundle", + "main": "../dist/node-cjs/bundle.js", + "main-esm2017": "../dist/node-esm2017/bundle.js", + "react-native": "../dist/rn/bundle.js", + "browser": "../dist/esm5/bundle.js", + "module": "../dist/esm5/bundle.js", + "esm2017": "../dist/esm2017/bundle.js" +} diff --git a/packages/firestore/memory/package.json b/packages/firestore/memory/package.json index 52decb62926..e6eb6025020 100644 --- a/packages/firestore/memory/package.json +++ b/packages/firestore/memory/package.json @@ -1,11 +1,11 @@ { "name": "@firebase/firestore/memory", "description": "A memory-only build of the Cloud Firestore JS SDK.", - "main": "../dist/index.memory.node.cjs.js", - "main-esm2017": "../dist/index.memory.node.esm2017.js", - "react-native": "../dist/index.memory.rn.esm2017.js", - "browser": "../dist/index.memory.esm.js", - "module": "../dist/index.memory.esm.js", - "esm2017": "../dist/index.memory.esm2017.js", + "main": "../dist/node-cjs/memory.js", + "main-esm2017": "../dist/node-esm2017/memory.js", + "react-native": "../dist/rn/memory.js", + "browser": "../dist/esm5/memory.js", + "module": "../dist/esm5/memory.js", + "esm2017": "../dist/esm2017/memory.js", "typings": "../dist/index.d.ts" } diff --git a/packages/firestore/package.json b/packages/firestore/package.json index b0353aaa949..ad7d05a471e 100644 --- a/packages/firestore/package.json +++ b/packages/firestore/package.json @@ -8,14 +8,15 @@ "author": "Firebase (https://firebase.google.com/)", "scripts": { "bundle": "rollup -c", - "build": "run-p 'bundle rollup.config.browser.js' 'bundle rollup.config.browser.memory.js' 'bundle rollup.config.node.js' 'bundle rollup.config.node.memory.js' 'bundle rollup.config.rn.js' 'bundle rollup.config.rn.memory.js' build:lite build:exp", + "build": "run-p 'bundle rollup.config.browser.js' 'bundle rollup.config.node.js' 'bundle rollup.config.rn.js' build:lite build:exp", "build:scripts": "tsc -moduleResolution node --module commonjs scripts/*.ts && ls scripts/*.js | xargs -I % sh -c 'terser % -o %'", - "build:release": "run-p 'bundle rollup.config.browser.js' 'bundle rollup.config.browser.memory.js' 'bundle rollup.config.node.js' 'bundle rollup.config.node.memory.js' 'bundle rollup.config.rn.js' 'bundle rollup.config.rn.memory.js'", + "build:release": "run-p 'bundle rollup.config.browser.js' 'bundle rollup.config.node.js' 'bundle rollup.config.rn.js'", "build:deps": "lerna run --scope @firebase/firestore --include-dependencies build", "build:console": "node tools/console.build.js", "build:exp": "rollup -c rollup.config.exp.js", "build:lite": "rollup -c rollup.config.lite.js", "build:exp:release": "yarn build:exp && yarn build:lite", + "build:browser": "rollup -c rollup.config.browser.js", "predev": "yarn prebuild", "dev": "rollup -c -w", "lint": "eslint -c .eslintrc.js '**/*.ts' --ignore-path '../../.gitignore'", @@ -45,16 +46,17 @@ "predoc": "node ../../scripts/exp/remove-exp.js temp", "doc": "api-documenter markdown --input temp --output docs" }, - "main": "dist/index.node.cjs.js", - "main-esm2017": "dist/index.node.esm2017.js", - "react-native": "dist/index.rn.esm2017.js", - "browser": "dist/index.esm.js", - "module": "dist/index.esm.js", - "esm2017": "dist/index.esm2017.js", + "main": "dist/node-cjs/index.js", + "main-esm2017": "dist/node-esm2017/index.js", + "react-native": "dist/rn/index.js", + "browser": "dist/esm5/index.js", + "module": "dist/esm5/index.js", + "esm2017": "dist/esm2017/index.js", "license": "Apache-2.0", "files": [ "dist", - "memory/package.json" + "memory/package.json", + "bundle/package.json" ], "dependencies": { "@firebase/component": "0.1.21", @@ -80,7 +82,7 @@ "json-stable-stringify": "1.0.1", "protobufjs": "6.10.2", "rollup": "2.33.2", - "rollup-plugin-copy-assets": "1.1.0", + "rollup-plugin-copy-assets": "2.0.3", "rollup-plugin-replace": "2.2.0", "rollup-plugin-sourcemaps": "0.6.3", "rollup-plugin-terser": "7.0.2", diff --git a/packages/firestore/rollup.config.browser.js b/packages/firestore/rollup.config.browser.js index 6f3d2582a5f..3e146738650 100644 --- a/packages/firestore/rollup.config.browser.js +++ b/packages/firestore/rollup.config.browser.js @@ -16,14 +16,21 @@ */ import pkg from './package.json'; +import bundlePkg from './bundle/package.json'; +import memoryPkg from './memory/package.json'; +import path from 'path'; const util = require('./rollup.shared'); export default [ { - input: 'index.ts', + input: { + index: 'index.ts', + memory: 'index.memory.ts', + bundle: 'index.bundle.ts' + }, output: { - file: pkg.esm2017, + dir: 'dist/esm2017', format: 'es', sourcemap: true }, @@ -32,14 +39,5 @@ export default [ treeshake: { moduleSideEffects: false } - }, - { - input: pkg.esm2017, - output: { file: pkg.module, format: 'es', sourcemap: true }, - plugins: util.es2017ToEs5Plugins(/* mangled= */ true), - external: util.resolveBrowserExterns, - treeshake: { - moduleSideEffects: false - } } ]; diff --git a/packages/firestore/rollup.config.node.js b/packages/firestore/rollup.config.node.js index 7023ef7354d..5f7f0bca085 100644 --- a/packages/firestore/rollup.config.node.js +++ b/packages/firestore/rollup.config.node.js @@ -18,25 +18,31 @@ import replace from 'rollup-plugin-replace'; import copy from 'rollup-plugin-copy-assets'; import pkg from './package.json'; +import bundlePkg from './bundle/package.json'; +import memoryPkg from './memory/package.json'; +import path from 'path'; const util = require('./rollup.shared'); export default [ { - input: 'index.node.ts', + input: { + index: 'index.node.ts', + memory: 'index.node.memory.ts', + bundle: 'index.bundle.ts' + }, output: { - file: pkg['main-esm2017'], + dir: 'dist/node-esm2017', format: 'es', sourcemap: true }, plugins: [ ...util.es2017Plugins('node'), - // Needed as we also use the *.proto files - copy({ - assets: ['./src/protos'] - }), replace({ 'process.env.FIRESTORE_PROTO_ROOT': JSON.stringify('src/protos') + }), + copy({ + assets: ['./src/protos'] }) ], external: util.resolveNodeExterns, @@ -45,8 +51,18 @@ export default [ } }, { - input: pkg['main-esm2017'], - output: [{ file: pkg.main, format: 'cjs', sourcemap: true }], + input: { + index: pkg['main-esm2017'], + memory: path.resolve('./memory', memoryPkg['main-esm2017']), + bundle: path.resolve('./bundle', bundlePkg['main-esm2017']) + }, + output: [ + { + dir: 'dist/node-cjs', + format: 'cjs', + sourcemap: true + } + ], plugins: util.es2017ToEs5Plugins(), external: util.resolveNodeExterns, treeshake: { diff --git a/packages/firestore/rollup.config.rn.js b/packages/firestore/rollup.config.rn.js index 54a4a4efde5..eb70aecda2f 100644 --- a/packages/firestore/rollup.config.rn.js +++ b/packages/firestore/rollup.config.rn.js @@ -15,14 +15,16 @@ * limitations under the License. */ -import pkg from './package.json'; - const util = require('./rollup.shared'); export default { - input: 'index.rn.ts', + input: { + index: 'index.rn.ts', + memory: 'index.rn.memory.ts', + bundle: 'index.bundle.ts' + }, output: { - file: pkg['react-native'], + dir: 'dist/rn', format: 'es', sourcemap: true }, diff --git a/packages/firestore/rollup.shared.js b/packages/firestore/rollup.shared.js index 009facb4041..52574f4dda2 100644 --- a/packages/firestore/rollup.shared.js +++ b/packages/firestore/rollup.shared.js @@ -248,7 +248,7 @@ exports.es2017ToEs5Plugins = function (mangled = false) { allowJs: true } }, - include: ['dist/*.js', 'dist/exp/*.js'] + include: ['dist/**/*.js'] }), terser({ output: { diff --git a/packages/firestore/test/integration/api/bundle.test.ts b/packages/firestore/test/integration/api/bundle.test.ts index cfc507d7e62..8d064cbc7ac 100644 --- a/packages/firestore/test/integration/api/bundle.test.ts +++ b/packages/firestore/test/integration/api/bundle.test.ts @@ -15,249 +15,239 @@ * limitations under the License. */ -// TODO(wuandy): Uncomment this file once prototype patch works for bundles - -// import * as firestore from '@firebase/firestore-types'; -// import { expect } from 'chai'; -// import { -// apiDescribe, -// toDataArray, -// withAlternateTestDb, -// withTestDb -// } from '../util/helpers'; -// import { EventsAccumulator } from '../util/events_accumulator'; -// import * as firebaseExport from '../util/firebase_export'; -// -// const loadBundle = firebaseExport.loadBundle; -// const namedQuery = firebaseExport.namedQuery; -// -// export const encoder = new TextEncoder(); -// -// function verifySuccessProgress(p: firestore.LoadBundleTaskProgress): void { -// expect(p.taskState).to.equal('Success'); -// expect(p.bytesLoaded).to.be.equal(p.totalBytes); -// expect(p.documentsLoaded).to.equal(p.totalDocuments); -// } -// -// function verifyInProgress( -// p: firestore.LoadBundleTaskProgress, -// expectedDocuments: number -// ): void { -// expect(p.taskState).to.equal('Running'); -// expect(p.bytesLoaded <= p.totalBytes).to.be.true; -// expect(p.documentsLoaded <= p.totalDocuments).to.be.true; -// expect(p.documentsLoaded).to.equal(expectedDocuments); -// } -// -// // This template is generated from bundleWithTestDocsAndQueries in '../util/internal_helpsers.ts', -// // and manually copied here. -// const BUNDLE_TEMPLATE = [ -// '{"metadata":{"id":"test-bundle","createTime":{"seconds":1001,"nanos":9999},"version":1,"totalDocuments":2,"totalBytes":1503}}', -// '{"namedQuery":{"name":"limit","readTime":{"seconds":1000,"nanos":9999},"bundledQuery":{"parent":"projects/{0}/databases/(default)/documents","structuredQuery":{"from":[{"collectionId":"coll-1"}],"orderBy":[{"field":{"fieldPath":"bar"},"direction":"DESCENDING"},{"field":{"fieldPath":"__name__"},"direction":"DESCENDING"}],"limit":{"value":1}},"limitType":"FIRST"}}}', -// '{"namedQuery":{"name":"limit-to-last","readTime":{"seconds":1000,"nanos":9999},"bundledQuery":{"parent":"projects/{0}/databases/(default)/documents","structuredQuery":{"from":[{"collectionId":"coll-1"}],"orderBy":[{"field":{"fieldPath":"bar"},"direction":"DESCENDING"},{"field":{"fieldPath":"__name__"},"direction":"DESCENDING"}],"limit":{"value":1}},"limitType":"LAST"}}}', -// '{"documentMetadata":{"name":"projects/{0}/databases/(default)/documents/coll-1/a","readTime":{"seconds":1000,"nanos":9999},"exists":true}}', -// '{"document":{"name":"projects/{0}/databases/(default)/documents/coll-1/a","createTime":{"seconds":1,"nanos":9},"updateTime":{"seconds":1,"nanos":9},"fields":{"k":{"stringValue":"a"},"bar":{"integerValue":1}}}}', -// '{"documentMetadata":{"name":"projects/{0}/databases/(default)/documents/coll-1/b","readTime":{"seconds":1000,"nanos":9999},"exists":true}}', -// '{"document":{"name":"projects/{0}/databases/(default)/documents/coll-1/b","createTime":{"seconds":1,"nanos":9},"updateTime":{"seconds":1,"nanos":9},"fields":{"k":{"stringValue":"b"},"bar":{"integerValue":2}}}}' -// ]; -// -// apiDescribe('Bundles', (persistence: boolean) => { -// function verifySnapEqualsTestDocs(snap: firestore.QuerySnapshot): void { -// expect(toDataArray(snap)).to.deep.equal([ -// { k: 'a', bar: 1 }, -// { k: 'b', bar: 2 } -// ]); -// } -// -// /** -// * Returns a valid bundle string from replacing project id in `BUNDLE_TEMPLATE` with the given -// * db project id (also recalculate length prefixes). -// */ -// function bundleString(db: firestore.FirebaseFirestore): string { -// const projectId: string = db.app.options.projectId; -// -// // Extract elements from BUNDLE_TEMPLATE and replace the project ID. -// const elements = BUNDLE_TEMPLATE.map(e => e.replace('{0}', projectId)); -// -// // Recalculating length prefixes for elements that are not BundleMetadata. -// let bundleContent = ''; -// for (const element of elements.slice(1)) { -// const length = encoder.encode(element).byteLength; -// bundleContent += `${length}${element}`; -// } -// -// // Update BundleMetadata with new totalBytes. -// const totalBytes = encoder.encode(bundleContent).byteLength; -// const metadata = JSON.parse(elements[0]); -// metadata.metadata.totalBytes = totalBytes; -// const metadataContent = JSON.stringify(metadata); -// const metadataLength = encoder.encode(metadataContent).byteLength; -// return `${metadataLength}${metadataContent}${bundleContent}`; -// } -// -// it('load with documents only with on progress and promise interface', () => { -// return withTestDb(persistence, async db => { -// const progressEvents: firestore.LoadBundleTaskProgress[] = []; -// let completeCalled = false; -// const task: firestore.LoadBundleTask = loadBundle(db, bundleString(db)); -// task.onProgress( -// progress => { -// progressEvents.push(progress); -// }, -// undefined, -// () => { -// completeCalled = true; -// } -// ); -// await task; -// let fulfillProgress: firestore.LoadBundleTaskProgress; -// await task.then(progress => { -// fulfillProgress = progress; -// }); -// -// expect(completeCalled).to.be.true; -// expect(progressEvents.length).to.equal(4); -// verifyInProgress(progressEvents[0], 0); -// verifyInProgress(progressEvents[1], 1); -// verifyInProgress(progressEvents[2], 2); -// verifySuccessProgress(progressEvents[3]); -// expect(fulfillProgress!).to.deep.equal(progressEvents[3]); -// -// // Read from cache. These documents do not exist in backend, so they can -// // only be read from cache. -// let snap = await db.collection('coll-1').get({ source: 'cache' }); -// verifySnapEqualsTestDocs(snap); -// -// snap = await (await namedQuery(db, 'limit'))!.get({ -// source: 'cache' -// }); -// expect(toDataArray(snap)).to.deep.equal([{ k: 'b', bar: 2 }]); -// -// snap = await (await namedQuery(db, 'limit-to-last'))!.get({ -// source: 'cache' -// }); -// expect(toDataArray(snap)).to.deep.equal([{ k: 'a', bar: 1 }]); -// }); -// }); -// -// it('load with documents and queries with promise interface', () => { -// return withTestDb(persistence, async db => { -// const fulfillProgress: firestore.LoadBundleTaskProgress = await loadBundle( -// db, -// bundleString(db) -// ); -// -// verifySuccessProgress(fulfillProgress!); -// -// // Read from cache. These documents do not exist in backend, so they can -// // only be read from cache. -// const snap = await db.collection('coll-1').get({ source: 'cache' }); -// verifySnapEqualsTestDocs(snap); -// }); -// }); -// -// it('load for a second time skips', () => { -// return withTestDb(persistence, async db => { -// await loadBundle(db, bundleString(db)); -// -// let completeCalled = false; -// const progressEvents: firestore.LoadBundleTaskProgress[] = []; -// const task: firestore.LoadBundleTask = loadBundle( -// db, -// encoder.encode(bundleString(db)) -// ); -// task.onProgress( -// progress => { -// progressEvents.push(progress); -// }, -// error => {}, -// () => { -// completeCalled = true; -// } -// ); -// await task; -// -// expect(completeCalled).to.be.true; -// // No loading actually happened in the second `loadBundle` call only the -// // success progress is recorded. -// expect(progressEvents.length).to.equal(1); -// verifySuccessProgress(progressEvents[0]); -// -// // Read from cache. These documents do not exist in backend, so they can -// // only be read from cache. -// const snap = await db.collection('coll-1').get({ source: 'cache' }); -// verifySnapEqualsTestDocs(snap); -// }); -// }); -// -// it('load with documents already pulled from backend', () => { -// return withTestDb(persistence, async db => { -// await db.doc('coll-1/a').set({ k: 'a', bar: 0 }); -// await db.doc('coll-1/b').set({ k: 'b', bar: 0 }); -// -// const accumulator = new EventsAccumulator(); -// db.collection('coll-1').onSnapshot(accumulator.storeEvent); -// await accumulator.awaitEvent(); -// -// const progress = await loadBundle( -// db, -// // Testing passing in non-string bundles. -// encoder.encode(bundleString(db)) -// ); -// -// verifySuccessProgress(progress); -// // The test bundle is holding ancient documents, so no events are -// // generated as a result. The case where a bundle has newer doc than -// // cache can only be tested in spec tests. -// await accumulator.assertNoAdditionalEvents(); -// -// let snap = await (await namedQuery(db, 'limit'))!.get(); -// expect(toDataArray(snap)).to.deep.equal([{ k: 'b', bar: 0 }]); -// -// snap = await (await namedQuery(db, 'limit-to-last'))!.get(); -// expect(toDataArray(snap)).to.deep.equal([{ k: 'a', bar: 0 }]); -// }); -// }); -// -// it('loaded documents should not be GC-ed right away', () => { -// return withTestDb(persistence, async db => { -// const fulfillProgress: firestore.LoadBundleTaskProgress = await loadBundle( -// db, -// bundleString(db) -// ); -// -// verifySuccessProgress(fulfillProgress!); -// -// // Read a different collection, this will trigger GC. -// let snap = await db.collection('coll-other').get(); -// expect(snap.empty).to.be.true; -// -// // Read the loaded documents, expecting document in cache. With memory -// // GC, the documents would get GC-ed if we did not hold the document keys -// // in a "umbrella" target. See local_store.ts for details. -// snap = await db.collection('coll-1').get({ source: 'cache' }); -// verifySnapEqualsTestDocs(snap); -// }); -// }); -// -// it('load with documents from other projects fails', () => { -// return withTestDb(persistence, async db => { -// return withAlternateTestDb(persistence, async otherDb => { -// await expect(loadBundle(otherDb, bundleString(db))).to.be.rejectedWith( -// 'Tried to deserialize key from different project' -// ); -// -// // Verify otherDb still functions, despite loaded a problematic bundle. -// const finalProgress = await loadBundle(otherDb, bundleString(otherDb)); -// verifySuccessProgress(finalProgress); -// -// // Read from cache. These documents do not exist in backend, so they can -// // only be read from cache. -// const snap = await otherDb -// .collection('coll-1') -// .get({ source: 'cache' }); -// verifySnapEqualsTestDocs(snap); -// }); -// }); -// }); -// }); +import * as firestore from '@firebase/firestore-types'; +import { expect } from 'chai'; +import { + apiDescribe, + toDataArray, + withAlternateTestDb, + withTestDb +} from '../util/helpers'; +import { EventsAccumulator } from '../util/events_accumulator'; + +export const encoder = new TextEncoder(); + +function verifySuccessProgress(p: firestore.LoadBundleTaskProgress): void { + expect(p.taskState).to.equal('Success'); + expect(p.bytesLoaded).to.be.equal(p.totalBytes); + expect(p.documentsLoaded).to.equal(p.totalDocuments); +} + +function verifyInProgress( + p: firestore.LoadBundleTaskProgress, + expectedDocuments: number +): void { + expect(p.taskState).to.equal('Running'); + expect(p.bytesLoaded <= p.totalBytes).to.be.true; + expect(p.documentsLoaded <= p.totalDocuments).to.be.true; + expect(p.documentsLoaded).to.equal(expectedDocuments); +} + +// This template is generated from bundleWithTestDocsAndQueries in '../util/internal_helpsers.ts', +// and manually copied here. +const BUNDLE_TEMPLATE = [ + '{"metadata":{"id":"test-bundle","createTime":{"seconds":1001,"nanos":9999},"version":1,"totalDocuments":2,"totalBytes":1503}}', + '{"namedQuery":{"name":"limit","readTime":{"seconds":1000,"nanos":9999},"bundledQuery":{"parent":"projects/{0}/databases/(default)/documents","structuredQuery":{"from":[{"collectionId":"coll-1"}],"orderBy":[{"field":{"fieldPath":"bar"},"direction":"DESCENDING"},{"field":{"fieldPath":"__name__"},"direction":"DESCENDING"}],"limit":{"value":1}},"limitType":"FIRST"}}}', + '{"namedQuery":{"name":"limit-to-last","readTime":{"seconds":1000,"nanos":9999},"bundledQuery":{"parent":"projects/{0}/databases/(default)/documents","structuredQuery":{"from":[{"collectionId":"coll-1"}],"orderBy":[{"field":{"fieldPath":"bar"},"direction":"DESCENDING"},{"field":{"fieldPath":"__name__"},"direction":"DESCENDING"}],"limit":{"value":1}},"limitType":"LAST"}}}', + '{"documentMetadata":{"name":"projects/{0}/databases/(default)/documents/coll-1/a","readTime":{"seconds":1000,"nanos":9999},"exists":true}}', + '{"document":{"name":"projects/{0}/databases/(default)/documents/coll-1/a","createTime":{"seconds":1,"nanos":9},"updateTime":{"seconds":1,"nanos":9},"fields":{"k":{"stringValue":"a"},"bar":{"integerValue":1}}}}', + '{"documentMetadata":{"name":"projects/{0}/databases/(default)/documents/coll-1/b","readTime":{"seconds":1000,"nanos":9999},"exists":true}}', + '{"document":{"name":"projects/{0}/databases/(default)/documents/coll-1/b","createTime":{"seconds":1,"nanos":9},"updateTime":{"seconds":1,"nanos":9},"fields":{"k":{"stringValue":"b"},"bar":{"integerValue":2}}}}' +]; + +apiDescribe('Bundles', (persistence: boolean) => { + function verifySnapEqualsTestDocs(snap: firestore.QuerySnapshot): void { + expect(toDataArray(snap)).to.deep.equal([ + { k: 'a', bar: 1 }, + { k: 'b', bar: 2 } + ]); + } + + /** + * Returns a valid bundle string from replacing project id in `BUNDLE_TEMPLATE` with the given + * db project id (also recalculate length prefixes). + */ + function bundleString(db: firestore.FirebaseFirestore): string { + const projectId: string = db.app.options.projectId; + + // Extract elements from BUNDLE_TEMPLATE and replace the project ID. + const elements = BUNDLE_TEMPLATE.map(e => e.replace('{0}', projectId)); + + // Recalculating length prefixes for elements that are not BundleMetadata. + let bundleContent = ''; + for (const element of elements.slice(1)) { + const length = encoder.encode(element).byteLength; + bundleContent += `${length}${element}`; + } + + // Update BundleMetadata with new totalBytes. + const totalBytes = encoder.encode(bundleContent).byteLength; + const metadata = JSON.parse(elements[0]); + metadata.metadata.totalBytes = totalBytes; + const metadataContent = JSON.stringify(metadata); + const metadataLength = encoder.encode(metadataContent).byteLength; + return `${metadataLength}${metadataContent}${bundleContent}`; + } + + it('load with documents only with on progress and promise interface', () => { + return withTestDb(persistence, async db => { + const progressEvents: firestore.LoadBundleTaskProgress[] = []; + let completeCalled = false; + const task: firestore.LoadBundleTask = db.loadBundle(bundleString(db)); + task.onProgress( + progress => { + progressEvents.push(progress); + }, + undefined, + () => { + completeCalled = true; + } + ); + await task; + let fulfillProgress: firestore.LoadBundleTaskProgress; + await task.then(progress => { + fulfillProgress = progress; + }); + + expect(completeCalled).to.be.true; + expect(progressEvents.length).to.equal(4); + verifyInProgress(progressEvents[0], 0); + verifyInProgress(progressEvents[1], 1); + verifyInProgress(progressEvents[2], 2); + verifySuccessProgress(progressEvents[3]); + expect(fulfillProgress!).to.deep.equal(progressEvents[3]); + + // Read from cache. These documents do not exist in backend, so they can + // only be read from cache. + let snap = await db.collection('coll-1').get({ source: 'cache' }); + verifySnapEqualsTestDocs(snap); + + snap = await (await db.namedQuery('limit'))!.get({ + source: 'cache' + }); + expect(toDataArray(snap)).to.deep.equal([{ k: 'b', bar: 2 }]); + + snap = await (await db.namedQuery('limit-to-last'))!.get({ + source: 'cache' + }); + expect(toDataArray(snap)).to.deep.equal([{ k: 'a', bar: 1 }]); + }); + }); + + it('load with documents and queries with promise interface', () => { + return withTestDb(persistence, async db => { + const fulfillProgress: firestore.LoadBundleTaskProgress = await db.loadBundle( + bundleString(db) + ); + + verifySuccessProgress(fulfillProgress!); + + // Read from cache. These documents do not exist in backend, so they can + // only be read from cache. + const snap = await db.collection('coll-1').get({ source: 'cache' }); + verifySnapEqualsTestDocs(snap); + }); + }); + + it('load for a second time skips', () => { + return withTestDb(persistence, async db => { + await db.loadBundle(bundleString(db)); + + let completeCalled = false; + const progressEvents: firestore.LoadBundleTaskProgress[] = []; + const task: firestore.LoadBundleTask = db.loadBundle( + encoder.encode(bundleString(db)) + ); + task.onProgress( + progress => { + progressEvents.push(progress); + }, + error => {}, + () => { + completeCalled = true; + } + ); + await task; + + expect(completeCalled).to.be.true; + // No loading actually happened in the second `loadBundle` call only the + // success progress is recorded. + expect(progressEvents.length).to.equal(1); + verifySuccessProgress(progressEvents[0]); + + // Read from cache. These documents do not exist in backend, so they can + // only be read from cache. + const snap = await db.collection('coll-1').get({ source: 'cache' }); + verifySnapEqualsTestDocs(snap); + }); + }); + + it('load with documents already pulled from backend', () => { + return withTestDb(persistence, async db => { + await db.doc('coll-1/a').set({ k: 'a', bar: 0 }); + await db.doc('coll-1/b').set({ k: 'b', bar: 0 }); + + const accumulator = new EventsAccumulator(); + db.collection('coll-1').onSnapshot(accumulator.storeEvent); + await accumulator.awaitEvent(); + + const progress = await db.loadBundle( + // Testing passing in non-string bundles. + encoder.encode(bundleString(db)) + ); + + verifySuccessProgress(progress); + // The test bundle is holding ancient documents, so no events are + // generated as a result. The case where a bundle has newer doc than + // cache can only be tested in spec tests. + await accumulator.assertNoAdditionalEvents(); + + let snap = await (await db.namedQuery('limit'))!.get(); + expect(toDataArray(snap)).to.deep.equal([{ k: 'b', bar: 0 }]); + + snap = await (await db.namedQuery('limit-to-last'))!.get(); + expect(toDataArray(snap)).to.deep.equal([{ k: 'a', bar: 0 }]); + }); + }); + + it('loaded documents should not be GC-ed right away', () => { + return withTestDb(persistence, async db => { + const fulfillProgress: firestore.LoadBundleTaskProgress = await db.loadBundle( + bundleString(db) + ); + + verifySuccessProgress(fulfillProgress!); + + // Read a different collection, this will trigger GC. + let snap = await db.collection('coll-other').get(); + expect(snap.empty).to.be.true; + + // Read the loaded documents, expecting document in cache. With memory + // GC, the documents would get GC-ed if we did not hold the document keys + // in a "umbrella" target. See local_store.ts for details. + snap = await db.collection('coll-1').get({ source: 'cache' }); + verifySnapEqualsTestDocs(snap); + }); + }); + + it('load with documents from other projects fails', () => { + return withTestDb(persistence, async db => { + return withAlternateTestDb(persistence, async otherDb => { + await expect(otherDb.loadBundle(bundleString(db))).to.be.rejectedWith( + 'Tried to deserialize key from different project' + ); + + // Verify otherDb still functions, despite loaded a problematic bundle. + const finalProgress = await otherDb.loadBundle(bundleString(otherDb)); + verifySuccessProgress(finalProgress); + + // Read from cache. These documents do not exist in backend, so they can + // only be read from cache. + const snap = await otherDb + .collection('coll-1') + .get({ source: 'cache' }); + verifySnapEqualsTestDocs(snap); + }); + }); + }); +}); From d4254db174e459185728306998d20ff71eacb6d5 Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Thu, 26 Nov 2020 10:39:08 -0500 Subject: [PATCH 06/16] WIP: manually copy prebuilt change. --- packages/firestore/export.ts | 39 +++++++++++++ packages/firestore/externs.json | 1 + packages/firestore/index.bundle.ts | 15 ++--- packages/firestore/index.memory.ts | 9 ++- packages/firestore/index.rn.memory.ts | 7 ++- packages/firestore/index.rn.ts | 7 ++- packages/firestore/index.ts | 7 ++- packages/firestore/rollup.config.browser.js | 32 +++++++++-- .../firestore/rollup.config.browser.memory.js | 50 ----------------- .../firestore/rollup.config.node.memory.js | 56 ------------------- packages/firestore/rollup.config.rn.js | 56 ++++++++++++++----- packages/firestore/rollup.config.rn.memory.js | 35 ------------ 12 files changed, 136 insertions(+), 178 deletions(-) create mode 100644 packages/firestore/export.ts delete mode 100644 packages/firestore/rollup.config.browser.memory.js delete mode 100644 packages/firestore/rollup.config.node.memory.js delete mode 100644 packages/firestore/rollup.config.rn.memory.js diff --git a/packages/firestore/export.ts b/packages/firestore/export.ts new file mode 100644 index 00000000000..22c2541da0c --- /dev/null +++ b/packages/firestore/export.ts @@ -0,0 +1,39 @@ +/** + * @license + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { Blob } from './src/api/blob'; +export { + CollectionReference, + DocumentReference, + DocumentSnapshot, + Firestore, + Query, + QueryDocumentSnapshot, + QuerySnapshot, + IndexedDbPersistenceProvider, + MemoryPersistenceProvider, + Transaction, + WriteBatch, + setLogLevel, + CACHE_SIZE_UNLIMITED +} from './src/api/database'; +export { GeoPoint } from './src/api/geo_point'; +export { FieldPath } from './src/api/field_path'; +export { FieldValue } from './src/compat/field_value'; +export { Timestamp } from './src/api/timestamp'; +export { FirebaseFirestore as ExpFirebaseFirestore } from './exp/src/api/database'; +export { loadBundle, namedQuery } from './src/api/bundle'; diff --git a/packages/firestore/externs.json b/packages/firestore/externs.json index 1a7749da1a1..0cbba3caa21 100644 --- a/packages/firestore/externs.json +++ b/packages/firestore/externs.json @@ -28,6 +28,7 @@ "packages/webchannel-wrapper/src/index.d.ts", "packages/util/dist/src/crypt.d.ts", "packages/util/dist/src/environment.d.ts", + "packages/firestore/export.ts", "packages/firestore/src/protos/firestore_bundle_proto.ts", "packages/firestore/src/protos/firestore_proto_api.ts", "packages/firestore/src/util/error.ts", diff --git a/packages/firestore/index.bundle.ts b/packages/firestore/index.bundle.ts index 48e2712fe90..102dd25137b 100644 --- a/packages/firestore/index.bundle.ts +++ b/packages/firestore/index.bundle.ts @@ -15,22 +15,23 @@ * limitations under the License. */ -import { Firestore } from './src/api/database'; -import { loadBundle, namedQuery } from './src/api/bundle'; +import { Firestore, loadBundle, namedQuery } from './export'; /** * Registers the memory-only Firestore build with the components framework. */ export function registerBundle(instance: typeof Firestore): void { instance.prototype.loadBundle = function ( + this: Firestore, data: ArrayBuffer | ReadableStream | string ) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return loadBundle(this as any, data); + return loadBundle(this, data); }; - instance.prototype.namedQuery = function (queryName: string) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return namedQuery(this as any, queryName); + instance.prototype.namedQuery = function ( + this: Firestore, + queryName: string + ) { + return namedQuery(this, queryName); }; //TODO: add loadBundle and namedQuery to the firestore namespace diff --git a/packages/firestore/index.memory.ts b/packages/firestore/index.memory.ts index 915cd1d4fdd..78b83ee528b 100644 --- a/packages/firestore/index.memory.ts +++ b/packages/firestore/index.memory.ts @@ -18,8 +18,11 @@ import firebase from '@firebase/app'; import { FirebaseNamespace } from '@firebase/app-types'; -import { Firestore, MemoryPersistenceProvider } from './src/api/database'; -import { FirebaseFirestore } from './exp/src/api/database'; +import { + Firestore, + MemoryPersistenceProvider, + ExpFirebaseFirestore +} from './export'; import { configureForFirebase } from './src/config'; import './register-module'; @@ -35,7 +38,7 @@ export function registerFirestore(instance: FirebaseNamespace): void { (app, auth) => new Firestore( app, - new FirebaseFirestore(app, auth), + new ExpFirebaseFirestore(app, auth), new MemoryPersistenceProvider() ) ); diff --git a/packages/firestore/index.rn.memory.ts b/packages/firestore/index.rn.memory.ts index ae767b0b053..a60d2643d87 100644 --- a/packages/firestore/index.rn.memory.ts +++ b/packages/firestore/index.rn.memory.ts @@ -18,8 +18,11 @@ import firebase from '@firebase/app'; import { FirebaseNamespace } from '@firebase/app-types'; -import { FirebaseFirestore as ExpFirebaseFirestore } from './exp/src/api/database'; -import { Firestore, MemoryPersistenceProvider } from './src/api/database'; +import { + Firestore, + MemoryPersistenceProvider, + ExpFirebaseFirestore +} from './export'; import { configureForFirebase } from './src/config'; import './register-module'; diff --git a/packages/firestore/index.rn.ts b/packages/firestore/index.rn.ts index 2b51d8a59c4..591c25f8cf1 100644 --- a/packages/firestore/index.rn.ts +++ b/packages/firestore/index.rn.ts @@ -17,9 +17,12 @@ import firebase from '@firebase/app'; import { FirebaseNamespace } from '@firebase/app-types'; -import { Firestore, IndexedDbPersistenceProvider } from './src/api/database'; +import { + Firestore, + IndexedDbPersistenceProvider, + ExpFirebaseFirestore +} from './export'; import { configureForFirebase } from './src/config'; -import { FirebaseFirestore as ExpFirebaseFirestore } from './exp/src/api/database'; import './register-module'; import { name, version } from './package.json'; diff --git a/packages/firestore/index.ts b/packages/firestore/index.ts index 349e29356b4..1e5ae557771 100644 --- a/packages/firestore/index.ts +++ b/packages/firestore/index.ts @@ -18,8 +18,11 @@ import firebase from '@firebase/app'; import { FirebaseNamespace } from '@firebase/app-types'; -import { Firestore, IndexedDbPersistenceProvider } from './src/api/database'; -import { FirebaseFirestore as ExpFirebaseFirestore } from './exp/src/api/database'; +import { + Firestore, + IndexedDbPersistenceProvider, + ExpFirebaseFirestore +} from './export'; import { configureForFirebase } from './src/config'; import { name, version } from './package.json'; diff --git a/packages/firestore/rollup.config.browser.js b/packages/firestore/rollup.config.browser.js index 3e146738650..b573c469bb8 100644 --- a/packages/firestore/rollup.config.browser.js +++ b/packages/firestore/rollup.config.browser.js @@ -15,14 +15,23 @@ * limitations under the License. */ -import pkg from './package.json'; -import bundlePkg from './bundle/package.json'; -import memoryPkg from './memory/package.json'; -import path from 'path'; - +const alias = require('@rollup/plugin-alias'); const util = require('./rollup.shared'); export default [ + { + input: 'export.ts', + output: { + file: 'dist/prebuild.js', + format: 'es', + sourcemap: true + }, + plugins: util.es2017Plugins('browser', /* mangled= */ true), + external: util.resolveBrowserExterns, + treeshake: { + moduleSideEffects: false + } + }, { input: { index: 'index.ts', @@ -34,10 +43,21 @@ export default [ format: 'es', sourcemap: true }, - plugins: util.es2017Plugins('browser', /* mangled= */ true), + plugins: [ + alias({ + entries: [ + { + find: /^(.*)\/export$/, + replacement: `$1\/dist/prebuild.js` + } + ] + }), + ...util.es2017Plugins('browser', /* mangled= */ false) + ], external: util.resolveBrowserExterns, treeshake: { moduleSideEffects: false } } + // TODO: Add ES5 export ]; diff --git a/packages/firestore/rollup.config.browser.memory.js b/packages/firestore/rollup.config.browser.memory.js deleted file mode 100644 index d97ae948c71..00000000000 --- a/packages/firestore/rollup.config.browser.memory.js +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @license - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import * as path from 'path'; -import memoryPkg from './memory/package.json'; - -const util = require('./rollup.shared'); - -export default [ - { - input: 'index.memory.ts', - output: { - file: path.resolve('./memory', memoryPkg['esm2017']), - format: 'es', - sourcemap: true - }, - plugins: util.es2017Plugins('browser', /* mangled= */ true), - external: util.resolveBrowserExterns, - treeshake: { - moduleSideEffects: false - } - }, - { - input: path.resolve('./memory', memoryPkg['esm2017']), - output: { - file: path.resolve('./memory', memoryPkg.module), - format: 'es', - sourcemap: true - }, - plugins: util.es2017ToEs5Plugins(/* mangled= */ true), - external: util.resolveBrowserExterns, - treeshake: { - moduleSideEffects: false - } - } -]; diff --git a/packages/firestore/rollup.config.node.memory.js b/packages/firestore/rollup.config.node.memory.js deleted file mode 100644 index f9a2640dd25..00000000000 --- a/packages/firestore/rollup.config.node.memory.js +++ /dev/null @@ -1,56 +0,0 @@ -/** - * @license - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import * as path from 'path'; -import replace from 'rollup-plugin-replace'; -import memoryPkg from './memory/package.json'; - -const util = require('./rollup.shared'); - -export default [ - { - input: 'index.node.memory.ts', - output: { - file: path.resolve('./memory', memoryPkg['main-esm2017']), - format: 'es', - sourcemap: true - }, - plugins: [ - ...util.es2017Plugins('node'), - replace({ - 'process.env.FIRESTORE_PROTO_ROOT': JSON.stringify('src/protos') - }) - ], - external: util.resolveNodeExterns, - treeshake: { - moduleSideEffects: false - } - }, - { - input: path.resolve('./memory', memoryPkg['main-esm2017']), - output: { - file: path.resolve('./memory', memoryPkg.main), - format: 'cjs', - sourcemap: true - }, - plugins: util.es2017ToEs5Plugins(), - external: util.resolveNodeExterns, - treeshake: { - moduleSideEffects: false - } - } -]; diff --git a/packages/firestore/rollup.config.rn.js b/packages/firestore/rollup.config.rn.js index eb70aecda2f..aeac8279669 100644 --- a/packages/firestore/rollup.config.rn.js +++ b/packages/firestore/rollup.config.rn.js @@ -15,22 +15,48 @@ * limitations under the License. */ +const alias = require('@rollup/plugin-alias'); const util = require('./rollup.shared'); -export default { - input: { - index: 'index.rn.ts', - memory: 'index.rn.memory.ts', - bundle: 'index.bundle.ts' +export default [ + { + input: 'export.ts', + output: { + file: 'dist/prebuild.rn.js', + format: 'es', + sourcemap: true + }, + plugins: util.es2017Plugins('rn', /* mangled= */ true), + external: util.resolveBrowserExterns, + treeshake: { + moduleSideEffects: false + } }, - output: { - dir: 'dist/rn', - format: 'es', - sourcemap: true - }, - plugins: util.es2017Plugins('rn', /* mangled= */ true), - external: util.resolveBrowserExterns, - treeshake: { - moduleSideEffects: false + { + input: { + index: 'index.ts', + memory: 'index.memory.ts', + bundle: 'index.bundle.ts' + }, + output: { + dir: 'dist/rn', + format: 'es', + sourcemap: true + }, + plugins: [ + alias({ + entries: [ + { + find: /^(.*)\/export$/, + replacement: `$1\/dist/prebuild.rn.js` + } + ] + }), + ...util.es2017Plugins('rn', /* mangled= */ false) + ], + external: util.resolveBrowserExterns, + treeshake: { + moduleSideEffects: false + } } -}; +]; diff --git a/packages/firestore/rollup.config.rn.memory.js b/packages/firestore/rollup.config.rn.memory.js deleted file mode 100644 index c5e9998fea4..00000000000 --- a/packages/firestore/rollup.config.rn.memory.js +++ /dev/null @@ -1,35 +0,0 @@ -/** - * @license - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import * as path from 'path'; -import memoryPkg from './memory/package.json'; - -const util = require('./rollup.shared'); - -export default { - input: 'index.rn.memory.ts', - output: { - file: path.resolve('./memory', memoryPkg['react-native']), - format: 'es', - sourcemap: true - }, - plugins: util.es2017Plugins('rn', /* mangled= */ true), - external: util.resolveBrowserExterns, - treeshake: { - moduleSideEffects: false - } -}; From 76efda24e411718b0f3946d560d11b93becddb8f Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Thu, 26 Nov 2020 10:57:30 -0500 Subject: [PATCH 07/16] Add missing config.ts change. Now minified tests pass. --- packages/firestore/src/config.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/firestore/src/config.ts b/packages/firestore/src/config.ts index 78e1d96d945..b23ac8a27be 100644 --- a/packages/firestore/src/config.ts +++ b/packages/firestore/src/config.ts @@ -30,13 +30,13 @@ import { QuerySnapshot, Transaction, WriteBatch, - setLogLevel -} from './api/database'; -import { Blob } from './api/blob'; -import { FieldPath } from './api/field_path'; -import { GeoPoint } from './api/geo_point'; -import { Timestamp } from './api/timestamp'; -import { FieldValue } from './compat/field_value'; + setLogLevel, + Blob, + FieldPath, + GeoPoint, + Timestamp, + FieldValue +} from '../export'; const firestoreNamespace = { Firestore, From 99a0ce079eafa216571a1598b653ee597c0e1fe1 Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Thu, 26 Nov 2020 11:23:29 -0500 Subject: [PATCH 08/16] Fix comment. --- packages/firestore/index.bundle.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/firestore/index.bundle.ts b/packages/firestore/index.bundle.ts index 102dd25137b..e85b8a09b31 100644 --- a/packages/firestore/index.bundle.ts +++ b/packages/firestore/index.bundle.ts @@ -18,7 +18,7 @@ import { Firestore, loadBundle, namedQuery } from './export'; /** - * Registers the memory-only Firestore build with the components framework. + * Prototype patches bundle loading to Firestore. */ export function registerBundle(instance: typeof Firestore): void { instance.prototype.loadBundle = function ( From 983296e56f22f3028f38fa6df4ae2eba3ea32b3a Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Fri, 27 Nov 2020 19:11:18 -0500 Subject: [PATCH 09/16] Make building firebase-*.js work --- packages/firebase/firestore/index.ts | 1 + packages/firebase/firestore/memory/index.ts | 1 + packages/firestore/rollup.config.browser.js | 25 ++++++++++++++++++++- packages/firestore/rollup.shared.js | 2 +- yarn.lock | 19 +++++----------- 5 files changed, 32 insertions(+), 16 deletions(-) diff --git a/packages/firebase/firestore/index.ts b/packages/firebase/firestore/index.ts index 08e45bd8e42..cafaed0902e 100644 --- a/packages/firebase/firestore/index.ts +++ b/packages/firebase/firestore/index.ts @@ -16,3 +16,4 @@ */ import '@firebase/firestore'; +import '@firebase/firestore/bundle'; diff --git a/packages/firebase/firestore/memory/index.ts b/packages/firebase/firestore/memory/index.ts index 3dcef63e67a..6199921021e 100644 --- a/packages/firebase/firestore/memory/index.ts +++ b/packages/firebase/firestore/memory/index.ts @@ -21,3 +21,4 @@ */ import '@firebase/firestore/memory'; +import '@firebase/firestore/bundle'; diff --git a/packages/firestore/rollup.config.browser.js b/packages/firestore/rollup.config.browser.js index b573c469bb8..54d0a1b2a28 100644 --- a/packages/firestore/rollup.config.browser.js +++ b/packages/firestore/rollup.config.browser.js @@ -1,3 +1,8 @@ +import pkg from './package.json'; +import path from 'path'; +import memoryPkg from './memory/package.json'; +import bundlePkg from './bundle/package.json'; + /** * @license * Copyright 2020 Google LLC @@ -58,6 +63,24 @@ export default [ treeshake: { moduleSideEffects: false } + }, + { + input: { + index: pkg['esm2017'], + memory: path.resolve('./memory', memoryPkg['esm2017']), + bundle: path.resolve('./bundle', bundlePkg['esm2017']) + }, + output: [ + { + dir: 'dist/esm5', + format: 'es', + sourcemap: true + } + ], + plugins: util.es2017ToEs5Plugins(), + external: util.resolveNodeExterns, + treeshake: { + moduleSideEffects: false + } } - // TODO: Add ES5 export ]; diff --git a/packages/firestore/rollup.shared.js b/packages/firestore/rollup.shared.js index 52574f4dda2..0ac2f92a136 100644 --- a/packages/firestore/rollup.shared.js +++ b/packages/firestore/rollup.shared.js @@ -268,7 +268,7 @@ exports.es2017ToEs5Plugins = function (mangled = false) { allowJs: true } }, - include: ['dist/*.js', 'dist/exp/*.js'] + include: ['dist/**/*.js'] }), sourcemaps() ]; diff --git a/yarn.lock b/yarn.lock index 75078c24d4c..33bf133b5c1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7681,15 +7681,6 @@ fs-extra@^0.30.0: path-is-absolute "^1.0.0" rimraf "^2.2.8" -fs-extra@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz#414d0110cdd06705734d055652c5411260c31abd" - integrity sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ== - dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" - fs-extra@^7.0.1, fs-extra@~7.0.1: version "7.0.1" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" @@ -13824,12 +13815,12 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" -rollup-plugin-copy-assets@1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/rollup-plugin-copy-assets/-/rollup-plugin-copy-assets-1.1.0.tgz#017e22bef9a9f6ddc632bae60cd660e0270c7706" - integrity sha512-hfBZns2x15XC6N97jhGUiYoNQp1jZ1/mFrM3sbf3GIqGaDEcFPe7+J1H50HIMLG4nVM8YxTJOhMsrXnD4E+2NQ== +rollup-plugin-copy-assets@2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/rollup-plugin-copy-assets/-/rollup-plugin-copy-assets-2.0.3.tgz#9a9098894c3ded16d2eee8c4108055e332b5f59f" + integrity sha512-ETShhQGb9SoiwcNrvb3BhUNSGR89Jao0+XxxfzzLW1YsUzx8+rMO4z9oqWWmo6OHUmfNQRvqRj0cAyPkS9lN9w== dependencies: - fs-extra "^5.0.0" + fs-extra "^7.0.1" rollup-plugin-license@2.2.0: version "2.2.0" From 66428e05c09090c5971ac66d944ce72fe8caec9d Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Fri, 27 Nov 2020 20:54:20 -0500 Subject: [PATCH 10/16] revert yarn.lock change. --- yarn.lock | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/yarn.lock b/yarn.lock index 33bf133b5c1..75078c24d4c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7681,6 +7681,15 @@ fs-extra@^0.30.0: path-is-absolute "^1.0.0" rimraf "^2.2.8" +fs-extra@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz#414d0110cdd06705734d055652c5411260c31abd" + integrity sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-extra@^7.0.1, fs-extra@~7.0.1: version "7.0.1" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" @@ -13815,12 +13824,12 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" -rollup-plugin-copy-assets@2.0.3: - version "2.0.3" - resolved "https://registry.npmjs.org/rollup-plugin-copy-assets/-/rollup-plugin-copy-assets-2.0.3.tgz#9a9098894c3ded16d2eee8c4108055e332b5f59f" - integrity sha512-ETShhQGb9SoiwcNrvb3BhUNSGR89Jao0+XxxfzzLW1YsUzx8+rMO4z9oqWWmo6OHUmfNQRvqRj0cAyPkS9lN9w== +rollup-plugin-copy-assets@1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/rollup-plugin-copy-assets/-/rollup-plugin-copy-assets-1.1.0.tgz#017e22bef9a9f6ddc632bae60cd660e0270c7706" + integrity sha512-hfBZns2x15XC6N97jhGUiYoNQp1jZ1/mFrM3sbf3GIqGaDEcFPe7+J1H50HIMLG4nVM8YxTJOhMsrXnD4E+2NQ== dependencies: - fs-extra "^7.0.1" + fs-extra "^5.0.0" rollup-plugin-license@2.2.0: version "2.2.0" From fe8ac37aab1253a5f9a8d32e69c25c0892c95613 Mon Sep 17 00:00:00 2001 From: wu-hui <53845758+wu-hui@users.noreply.github.com> Date: Mon, 30 Nov 2020 20:19:36 -0500 Subject: [PATCH 11/16] Update packages/firestore/index.bundle.ts Co-authored-by: Sebastian Schmidt --- packages/firestore/index.bundle.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/firestore/index.bundle.ts b/packages/firestore/index.bundle.ts index 48e2712fe90..25b973d98e4 100644 --- a/packages/firestore/index.bundle.ts +++ b/packages/firestore/index.bundle.ts @@ -23,10 +23,10 @@ import { loadBundle, namedQuery } from './src/api/bundle'; */ export function registerBundle(instance: typeof Firestore): void { instance.prototype.loadBundle = function ( + this: Firestore, data: ArrayBuffer | ReadableStream | string ) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return loadBundle(this as any, data); + return loadBundle(this data); }; instance.prototype.namedQuery = function (queryName: string) { // eslint-disable-next-line @typescript-eslint/no-explicit-any From 0b1a4c7933006a01772ef2d9a4b4f61bc8b96542 Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Mon, 30 Nov 2020 20:30:27 -0500 Subject: [PATCH 12/16] Move functions back to sync_engine and local_store.ts --- packages/firestore/index.bundle.ts | 12 +- packages/firestore/src/core/bundle.ts | 106 +------- .../firestore/src/core/firestore_client.ts | 4 +- packages/firestore/src/core/sync_engine.ts | 109 +++++++- packages/firestore/src/local/local_store.ts | 230 +++++++++++++++- .../firestore/src/local/local_store_bundle.ts | 245 ------------------ .../test/unit/local/local_store.test.ts | 12 +- .../test/unit/specs/spec_test_runner.ts | 2 +- 8 files changed, 350 insertions(+), 370 deletions(-) diff --git a/packages/firestore/index.bundle.ts b/packages/firestore/index.bundle.ts index 25b973d98e4..4143297c8df 100644 --- a/packages/firestore/index.bundle.ts +++ b/packages/firestore/index.bundle.ts @@ -26,14 +26,16 @@ export function registerBundle(instance: typeof Firestore): void { this: Firestore, data: ArrayBuffer | ReadableStream | string ) { - return loadBundle(this data); + return loadBundle(this, data); }; - instance.prototype.namedQuery = function (queryName: string) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return namedQuery(this as any, queryName); + instance.prototype.namedQuery = function ( + this: Firestore, + queryName: string + ) { + return namedQuery(this, queryName); }; - //TODO: add loadBundle and namedQuery to the firestore namespace + //TODO(wuandy): add loadBundle and namedQuery to the firestore namespace } registerBundle(Firestore); diff --git a/packages/firestore/src/core/bundle.ts b/packages/firestore/src/core/bundle.ts index abc2b953e50..8c3ed6345c1 100644 --- a/packages/firestore/src/core/bundle.ts +++ b/packages/firestore/src/core/bundle.ts @@ -31,28 +31,18 @@ import { import * as api from '../protos/firestore_proto_api'; import { DocumentKey } from '../model/document_key'; import { MaybeDocument, NoDocument } from '../model/document'; -import { debugAssert, debugCast } from '../util/assert'; -import { LocalStore } from '../local/local_store'; -import { BundleReader, SizedBundleElement } from '../util/bundle_reader'; +import { debugAssert } from '../util/assert'; +import { + applyBundleDocuments, + LocalStore, + saveNamedQuery +} from '../local/local_store'; +import { SizedBundleElement } from '../util/bundle_reader'; import { documentKeySet, DocumentKeySet, MaybeDocumentMap } from '../model/collections'; -import { LoadBundleTask } from '../api/bundle'; -import { - emitNewSnapsAndNotifyLocalStore, - SyncEngine, - SyncEngineImpl -} from './sync_engine'; -import { logWarn } from '../util/log'; -import { LOG_TAG } from '../../lite/src/api/components'; -import { - applyBundleDocuments, - hasNewerBundle, - saveBundle, - saveNamedQuery -} from '../local/local_store_bundle'; /** * Represents a bundled document, including the metadata and the document @@ -105,7 +95,7 @@ export class BundleConverter { * Returns a `LoadBundleTaskProgress` representing the initial progress of * loading a bundle. */ -function bundleInitialProgress( +export function bundleInitialProgress( metadata: ProtoBundleMetadata ): LoadBundleTaskProgress { return { @@ -121,7 +111,7 @@ function bundleInitialProgress( * Returns a `LoadBundleTaskProgress` representing the progress that the loading * has succeeded. */ -function bundleSuccessProgress( +export function bundleSuccessProgress( metadata: ProtoBundleMetadata ): LoadBundleTaskProgress { return { @@ -133,7 +123,7 @@ function bundleSuccessProgress( }; } -class BundleLoadResult { +export class BundleLoadResult { constructor( readonly progress: LoadBundleTaskProgress, readonly changedDocs: MaybeDocumentMap @@ -144,7 +134,7 @@ class BundleLoadResult { * A class to process the elements from a bundle, load them into local * storage and provide progress update while loading. */ -class BundleLoader { +export class BundleLoader { /** The current progress of loading */ private progress: LoadBundleTaskProgress; /** Batched queries to be saved into storage */ @@ -249,77 +239,3 @@ class BundleLoader { return new BundleLoadResult({ ...this.progress }, changedDocuments); } } - -/** - * Loads a Firestore bundle into the SDK. The returned promise resolves when - * the bundle finished loading. - * - * @param bundleReader Bundle to load into the SDK. - * @param task LoadBundleTask used to update the loading progress to public API. - */ -export function syncEngineLoadBundle( - syncEngine: SyncEngine, - bundleReader: BundleReader, - task: LoadBundleTask -): void { - const syncEngineImpl = debugCast(syncEngine, SyncEngineImpl); - - // eslint-disable-next-line @typescript-eslint/no-floating-promises - loadBundleImpl(syncEngineImpl, bundleReader, task).then(() => { - syncEngineImpl.sharedClientState.notifyBundleLoaded(); - }); -} - -async function loadBundleImpl( - syncEngine: SyncEngineImpl, - reader: BundleReader, - task: LoadBundleTask -): Promise { - try { - const metadata = await reader.getMetadata(); - const skip = await hasNewerBundle(syncEngine.localStore, metadata); - if (skip) { - await reader.close(); - task._completeWith(bundleSuccessProgress(metadata)); - return; - } - - task._updateProgress(bundleInitialProgress(metadata)); - - const loader = new BundleLoader( - metadata, - syncEngine.localStore, - reader.serializer - ); - let element = await reader.nextElement(); - while (element) { - debugAssert( - !element.payload.metadata, - 'Unexpected BundleMetadata element.' - ); - const progress = await loader.addSizedElement(element); - if (progress) { - task._updateProgress(progress); - } - - element = await reader.nextElement(); - } - - const result = await loader.complete(); - // TODO(b/160876443): This currently raises snapshots with - // `fromCache=false` if users already listen to some queries and bundles - // has newer version. - await emitNewSnapsAndNotifyLocalStore( - syncEngine, - result.changedDocs, - /* remoteEvent */ undefined - ); - - // Save metadata, so loading the same bundle will skip. - await saveBundle(syncEngine.localStore, metadata); - task._completeWith(result.progress); - } catch (e) { - logWarn(LOG_TAG, `Loading bundle failed with ${e}`); - task._failWith(e); - } -} diff --git a/packages/firestore/src/core/firestore_client.ts b/packages/firestore/src/core/firestore_client.ts index ca5f6ed982c..7f0585a7143 100644 --- a/packages/firestore/src/core/firestore_client.ts +++ b/packages/firestore/src/core/firestore_client.ts @@ -24,6 +24,7 @@ import { import { User } from '../auth/user'; import { executeQuery, + getNamedQuery, handleUserChange, LocalStore, readLocalDocument @@ -54,6 +55,7 @@ import { registerPendingWritesCallback, SyncEngine, syncEngineListen, + syncEngineLoadBundle, syncEngineUnlisten, syncEngineWrite } from './sync_engine'; @@ -77,11 +79,9 @@ import { Persistence } from '../local/persistence'; import { Datastore } from '../remote/datastore'; import { LoadBundleTask } from '../api/bundle'; import { newSerializer, newTextEncoder } from '../platform/serializer'; -import { getNamedQuery } from '../local/local_store_bundle'; import { JsonProtoSerializer } from '../remote/serializer'; import { BundleReader } from '../util/bundle_reader'; import { toByteStreamReader } from '../platform/byte_stream_reader'; -import { syncEngineLoadBundle } from './bundle'; import { NamedQuery } from './bundle_types'; const LOG_TAG = 'FirestoreClient'; diff --git a/packages/firestore/src/core/sync_engine.ts b/packages/firestore/src/core/sync_engine.ts index 289256db59d..d23ca31da9a 100644 --- a/packages/firestore/src/core/sync_engine.ts +++ b/packages/firestore/src/core/sync_engine.ts @@ -17,23 +17,25 @@ import { User } from '../auth/user'; import { + acknowledgeBatch, + allocateTarget, applyRemoteEventToLocalCache, - getNewDocumentChanges, + executeQuery, + getActiveClientsFromPersistence, getCachedTarget, + getHighestUnacknowledgedBatchId, + getNewDocumentChanges, + handleUserChange, + hasNewerBundle, ignoreIfPrimaryLeaseLoss, LocalStore, - getActiveClientsFromPersistence, + localWrite, lookupMutationDocuments, - removeCachedMutationBatchMetadata, - allocateTarget, - executeQuery, - releaseTarget, + notifyLocalViewChanges, rejectBatch, - handleUserChange, - localWrite, - acknowledgeBatch, - getHighestUnacknowledgedBatchId, - notifyLocalViewChanges + releaseTarget, + removeCachedMutationBatchMetadata, + saveBundle } from '../local/local_store'; import { LocalViewChanges } from '../local/local_view_changes'; import { ReferenceSet } from '../local/reference_set'; @@ -58,7 +60,7 @@ import { } from '../remote/remote_store'; import { debugAssert, debugCast, fail, hardAssert } from '../util/assert'; import { Code, FirestoreError } from '../util/error'; -import { logDebug } from '../util/log'; +import { logDebug, logWarn } from '../util/log'; import { primitiveComparator } from '../util/misc'; import { ObjectMap } from '../util/obj_map'; import { Deferred } from '../util/promise'; @@ -102,6 +104,13 @@ import { eventManagerOnWatchChange, eventManagerOnWatchError } from './event_manager'; +import { BundleReader } from '../util/bundle_reader'; +import { LoadBundleTask } from '../api/bundle'; +import { + bundleInitialProgress, + bundleSuccessProgress, + BundleLoader +} from './bundle'; const LOG_TAG = 'SyncEngine'; @@ -200,7 +209,7 @@ export interface SyncEngine { * This is useful to implement optional features (like bundles) in free * functions, such that they are tree-shakeable. */ -export class SyncEngineImpl implements SyncEngine { +class SyncEngineImpl implements SyncEngine { syncEngineListener: SyncEngineListener = {}; /** @@ -1523,3 +1532,77 @@ export function ensureWriteCallbacks(syncEngine: SyncEngine): SyncEngineImpl { ); return syncEngineImpl; } + +/** + * Loads a Firestore bundle into the SDK. The returned promise resolves when + * the bundle finished loading. + * + * @param bundleReader Bundle to load into the SDK. + * @param task LoadBundleTask used to update the loading progress to public API. + */ +export function syncEngineLoadBundle( + syncEngine: SyncEngine, + bundleReader: BundleReader, + task: LoadBundleTask +): void { + const syncEngineImpl = debugCast(syncEngine, SyncEngineImpl); + + // eslint-disable-next-line @typescript-eslint/no-floating-promises + loadBundleImpl(syncEngineImpl, bundleReader, task).then(() => { + syncEngineImpl.sharedClientState.notifyBundleLoaded(); + }); +} + +async function loadBundleImpl( + syncEngine: SyncEngineImpl, + reader: BundleReader, + task: LoadBundleTask +): Promise { + try { + const metadata = await reader.getMetadata(); + const skip = await hasNewerBundle(syncEngine.localStore, metadata); + if (skip) { + await reader.close(); + task._completeWith(bundleSuccessProgress(metadata)); + return; + } + + task._updateProgress(bundleInitialProgress(metadata)); + + const loader = new BundleLoader( + metadata, + syncEngine.localStore, + reader.serializer + ); + let element = await reader.nextElement(); + while (element) { + debugAssert( + !element.payload.metadata, + 'Unexpected BundleMetadata element.' + ); + const progress = await loader.addSizedElement(element); + if (progress) { + task._updateProgress(progress); + } + + element = await reader.nextElement(); + } + + const result = await loader.complete(); + // TODO(b/160876443): This currently raises snapshots with + // `fromCache=false` if users already listen to some queries and bundles + // has newer version. + await emitNewSnapsAndNotifyLocalStore( + syncEngine, + result.changedDocs, + /* remoteEvent */ undefined + ); + + // Save metadata, so loading the same bundle will skip. + await saveBundle(syncEngine.localStore, metadata); + task._completeWith(result.progress); + } catch (e) { + logWarn(LOG_TAG, `Loading bundle failed with ${e}`); + task._failWith(e); + } +} diff --git a/packages/firestore/src/local/local_store.ts b/packages/firestore/src/local/local_store.ts index 8c332d75f33..355a1b105a6 100644 --- a/packages/firestore/src/local/local_store.ts +++ b/packages/firestore/src/local/local_store.ts @@ -17,7 +17,7 @@ import { Timestamp } from '../api/timestamp'; import { User } from '../auth/user'; -import { Query, queryToTarget } from '../core/query'; +import { newQueryForPath, Query, queryToTarget } from '../core/query'; import { SnapshotVersion } from '../core/snapshot_version'; import { canonifyTarget, Target, targetEquals } from '../core/target'; import { BatchId, TargetId } from '../core/types'; @@ -25,6 +25,7 @@ import { DocumentKeySet, documentKeySet, DocumentMap, + documentVersionMap, DocumentVersionMap, maybeDocumentMap, MaybeDocumentMap @@ -76,7 +77,16 @@ import { IndexedDbTargetCache } from './indexeddb_target_cache'; import { extractFieldMask } from '../model/object_value'; import { isIndexedDbTransactionError } from './simple_db'; import { BundleCache } from './bundle_cache'; -import { JsonProtoSerializer } from '../remote/serializer'; +import { fromVersion, JsonProtoSerializer } from '../remote/serializer'; +import { + BundleMetadata, + NamedQuery as ProtoNamedQuery +} from '../protos/firestore_bundle_proto'; +import { NamedQuery } from '../core/bundle_types'; +import { fromBundledQuery } from './local_serializer'; +import { ByteString } from '../util/byte_string'; +import { BundleConverter, BundledDocuments } from '../core/bundle'; +import { ResourcePath } from '../model/path'; const LOG_TAG = 'LocalStore'; @@ -1265,3 +1275,219 @@ export async function ignoreIfPrimaryLeaseLoss( throw err; } } + +/** + * Applies the documents from a bundle to the "ground-state" (remote) + * documents. + * + * LocalDocuments are re-calculated if there are remaining mutations in the + * queue. + */ +export async function applyBundleDocuments( + localStore: LocalStore, + documents: BundledDocuments, + bundleName: string +): Promise { + const localStoreImpl = debugCast(localStore, LocalStoreImpl); + const bundleConverter = new BundleConverter(localStoreImpl.serializer); + let documentKeys = documentKeySet(); + let documentMap = maybeDocumentMap(); + let versionMap = documentVersionMap(); + for (const bundleDoc of documents) { + const documentKey = bundleConverter.toDocumentKey(bundleDoc.metadata.name!); + if (bundleDoc.document) { + documentKeys = documentKeys.add(documentKey); + } + documentMap = documentMap.insert( + documentKey, + bundleConverter.toMaybeDocument(bundleDoc) + ); + versionMap = versionMap.insert( + documentKey, + bundleConverter.toSnapshotVersion(bundleDoc.metadata.readTime!) + ); + } + + const documentBuffer = localStoreImpl.remoteDocuments.newChangeBuffer({ + trackRemovals: true // Make sure document removals show up in `getNewDocumentChanges()` + }); + + // Allocates a target to hold all document keys from the bundle, such that + // they will not get garbage collected right away. + const umbrellaTargetData = await allocateTarget( + localStoreImpl, + umbrellaTarget(bundleName) + ); + return localStoreImpl.persistence.runTransaction( + 'Apply bundle documents', + 'readwrite', + txn => { + return populateDocumentChangeBuffer( + txn, + documentBuffer, + documentMap, + SnapshotVersion.min(), + versionMap + ) + .next(changedDocs => { + documentBuffer.apply(txn); + return changedDocs; + }) + .next(changedDocs => { + return localStoreImpl.targetCache + .removeMatchingKeysForTargetId(txn, umbrellaTargetData.targetId) + .next(() => + localStoreImpl.targetCache.addMatchingKeys( + txn, + documentKeys, + umbrellaTargetData.targetId + ) + ) + .next(() => + localStoreImpl.localDocuments.getLocalViewOfDocuments( + txn, + changedDocs + ) + ); + }); + } + ); +} + +/** + * Returns a promise of a boolean to indicate if the given bundle has already + * been loaded and the create time is newer than the current loading bundle. + */ +export function hasNewerBundle( + localStore: LocalStore, + bundleMetadata: BundleMetadata +): Promise { + const localStoreImpl = debugCast(localStore, LocalStoreImpl); + const bundleConverter = new BundleConverter(localStoreImpl.serializer); + const currentReadTime = bundleConverter.toSnapshotVersion( + bundleMetadata.createTime! + ); + return localStoreImpl.persistence + .runTransaction('hasNewerBundle', 'readonly', transaction => { + return localStoreImpl.bundleCache.getBundleMetadata( + transaction, + bundleMetadata.id! + ); + }) + .then(cached => { + return !!cached && cached.createTime!.compareTo(currentReadTime) >= 0; + }); +} + +/** + * Saves the given `BundleMetadata` to local persistence. + * @param bundleMetadata + */ +export function saveBundle( + localStore: LocalStore, + bundleMetadata: BundleMetadata +): Promise { + const localStoreImpl = debugCast(localStore, LocalStoreImpl); + return localStoreImpl.persistence.runTransaction( + 'Save bundle', + 'readwrite', + transaction => { + return localStoreImpl.bundleCache.saveBundleMetadata( + transaction, + bundleMetadata + ); + } + ); +} + +/** + * Returns a promise of a `NamedQuery` associated with given query name. Promise + * resolves to undefined if no persisted data can be found. + */ +export function getNamedQuery( + localStore: LocalStore, + queryName: string +): Promise { + const localStoreImpl = debugCast(localStore, LocalStoreImpl); + return localStoreImpl.persistence.runTransaction( + 'Get named query', + 'readonly', + transaction => + localStoreImpl.bundleCache.getNamedQuery(transaction, queryName) + ); +} + +/** + * Saves the given `NamedQuery` to local persistence. + */ +export async function saveNamedQuery( + localStore: LocalStore, + query: ProtoNamedQuery, + documents: DocumentKeySet = documentKeySet() +): Promise { + // Allocate a target for the named query such that it can be resumed + // from associated read time if users use it to listen. + // NOTE: this also means if no corresponding target exists, the new target + // will remain active and will not get collected, unless users happen to + // unlisten the query somehow. + const allocated = await allocateTarget( + localStore, + queryToTarget(fromBundledQuery(query.bundledQuery!)) + ); + + const localStoreImpl = debugCast(localStore, LocalStoreImpl); + return localStoreImpl.persistence.runTransaction( + 'Save named query', + 'readwrite', + transaction => { + const readTime = fromVersion(query.readTime!); + // Simply save the query itself if it is older than what the SDK already + // has. + if (allocated.snapshotVersion.compareTo(readTime) >= 0) { + return localStoreImpl.bundleCache.saveNamedQuery(transaction, query); + } + + // Update existing target data because the query from the bundle is newer. + const newTargetData = allocated.withResumeToken( + ByteString.EMPTY_BYTE_STRING, + readTime + ); + localStoreImpl.targetDataByTarget = localStoreImpl.targetDataByTarget.insert( + newTargetData.targetId, + newTargetData + ); + return localStoreImpl.targetCache + .updateTargetData(transaction, newTargetData) + .next(() => + localStoreImpl.targetCache.removeMatchingKeysForTargetId( + transaction, + allocated.targetId + ) + ) + .next(() => + localStoreImpl.targetCache.addMatchingKeys( + transaction, + documents, + allocated.targetId + ) + ) + .next(() => + localStoreImpl.bundleCache.saveNamedQuery(transaction, query) + ); + } + ); +} + +/** + * Creates a new target using the given bundle name, which will be used to + * hold the keys of all documents from the bundle in query-document mappings. + * This ensures that the loaded documents do not get garbage collected + * right away. + */ +function umbrellaTarget(bundleName: string): Target { + // It is OK that the path used for the query is not valid, because this will + // not be read and queried. + return queryToTarget( + newQueryForPath(ResourcePath.fromString(`__bundle__/docs/${bundleName}`)) + ); +} diff --git a/packages/firestore/src/local/local_store_bundle.ts b/packages/firestore/src/local/local_store_bundle.ts index 64a4c608087..d5305f1f08a 100644 --- a/packages/firestore/src/local/local_store_bundle.ts +++ b/packages/firestore/src/local/local_store_bundle.ts @@ -14,248 +14,3 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -import { newQueryForPath, queryToTarget } from '../core/query'; -import { SnapshotVersion } from '../core/snapshot_version'; -import { Target } from '../core/target'; -import { - DocumentKeySet, - documentKeySet, - documentVersionMap, - maybeDocumentMap, - MaybeDocumentMap -} from '../model/collections'; -import { debugCast } from '../util/assert'; - -import { - BundleMetadata, - NamedQuery as ProtoNamedQuery -} from '../protos/firestore_bundle_proto'; -import { BundleConverter, BundledDocuments } from '../core/bundle'; -import { fromVersion } from '../remote/serializer'; -import { fromBundledQuery } from './local_serializer'; -import { ByteString } from '../util/byte_string'; -import { ResourcePath } from '../model/path'; -import { - LocalStore, - LocalStoreImpl, - allocateTarget, - populateDocumentChangeBuffer -} from './local_store'; -import { NamedQuery } from '../core/bundle_types'; - -/** - * Applies the documents from a bundle to the "ground-state" (remote) - * documents. - * - * LocalDocuments are re-calculated if there are remaining mutations in the - * queue. - */ -export async function applyBundleDocuments( - localStore: LocalStore, - documents: BundledDocuments, - bundleName: string -): Promise { - const localStoreImpl = debugCast(localStore, LocalStoreImpl); - const bundleConverter = new BundleConverter(localStoreImpl.serializer); - let documentKeys = documentKeySet(); - let documentMap = maybeDocumentMap(); - let versionMap = documentVersionMap(); - for (const bundleDoc of documents) { - const documentKey = bundleConverter.toDocumentKey(bundleDoc.metadata.name!); - if (bundleDoc.document) { - documentKeys = documentKeys.add(documentKey); - } - documentMap = documentMap.insert( - documentKey, - bundleConverter.toMaybeDocument(bundleDoc) - ); - versionMap = versionMap.insert( - documentKey, - bundleConverter.toSnapshotVersion(bundleDoc.metadata.readTime!) - ); - } - - const documentBuffer = localStoreImpl.remoteDocuments.newChangeBuffer({ - trackRemovals: true // Make sure document removals show up in `getNewDocumentChanges()` - }); - - // Allocates a target to hold all document keys from the bundle, such that - // they will not get garbage collected right away. - const umbrellaTargetData = await allocateTarget( - localStoreImpl, - umbrellaTarget(bundleName) - ); - return localStoreImpl.persistence.runTransaction( - 'Apply bundle documents', - 'readwrite', - txn => { - return populateDocumentChangeBuffer( - txn, - documentBuffer, - documentMap, - SnapshotVersion.min(), - versionMap - ) - .next(changedDocs => { - documentBuffer.apply(txn); - return changedDocs; - }) - .next(changedDocs => { - return localStoreImpl.targetCache - .removeMatchingKeysForTargetId(txn, umbrellaTargetData.targetId) - .next(() => - localStoreImpl.targetCache.addMatchingKeys( - txn, - documentKeys, - umbrellaTargetData.targetId - ) - ) - .next(() => - localStoreImpl.localDocuments.getLocalViewOfDocuments( - txn, - changedDocs - ) - ); - }); - } - ); -} - -/** - * Returns a promise of a boolean to indicate if the given bundle has already - * been loaded and the create time is newer than the current loading bundle. - */ -export function hasNewerBundle( - localStore: LocalStore, - bundleMetadata: BundleMetadata -): Promise { - const localStoreImpl = debugCast(localStore, LocalStoreImpl); - const bundleConverter = new BundleConverter(localStoreImpl.serializer); - const currentReadTime = bundleConverter.toSnapshotVersion( - bundleMetadata.createTime! - ); - return localStoreImpl.persistence - .runTransaction('hasNewerBundle', 'readonly', transaction => { - return localStoreImpl.bundleCache.getBundleMetadata( - transaction, - bundleMetadata.id! - ); - }) - .then(cached => { - return !!cached && cached.createTime!.compareTo(currentReadTime) >= 0; - }); -} - -/** - * Saves the given `BundleMetadata` to local persistence. - * @param bundleMetadata - */ -export function saveBundle( - localStore: LocalStore, - bundleMetadata: BundleMetadata -): Promise { - const localStoreImpl = debugCast(localStore, LocalStoreImpl); - return localStoreImpl.persistence.runTransaction( - 'Save bundle', - 'readwrite', - transaction => { - return localStoreImpl.bundleCache.saveBundleMetadata( - transaction, - bundleMetadata - ); - } - ); -} - -/** - * Returns a promise of a `NamedQuery` associated with given query name. Promise - * resolves to undefined if no persisted data can be found. - */ -export function getNamedQuery( - localStore: LocalStore, - queryName: string -): Promise { - const localStoreImpl = debugCast(localStore, LocalStoreImpl); - return localStoreImpl.persistence.runTransaction( - 'Get named query', - 'readonly', - transaction => - localStoreImpl.bundleCache.getNamedQuery(transaction, queryName) - ); -} - -/** - * Saves the given `NamedQuery` to local persistence. - */ -export async function saveNamedQuery( - localStore: LocalStore, - query: ProtoNamedQuery, - documents: DocumentKeySet = documentKeySet() -): Promise { - // Allocate a target for the named query such that it can be resumed - // from associated read time if users use it to listen. - // NOTE: this also means if no corresponding target exists, the new target - // will remain active and will not get collected, unless users happen to - // unlisten the query somehow. - const allocated = await allocateTarget( - localStore, - queryToTarget(fromBundledQuery(query.bundledQuery!)) - ); - - const localStoreImpl = debugCast(localStore, LocalStoreImpl); - return localStoreImpl.persistence.runTransaction( - 'Save named query', - 'readwrite', - transaction => { - const readTime = fromVersion(query.readTime!); - // Simply save the query itself if it is older than what the SDK already - // has. - if (allocated.snapshotVersion.compareTo(readTime) >= 0) { - return localStoreImpl.bundleCache.saveNamedQuery(transaction, query); - } - - // Update existing target data because the query from the bundle is newer. - const newTargetData = allocated.withResumeToken( - ByteString.EMPTY_BYTE_STRING, - readTime - ); - localStoreImpl.targetDataByTarget = localStoreImpl.targetDataByTarget.insert( - newTargetData.targetId, - newTargetData - ); - return localStoreImpl.targetCache - .updateTargetData(transaction, newTargetData) - .next(() => - localStoreImpl.targetCache.removeMatchingKeysForTargetId( - transaction, - allocated.targetId - ) - ) - .next(() => - localStoreImpl.targetCache.addMatchingKeys( - transaction, - documents, - allocated.targetId - ) - ) - .next(() => - localStoreImpl.bundleCache.saveNamedQuery(transaction, query) - ); - } - ); -} - -/** - * Creates a new target using the given bundle name, which will be used to - * hold the keys of all documents from the bundle in query-document mappings. - * This ensures that the loaded documents do not get garbage collected - * right away. - */ -function umbrellaTarget(bundleName: string): Target { - // It is OK that the path used for the query is not valid, because this will - // not be read and queried. - return queryToTarget( - newQueryForPath(ResourcePath.fromString(`__bundle__/docs/${bundleName}`)) - ); -} diff --git a/packages/firestore/test/unit/local/local_store.test.ts b/packages/firestore/test/unit/local/local_store.test.ts index 91558811f2c..e05d0425b09 100644 --- a/packages/firestore/test/unit/local/local_store.test.ts +++ b/packages/firestore/test/unit/local/local_store.test.ts @@ -48,15 +48,13 @@ import { releaseTarget, getLocalTargetData, getHighestUnacknowledgedBatchId, - rejectBatch -} from '../../../src/local/local_store'; -import { - applyBundleDocuments, + rejectBatch, + saveNamedQuery, getNamedQuery, - hasNewerBundle, saveBundle, - saveNamedQuery -} from '../../../src/local/local_store_bundle'; + hasNewerBundle, + applyBundleDocuments +} from '../../../src/local/local_store'; import { LocalViewChanges } from '../../../src/local/local_view_changes'; import { Persistence } from '../../../src/local/persistence'; import { SimpleQueryEngine } from '../../../src/local/simple_query_engine'; diff --git a/packages/firestore/test/unit/specs/spec_test_runner.ts b/packages/firestore/test/unit/specs/spec_test_runner.ts index d48edeaa190..258891614e3 100644 --- a/packages/firestore/test/unit/specs/spec_test_runner.ts +++ b/packages/firestore/test/unit/specs/spec_test_runner.ts @@ -47,6 +47,7 @@ import { registerPendingWritesCallback, SyncEngine, syncEngineListen, + syncEngineLoadBundle, syncEngineUnlisten, syncEngineWrite } from '../../../src/core/sync_engine'; @@ -155,7 +156,6 @@ import { import { toByteStreamReader } from '../../../src/platform/byte_stream_reader'; import { logWarn } from '../../../src/util/log'; import { newTextEncoder } from '../../../src/platform/serializer'; -import { syncEngineLoadBundle } from '../../../src/core/bundle'; const ARBITRARY_SEQUENCE_NUMBER = 2; From 686d333e0693b776015c9f5c2fd6f1c8e8b118b8 Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Mon, 30 Nov 2020 20:41:28 -0500 Subject: [PATCH 13/16] Remove exports --- packages/firestore/src/local/local_store.ts | 32 +++++++++---------- .../firestore/src/local/local_store_bundle.ts | 16 ---------- 2 files changed, 16 insertions(+), 32 deletions(-) delete mode 100644 packages/firestore/src/local/local_store_bundle.ts diff --git a/packages/firestore/src/local/local_store.ts b/packages/firestore/src/local/local_store.ts index 355a1b105a6..94eedc5f033 100644 --- a/packages/firestore/src/local/local_store.ts +++ b/packages/firestore/src/local/local_store.ts @@ -184,7 +184,7 @@ export interface LocalStore { * This is useful to implement optional features (like bundles) in free * functions, such that they are tree-shakeable. */ -export class LocalStoreImpl implements LocalStore { +class LocalStoreImpl implements LocalStore { /** * The set of all mutations that have been sent but not yet been applied to * the backend. @@ -668,7 +668,7 @@ export function applyRemoteEventToLocalCache( * Note: this function will use `documentVersions` if it is defined; * when it is not defined, resorts to `globalVersion`. */ -export function populateDocumentChangeBuffer( +function populateDocumentChangeBuffer( txn: PersistenceTransaction, documentBuffer: RemoteDocumentChangeBuffer, documents: MaybeDocumentMap, @@ -1276,6 +1276,20 @@ export async function ignoreIfPrimaryLeaseLoss( } } +/** + * Creates a new target using the given bundle name, which will be used to + * hold the keys of all documents from the bundle in query-document mappings. + * This ensures that the loaded documents do not get garbage collected + * right away. + */ +function umbrellaTarget(bundleName: string): Target { + // It is OK that the path used for the query is not valid, because this will + // not be read and queried. + return queryToTarget( + newQueryForPath(ResourcePath.fromString(`__bundle__/docs/${bundleName}`)) + ); +} + /** * Applies the documents from a bundle to the "ground-state" (remote) * documents. @@ -1477,17 +1491,3 @@ export async function saveNamedQuery( } ); } - -/** - * Creates a new target using the given bundle name, which will be used to - * hold the keys of all documents from the bundle in query-document mappings. - * This ensures that the loaded documents do not get garbage collected - * right away. - */ -function umbrellaTarget(bundleName: string): Target { - // It is OK that the path used for the query is not valid, because this will - // not be read and queried. - return queryToTarget( - newQueryForPath(ResourcePath.fromString(`__bundle__/docs/${bundleName}`)) - ); -} diff --git a/packages/firestore/src/local/local_store_bundle.ts b/packages/firestore/src/local/local_store_bundle.ts deleted file mode 100644 index d5305f1f08a..00000000000 --- a/packages/firestore/src/local/local_store_bundle.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @license - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ From 26c021dbda9e59aa7ab317fef8deb779d92377e0 Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Mon, 30 Nov 2020 21:07:43 -0500 Subject: [PATCH 14/16] Enable mangling. --- packages/firestore/rollup.config.browser.js | 4 ++-- packages/firestore/rollup.config.rn.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/firestore/rollup.config.browser.js b/packages/firestore/rollup.config.browser.js index 54d0a1b2a28..748e5410057 100644 --- a/packages/firestore/rollup.config.browser.js +++ b/packages/firestore/rollup.config.browser.js @@ -57,7 +57,7 @@ export default [ } ] }), - ...util.es2017Plugins('browser', /* mangled= */ false) + ...util.es2017Plugins('browser', /* mangled= */ true) ], external: util.resolveBrowserExterns, treeshake: { @@ -78,7 +78,7 @@ export default [ } ], plugins: util.es2017ToEs5Plugins(), - external: util.resolveNodeExterns, + external: util.resolveBrowserExterns, treeshake: { moduleSideEffects: false } diff --git a/packages/firestore/rollup.config.rn.js b/packages/firestore/rollup.config.rn.js index aeac8279669..7bdac6362f0 100644 --- a/packages/firestore/rollup.config.rn.js +++ b/packages/firestore/rollup.config.rn.js @@ -52,7 +52,7 @@ export default [ } ] }), - ...util.es2017Plugins('rn', /* mangled= */ false) + ...util.es2017Plugins('rn', /* mangled= */ true) ], external: util.resolveBrowserExterns, treeshake: { From 68f162ab6d5c3b0f7511f2d22a5447a2ed677119 Mon Sep 17 00:00:00 2001 From: wu-hui <53845758+wu-hui@users.noreply.github.com> Date: Thu, 3 Dec 2020 09:33:03 -0500 Subject: [PATCH 15/16] Separate memory build further. (#4143) * Split up firestore and firestore-memory build We need to verify that the Node build still works * Fix sebstian's memory build my separating it further.. * To mangle or not to mangle. * Add the missing return! Co-authored-by: Sebastian Schmidt --- .../firestore/firebase_export_memory.ts | 2 +- integration/firestore/package.json | 2 +- packages/firebase/firestore/memory/index.ts | 2 +- packages/firestore/memory-bundle/package.json | 10 +++ packages/firestore/memory/package.json | 12 ++-- packages/firestore/package.json | 1 + packages/firestore/rollup.config.browser.js | 61 ++++++++++++++----- packages/firestore/rollup.config.node.js | 47 ++++++++++++-- packages/firestore/rollup.config.rn.js | 35 +++++++---- packages/firestore/rollup.shared.js | 11 ++++ yarn.lock | 54 +++++++++++----- 11 files changed, 184 insertions(+), 53 deletions(-) create mode 100644 packages/firestore/memory-bundle/package.json diff --git a/integration/firestore/firebase_export_memory.ts b/integration/firestore/firebase_export_memory.ts index ae08246a745..98e6ce3ae78 100644 --- a/integration/firestore/firebase_export_memory.ts +++ b/integration/firestore/firebase_export_memory.ts @@ -17,7 +17,7 @@ import firebase from '@firebase/app'; import '@firebase/firestore/memory'; -import '@firebase/firestore/bundle'; +import '@firebase/firestore/memory-bundle'; import { FirebaseApp } from '@firebase/app-types'; import { Settings, FirebaseFirestore } from '@firebase/firestore-types'; diff --git a/integration/firestore/package.json b/integration/firestore/package.json index 7c898f0004d..139199fcc93 100644 --- a/integration/firestore/package.json +++ b/integration/firestore/package.json @@ -9,7 +9,7 @@ "test": "yarn build:memory; karma start --single-run; yarn build:persistence; karma start --single-run;", "test:ci": "node ../../scripts/run_tests_in_ci.js -s test", "test:persistence": " yarn build:persistence; karma start --single-run", - "test:persistence:debug:": "yarn build:persistence; karma start --auto-watch --browsers Chrome", + "test:persistence:debug": "yarn build:persistence; karma start --auto-watch --browsers Chrome", "test:memory": "yarn build:memory; karma start --single-run", "test:memory:debug": "yarn build:memory; karma start --auto-watch --browsers Chrome" }, diff --git a/packages/firebase/firestore/memory/index.ts b/packages/firebase/firestore/memory/index.ts index 6199921021e..6f8f730e3d1 100644 --- a/packages/firebase/firestore/memory/index.ts +++ b/packages/firebase/firestore/memory/index.ts @@ -21,4 +21,4 @@ */ import '@firebase/firestore/memory'; -import '@firebase/firestore/bundle'; +import '@firebase/firestore/memory-bundle'; diff --git a/packages/firestore/memory-bundle/package.json b/packages/firestore/memory-bundle/package.json new file mode 100644 index 00000000000..b29c357c34e --- /dev/null +++ b/packages/firestore/memory-bundle/package.json @@ -0,0 +1,10 @@ +{ + "name": "@firebase/firestore/memory-bundle", + "description": "Firestore bundle", + "main": "../dist/memory/node-cjs/bundle.js", + "main-esm2017": "../dist/memory/node-esm2017/bundle.js", + "react-native": "../dist/memory/rn/bundle.js", + "browser": "../dist/memory/esm5/bundle.js", + "module": "../dist/memory/esm5/bundle.js", + "esm2017": "../dist/memory/esm2017/bundle.js" +} diff --git a/packages/firestore/memory/package.json b/packages/firestore/memory/package.json index e6eb6025020..0879ff8fa39 100644 --- a/packages/firestore/memory/package.json +++ b/packages/firestore/memory/package.json @@ -1,11 +1,11 @@ { "name": "@firebase/firestore/memory", "description": "A memory-only build of the Cloud Firestore JS SDK.", - "main": "../dist/node-cjs/memory.js", - "main-esm2017": "../dist/node-esm2017/memory.js", - "react-native": "../dist/rn/memory.js", - "browser": "../dist/esm5/memory.js", - "module": "../dist/esm5/memory.js", - "esm2017": "../dist/esm2017/memory.js", + "main": "../dist/memory/node-cjs/index.js", + "main-esm2017": "../dist/memory/node-esm2017/index.js", + "react-native": "../dist/memory/rn/index.js", + "browser": "../dist/memory/esm5/index.js", + "module": "../dist/memory/esm5/index.js", + "esm2017": "../dist/memory/esm2017/index.js", "typings": "../dist/index.d.ts" } diff --git a/packages/firestore/package.json b/packages/firestore/package.json index ad7d05a471e..c25cb971f5c 100644 --- a/packages/firestore/package.json +++ b/packages/firestore/package.json @@ -82,6 +82,7 @@ "json-stable-stringify": "1.0.1", "protobufjs": "6.10.2", "rollup": "2.33.2", + "rollup-plugin-copy": "3.3.0", "rollup-plugin-copy-assets": "2.0.3", "rollup-plugin-replace": "2.2.0", "rollup-plugin-sourcemaps": "0.6.3", diff --git a/packages/firestore/rollup.config.browser.js b/packages/firestore/rollup.config.browser.js index 748e5410057..7eeaaaa1a2a 100644 --- a/packages/firestore/rollup.config.browser.js +++ b/packages/firestore/rollup.config.browser.js @@ -2,6 +2,7 @@ import pkg from './package.json'; import path from 'path'; import memoryPkg from './memory/package.json'; import bundlePkg from './bundle/package.json'; +import memoryBundlePkg from './memory-bundle/package.json'; /** * @license @@ -20,14 +21,14 @@ import bundlePkg from './bundle/package.json'; * limitations under the License. */ -const alias = require('@rollup/plugin-alias'); const util = require('./rollup.shared'); export default [ + // Create a temporary build that includes the mangled classes for all exports { input: 'export.ts', output: { - file: 'dist/prebuild.js', + file: 'dist/prebuilt.js', format: 'es', sourcemap: true }, @@ -37,10 +38,10 @@ export default [ moduleSideEffects: false } }, + // Create main build { input: { index: 'index.ts', - memory: 'index.memory.ts', bundle: 'index.bundle.ts' }, output: { @@ -49,25 +50,18 @@ export default [ sourcemap: true }, plugins: [ - alias({ - entries: [ - { - find: /^(.*)\/export$/, - replacement: `$1\/dist/prebuild.js` - } - ] - }), - ...util.es2017Plugins('browser', /* mangled= */ true) + util.applyPrebuilt(), + ...util.es2017Plugins('browser', /* mangled= */ false) ], external: util.resolveBrowserExterns, treeshake: { moduleSideEffects: false } }, + // Convert main build to ES5 { input: { index: pkg['esm2017'], - memory: path.resolve('./memory', memoryPkg['esm2017']), bundle: path.resolve('./bundle', bundlePkg['esm2017']) }, output: [ @@ -77,7 +71,46 @@ export default [ sourcemap: true } ], - plugins: util.es2017ToEs5Plugins(), + plugins: util.es2017ToEs5Plugins(/* mangled= */ true), + external: util.resolveBrowserExterns, + treeshake: { + moduleSideEffects: false + } + }, + // Create memory build + { + input: { + index: 'index.memory.ts', + bundle: 'index.bundle.ts' + }, + output: { + dir: 'dist/memory/esm2017', + format: 'es', + sourcemap: true + }, + plugins: [ + util.applyPrebuilt(), + ...util.es2017Plugins('browser', /* mangled= */ false) + ], + external: util.resolveBrowserExterns, + treeshake: { + moduleSideEffects: false + } + }, + // Convert memory build to ES5 + { + input: { + index: path.resolve('./memory', memoryPkg['esm2017']), + bundle: path.resolve('./bundle', memoryBundlePkg['esm2017']) + }, + output: [ + { + dir: 'dist/memory/esm5', + format: 'es', + sourcemap: true + } + ], + plugins: util.es2017ToEs5Plugins(/* mangled= */ true), external: util.resolveBrowserExterns, treeshake: { moduleSideEffects: false diff --git a/packages/firestore/rollup.config.node.js b/packages/firestore/rollup.config.node.js index 5f7f0bca085..8f1f9f7a24e 100644 --- a/packages/firestore/rollup.config.node.js +++ b/packages/firestore/rollup.config.node.js @@ -16,10 +16,11 @@ */ import replace from 'rollup-plugin-replace'; -import copy from 'rollup-plugin-copy-assets'; +import copy from 'rollup-plugin-copy'; import pkg from './package.json'; import bundlePkg from './bundle/package.json'; import memoryPkg from './memory/package.json'; +import memoryBundlePkg from './memory-bundle/package.json'; import path from 'path'; const util = require('./rollup.shared'); @@ -28,7 +29,6 @@ export default [ { input: { index: 'index.node.ts', - memory: 'index.node.memory.ts', bundle: 'index.bundle.ts' }, output: { @@ -39,10 +39,10 @@ export default [ plugins: [ ...util.es2017Plugins('node'), replace({ - 'process.env.FIRESTORE_PROTO_ROOT': JSON.stringify('src/protos') + 'process.env.FIRESTORE_PROTO_ROOT': JSON.stringify('../protos') }), copy({ - assets: ['./src/protos'] + targets: [{ src: 'src/protos', dest: 'dist' }] }) ], external: util.resolveNodeExterns, @@ -53,7 +53,6 @@ export default [ { input: { index: pkg['main-esm2017'], - memory: path.resolve('./memory', memoryPkg['main-esm2017']), bundle: path.resolve('./bundle', bundlePkg['main-esm2017']) }, output: [ @@ -68,5 +67,43 @@ export default [ treeshake: { moduleSideEffects: false } + }, + { + input: { + index: 'index.node.memory.ts', + bundle: 'index.bundle.ts' + }, + output: { + dir: 'dist/memory/node-esm2017', + format: 'es', + sourcemap: true + }, + plugins: [ + ...util.es2017Plugins('node'), + replace({ + 'process.env.FIRESTORE_PROTO_ROOT': JSON.stringify('../protos') + }) + ], + external: util.resolveNodeExterns, + treeshake: { + moduleSideEffects: false + } + }, + { + input: { + index: path.resolve('./memory', memoryPkg['main-esm2017']), + bundle: path.resolve('./bundle', memoryBundlePkg['main-esm2017']) + }, + output: [ + { + dir: 'dist/memory/node-cjs', + format: 'cjs', + sourcemap: true + } + ], + external: util.resolveNodeExterns, + treeshake: { + moduleSideEffects: false + } } ]; diff --git a/packages/firestore/rollup.config.rn.js b/packages/firestore/rollup.config.rn.js index 7bdac6362f0..fcbd6225a8d 100644 --- a/packages/firestore/rollup.config.rn.js +++ b/packages/firestore/rollup.config.rn.js @@ -15,14 +15,14 @@ * limitations under the License. */ -const alias = require('@rollup/plugin-alias'); const util = require('./rollup.shared'); export default [ + // Create a temporary build that includes the mangled classes for all exports { input: 'export.ts', output: { - file: 'dist/prebuild.rn.js', + file: 'dist/prebuilt.rn.js', format: 'es', sourcemap: true }, @@ -32,10 +32,10 @@ export default [ moduleSideEffects: false } }, + // Create main build { input: { index: 'index.ts', - memory: 'index.memory.ts', bundle: 'index.bundle.ts' }, output: { @@ -44,14 +44,27 @@ export default [ sourcemap: true }, plugins: [ - alias({ - entries: [ - { - find: /^(.*)\/export$/, - replacement: `$1\/dist/prebuild.rn.js` - } - ] - }), + util.applyPrebuilt('prebuilt.rn.js'), + ...util.es2017Plugins('rn', /* mangled= */ false) + ], + external: util.resolveBrowserExterns, + treeshake: { + moduleSideEffects: false + } + }, + // Create memory build + { + input: { + index: 'index.memory.ts', + bundle: 'index.bundle.ts' + }, + output: { + dir: 'dist/rn', + format: 'es', + sourcemap: true + }, + plugins: [ + util.applyPrebuilt('prebuilt.rn.js'), ...util.es2017Plugins('rn', /* mangled= */ true) ], external: util.resolveBrowserExterns, diff --git a/packages/firestore/rollup.shared.js b/packages/firestore/rollup.shared.js index 0ac2f92a136..a8f78f6902c 100644 --- a/packages/firestore/rollup.shared.js +++ b/packages/firestore/rollup.shared.js @@ -203,6 +203,17 @@ const manglePrivatePropertiesOptions = { }; exports.manglePrivatePropertiesOptions = manglePrivatePropertiesOptions; +exports.applyPrebuilt = function (name = 'prebuilt.js') { + return alias({ + entries: [ + { + find: /^(.*)\/export$/, + replacement: `$1\/dist/${name}` + } + ] + }); +}; + exports.es2017Plugins = function (platform, mangled = false) { if (mangled) { return [ diff --git a/yarn.lock b/yarn.lock index 75078c24d4c..91f2923fa29 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5175,6 +5175,11 @@ color@3.0.x: color-convert "^1.9.1" color-string "^1.5.2" +colorette@^1.1.0: + version "1.2.1" + resolved "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz#4d0b921325c14faf92633086a536db6e89564b1b" + integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw== + colors@1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" @@ -7681,15 +7686,6 @@ fs-extra@^0.30.0: path-is-absolute "^1.0.0" rimraf "^2.2.8" -fs-extra@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz#414d0110cdd06705734d055652c5411260c31abd" - integrity sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ== - dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" - fs-extra@^7.0.1, fs-extra@~7.0.1: version "7.0.1" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" @@ -8140,6 +8136,20 @@ globals@^9.18.0: resolved "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ== +globby@10.0.1: + version "10.0.1" + resolved "https://registry.npmjs.org/globby/-/globby-10.0.1.tgz#4782c34cb75dd683351335c5829cc3420e606b22" + integrity sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A== + dependencies: + "@types/glob" "^7.1.1" + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.0.3" + glob "^7.1.3" + ignore "^5.1.1" + merge2 "^1.2.3" + slash "^3.0.0" + globby@^10.0.1: version "10.0.2" resolved "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz#277593e745acaa4646c3ab411289ec47a0392543" @@ -9478,6 +9488,11 @@ is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" +is-plain-object@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.1.tgz#662d92d24c0aa4302407b0d45d21f2251c85f85b" + integrity sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g== + is-plain-object@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" @@ -13824,12 +13839,23 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" -rollup-plugin-copy-assets@1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/rollup-plugin-copy-assets/-/rollup-plugin-copy-assets-1.1.0.tgz#017e22bef9a9f6ddc632bae60cd660e0270c7706" - integrity sha512-hfBZns2x15XC6N97jhGUiYoNQp1jZ1/mFrM3sbf3GIqGaDEcFPe7+J1H50HIMLG4nVM8YxTJOhMsrXnD4E+2NQ== +rollup-plugin-copy-assets@2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/rollup-plugin-copy-assets/-/rollup-plugin-copy-assets-2.0.3.tgz#9a9098894c3ded16d2eee8c4108055e332b5f59f" + integrity sha512-ETShhQGb9SoiwcNrvb3BhUNSGR89Jao0+XxxfzzLW1YsUzx8+rMO4z9oqWWmo6OHUmfNQRvqRj0cAyPkS9lN9w== dependencies: - fs-extra "^5.0.0" + fs-extra "^7.0.1" + +rollup-plugin-copy@3.3.0: + version "3.3.0" + resolved "https://registry.npmjs.org/rollup-plugin-copy/-/rollup-plugin-copy-3.3.0.tgz#5ba230047f86b9f703a29288f242948a5580e7b9" + integrity sha512-euDjCUSBXZa06nqnwCNADbkAcYDfzwowfZQkto9K/TFhiH+QG7I4PUsEMwM9tDgomGWJc//z7KLW8t+tZwxADA== + dependencies: + "@types/fs-extra" "^8.0.1" + colorette "^1.1.0" + fs-extra "^8.1.0" + globby "10.0.1" + is-plain-object "^3.0.0" rollup-plugin-license@2.2.0: version "2.2.0" From 32b86c70da238b2b7e348682b0bfe4a2a2cba0c0 Mon Sep 17 00:00:00 2001 From: Wu-Hui Date: Thu, 3 Dec 2020 16:55:28 -0500 Subject: [PATCH 16/16] Add comments dropped from bad merge. --- packages/firestore/index.bundle.ts | 2 -- packages/firestore/package.json | 3 ++- packages/firestore/rollup.config.rn.js | 2 +- packages/firestore/src/core/bundle_types.ts | 9 +++++++++ 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/firestore/index.bundle.ts b/packages/firestore/index.bundle.ts index 73466b635b1..e804ac41d19 100644 --- a/packages/firestore/index.bundle.ts +++ b/packages/firestore/index.bundle.ts @@ -33,8 +33,6 @@ export function registerBundle(instance: typeof Firestore): void { ) { return namedQuery(this, queryName); }; - - //TODO(wuandy): add loadBundle and namedQuery to the firestore namespace } registerBundle(Firestore); diff --git a/packages/firestore/package.json b/packages/firestore/package.json index c25cb971f5c..7c763edfa3e 100644 --- a/packages/firestore/package.json +++ b/packages/firestore/package.json @@ -56,7 +56,8 @@ "files": [ "dist", "memory/package.json", - "bundle/package.json" + "bundle/package.json", + "memory-bundle/package.json" ], "dependencies": { "@firebase/component": "0.1.21", diff --git a/packages/firestore/rollup.config.rn.js b/packages/firestore/rollup.config.rn.js index fcbd6225a8d..1b8e2d8c507 100644 --- a/packages/firestore/rollup.config.rn.js +++ b/packages/firestore/rollup.config.rn.js @@ -65,7 +65,7 @@ export default [ }, plugins: [ util.applyPrebuilt('prebuilt.rn.js'), - ...util.es2017Plugins('rn', /* mangled= */ true) + ...util.es2017Plugins('rn', /* mangled= */ false) ], external: util.resolveBrowserExterns, treeshake: { diff --git a/packages/firestore/src/core/bundle_types.ts b/packages/firestore/src/core/bundle_types.ts index a4e7bf9bc19..c25d0005879 100644 --- a/packages/firestore/src/core/bundle_types.ts +++ b/packages/firestore/src/core/bundle_types.ts @@ -25,8 +25,15 @@ import { SnapshotVersion } from './snapshot_version'; * Represents a Firestore bundle saved by the SDK in its local storage. */ export interface Bundle { + /** + * Id of the bundle. It is used together with `createTime` to determine if a + * bundle has been loaded by the SDK. + */ readonly id: string; + + /** Schema version of the bundle. */ readonly version: number; + /** * Set to the snapshot version of the bundle if created by the Server SDKs. * Otherwise set to SnapshotVersion.MIN. @@ -38,7 +45,9 @@ export interface Bundle { * Represents a Query saved by the SDK in its local storage. */ export interface NamedQuery { + /** The name of the query. */ readonly name: string; + /** The underlying query associated with `name`. */ readonly query: Query; /** The time at which the results for this query were read. */ readonly readTime: SnapshotVersion;