diff --git a/common/api-review/auth.api.md b/common/api-review/auth.api.md index 87ce191c1b2..0155a4509fd 100644 --- a/common/api-review/auth.api.md +++ b/common/api-review/auth.api.md @@ -1,826 +1,824 @@ -## API Report File for "@firebase/auth" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts - -import { CompleteFn } from '@firebase/util'; -import { ErrorFactory } from '@firebase/util'; -import { ErrorFn } from '@firebase/util'; -import { FirebaseApp } from '@firebase/app'; -import { FirebaseError } from '@firebase/util'; -import { NextFn } from '@firebase/util'; -import { Observer } from '@firebase/util'; -import { Unsubscribe } from '@firebase/util'; - -// @public -export interface ActionCodeInfo { - data: { - email?: string | null; - multiFactorInfo?: MultiFactorInfo | null; - previousEmail?: string | null; - }; - operation: typeof ActionCodeOperation[keyof typeof ActionCodeOperation]; -} - -// @public -export const ActionCodeOperation: { - readonly EMAIL_SIGNIN: "EMAIL_SIGNIN"; - readonly PASSWORD_RESET: "PASSWORD_RESET"; - readonly RECOVER_EMAIL: "RECOVER_EMAIL"; - readonly REVERT_SECOND_FACTOR_ADDITION: "REVERT_SECOND_FACTOR_ADDITION"; - readonly VERIFY_AND_CHANGE_EMAIL: "VERIFY_AND_CHANGE_EMAIL"; - readonly VERIFY_EMAIL: "VERIFY_EMAIL"; -}; - -// @public -export interface ActionCodeSettings { - android?: { - installApp?: boolean; - minimumVersion?: string; - packageName: string; - }; - dynamicLinkDomain?: string; - handleCodeInApp?: boolean; - iOS?: { - bundleId: string; - }; - url: string; -} - -// @public -export class ActionCodeURL { - // @internal - constructor(actionLink: string); - readonly apiKey: string; - readonly code: string; - readonly continueUrl: string | null; - readonly languageCode: string | null; - readonly operation: string; - static parseLink(link: string): ActionCodeURL | null; - readonly tenantId: string | null; -} - -// @public -export interface AdditionalUserInfo { - readonly isNewUser: boolean; - readonly profile: Record | null; - readonly providerId: string | null; - readonly username?: string | null; -} - -// @public -export interface ApplicationVerifier { - readonly type: string; - verify(): Promise; -} - -// @public -export function applyActionCode(auth: Auth, oobCode: string): Promise; - -// @public -export interface Auth { - readonly config: Config; - readonly currentUser: User | null; - readonly emulatorConfig: EmulatorConfig | null; - languageCode: string | null; - readonly name: string; - onAuthStateChanged(nextOrObserver: NextOrObserver, error?: ErrorFn, completed?: CompleteFn): Unsubscribe; - onIdTokenChanged(nextOrObserver: NextOrObserver, error?: ErrorFn, completed?: CompleteFn): Unsubscribe; - setPersistence(persistence: Persistence): Promise; - readonly settings: AuthSettings; - signOut(): Promise; - tenantId: string | null; - updateCurrentUser(user: User | null): Promise; - useDeviceLanguage(): void; -} - -// @public -export class AuthCredential { - // @internal - protected constructor( - providerId: string, - signInMethod: string); - // Warning: (ae-forgotten-export) The symbol "AuthInternal" needs to be exported by the entry point index.doc.d.ts - // Warning: (ae-forgotten-export) The symbol "PhoneOrOauthTokenResponse" needs to be exported by the entry point index.doc.d.ts - // - // @internal (undocumented) - _getIdTokenResponse(_auth: AuthInternal): Promise; - // @internal (undocumented) - _getReauthenticationResolver(_auth: AuthInternal): Promise; - // Warning: (ae-forgotten-export) The symbol "IdTokenResponse" needs to be exported by the entry point index.doc.d.ts - // - // @internal (undocumented) - _linkToIdToken(_auth: AuthInternal, _idToken: string): Promise; - readonly providerId: string; - readonly signInMethod: string; - toJSON(): object; -} - -// @public -export interface AuthError extends FirebaseError { - readonly appName: string; - readonly email?: string; - readonly phoneNumber?: string; - readonly tenantid?: string; -} - -// @public -export const AuthErrorCodes: { - readonly ADMIN_ONLY_OPERATION: "auth/admin-restricted-operation"; - readonly ARGUMENT_ERROR: "auth/argument-error"; - readonly APP_NOT_AUTHORIZED: "auth/app-not-authorized"; - readonly APP_NOT_INSTALLED: "auth/app-not-installed"; - readonly CAPTCHA_CHECK_FAILED: "auth/captcha-check-failed"; - readonly CODE_EXPIRED: "auth/code-expired"; - readonly CORDOVA_NOT_READY: "auth/cordova-not-ready"; - readonly CORS_UNSUPPORTED: "auth/cors-unsupported"; - readonly CREDENTIAL_ALREADY_IN_USE: "auth/credential-already-in-use"; - readonly CREDENTIAL_MISMATCH: "auth/custom-token-mismatch"; - readonly CREDENTIAL_TOO_OLD_LOGIN_AGAIN: "auth/requires-recent-login"; - readonly DEPENDENT_SDK_INIT_BEFORE_AUTH: "auth/dependent-sdk-initialized-before-auth"; - readonly DYNAMIC_LINK_NOT_ACTIVATED: "auth/dynamic-link-not-activated"; - readonly EMAIL_CHANGE_NEEDS_VERIFICATION: "auth/email-change-needs-verification"; - readonly EMAIL_EXISTS: "auth/email-already-in-use"; - readonly EMULATOR_CONFIG_FAILED: "auth/emulator-config-failed"; - readonly EXPIRED_OOB_CODE: "auth/expired-action-code"; - readonly EXPIRED_POPUP_REQUEST: "auth/cancelled-popup-request"; - readonly INTERNAL_ERROR: "auth/internal-error"; - readonly INVALID_API_KEY: "auth/invalid-api-key"; - readonly INVALID_APP_CREDENTIAL: "auth/invalid-app-credential"; - readonly INVALID_APP_ID: "auth/invalid-app-id"; - readonly INVALID_AUTH: "auth/invalid-user-token"; - readonly INVALID_AUTH_EVENT: "auth/invalid-auth-event"; - readonly INVALID_CERT_HASH: "auth/invalid-cert-hash"; - readonly INVALID_CODE: "auth/invalid-verification-code"; - readonly INVALID_CONTINUE_URI: "auth/invalid-continue-uri"; - readonly INVALID_CORDOVA_CONFIGURATION: "auth/invalid-cordova-configuration"; - readonly INVALID_CUSTOM_TOKEN: "auth/invalid-custom-token"; - readonly INVALID_DYNAMIC_LINK_DOMAIN: "auth/invalid-dynamic-link-domain"; - readonly INVALID_EMAIL: "auth/invalid-email"; - readonly INVALID_EMULATOR_SCHEME: "auth/invalid-emulator-scheme"; - readonly INVALID_IDP_RESPONSE: "auth/invalid-credential"; - readonly INVALID_MESSAGE_PAYLOAD: "auth/invalid-message-payload"; - readonly INVALID_MFA_SESSION: "auth/invalid-multi-factor-session"; - readonly INVALID_OAUTH_CLIENT_ID: "auth/invalid-oauth-client-id"; - readonly INVALID_OAUTH_PROVIDER: "auth/invalid-oauth-provider"; - readonly INVALID_OOB_CODE: "auth/invalid-action-code"; - readonly INVALID_ORIGIN: "auth/unauthorized-domain"; - readonly INVALID_PASSWORD: "auth/wrong-password"; - readonly INVALID_PERSISTENCE: "auth/invalid-persistence-type"; - readonly INVALID_PHONE_NUMBER: "auth/invalid-phone-number"; - readonly INVALID_PROVIDER_ID: "auth/invalid-provider-id"; - readonly INVALID_RECIPIENT_EMAIL: "auth/invalid-recipient-email"; - readonly INVALID_SENDER: "auth/invalid-sender"; - readonly INVALID_SESSION_INFO: "auth/invalid-verification-id"; - readonly INVALID_TENANT_ID: "auth/invalid-tenant-id"; - readonly MFA_INFO_NOT_FOUND: "auth/multi-factor-info-not-found"; - readonly MFA_REQUIRED: "auth/multi-factor-auth-required"; - readonly MISSING_ANDROID_PACKAGE_NAME: "auth/missing-android-pkg-name"; - readonly MISSING_APP_CREDENTIAL: "auth/missing-app-credential"; - readonly MISSING_AUTH_DOMAIN: "auth/auth-domain-config-required"; - readonly MISSING_CODE: "auth/missing-verification-code"; - readonly MISSING_CONTINUE_URI: "auth/missing-continue-uri"; - readonly MISSING_IFRAME_START: "auth/missing-iframe-start"; - readonly MISSING_IOS_BUNDLE_ID: "auth/missing-ios-bundle-id"; - readonly MISSING_OR_INVALID_NONCE: "auth/missing-or-invalid-nonce"; - readonly MISSING_MFA_INFO: "auth/missing-multi-factor-info"; - readonly MISSING_MFA_SESSION: "auth/missing-multi-factor-session"; - readonly MISSING_PHONE_NUMBER: "auth/missing-phone-number"; - readonly MISSING_SESSION_INFO: "auth/missing-verification-id"; - readonly MODULE_DESTROYED: "auth/app-deleted"; - readonly NEED_CONFIRMATION: "auth/account-exists-with-different-credential"; - readonly NETWORK_REQUEST_FAILED: "auth/network-request-failed"; - readonly NULL_USER: "auth/null-user"; - readonly NO_AUTH_EVENT: "auth/no-auth-event"; - readonly NO_SUCH_PROVIDER: "auth/no-such-provider"; - readonly OPERATION_NOT_ALLOWED: "auth/operation-not-allowed"; - readonly OPERATION_NOT_SUPPORTED: "auth/operation-not-supported-in-this-environment"; - readonly POPUP_BLOCKED: "auth/popup-blocked"; - readonly POPUP_CLOSED_BY_USER: "auth/popup-closed-by-user"; - readonly PROVIDER_ALREADY_LINKED: "auth/provider-already-linked"; - readonly QUOTA_EXCEEDED: "auth/quota-exceeded"; - readonly REDIRECT_CANCELLED_BY_USER: "auth/redirect-cancelled-by-user"; - readonly REDIRECT_OPERATION_PENDING: "auth/redirect-operation-pending"; - readonly REJECTED_CREDENTIAL: "auth/rejected-credential"; - readonly SECOND_FACTOR_ALREADY_ENROLLED: "auth/second-factor-already-in-use"; - readonly SECOND_FACTOR_LIMIT_EXCEEDED: "auth/maximum-second-factor-count-exceeded"; - readonly TENANT_ID_MISMATCH: "auth/tenant-id-mismatch"; - readonly TIMEOUT: "auth/timeout"; - readonly TOKEN_EXPIRED: "auth/user-token-expired"; - readonly TOO_MANY_ATTEMPTS_TRY_LATER: "auth/too-many-requests"; - readonly UNAUTHORIZED_DOMAIN: "auth/unauthorized-continue-uri"; - readonly UNSUPPORTED_FIRST_FACTOR: "auth/unsupported-first-factor"; - readonly UNSUPPORTED_PERSISTENCE: "auth/unsupported-persistence-type"; - readonly UNSUPPORTED_TENANT_OPERATION: "auth/unsupported-tenant-operation"; - readonly UNVERIFIED_EMAIL: "auth/unverified-email"; - readonly USER_CANCELLED: "auth/user-cancelled"; - readonly USER_DELETED: "auth/user-not-found"; - readonly USER_DISABLED: "auth/user-disabled"; - readonly USER_MISMATCH: "auth/user-mismatch"; - readonly USER_SIGNED_OUT: "auth/user-signed-out"; - readonly WEAK_PASSWORD: "auth/weak-password"; - readonly WEB_STORAGE_UNSUPPORTED: "auth/web-storage-unsupported"; - readonly ALREADY_INITIALIZED: "auth/already-initialized"; -}; - -// @public -export interface AuthErrorMap { -} - -// @public -export interface AuthProvider { - readonly providerId: string; -} - -// @public -export interface AuthSettings { - appVerificationDisabledForTesting: boolean; -} - -// @public -export const browserLocalPersistence: Persistence; - -// @public -export const browserPopupRedirectResolver: PopupRedirectResolver; - -// @public -export const browserSessionPersistence: Persistence; - -// @public -export function checkActionCode(auth: Auth, oobCode: string): Promise; - -export { CompleteFn } - -// @public -export interface Config { - apiHost: string; - apiKey: string; - apiScheme: string; - authDomain?: string; - sdkClientVersion: string; - tokenApiHost: string; -} - -// @public -export interface ConfirmationResult { - confirm(verificationCode: string): Promise; - readonly verificationId: string; -} - -// @public -export function confirmPasswordReset(auth: Auth, oobCode: string, newPassword: string): Promise; - -// @public -export function connectAuthEmulator(auth: Auth, url: string, options?: { - disableWarnings: boolean; -}): void; - -// @public -export const cordovaPopupRedirectResolver: PopupRedirectResolver; - -// @public -export function createUserWithEmailAndPassword(auth: Auth, email: string, password: string): Promise; - -// @public -export type CustomParameters = Record; - -// @public -export const debugErrorMap: AuthErrorMap; - -// @public -export function deleteUser(user: User): Promise; - -// @public -export interface Dependencies { - errorMap?: AuthErrorMap; - persistence?: Persistence | Persistence[]; - popupRedirectResolver?: PopupRedirectResolver; -} - -// @public -export class EmailAuthCredential extends AuthCredential { - // @internal (undocumented) - readonly _email: string; - // @internal (undocumented) - static _fromEmailAndCode(email: string, oobCode: string, tenantId?: string | null): EmailAuthCredential; - // @internal (undocumented) - static _fromEmailAndPassword(email: string, password: string): EmailAuthCredential; - static fromJSON(json: object | string): EmailAuthCredential | null; - // @internal (undocumented) - _getIdTokenResponse(auth: AuthInternal): Promise; - // @internal (undocumented) - _getReauthenticationResolver(auth: AuthInternal): Promise; - // @internal (undocumented) - _linkToIdToken(auth: AuthInternal, idToken: string): Promise; - // @internal (undocumented) - readonly _password: string; - // @internal (undocumented) - readonly _tenantId: string | null; - toJSON(): object; -} - -// @public -export class EmailAuthProvider implements AuthProvider { - static credential(email: string, password: string): EmailAuthCredential; - static credentialWithLink(email: string, emailLink: string): EmailAuthCredential; - static readonly EMAIL_LINK_SIGN_IN_METHOD: 'emailLink'; - static readonly EMAIL_PASSWORD_SIGN_IN_METHOD: 'password'; - static readonly PROVIDER_ID: 'password'; - readonly providerId: "password"; -} - -// @public -export interface EmulatorConfig { - readonly host: string; - readonly options: { - readonly disableWarnings: boolean; - }; - readonly port: number | null; - readonly protocol: string; -} - -export { ErrorFn } - -// Warning: (ae-forgotten-export) The symbol "BaseOAuthProvider" needs to be exported by the entry point index.doc.d.ts -// -// @public -export class FacebookAuthProvider extends BaseOAuthProvider { - constructor(); - static credential(accessToken: string): OAuthCredential; - static credentialFromError(error: FirebaseError): OAuthCredential | null; - static credentialFromResult(userCredential: UserCredential): OAuthCredential | null; - static readonly FACEBOOK_SIGN_IN_METHOD: 'facebook.com'; - static readonly PROVIDER_ID: 'facebook.com'; -} - -// @public -export const FactorId: { - readonly PHONE: "phone"; -}; - -// @public -export function fetchSignInMethodsForEmail(auth: Auth, email: string): Promise; - -// @public -export function getAdditionalUserInfo(userCredential: UserCredential): AdditionalUserInfo | null; - -// @public -export function getAuth(app?: FirebaseApp): Auth; - -// @public -export function getIdToken(user: User, forceRefresh?: boolean): Promise; - -// @public -export function getIdTokenResult(user: User, forceRefresh?: boolean): Promise; - -// @public -export function getMultiFactorResolver(auth: Auth, error: MultiFactorError): MultiFactorResolver; - -// @public -export function getRedirectResult(auth: Auth, resolver?: PopupRedirectResolver): Promise; - -// @public -export class GithubAuthProvider extends BaseOAuthProvider { - constructor(); - static credential(accessToken: string): OAuthCredential; - static credentialFromError(error: FirebaseError): OAuthCredential | null; - static credentialFromResult(userCredential: UserCredential): OAuthCredential | null; - static readonly GITHUB_SIGN_IN_METHOD: 'github.com'; - static readonly PROVIDER_ID: 'github.com'; -} - -// @public -export class GoogleAuthProvider extends BaseOAuthProvider { - constructor(); - static credential(idToken?: string | null, accessToken?: string | null): OAuthCredential; - static credentialFromError(error: FirebaseError): OAuthCredential | null; - static credentialFromResult(userCredential: UserCredential): OAuthCredential | null; - static readonly GOOGLE_SIGN_IN_METHOD: 'google.com'; - static readonly PROVIDER_ID: 'google.com'; -} - -// @public -export interface IdTokenResult { - authTime: string; - claims: ParsedToken; - expirationTime: string; - issuedAtTime: string; - signInProvider: string | null; - signInSecondFactor: string | null; - token: string; -} - -// @public -export const indexedDBLocalPersistence: Persistence; - -// @public -export function initializeAuth(app: FirebaseApp, deps?: Dependencies): Auth; - -// @public -export const inMemoryPersistence: Persistence; - -// @public -export function isSignInWithEmailLink(auth: Auth, emailLink: string): boolean; - -// @public -export function linkWithCredential(user: User, credential: AuthCredential): Promise; - -// @public -export function linkWithPhoneNumber(user: User, phoneNumber: string, appVerifier: ApplicationVerifier): Promise; - -// @public -export function linkWithPopup(user: User, provider: AuthProvider, resolver?: PopupRedirectResolver): Promise; - -// @public -export function linkWithRedirect(user: User, provider: AuthProvider, resolver?: PopupRedirectResolver): Promise; - -// @public -export function multiFactor(user: User): MultiFactorUser; - -// @public -export interface MultiFactorAssertion { - readonly factorId: typeof FactorId[keyof typeof FactorId]; -} - -// @public -export interface MultiFactorError extends AuthError { - readonly operationType: typeof OperationType[keyof typeof OperationType]; -} - -// @public -export interface MultiFactorInfo { - readonly displayName?: string | null; - readonly enrollmentTime: string; - readonly factorId: typeof FactorId[keyof typeof FactorId]; - readonly uid: string; -} - -// @public -export interface MultiFactorResolver { - readonly hints: MultiFactorInfo[]; - resolveSignIn(assertion: MultiFactorAssertion): Promise; - readonly session: MultiFactorSession; -} - -// @public -export interface MultiFactorSession { -} - -// @public -export interface MultiFactorUser { - enroll(assertion: MultiFactorAssertion, displayName?: string | null): Promise; - readonly enrolledFactors: MultiFactorInfo[]; - getSession(): Promise; - unenroll(option: MultiFactorInfo | string): Promise; -} - -export { NextFn } - -// @public -export type NextOrObserver = NextFn | Observer; - -// @public -export class OAuthCredential extends AuthCredential { - accessToken?: string; - static fromJSON(json: string | object): OAuthCredential | null; - // Warning: (ae-forgotten-export) The symbol "OAuthCredentialParams" needs to be exported by the entry point index.doc.d.ts - // - // @internal (undocumented) - static _fromParams(params: OAuthCredentialParams): OAuthCredential; - // @internal (undocumented) - _getIdTokenResponse(auth: AuthInternal): Promise; - // @internal (undocumented) - _getReauthenticationResolver(auth: AuthInternal): Promise; - idToken?: string; - // @internal (undocumented) - _linkToIdToken(auth: AuthInternal, idToken: string): Promise; - // @internal (undocumented) - nonce?: string; - secret?: string; - toJSON(): object; -} - -// @public -export interface OAuthCredentialOptions { - accessToken?: string; - idToken?: string; - rawNonce?: string; -} - -// @public -export class OAuthProvider extends BaseOAuthProvider { - credential(params: OAuthCredentialOptions): OAuthCredential; - static credentialFromError(error: FirebaseError): OAuthCredential | null; - static credentialFromJSON(json: object | string): OAuthCredential; - static credentialFromResult(userCredential: UserCredential): OAuthCredential | null; - } - -// @public -export function onAuthStateChanged(auth: Auth, nextOrObserver: NextOrObserver, error?: ErrorFn, completed?: CompleteFn): Unsubscribe; - -// @public -export function onIdTokenChanged(auth: Auth, nextOrObserver: NextOrObserver, error?: ErrorFn, completed?: CompleteFn): Unsubscribe; - -// @public -export const OperationType: { - readonly LINK: "link"; - readonly REAUTHENTICATE: "reauthenticate"; - readonly SIGN_IN: "signIn"; -}; - -// @public -export function parseActionCodeURL(link: string): ActionCodeURL | null; - -// @public -export interface ParsedToken { - [key: string]: string | object | undefined; - 'auth_time'?: string; - 'exp'?: string; - 'firebase'?: { - 'sign_in_provider'?: string; - 'sign_in_second_factor'?: string; - }; - 'iat'?: string; - 'sub'?: string; -} - -// @public -export interface Persistence { - readonly type: 'SESSION' | 'LOCAL' | 'NONE'; -} - -// @public -export class PhoneAuthCredential extends AuthCredential { - static fromJSON(json: object | string): PhoneAuthCredential | null; - // @internal (undocumented) - static _fromTokenResponse(phoneNumber: string, temporaryProof: string): PhoneAuthCredential; - // @internal (undocumented) - static _fromVerification(verificationId: string, verificationCode: string): PhoneAuthCredential; - // @internal (undocumented) - _getIdTokenResponse(auth: AuthInternal): Promise; - // @internal (undocumented) - _getReauthenticationResolver(auth: AuthInternal): Promise; - // @internal (undocumented) - _linkToIdToken(auth: AuthInternal, idToken: string): Promise; - // Warning: (ae-forgotten-export) The symbol "SignInWithPhoneNumberRequest" needs to be exported by the entry point index.doc.d.ts - // - // @internal (undocumented) - _makeVerificationRequest(): SignInWithPhoneNumberRequest; - toJSON(): object; -} - -// @public -export class PhoneAuthProvider { - constructor(auth: Auth); - static credential(verificationId: string, verificationCode: string): PhoneAuthCredential; - static credentialFromError(error: FirebaseError): AuthCredential | null; - static credentialFromResult(userCredential: UserCredential): AuthCredential | null; - static readonly PHONE_SIGN_IN_METHOD: 'phone'; - static readonly PROVIDER_ID: 'phone'; - readonly providerId: "phone"; - verifyPhoneNumber(phoneOptions: PhoneInfoOptions | string, applicationVerifier: ApplicationVerifier): Promise; -} - -// @public -export type PhoneInfoOptions = PhoneSingleFactorInfoOptions | PhoneMultiFactorEnrollInfoOptions | PhoneMultiFactorSignInInfoOptions; - -// @public -export interface PhoneMultiFactorAssertion extends MultiFactorAssertion { -} - -// @public -export interface PhoneMultiFactorEnrollInfoOptions { - phoneNumber: string; - session: MultiFactorSession; -} - -// @public -export class PhoneMultiFactorGenerator { - static assertion(credential: PhoneAuthCredential): PhoneMultiFactorAssertion; -} - -// @public -export interface PhoneMultiFactorSignInInfoOptions { - multiFactorHint?: MultiFactorInfo; - multiFactorUid?: string; - session: MultiFactorSession; -} - -// @public -export interface PhoneSingleFactorInfoOptions { - phoneNumber: string; -} - -// @public -export interface PopupRedirectResolver { -} - -// @public -export const prodErrorMap: AuthErrorMap; - -// @public -export const ProviderId: { - readonly FACEBOOK: "facebook.com"; - readonly GITHUB: "github.com"; - readonly GOOGLE: "google.com"; - readonly PASSWORD: "password"; - readonly PHONE: "phone"; - readonly TWITTER: "twitter.com"; -}; - -// @public -export interface ReactNativeAsyncStorage { - getItem(key: string): Promise; - removeItem(key: string): Promise; - setItem(key: string, value: string): Promise; -} - -// @public -export const reactNativeLocalPersistence: Persistence; - -// @public -export function reauthenticateWithCredential(user: User, credential: AuthCredential): Promise; - -// @public -export function reauthenticateWithPhoneNumber(user: User, phoneNumber: string, appVerifier: ApplicationVerifier): Promise; - -// @public -export function reauthenticateWithPopup(user: User, provider: AuthProvider, resolver?: PopupRedirectResolver): Promise; - -// @public -export function reauthenticateWithRedirect(user: User, provider: AuthProvider, resolver?: PopupRedirectResolver): Promise; - -// @public -export interface RecaptchaParameters { - // (undocumented) - [key: string]: any; -} - -// Warning: (ae-forgotten-export) The symbol "ApplicationVerifierInternal" needs to be exported by the entry point index.doc.d.ts -// -// @public -export class RecaptchaVerifier implements ApplicationVerifierInternal { - constructor(containerOrId: HTMLElement | string, parameters: RecaptchaParameters, authExtern: Auth); - clear(): void; - // Warning: (ae-forgotten-export) The symbol "ReCaptchaLoader" needs to be exported by the entry point index.doc.d.ts - // - // @internal (undocumented) - readonly _recaptchaLoader: ReCaptchaLoader; - render(): Promise; - // @internal (undocumented) - _reset(): void; - readonly type = "recaptcha"; - verify(): Promise; - } - -// @public -export function reload(user: User): Promise; - -// Warning: (ae-forgotten-export) The symbol "FederatedAuthProvider" needs to be exported by the entry point index.doc.d.ts -// -// @public -export class SAMLAuthProvider extends FederatedAuthProvider { - constructor(providerId: string); - static credentialFromError(error: FirebaseError): AuthCredential | null; - static credentialFromJSON(json: string | object): AuthCredential; - static credentialFromResult(userCredential: UserCredential): AuthCredential | null; - } - -// @public -export function sendEmailVerification(user: User, actionCodeSettings?: ActionCodeSettings | null): Promise; - -// @public -export function sendPasswordResetEmail(auth: Auth, email: string, actionCodeSettings?: ActionCodeSettings): Promise; - -// @public -export function sendSignInLinkToEmail(auth: Auth, email: string, actionCodeSettings: ActionCodeSettings): Promise; - -// @public -export function setPersistence(auth: Auth, persistence: Persistence): Promise; - -// @public -export function signInAnonymously(auth: Auth): Promise; - -// @public -export const SignInMethod: { - readonly EMAIL_LINK: "emailLink"; - readonly EMAIL_PASSWORD: "password"; - readonly FACEBOOK: "facebook.com"; - readonly GITHUB: "github.com"; - readonly GOOGLE: "google.com"; - readonly PHONE: "phone"; - readonly TWITTER: "twitter.com"; -}; - -// @public -export function signInWithCredential(auth: Auth, credential: AuthCredential): Promise; - -// @public -export function signInWithCustomToken(auth: Auth, customToken: string): Promise; - -// @public -export function signInWithEmailAndPassword(auth: Auth, email: string, password: string): Promise; - -// @public -export function signInWithEmailLink(auth: Auth, email: string, emailLink?: string): Promise; - -// @public -export function signInWithPhoneNumber(auth: Auth, phoneNumber: string, appVerifier: ApplicationVerifier): Promise; - -// @public -export function signInWithPopup(auth: Auth, provider: AuthProvider, resolver?: PopupRedirectResolver): Promise; - -// @public -export function signInWithRedirect(auth: Auth, provider: AuthProvider, resolver?: PopupRedirectResolver): Promise; - -// @public -export function signOut(auth: Auth): Promise; - -// @public -export class TwitterAuthProvider extends BaseOAuthProvider { - constructor(); - static credential(token: string, secret: string): OAuthCredential; - static credentialFromError(error: FirebaseError): OAuthCredential | null; - static credentialFromResult(userCredential: UserCredential): OAuthCredential | null; - static readonly PROVIDER_ID: 'twitter.com'; - static readonly TWITTER_SIGN_IN_METHOD: 'twitter.com'; -} - -// @public -export function unlink(user: User, providerId: string): Promise; - -export { Unsubscribe } - -// @public -export function updateCurrentUser(auth: Auth, user: User | null): Promise; - -// @public -export function updateEmail(user: User, newEmail: string): Promise; - -// @public -export function updatePassword(user: User, newPassword: string): Promise; - -// @public -export function updatePhoneNumber(user: User, credential: PhoneAuthCredential): Promise; - -// @public -export function updateProfile(user: User, { displayName, photoURL: photoUrl }: { - displayName?: string | null; - photoURL?: string | null; -}): Promise; - -// @public -export function useDeviceLanguage(auth: Auth): void; - -// @public -export interface User extends UserInfo { - delete(): Promise; - readonly emailVerified: boolean; - getIdToken(forceRefresh?: boolean): Promise; - getIdTokenResult(forceRefresh?: boolean): Promise; - readonly isAnonymous: boolean; - readonly metadata: UserMetadata; - readonly providerData: UserInfo[]; - readonly refreshToken: string; - reload(): Promise; - readonly tenantId: string | null; - toJSON(): object; -} - -// @public -export interface UserCredential { - operationType: typeof OperationType[keyof typeof OperationType]; - providerId: string | null; - user: User; -} - -// @public -export interface UserInfo { - readonly displayName: string | null; - readonly email: string | null; - readonly phoneNumber: string | null; - readonly photoURL: string | null; - readonly providerId: string; - readonly uid: string; -} - -// @public -export interface UserMetadata { - readonly creationTime?: string; - readonly lastSignInTime?: string; -} - -// @public -export type UserProfile = Record; - -// @public -export function verifyBeforeUpdateEmail(user: User, newEmail: string, actionCodeSettings?: ActionCodeSettings | null): Promise; - -// @public -export function verifyPasswordResetCode(auth: Auth, code: string): Promise; - - -// (No @packageDocumentation comment for this package) - -``` +## API Report File for "@firebase/auth" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import { CompleteFn } from '@firebase/util'; +import { ErrorFactory } from '@firebase/util'; +import { ErrorFn } from '@firebase/util'; +import { FirebaseApp } from '@firebase/app'; +import { FirebaseError } from '@firebase/util'; +import { NextFn } from '@firebase/util'; +import { Observer } from '@firebase/util'; +import { Unsubscribe } from '@firebase/util'; + +// @public +export interface ActionCodeInfo { + data: { + email?: string | null; + multiFactorInfo?: MultiFactorInfo | null; + previousEmail?: string | null; + }; + operation: typeof ActionCodeOperation[keyof typeof ActionCodeOperation]; +} + +// @public +export const ActionCodeOperation: { + readonly EMAIL_SIGNIN: "EMAIL_SIGNIN"; + readonly PASSWORD_RESET: "PASSWORD_RESET"; + readonly RECOVER_EMAIL: "RECOVER_EMAIL"; + readonly REVERT_SECOND_FACTOR_ADDITION: "REVERT_SECOND_FACTOR_ADDITION"; + readonly VERIFY_AND_CHANGE_EMAIL: "VERIFY_AND_CHANGE_EMAIL"; + readonly VERIFY_EMAIL: "VERIFY_EMAIL"; +}; + +// @public +export interface ActionCodeSettings { + android?: { + installApp?: boolean; + minimumVersion?: string; + packageName: string; + }; + dynamicLinkDomain?: string; + handleCodeInApp?: boolean; + iOS?: { + bundleId: string; + }; + url: string; +} + +// @public +export class ActionCodeURL { + // @internal + constructor(actionLink: string); + readonly apiKey: string; + readonly code: string; + readonly continueUrl: string | null; + readonly languageCode: string | null; + readonly operation: string; + static parseLink(link: string): ActionCodeURL | null; + readonly tenantId: string | null; +} + +// @public +export interface AdditionalUserInfo { + readonly isNewUser: boolean; + readonly profile: Record | null; + readonly providerId: string | null; + readonly username?: string | null; +} + +// @public +export interface ApplicationVerifier { + readonly type: string; + verify(): Promise; +} + +// @public +export function applyActionCode(auth: Auth, oobCode: string): Promise; + +// @public +export interface Auth { + readonly config: Config; + readonly currentUser: User | null; + readonly emulatorConfig: EmulatorConfig | null; + languageCode: string | null; + readonly name: string; + onAuthStateChanged(nextOrObserver: NextOrObserver, error?: ErrorFn, completed?: CompleteFn): Unsubscribe; + onIdTokenChanged(nextOrObserver: NextOrObserver, error?: ErrorFn, completed?: CompleteFn): Unsubscribe; + setPersistence(persistence: Persistence): Promise; + readonly settings: AuthSettings; + signOut(): Promise; + tenantId: string | null; + updateCurrentUser(user: User | null): Promise; + useDeviceLanguage(): void; +} + +// @public +export class AuthCredential { + // @internal + protected constructor( + providerId: string, + signInMethod: string); + // Warning: (ae-forgotten-export) The symbol "AuthInternal" needs to be exported by the entry point index.doc.d.ts + // Warning: (ae-forgotten-export) The symbol "PhoneOrOauthTokenResponse" needs to be exported by the entry point index.doc.d.ts + // + // @internal (undocumented) + _getIdTokenResponse(_auth: AuthInternal): Promise; + // @internal (undocumented) + _getReauthenticationResolver(_auth: AuthInternal): Promise; + // Warning: (ae-forgotten-export) The symbol "IdTokenResponse" needs to be exported by the entry point index.doc.d.ts + // + // @internal (undocumented) + _linkToIdToken(_auth: AuthInternal, _idToken: string): Promise; + readonly providerId: string; + readonly signInMethod: string; + toJSON(): object; +} + +// @public +export interface AuthError extends FirebaseError { + readonly appName: string; + readonly email?: string; + readonly phoneNumber?: string; + readonly tenantid?: string; +} + +// @public +export const AuthErrorCodes: { + readonly ADMIN_ONLY_OPERATION: "auth/admin-restricted-operation"; + readonly ARGUMENT_ERROR: "auth/argument-error"; + readonly APP_NOT_AUTHORIZED: "auth/app-not-authorized"; + readonly APP_NOT_INSTALLED: "auth/app-not-installed"; + readonly CAPTCHA_CHECK_FAILED: "auth/captcha-check-failed"; + readonly CODE_EXPIRED: "auth/code-expired"; + readonly CORDOVA_NOT_READY: "auth/cordova-not-ready"; + readonly CORS_UNSUPPORTED: "auth/cors-unsupported"; + readonly CREDENTIAL_ALREADY_IN_USE: "auth/credential-already-in-use"; + readonly CREDENTIAL_MISMATCH: "auth/custom-token-mismatch"; + readonly CREDENTIAL_TOO_OLD_LOGIN_AGAIN: "auth/requires-recent-login"; + readonly DEPENDENT_SDK_INIT_BEFORE_AUTH: "auth/dependent-sdk-initialized-before-auth"; + readonly DYNAMIC_LINK_NOT_ACTIVATED: "auth/dynamic-link-not-activated"; + readonly EMAIL_CHANGE_NEEDS_VERIFICATION: "auth/email-change-needs-verification"; + readonly EMAIL_EXISTS: "auth/email-already-in-use"; + readonly EMULATOR_CONFIG_FAILED: "auth/emulator-config-failed"; + readonly EXPIRED_OOB_CODE: "auth/expired-action-code"; + readonly EXPIRED_POPUP_REQUEST: "auth/cancelled-popup-request"; + readonly INTERNAL_ERROR: "auth/internal-error"; + readonly INVALID_API_KEY: "auth/invalid-api-key"; + readonly INVALID_APP_CREDENTIAL: "auth/invalid-app-credential"; + readonly INVALID_APP_ID: "auth/invalid-app-id"; + readonly INVALID_AUTH: "auth/invalid-user-token"; + readonly INVALID_AUTH_EVENT: "auth/invalid-auth-event"; + readonly INVALID_CERT_HASH: "auth/invalid-cert-hash"; + readonly INVALID_CODE: "auth/invalid-verification-code"; + readonly INVALID_CONTINUE_URI: "auth/invalid-continue-uri"; + readonly INVALID_CORDOVA_CONFIGURATION: "auth/invalid-cordova-configuration"; + readonly INVALID_CUSTOM_TOKEN: "auth/invalid-custom-token"; + readonly INVALID_DYNAMIC_LINK_DOMAIN: "auth/invalid-dynamic-link-domain"; + readonly INVALID_EMAIL: "auth/invalid-email"; + readonly INVALID_EMULATOR_SCHEME: "auth/invalid-emulator-scheme"; + readonly INVALID_IDP_RESPONSE: "auth/invalid-credential"; + readonly INVALID_MESSAGE_PAYLOAD: "auth/invalid-message-payload"; + readonly INVALID_MFA_SESSION: "auth/invalid-multi-factor-session"; + readonly INVALID_OAUTH_CLIENT_ID: "auth/invalid-oauth-client-id"; + readonly INVALID_OAUTH_PROVIDER: "auth/invalid-oauth-provider"; + readonly INVALID_OOB_CODE: "auth/invalid-action-code"; + readonly INVALID_ORIGIN: "auth/unauthorized-domain"; + readonly INVALID_PASSWORD: "auth/wrong-password"; + readonly INVALID_PERSISTENCE: "auth/invalid-persistence-type"; + readonly INVALID_PHONE_NUMBER: "auth/invalid-phone-number"; + readonly INVALID_PROVIDER_ID: "auth/invalid-provider-id"; + readonly INVALID_RECIPIENT_EMAIL: "auth/invalid-recipient-email"; + readonly INVALID_SENDER: "auth/invalid-sender"; + readonly INVALID_SESSION_INFO: "auth/invalid-verification-id"; + readonly INVALID_TENANT_ID: "auth/invalid-tenant-id"; + readonly MFA_INFO_NOT_FOUND: "auth/multi-factor-info-not-found"; + readonly MFA_REQUIRED: "auth/multi-factor-auth-required"; + readonly MISSING_ANDROID_PACKAGE_NAME: "auth/missing-android-pkg-name"; + readonly MISSING_APP_CREDENTIAL: "auth/missing-app-credential"; + readonly MISSING_AUTH_DOMAIN: "auth/auth-domain-config-required"; + readonly MISSING_CODE: "auth/missing-verification-code"; + readonly MISSING_CONTINUE_URI: "auth/missing-continue-uri"; + readonly MISSING_IFRAME_START: "auth/missing-iframe-start"; + readonly MISSING_IOS_BUNDLE_ID: "auth/missing-ios-bundle-id"; + readonly MISSING_OR_INVALID_NONCE: "auth/missing-or-invalid-nonce"; + readonly MISSING_MFA_INFO: "auth/missing-multi-factor-info"; + readonly MISSING_MFA_SESSION: "auth/missing-multi-factor-session"; + readonly MISSING_PHONE_NUMBER: "auth/missing-phone-number"; + readonly MISSING_SESSION_INFO: "auth/missing-verification-id"; + readonly MODULE_DESTROYED: "auth/app-deleted"; + readonly NEED_CONFIRMATION: "auth/account-exists-with-different-credential"; + readonly NETWORK_REQUEST_FAILED: "auth/network-request-failed"; + readonly NULL_USER: "auth/null-user"; + readonly NO_AUTH_EVENT: "auth/no-auth-event"; + readonly NO_SUCH_PROVIDER: "auth/no-such-provider"; + readonly OPERATION_NOT_ALLOWED: "auth/operation-not-allowed"; + readonly OPERATION_NOT_SUPPORTED: "auth/operation-not-supported-in-this-environment"; + readonly POPUP_BLOCKED: "auth/popup-blocked"; + readonly POPUP_CLOSED_BY_USER: "auth/popup-closed-by-user"; + readonly PROVIDER_ALREADY_LINKED: "auth/provider-already-linked"; + readonly QUOTA_EXCEEDED: "auth/quota-exceeded"; + readonly REDIRECT_CANCELLED_BY_USER: "auth/redirect-cancelled-by-user"; + readonly REDIRECT_OPERATION_PENDING: "auth/redirect-operation-pending"; + readonly REJECTED_CREDENTIAL: "auth/rejected-credential"; + readonly SECOND_FACTOR_ALREADY_ENROLLED: "auth/second-factor-already-in-use"; + readonly SECOND_FACTOR_LIMIT_EXCEEDED: "auth/maximum-second-factor-count-exceeded"; + readonly TENANT_ID_MISMATCH: "auth/tenant-id-mismatch"; + readonly TIMEOUT: "auth/timeout"; + readonly TOKEN_EXPIRED: "auth/user-token-expired"; + readonly TOO_MANY_ATTEMPTS_TRY_LATER: "auth/too-many-requests"; + readonly UNAUTHORIZED_DOMAIN: "auth/unauthorized-continue-uri"; + readonly UNSUPPORTED_FIRST_FACTOR: "auth/unsupported-first-factor"; + readonly UNSUPPORTED_PERSISTENCE: "auth/unsupported-persistence-type"; + readonly UNSUPPORTED_TENANT_OPERATION: "auth/unsupported-tenant-operation"; + readonly UNVERIFIED_EMAIL: "auth/unverified-email"; + readonly USER_CANCELLED: "auth/user-cancelled"; + readonly USER_DELETED: "auth/user-not-found"; + readonly USER_DISABLED: "auth/user-disabled"; + readonly USER_MISMATCH: "auth/user-mismatch"; + readonly USER_SIGNED_OUT: "auth/user-signed-out"; + readonly WEAK_PASSWORD: "auth/weak-password"; + readonly WEB_STORAGE_UNSUPPORTED: "auth/web-storage-unsupported"; + readonly ALREADY_INITIALIZED: "auth/already-initialized"; +}; + +// @public +export interface AuthErrorMap { +} + +// @public +export interface AuthProvider { + readonly providerId: string; +} + +// @public +export interface AuthSettings { + appVerificationDisabledForTesting: boolean; +} + +// @public +export const browserLocalPersistence: Persistence; + +// @public +export const browserPopupRedirectResolver: PopupRedirectResolver; + +// @public +export const browserSessionPersistence: Persistence; + +// @public +export function checkActionCode(auth: Auth, oobCode: string): Promise; + +export { CompleteFn } + +// @public +export interface Config { + apiHost: string; + apiKey: string; + apiScheme: string; + authDomain?: string; + sdkClientVersion: string; + tokenApiHost: string; +} + +// @public +export interface ConfirmationResult { + confirm(verificationCode: string): Promise; + readonly verificationId: string; +} + +// @public +export function confirmPasswordReset(auth: Auth, oobCode: string, newPassword: string): Promise; + +// @public +export function connectAuthEmulator(auth: Auth, url: string, options?: { + disableWarnings: boolean; +}): void; + +// @public +export const cordovaPopupRedirectResolver: PopupRedirectResolver; + +// @public +export function createUserWithEmailAndPassword(auth: Auth, email: string, password: string): Promise; + +// @public +export type CustomParameters = Record; + +// @public +export const debugErrorMap: AuthErrorMap; + +// @public +export function deleteUser(user: User): Promise; + +// @public +export interface Dependencies { + errorMap?: AuthErrorMap; + persistence?: Persistence | Persistence[]; + popupRedirectResolver?: PopupRedirectResolver; +} + +// @public +export class EmailAuthCredential extends AuthCredential { + // @internal (undocumented) + readonly _email: string; + // @internal (undocumented) + static _fromEmailAndCode(email: string, oobCode: string, tenantId?: string | null): EmailAuthCredential; + // @internal (undocumented) + static _fromEmailAndPassword(email: string, password: string): EmailAuthCredential; + static fromJSON(json: object | string): EmailAuthCredential | null; + // @internal (undocumented) + _getIdTokenResponse(auth: AuthInternal): Promise; + // @internal (undocumented) + _getReauthenticationResolver(auth: AuthInternal): Promise; + // @internal (undocumented) + _linkToIdToken(auth: AuthInternal, idToken: string): Promise; + // @internal (undocumented) + readonly _password: string; + // @internal (undocumented) + readonly _tenantId: string | null; + toJSON(): object; +} + +// @public +export class EmailAuthProvider implements AuthProvider { + static credential(email: string, password: string): EmailAuthCredential; + static credentialWithLink(email: string, emailLink: string): EmailAuthCredential; + static readonly EMAIL_LINK_SIGN_IN_METHOD: 'emailLink'; + static readonly EMAIL_PASSWORD_SIGN_IN_METHOD: 'password'; + static readonly PROVIDER_ID: 'password'; + readonly providerId: "password"; +} + +// @public +export interface EmulatorConfig { + readonly host: string; + readonly options: { + readonly disableWarnings: boolean; + }; + readonly port: number | null; + readonly protocol: string; +} + +export { ErrorFn } + +// Warning: (ae-forgotten-export) The symbol "BaseOAuthProvider" needs to be exported by the entry point index.doc.d.ts +// +// @public +export class FacebookAuthProvider extends BaseOAuthProvider { + constructor(); + static credential(accessToken: string): OAuthCredential; + static credentialFromError(error: FirebaseError): OAuthCredential | null; + static credentialFromResult(userCredential: UserCredential): OAuthCredential | null; + static readonly FACEBOOK_SIGN_IN_METHOD: 'facebook.com'; + static readonly PROVIDER_ID: 'facebook.com'; +} + +// @public +export const FactorId: { + readonly PHONE: "phone"; +}; + +// @public +export function fetchSignInMethodsForEmail(auth: Auth, email: string): Promise; + +// @public +export function getAdditionalUserInfo(userCredential: UserCredential): AdditionalUserInfo | null; + +// @public +export function getAuth(app?: FirebaseApp): Auth; + +// @public +export function getIdToken(user: User, forceRefresh?: boolean): Promise; + +// @public +export function getIdTokenResult(user: User, forceRefresh?: boolean): Promise; + +// @public +export function getMultiFactorResolver(auth: Auth, error: MultiFactorError): MultiFactorResolver; + +// @public +export function getRedirectResult(auth: Auth, resolver?: PopupRedirectResolver): Promise; + +// @public +export class GithubAuthProvider extends BaseOAuthProvider { + constructor(); + static credential(accessToken: string): OAuthCredential; + static credentialFromError(error: FirebaseError): OAuthCredential | null; + static credentialFromResult(userCredential: UserCredential): OAuthCredential | null; + static readonly GITHUB_SIGN_IN_METHOD: 'github.com'; + static readonly PROVIDER_ID: 'github.com'; +} + +// @public +export class GoogleAuthProvider extends BaseOAuthProvider { + constructor(); + static credential(idToken?: string | null, accessToken?: string | null): OAuthCredential; + static credentialFromError(error: FirebaseError): OAuthCredential | null; + static credentialFromResult(userCredential: UserCredential): OAuthCredential | null; + static readonly GOOGLE_SIGN_IN_METHOD: 'google.com'; + static readonly PROVIDER_ID: 'google.com'; +} + +// @public +export interface IdTokenResult { + authTime: string; + claims: ParsedToken; + expirationTime: string; + issuedAtTime: string; + signInProvider: string | null; + signInSecondFactor: string | null; + token: string; +} + +// @public +export const indexedDBLocalPersistence: Persistence; + +// @public +export function initializeAuth(app: FirebaseApp, deps?: Dependencies): Auth; + +// @public +export const inMemoryPersistence: Persistence; + +// @public +export function isSignInWithEmailLink(auth: Auth, emailLink: string): boolean; + +// @public +export function linkWithCredential(user: User, credential: AuthCredential): Promise; + +// @public +export function linkWithPhoneNumber(user: User, phoneNumber: string, appVerifier: ApplicationVerifier): Promise; + +// @public +export function linkWithPopup(user: User, provider: AuthProvider, resolver?: PopupRedirectResolver): Promise; + +// @public +export function linkWithRedirect(user: User, provider: AuthProvider, resolver?: PopupRedirectResolver): Promise; + +// @public +export function multiFactor(user: User): MultiFactorUser; + +// @public +export interface MultiFactorAssertion { + readonly factorId: typeof FactorId[keyof typeof FactorId]; +} + +// @public +export interface MultiFactorError extends AuthError { + readonly operationType: typeof OperationType[keyof typeof OperationType]; +} + +// @public +export interface MultiFactorInfo { + readonly displayName?: string | null; + readonly enrollmentTime: string; + readonly factorId: typeof FactorId[keyof typeof FactorId]; + readonly uid: string; +} + +// @public +export interface MultiFactorResolver { + readonly hints: MultiFactorInfo[]; + resolveSignIn(assertion: MultiFactorAssertion): Promise; + readonly session: MultiFactorSession; +} + +// @public +export interface MultiFactorSession { +} + +// @public +export interface MultiFactorUser { + enroll(assertion: MultiFactorAssertion, displayName?: string | null): Promise; + readonly enrolledFactors: MultiFactorInfo[]; + getSession(): Promise; + unenroll(option: MultiFactorInfo | string): Promise; +} + +export { NextFn } + +// @public +export type NextOrObserver = NextFn | Observer; + +// @public +export class OAuthCredential extends AuthCredential { + accessToken?: string; + static fromJSON(json: string | object): OAuthCredential | null; + // Warning: (ae-forgotten-export) The symbol "OAuthCredentialParams" needs to be exported by the entry point index.doc.d.ts + // + // @internal (undocumented) + static _fromParams(params: OAuthCredentialParams): OAuthCredential; + // @internal (undocumented) + _getIdTokenResponse(auth: AuthInternal): Promise; + // @internal (undocumented) + _getReauthenticationResolver(auth: AuthInternal): Promise; + idToken?: string; + // @internal (undocumented) + _linkToIdToken(auth: AuthInternal, idToken: string): Promise; + // @internal (undocumented) + nonce?: string; + secret?: string; + toJSON(): object; +} + +// @public +export interface OAuthCredentialOptions { + accessToken?: string; + idToken?: string; + rawNonce?: string; +} + +// @public +export class OAuthProvider extends BaseOAuthProvider { + credential(params: OAuthCredentialOptions): OAuthCredential; + static credentialFromError(error: FirebaseError): OAuthCredential | null; + static credentialFromJSON(json: object | string): OAuthCredential; + static credentialFromResult(userCredential: UserCredential): OAuthCredential | null; + } + +// @public +export function onAuthStateChanged(auth: Auth, nextOrObserver: NextOrObserver, error?: ErrorFn, completed?: CompleteFn): Unsubscribe; + +// @public +export function onIdTokenChanged(auth: Auth, nextOrObserver: NextOrObserver, error?: ErrorFn, completed?: CompleteFn): Unsubscribe; + +// @public +export const OperationType: { + readonly LINK: "link"; + readonly REAUTHENTICATE: "reauthenticate"; + readonly SIGN_IN: "signIn"; +}; + +// @public +export function parseActionCodeURL(link: string): ActionCodeURL | null; + +// @public +export interface ParsedToken { + [key: string]: string | object | undefined; + 'auth_time'?: string; + 'exp'?: string; + 'firebase'?: { + 'sign_in_provider'?: string; + 'sign_in_second_factor'?: string; + }; + 'iat'?: string; + 'sub'?: string; +} + +// @public +export interface Persistence { + readonly type: 'SESSION' | 'LOCAL' | 'NONE'; +} + +// @public +export class PhoneAuthCredential extends AuthCredential { + static fromJSON(json: object | string): PhoneAuthCredential | null; + // @internal (undocumented) + static _fromTokenResponse(phoneNumber: string, temporaryProof: string): PhoneAuthCredential; + // @internal (undocumented) + static _fromVerification(verificationId: string, verificationCode: string): PhoneAuthCredential; + // @internal (undocumented) + _getIdTokenResponse(auth: AuthInternal): Promise; + // @internal (undocumented) + _getReauthenticationResolver(auth: AuthInternal): Promise; + // @internal (undocumented) + _linkToIdToken(auth: AuthInternal, idToken: string): Promise; + // Warning: (ae-forgotten-export) The symbol "SignInWithPhoneNumberRequest" needs to be exported by the entry point index.doc.d.ts + // + // @internal (undocumented) + _makeVerificationRequest(): SignInWithPhoneNumberRequest; + toJSON(): object; +} + +// @public +export class PhoneAuthProvider { + constructor(auth: Auth); + static credential(verificationId: string, verificationCode: string): PhoneAuthCredential; + static credentialFromError(error: FirebaseError): AuthCredential | null; + static credentialFromResult(userCredential: UserCredential): AuthCredential | null; + static readonly PHONE_SIGN_IN_METHOD: 'phone'; + static readonly PROVIDER_ID: 'phone'; + readonly providerId: "phone"; + verifyPhoneNumber(phoneOptions: PhoneInfoOptions | string, applicationVerifier: ApplicationVerifier): Promise; +} + +// @public +export type PhoneInfoOptions = PhoneSingleFactorInfoOptions | PhoneMultiFactorEnrollInfoOptions | PhoneMultiFactorSignInInfoOptions; + +// @public +export interface PhoneMultiFactorAssertion extends MultiFactorAssertion { +} + +// @public +export interface PhoneMultiFactorEnrollInfoOptions { + phoneNumber: string; + session: MultiFactorSession; +} + +// @public +export class PhoneMultiFactorGenerator { + static assertion(credential: PhoneAuthCredential): PhoneMultiFactorAssertion; +} + +// @public +export interface PhoneMultiFactorSignInInfoOptions { + multiFactorHint?: MultiFactorInfo; + multiFactorUid?: string; + session: MultiFactorSession; +} + +// @public +export interface PhoneSingleFactorInfoOptions { + phoneNumber: string; +} + +// @public +export interface PopupRedirectResolver { +} + +// @public +export const prodErrorMap: AuthErrorMap; + +// @public +export const ProviderId: { + readonly FACEBOOK: "facebook.com"; + readonly GITHUB: "github.com"; + readonly GOOGLE: "google.com"; + readonly PASSWORD: "password"; + readonly PHONE: "phone"; + readonly TWITTER: "twitter.com"; +}; + +// @public +export interface ReactNativeAsyncStorage { + getItem(key: string): Promise; + removeItem(key: string): Promise; + setItem(key: string, value: string): Promise; +} + +// @public +export const reactNativeLocalPersistence: Persistence; + +// @public +export function reauthenticateWithCredential(user: User, credential: AuthCredential): Promise; + +// @public +export function reauthenticateWithPhoneNumber(user: User, phoneNumber: string, appVerifier: ApplicationVerifier): Promise; + +// @public +export function reauthenticateWithPopup(user: User, provider: AuthProvider, resolver?: PopupRedirectResolver): Promise; + +// @public +export function reauthenticateWithRedirect(user: User, provider: AuthProvider, resolver?: PopupRedirectResolver): Promise; + +// @public +export interface RecaptchaParameters { + // (undocumented) + [key: string]: any; +} + +// Warning: (ae-forgotten-export) The symbol "ApplicationVerifierInternal" needs to be exported by the entry point index.doc.d.ts +// +// @public +export class RecaptchaVerifier implements ApplicationVerifierInternal { + constructor(containerOrId: HTMLElement | string, parameters: RecaptchaParameters, authExtern: Auth); + clear(): void; + // Warning: (ae-forgotten-export) The symbol "ReCaptchaLoader" needs to be exported by the entry point index.doc.d.ts + // + // @internal (undocumented) + readonly _recaptchaLoader: ReCaptchaLoader; + render(): Promise; + // @internal (undocumented) + _reset(): void; + readonly type = "recaptcha"; + verify(): Promise; + } + +// @public +export function reload(user: User): Promise; + +// Warning: (ae-forgotten-export) The symbol "FederatedAuthProvider" needs to be exported by the entry point index.doc.d.ts +// +// @public +export class SAMLAuthProvider extends FederatedAuthProvider { + constructor(providerId: string); + static credentialFromError(error: FirebaseError): AuthCredential | null; + static credentialFromJSON(json: string | object): AuthCredential; + static credentialFromResult(userCredential: UserCredential): AuthCredential | null; + } + +// @public +export function sendEmailVerification(user: User, actionCodeSettings?: ActionCodeSettings | null): Promise; + +// @public +export function sendPasswordResetEmail(auth: Auth, email: string, actionCodeSettings?: ActionCodeSettings): Promise; + +// @public +export function sendSignInLinkToEmail(auth: Auth, email: string, actionCodeSettings: ActionCodeSettings): Promise; + +// @public +export function setPersistence(auth: Auth, persistence: Persistence): Promise; + +// @public +export function signInAnonymously(auth: Auth): Promise; + +// @public +export const SignInMethod: { + readonly EMAIL_LINK: "emailLink"; + readonly EMAIL_PASSWORD: "password"; + readonly FACEBOOK: "facebook.com"; + readonly GITHUB: "github.com"; + readonly GOOGLE: "google.com"; + readonly PHONE: "phone"; + readonly TWITTER: "twitter.com"; +}; + +// @public +export function signInWithCredential(auth: Auth, credential: AuthCredential): Promise; + +// @public +export function signInWithCustomToken(auth: Auth, customToken: string): Promise; + +// @public +export function signInWithEmailAndPassword(auth: Auth, email: string, password: string): Promise; + +// @public +export function signInWithEmailLink(auth: Auth, email: string, emailLink?: string): Promise; + +// @public +export function signInWithPhoneNumber(auth: Auth, phoneNumber: string, appVerifier: ApplicationVerifier): Promise; + +// @public +export function signInWithPopup(auth: Auth, provider: AuthProvider, resolver?: PopupRedirectResolver): Promise; + +// @public +export function signInWithRedirect(auth: Auth, provider: AuthProvider, resolver?: PopupRedirectResolver): Promise; + +// @public +export function signOut(auth: Auth): Promise; + +// @public +export class TwitterAuthProvider extends BaseOAuthProvider { + constructor(); + static credential(token: string, secret: string): OAuthCredential; + static credentialFromError(error: FirebaseError): OAuthCredential | null; + static credentialFromResult(userCredential: UserCredential): OAuthCredential | null; + static readonly PROVIDER_ID: 'twitter.com'; + static readonly TWITTER_SIGN_IN_METHOD: 'twitter.com'; +} + +// @public +export function unlink(user: User, providerId: string): Promise; + +export { Unsubscribe } + +// @public +export function updateCurrentUser(auth: Auth, user: User | null): Promise; + +// @public +export function updateEmail(user: User, newEmail: string): Promise; + +// @public +export function updatePassword(user: User, newPassword: string): Promise; + +// @public +export function updatePhoneNumber(user: User, credential: PhoneAuthCredential): Promise; + +// @public +export function updateProfile(user: User, { displayName, photoURL: photoUrl }: { + displayName?: string | null; + photoURL?: string | null; +}): Promise; + +// @public +export function useDeviceLanguage(auth: Auth): void; + +// @public +export interface User extends UserInfo { + delete(): Promise; + readonly emailVerified: boolean; + getIdToken(forceRefresh?: boolean): Promise; + getIdTokenResult(forceRefresh?: boolean): Promise; + readonly isAnonymous: boolean; + readonly metadata: UserMetadata; + readonly providerData: UserInfo[]; + readonly refreshToken: string; + reload(): Promise; + readonly tenantId: string | null; + toJSON(): object; +} + +// @public +export interface UserCredential { + operationType: typeof OperationType[keyof typeof OperationType]; + providerId: string | null; + user: User; +} + +// @public +export interface UserInfo { + readonly displayName: string | null; + readonly email: string | null; + readonly phoneNumber: string | null; + readonly photoURL: string | null; + readonly providerId: string; + readonly uid: string; +} + +// @public +export interface UserMetadata { + readonly creationTime?: string; + readonly lastSignInTime?: string; +} + +// @public +export type UserProfile = Record; + +// @public +export function verifyBeforeUpdateEmail(user: User, newEmail: string, actionCodeSettings?: ActionCodeSettings | null): Promise; + +// @public +export function verifyPasswordResetCode(auth: Auth, code: string): Promise; + + +``` diff --git a/common/api-review/firestore-lite.api.md b/common/api-review/firestore-lite.api.md index 0d3d3c0ea18..70f9a27dba6 100644 --- a/common/api-review/firestore-lite.api.md +++ b/common/api-review/firestore-lite.api.md @@ -5,7 +5,7 @@ ```ts import { EmulatorMockTokenOptions } from '@firebase/util'; -import { FirebaseApp } from '@firebase/app-exp'; +import { FirebaseApp } from '@firebase/app'; import { LogLevelString as LogLevel } from '@firebase/logger'; // @public @@ -104,6 +104,8 @@ export class DocumentSnapshot { get ref(): DocumentReference; } +export { EmulatorMockTokenOptions } + // @public export function endAt(snapshot: DocumentSnapshot): QueryConstraint; diff --git a/common/api-review/firestore.api.md b/common/api-review/firestore.api.md index 31fd69704ca..56ee1729a4e 100644 --- a/common/api-review/firestore.api.md +++ b/common/api-review/firestore.api.md @@ -5,7 +5,7 @@ ```ts import { EmulatorMockTokenOptions } from '@firebase/util'; -import { FirebaseApp } from '@firebase/app-exp'; +import { FirebaseApp } from '@firebase/app'; import { LogLevelString as LogLevel } from '@firebase/logger'; // @public @@ -125,6 +125,8 @@ export class DocumentSnapshot { get ref(): DocumentReference; } +export { EmulatorMockTokenOptions } + // @public export function enableIndexedDbPersistence(firestore: Firestore, persistenceSettings?: PersistenceSettings): Promise; diff --git a/integration/firebase/test/namespaceDefinition.json b/integration/firebase/test/namespaceDefinition.json index 3f4bb783cc9..cc3b8f3a2e8 100644 --- a/integration/firebase/test/namespaceDefinition.json +++ b/integration/firebase/test/namespaceDefinition.json @@ -55,9 +55,6 @@ "removeApp": { "__type": "function" }, - "components": { - "__type": "Map" - }, "ErrorFactory": { "__type": "function" }, @@ -223,16 +220,10 @@ "onMessage": { "__type": "function" }, - "onTokenRefresh": { - "__type": "function" - }, - "requestPermission": { - "__type": "function" - }, "deleteToken": { "__type": "function" }, - "setBackgroundMessageHandler": { + "onBackgroundMessage": { "__type": "function" } } diff --git a/integration/firestore/firebase_export.ts b/integration/firestore/firebase_export.ts index 92d28a23709..f651bdaf839 100644 --- a/integration/firestore/firebase_export.ts +++ b/integration/firestore/firebase_export.ts @@ -15,9 +15,8 @@ * limitations under the License. */ -import firebase from '@firebase/app'; -import '@firebase/firestore'; -import '@firebase/firestore/bundle'; +import firebase from '@firebase/app-compat'; +import '@firebase/firestore-compat'; 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 deleted file mode 100644 index 063113e7409..00000000000 --- a/integration/firestore/firebase_export_memory.ts +++ /dev/null @@ -1,71 +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 firebase from '@firebase/app'; -import '@firebase/firestore/memory'; -import '@firebase/firestore/memory-bundle'; -import { FirebaseApp } from '@firebase/app-types'; -import { Settings, FirebaseFirestore } from '@firebase/firestore-types'; - -// This file replaces "packages/firestore/test/integration/util/firebase_export" -// and depends on the minified sources. - -let appCount = 0; - -export function newTestFirestore( - projectId: string, - nameOrApp?: string | FirebaseApp, - settings?: Settings -): FirebaseFirestore { - if (nameOrApp === undefined) { - nameOrApp = 'test-app-' + appCount++; - } - const app = - typeof nameOrApp === 'string' - ? firebase.initializeApp({ apiKey: 'fake-api-key', projectId }, nameOrApp) - : nameOrApp; - - const firestore = firebase.firestore(app); - if (settings) { - firestore.settings(settings); - } - return firestore; -} - -export function usesFunctionalApi(): false { - return false; -} - -const Blob = firebase.firestore.Blob; -const DocumentReference = firebase.firestore.DocumentReference; -const FieldPath = firebase.firestore.FieldPath; -const FieldValue = firebase.firestore.FieldValue; -const Firestore = firebase.firestore.Firestore; -const GeoPoint = firebase.firestore.GeoPoint; -const QueryDocumentSnapshot = firebase.firestore.QueryDocumentSnapshot; -const Timestamp = firebase.firestore.Timestamp; - -export { - Blob, - DocumentReference, - FieldPath, - FieldValue, - Firestore, - GeoPoint, - QueryDocumentSnapshot, - Timestamp -}; diff --git a/integration/firestore/gulpfile.js b/integration/firestore/gulpfile.js index 83b3b13f86f..62a0c470e9f 100644 --- a/integration/firestore/gulpfile.js +++ b/integration/firestore/gulpfile.js @@ -63,10 +63,7 @@ function copyTests() { */ /import\s+\* as firebaseExport\s+from\s+('|")[^\1]+firebase_export\1;?/, `import * as firebaseExport from '${resolve( - __dirname, - isPersistenceEnabled() - ? './firebase_export' - : './firebase_export_memory' + __dirname, './firebase_export' )}'; if (typeof process === 'undefined') { diff --git a/integration/firestore/package.json b/integration/firestore/package.json index 87e7910084c..844c101cde6 100644 --- a/integration/firestore/package.json +++ b/integration/firestore/package.json @@ -15,7 +15,7 @@ }, "devDependencies": { "@firebase/app": "0.6.30", - "@firebase/firestore": "2.4.0", + "@firebase/firestore-compat": "0.0.900", "@types/mocha": "8.2.3", "gulp": "4.0.2", "gulp-filter": "7.0.0", diff --git a/package.json b/package.json index 5b7ac2413bf..e1ffeadcdfe 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ ], "scripts": { "dev": "lerna run --parallel --scope @firebase/* --scope firebase dev", - "build": "lerna run --scope @firebase/* --scope firebase --ignore @firebase/rules-unit-testing --ignore @firebase/firestore build", + "build": "lerna run --scope @firebase/* --scope firebase --ignore @firebase/rules-unit-testing build", "build:changed": "ts-node-script scripts/ci-test/build_changed.ts", "release:prepare": "lerna run --scope @firebase/* add-compat-overloads && lerna run --scope @firebase/* typings:public", "link:packages": "lerna exec --scope @firebase/* --scope firebase -- yarn link", @@ -30,7 +30,7 @@ "repl": "node tools/repl.js", "release": "ts-node-script scripts/release/cli.ts", "pretest": "node tools/pretest.js", - "test": "lerna run --ignore firebase-repo-scripts-prune-dts --ignore @firebase/rules-unit-testing --ignore @firebase/auth --ignore @firebase/firestore --ignore @firebase/database-compat --ignore firebase-firestore-integration-test --ignore firebase-messaging-integration-test --ignore firebase-namespace-integration-test --concurrency 4 --stream test", + "test": "lerna run --ignore firebase-repo-scripts-prune-dts --ignore @firebase/rules-unit-testing --ignore @firebase/database-compat --ignore firebase-messaging-integration-test --concurrency 4 --stream test", "test:ci": "lerna run --concurrency 4 test:ci", "pretest:coverage": "mkdirp coverage", "ci:coverage": "lcov-result-merger 'packages/**/lcov.info' 'lcov-all.info'", diff --git a/packages/auth-compat/index.ts b/packages/auth-compat/index.ts index 1aecc67ce27..6ed7faf829b 100644 --- a/packages/auth-compat/index.ts +++ b/packages/auth-compat/index.ts @@ -24,11 +24,11 @@ import { ComponentType, InstantiationMode } from '@firebase/component'; +import { FirebaseError } from '@firebase/util'; import * as types from '@firebase/auth-types'; import { name, version } from './package.json'; import { Auth } from './src/auth'; -import { Persistence } from './src/persistence'; import { PhoneAuthProvider as CompatAuthProvider } from './src/phone_auth_provider'; import { RecaptchaVerifier as CompatRecaptchaVerifier } from './src/recaptcha_verifier'; @@ -106,11 +106,9 @@ function registerAuthCompat(instance: _FirebaseNamespace): void { PhoneMultiFactorGenerator: impl.PhoneMultiFactorGenerator, RecaptchaVerifier: CompatRecaptchaVerifier, TwitterAuthProvider: impl.TwitterAuthProvider, - Auth: { - Persistence - }, - AuthCredential: impl.AuthCredential - // 'Error': fireauth.AuthError + Auth, + AuthCredential: impl.AuthCredential, + Error: FirebaseError }) .setInstantiationMode(InstantiationMode.LAZY) .setMultipleInstances(false) diff --git a/packages/auth-compat/src/auth.ts b/packages/auth-compat/src/auth.ts index 648adc0a5b7..bb71ed91a90 100644 --- a/packages/auth-compat/src/auth.ts +++ b/packages/auth-compat/src/auth.ts @@ -41,6 +41,7 @@ const _assert: typeof exp._assert = exp._assert; export class Auth implements compat.FirebaseAuth, Wrapper, _FirebaseService { + static Persistence = Persistence; readonly _delegate: exp.AuthImpl; constructor(readonly app: FirebaseApp, provider: Provider<'auth'>) { diff --git a/packages/auth/index.doc.ts b/packages/auth/index.doc.ts index a89d97aa7db..8b1699f6d20 100644 --- a/packages/auth/index.doc.ts +++ b/packages/auth/index.doc.ts @@ -1,3 +1,9 @@ +/** + * Firebase Authentication + * + * @packageDocumentation + */ + /** * @license * Copyright 2021 Google LLC diff --git a/packages/firebase/package.json b/packages/firebase/package.json index 27833677549..d72f9360fe5 100644 --- a/packages/firebase/package.json +++ b/packages/firebase/package.json @@ -226,6 +226,7 @@ "@firebase/database": "0.11.0", "@firebase/database-compat": "0.0.900", "@firebase/firestore": "2.4.0", + "@firebase/firestore-compat": "0.0.900", "@firebase/functions": "0.6.15", "@firebase/functions-compat": "0.0.900", "@firebase/installations": "0.4.32", diff --git a/packages/firestore-compat/.eslintrc.js b/packages/firestore-compat/.eslintrc.js new file mode 100644 index 00000000000..844d24a0a9f --- /dev/null +++ b/packages/firestore-compat/.eslintrc.js @@ -0,0 +1,76 @@ +/** + * @license + * Copyright 2021 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. + */ + +module.exports = { + extends: '../../config/.eslintrc.js', + parserOptions: { + project: 'tsconfig.json', + // to make vscode-eslint work with monorepo + // https://github.com/typescript-eslint/typescript-eslint/issues/251#issuecomment-463943250 + tsconfigRootDir: __dirname + }, + plugins: ['import'], + rules: { + 'no-console': ['error', { allow: ['warn', 'error'] }], + '@typescript-eslint/no-unused-vars': [ + 'error', + { + varsIgnorePattern: '^_', + args: 'none' + } + ], + 'import/order': [ + 'error', + { + 'groups': [ + 'builtin', + 'external', + 'internal', + 'parent', + 'sibling', + 'index' + ], + 'newlines-between': 'always', + 'alphabetize': { 'order': 'asc', 'caseInsensitive': true } + } + ] + }, + overrides: [ + { + files: ['**/*.d.ts'], + rules: { + 'camelcase': 'off', + 'import/no-duplicates': 'off', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-unused-vars': 'off' + } + }, + { + files: ['**/*.test.ts', '**/test/**/*.ts'], + rules: { + '@typescript-eslint/no-explicit-any': 'error' + } + }, + { + files: ['scripts/*.ts'], + rules: { + 'import/no-extraneous-dependencies': 'off', + '@typescript-eslint/no-require-imports': 'off' + } + } + ] +}; diff --git a/packages/firestore-compat/README.md b/packages/firestore-compat/README.md new file mode 100644 index 00000000000..ec4c1e065c4 --- /dev/null +++ b/packages/firestore-compat/README.md @@ -0,0 +1,24 @@ +# @firebase/firestore-compat + +This is the [Cloud Firestore](https://firebase.google.com/docs/firestore/) component of the +[Firebase JS SDK](https://www.npmjs.com/package/firebase). + +**This package is not intended for direct usage, and should only be used via the officially +supported [firebase](https://www.npmjs.com/package/firebase) package.** + +If you are developing a Node.js application that requires administrative access to Cloud Firestore, +use the [`@google-cloud/firestore`](https://www.npmjs.com/package/@google-cloud/firestore) Server +SDK with your developer credentials. + +## Documentation + +For comprehensive documentation please see the [Firebase Reference +Docs][reference-docs]. + +[reference-docs]: https://firebase.google.com/docs/reference/js/ + +## Contributing +See [Contributing to the Firebase SDK](../../CONTRIBUTING.md) for general +information about contributing to the firebase-js-sdk repo and +[Contributing to the Cloud Firestore Component](./CONTRIBUTING.md) for +details specific to the Cloud Firestore code and tests. diff --git a/packages/firestore-compat/karma.conf.js b/packages/firestore-compat/karma.conf.js new file mode 100644 index 00000000000..15b6cdce68a --- /dev/null +++ b/packages/firestore-compat/karma.conf.js @@ -0,0 +1,83 @@ +/** + * @license + * Copyright 2017 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. + */ + +const karmaBase = require('../../config/karma.base'); +const { argv } = require('yargs'); + +module.exports = function (config) { + const karmaConfig = Object.assign({}, karmaBase, { + // files to load into karma + files: getTestFiles(argv), + + preprocessors: { + 'test/**/*.ts': ['webpack', 'sourcemap'] + }, + + // frameworks to use + // available frameworks: https://npmjs.org/browse/keyword/karma-adapter + frameworks: ['mocha'], + + client: Object.assign({}, karmaBase.client, { + firestoreSettings: getFirestoreSettings(argv) + }) + }); + + config.set(karmaConfig); +}; + +/** + * Gets the list of files to execute, based on existence of the + * --unit and --integration command-line arguments. + */ +function getTestFiles(argv) { + const unitTests = 'test/unit/bootstrap.ts'; + const legcayIntegrationTests = 'test/integration/bootstrap.ts'; + const liteIntegrationTests = 'test/lite/bootstrap.ts'; + if (argv.unit) { + return [unitTests]; + } else if (argv.integration) { + return [legcayIntegrationTests]; + } else if (argv.lite) { + process.env.TEST_PLATFORM = 'browser_lite'; + return [liteIntegrationTests]; + } else { + // Note that we cannot include both the firestore-exp and the legacy SDK + // as the test runners modify the global namespace cannot be both included + // in the same bundle. + return [unitTests, legcayIntegrationTests]; + } +} + +/** + * If the --local argument is passed, returns a {host, ssl} FirestoreSettings + * object that point to localhost instead of production. + */ +function getFirestoreSettings(argv) { + if (argv.local) { + return { + host: 'localhost:8080', + ssl: false + }; + } else { + return { + host: 'firestore.googleapis.com', + ssl: true + }; + } +} + +module.exports.files = getTestFiles(argv); diff --git a/packages/firestore-compat/package.json b/packages/firestore-compat/package.json new file mode 100644 index 00000000000..f746330ed4d --- /dev/null +++ b/packages/firestore-compat/package.json @@ -0,0 +1,46 @@ +{ + "name": "@firebase/firestore-compat", + "version": "0.0.900", + "description": "The Cloud Firestore component of the Firebase JS SDK.", + "author": "Firebase (https://firebase.google.com/)", + "main": "dist/index.node.cjs.js", + "react-native": "dist/index.rn.js", + "browser": "dist/index.esm2017.js", + "module": "dist/index.esm2017.js", + "esm5": "dist/index.esm5.js", + "scripts": { + "lint": "eslint -c .eslintrc.js '**/*.ts' --ignore-path '../../.gitignore'", + "lint:fix": "eslint --fix -c .eslintrc.js '**/*.ts' --ignore-path '../../.gitignore'", + "prettier": "prettier --write '*.js' '*.ts' '@(src|test)/**/*.ts'", + "build": "rollup -c ./rollup.config.js", + "build:console": "node tools/console.build.js", + "build:deps": "lerna run --scope @firebase/firestore-compat --include-dependencies build", + "build:release": "yarn build && yarn add-compat-overloads", + "test": "echo 'tested as part of firestore'", + "add-compat-overloads": "ts-node-script ../../scripts/exp/create-overloads.ts -i ../firestore/dist/index.d.ts -o dist/src/index.d.ts -a -r Firestore:types.FirebaseFirestore -r CollectionReference:types.CollectionReference -r DocumentReference:types.DocumentReference -r Query:types.Query -r FirebaseApp:FirebaseAppCompat --moduleToEnhance @firebase/firestore" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + }, + "dependencies": { + "@firebase/component": "0.5.6", + "@firebase/firestore": "2.4.0", + "@firebase/util": "1.3.0", + "@firebase/firestore-types": "2.4.0", + "tslib": "^2.1.0" + }, + "devDependencies": { + "@firebase/app-compat": "0.0.900", + "@types/eslint": "7.2.10", + "rollup": "2.52.2", + "rollup-plugin-sourcemaps": "0.6.3", + "rollup-plugin-terser": "7.0.2", + "rollup-plugin-typescript2": "0.30.0", + "@rollup/plugin-node-resolve": "11.2.0", + "ts-node": "9.1.1", + "typescript": "4.2.2" + }, + "license": "Apache-2.0", + "typings": "dist/src/index.d.ts" + } + \ No newline at end of file diff --git a/packages/firestore-compat/rollup.config.js b/packages/firestore-compat/rollup.config.js new file mode 100644 index 00000000000..4117d56023f --- /dev/null +++ b/packages/firestore-compat/rollup.config.js @@ -0,0 +1,99 @@ +/** + * @license + * Copyright 2021 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 pkg from './package.json'; +import typescriptPlugin from 'rollup-plugin-typescript2'; +import typescript from 'typescript'; +import json from '@rollup/plugin-json'; + +const util = require('../firestore/rollup.shared'); + +const deps = Object.keys({ ...pkg.peerDependencies, ...pkg.dependencies }); + +const es2017Plugins = [ + typescriptPlugin({ + typescript, + tsconfigOverride: { + compilerOptions: { + target: 'es2017' + } + }, + transformers: [util.removeAssertTransformer] + }), + json({ preferConst: true }) +]; + +const es5Plugings = [ + typescriptPlugin({ + typescript, + transformers: [util.removeAssertTransformer] + }), + json({ preferConst: true }) +]; + +const browserBuilds = [ + { + input: './src/index.ts', + output: { + file: pkg.browser, + format: 'es', + sourcemap: true + }, + plugins: es2017Plugins, + external: id => deps.some(dep => id === dep || id.startsWith(`${dep}/`)) + }, + { + input: './src/index.ts', + output: [ + { + file: pkg.esm5, + format: 'es', + sourcemap: true + } + ], + plugins: es5Plugings, + external: id => deps.some(dep => id === dep || id.startsWith(`${dep}/`)) + } +]; + +const nodeBuilds = [ + { + input: './src/index.node.ts', + output: { + file: pkg.main, + format: 'es', + sourcemap: true + }, + plugins: es2017Plugins, + external: deps + } +]; + +const rnBuilds = [ + { + input: './src/index.rn.ts', + output: { + file: pkg['react-native'], + format: 'es', + sourcemap: true + }, + plugins: es2017Plugins, + external: deps + } +]; + +export default [...browserBuilds, ...nodeBuilds, ...rnBuilds]; diff --git a/packages/firestore/src/api/blob.ts b/packages/firestore-compat/src/api/blob.ts similarity index 89% rename from packages/firestore/src/api/blob.ts rename to packages/firestore-compat/src/api/blob.ts index 37ee3f54411..e79f8220500 100644 --- a/packages/firestore/src/api/blob.ts +++ b/packages/firestore-compat/src/api/blob.ts @@ -15,17 +15,14 @@ * limitations under the License. */ +import { Bytes, FirestoreError, _isBase64Available } from '@firebase/firestore'; import { Compat } from '@firebase/util'; -import { Bytes } from '../../exp/index'; -import { isBase64Available } from '../platform/base64'; -import { Code, FirestoreError } from '../util/error'; - /** Helper function to assert Uint8Array is available at runtime. */ function assertUint8ArrayAvailable(): void { if (typeof Uint8Array === 'undefined') { throw new FirestoreError( - Code.UNIMPLEMENTED, + 'unimplemented', 'Uint8Arrays are not available in this environment.' ); } @@ -33,9 +30,9 @@ function assertUint8ArrayAvailable(): void { /** Helper function to assert Base64 functions are available at runtime. */ function assertBase64Available(): void { - if (!isBase64Available()) { + if (!_isBase64Available()) { throw new FirestoreError( - Code.UNIMPLEMENTED, + 'unimplemented', 'Blobs are unavailable in Firestore in this environment.' ); } diff --git a/packages/firestore-compat/src/api/database.ts b/packages/firestore-compat/src/api/database.ts new file mode 100644 index 00000000000..c027e9177e5 --- /dev/null +++ b/packages/firestore-compat/src/api/database.ts @@ -0,0 +1,1339 @@ +/** + * @license + * Copyright 2017 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 { FirebaseApp } from '@firebase/app-types'; +import { _FirebaseApp, FirebaseService } from '@firebase/app-types/private'; +import { + LoadBundleTask, + Bytes, + clearIndexedDbPersistence, + disableNetwork, + enableIndexedDbPersistence, + enableMultiTabIndexedDbPersistence, + enableNetwork, + ensureFirestoreConfigured, + Firestore as ExpFirestore, + connectFirestoreEmulator, + waitForPendingWrites, + FieldPath as ExpFieldPath, + limit, + limitToLast, + where, + orderBy, + startAfter, + startAt, + query, + endBefore, + endAt, + doc, + collection, + collectionGroup, + queryEqual, + Query as ExpQuery, + CollectionReference as ExpCollectionReference, + DocumentReference as ExpDocumentReference, + refEqual, + addDoc, + deleteDoc, + executeWrite, + getDoc, + getDocFromCache, + getDocFromServer, + getDocs, + getDocsFromCache, + getDocsFromServer, + onSnapshot, + onSnapshotsInSync, + setDoc, + updateDoc, + Unsubscribe, + DocumentChange as ExpDocumentChange, + DocumentSnapshot as ExpDocumentSnapshot, + QueryDocumentSnapshot as ExpQueryDocumentSnapshot, + QuerySnapshot as ExpQuerySnapshot, + snapshotEqual, + SnapshotMetadata, + runTransaction, + Transaction as ExpTransaction, + WriteBatch as ExpWriteBatch, + AbstractUserDataWriter, + FirestoreError, + FirestoreDataConverter as ModularFirestoreDataConverter, + setLogLevel as setClientLogLevel, + _DatabaseId, + _validateIsNotUsedTogether, + _cast, + _DocumentKey, + _debugAssert, + _FieldPath, + _ResourcePath, + _ByteString, + _logWarn, + namedQuery, + loadBundle, + PartialWithFieldValue, + WithFieldValue +} from '@firebase/firestore'; +import { + CollectionReference as PublicCollectionReference, + DocumentChange as PublicDocumentChange, + DocumentChangeType as PublicDocumentChangeType, + DocumentData, + DocumentData as PublicDocumentData, + DocumentReference as PublicDocumentReference, + DocumentSnapshot as PublicDocumentSnapshot, + FieldPath as PublicFieldPath, + FirebaseFirestore as PublicFirestore, + FirestoreDataConverter as PublicFirestoreDataConverter, + GetOptions as PublicGetOptions, + LogLevel as PublicLogLevel, + OrderByDirection as PublicOrderByDirection, + PersistenceSettings as PublicPersistenceSettings, + Query as PublicQuery, + QueryDocumentSnapshot as PublicQueryDocumentSnapshot, + QuerySnapshot as PublicQuerySnapshot, + SetOptions as PublicSetOptions, + Settings as PublicSettings, + SnapshotListenOptions as PublicSnapshotListenOptions, + SnapshotOptions as PublicSnapshotOptions, + Transaction as PublicTransaction, + UpdateData as PublicUpdateData, + WhereFilterOp as PublicWhereFilterOp, + WriteBatch as PublicWriteBatch +} from '@firebase/firestore-types'; +import { + Compat, + EmulatorMockTokenOptions, + getModularInstance +} from '@firebase/util'; + +import { validateSetOptions } from '../util/input_validation'; + +import { Blob } from './blob'; +import { + CompleteFn, + ErrorFn, + isPartialObserver, + NextFn, + PartialObserver +} from './observer'; + +/** + * A persistence provider for either memory-only or IndexedDB persistence. + * Mainly used to allow optional inclusion of IndexedDB code. + */ +export interface PersistenceProvider { + enableIndexedDbPersistence( + firestore: Firestore, + forceOwnership: boolean + ): Promise; + enableMultiTabIndexedDbPersistence(firestore: Firestore): Promise; + clearIndexedDbPersistence(firestore: Firestore): Promise; +} + +const MEMORY_ONLY_PERSISTENCE_ERROR_MESSAGE = + 'You are using the memory-only build of Firestore. Persistence support is ' + + 'only available via the @firebase/firestore bundle or the ' + + 'firebase-firestore.js build.'; + +/** + * The persistence provider included with the memory-only SDK. This provider + * errors for all attempts to access persistence. + */ +export class MemoryPersistenceProvider implements PersistenceProvider { + enableIndexedDbPersistence( + firestore: Firestore, + forceOwnership: boolean + ): Promise { + throw new FirestoreError( + 'failed-precondition', + MEMORY_ONLY_PERSISTENCE_ERROR_MESSAGE + ); + } + + enableMultiTabIndexedDbPersistence(firestore: Firestore): Promise { + throw new FirestoreError( + 'failed-precondition', + MEMORY_ONLY_PERSISTENCE_ERROR_MESSAGE + ); + } + + clearIndexedDbPersistence(firestore: Firestore): Promise { + throw new FirestoreError( + 'failed-precondition', + MEMORY_ONLY_PERSISTENCE_ERROR_MESSAGE + ); + } +} + +/** + * The persistence provider included with the full Firestore SDK. + */ +export class IndexedDbPersistenceProvider implements PersistenceProvider { + enableIndexedDbPersistence( + firestore: Firestore, + forceOwnership: boolean + ): Promise { + return enableIndexedDbPersistence(firestore._delegate, { forceOwnership }); + } + enableMultiTabIndexedDbPersistence(firestore: Firestore): Promise { + return enableMultiTabIndexedDbPersistence(firestore._delegate); + } + clearIndexedDbPersistence(firestore: Firestore): Promise { + return clearIndexedDbPersistence(firestore._delegate); + } +} + +/** + * Compat class for Firestore. Exposes Firestore Legacy API, but delegates + * to the functional API of firestore-exp. + */ +export class Firestore + implements PublicFirestore, FirebaseService, Compat +{ + _appCompat?: FirebaseApp; + constructor( + databaseIdOrApp: _DatabaseId | FirebaseApp, + readonly _delegate: ExpFirestore, + private _persistenceProvider: PersistenceProvider + ) { + if (!(databaseIdOrApp instanceof _DatabaseId)) { + this._appCompat = databaseIdOrApp as FirebaseApp; + } + } + + get _databaseId(): _DatabaseId { + return this._delegate._databaseId; + } + + settings(settingsLiteral: PublicSettings): void { + const currentSettings = this._delegate._getSettings(); + if ( + !settingsLiteral.merge && + currentSettings.host !== settingsLiteral.host + ) { + _logWarn( + 'You are overriding the original host. If you did not intend ' + + 'to override your settings, use {merge: true}.' + ); + } + + if (settingsLiteral.merge) { + settingsLiteral = { + ...currentSettings, + ...settingsLiteral + }; + // Remove the property from the settings once the merge is completed + delete settingsLiteral.merge; + } + + this._delegate._setSettings(settingsLiteral); + } + + useEmulator( + host: string, + port: number, + options: { + mockUserToken?: EmulatorMockTokenOptions | string; + } = {} + ): void { + connectFirestoreEmulator(this._delegate, host, port, options); + } + + enableNetwork(): Promise { + return enableNetwork(this._delegate); + } + + disableNetwork(): Promise { + return disableNetwork(this._delegate); + } + + enablePersistence(settings?: PublicPersistenceSettings): Promise { + let synchronizeTabs = false; + let experimentalForceOwningTab = false; + + if (settings) { + synchronizeTabs = !!settings.synchronizeTabs; + experimentalForceOwningTab = !!settings.experimentalForceOwningTab; + + _validateIsNotUsedTogether( + 'synchronizeTabs', + synchronizeTabs, + 'experimentalForceOwningTab', + experimentalForceOwningTab + ); + } + + return synchronizeTabs + ? this._persistenceProvider.enableMultiTabIndexedDbPersistence(this) + : this._persistenceProvider.enableIndexedDbPersistence( + this, + experimentalForceOwningTab + ); + } + + clearPersistence(): Promise { + return this._persistenceProvider.clearIndexedDbPersistence(this); + } + + terminate(): Promise { + if (this._appCompat) { + (this._appCompat as _FirebaseApp)._removeServiceInstance( + 'firestore-compat' + ); + (this._appCompat as _FirebaseApp)._removeServiceInstance('firestore'); + } + return this._delegate._delete(); + } + + waitForPendingWrites(): Promise { + return waitForPendingWrites(this._delegate); + } + + onSnapshotsInSync(observer: PartialObserver): Unsubscribe; + onSnapshotsInSync(onSync: () => void): Unsubscribe; + onSnapshotsInSync(arg: unknown): Unsubscribe { + return onSnapshotsInSync(this._delegate, arg as PartialObserver); + } + + get app(): FirebaseApp { + if (!this._appCompat) { + throw new FirestoreError( + 'failed-precondition', + "Firestore was not initialized using the Firebase SDK. 'app' is " + + 'not available' + ); + } + return this._appCompat as FirebaseApp; + } + + INTERNAL = { + delete: () => this.terminate() + }; + + collection(pathString: string): PublicCollectionReference { + try { + return new CollectionReference( + this, + collection(this._delegate, pathString) + ); + } catch (e) { + throw replaceFunctionName(e, 'collection()', 'Firestore.collection()'); + } + } + + doc(pathString: string): PublicDocumentReference { + try { + return new DocumentReference(this, doc(this._delegate, pathString)); + } catch (e) { + throw replaceFunctionName(e, 'doc()', 'Firestore.doc()'); + } + } + + collectionGroup(collectionId: string): PublicQuery { + try { + return new Query(this, collectionGroup(this._delegate, collectionId)); + } catch (e) { + throw replaceFunctionName( + e, + 'collectionGroup()', + 'Firestore.collectionGroup()' + ); + } + } + + runTransaction( + updateFunction: (transaction: PublicTransaction) => Promise + ): Promise { + return runTransaction(this._delegate, transaction => + updateFunction(new Transaction(this, transaction)) + ); + } + + batch(): PublicWriteBatch { + ensureFirestoreConfigured(this._delegate); + return new WriteBatch( + new ExpWriteBatch(this._delegate, mutations => + executeWrite(this._delegate, mutations) + ) + ); + } + + loadBundle( + bundleData: ArrayBuffer | ReadableStream | string + ): LoadBundleTask { + return loadBundle(this._delegate, bundleData); + } + + namedQuery(name: string): Promise | null> { + return namedQuery(this._delegate, name).then(expQuery => { + if (!expQuery) { + return null; + } + return new Query( + this, + // We can pass `expQuery` here directly since named queries don't have a UserDataConverter. + // Otherwise, we would have to create a new ExpQuery and pass the old UserDataConverter. + expQuery + ); + }); + } +} + +export class UserDataWriter extends AbstractUserDataWriter { + constructor(protected firestore: Firestore) { + super(); + } + + protected convertBytes(bytes: _ByteString): Blob { + return new Blob(new Bytes(bytes)); + } + + protected convertReference(name: string): DocumentReference { + const key = this.convertDocumentKey(name, this.firestore._databaseId); + return DocumentReference.forKey(key, this.firestore, /* converter= */ null); + } +} + +export function setLogLevel(level: PublicLogLevel): void { + setClientLogLevel(level); +} + +/** + * A reference to a transaction. + */ +export class Transaction implements PublicTransaction, Compat { + private _userDataWriter: UserDataWriter; + + constructor( + private readonly _firestore: Firestore, + readonly _delegate: ExpTransaction + ) { + this._userDataWriter = new UserDataWriter(_firestore); + } + + get( + documentRef: PublicDocumentReference + ): Promise> { + const ref = castReference(documentRef); + return this._delegate + .get(ref) + .then( + result => + new DocumentSnapshot( + this._firestore, + new ExpDocumentSnapshot( + this._firestore._delegate, + this._userDataWriter, + result._key, + result._document, + result.metadata, + ref.converter + ) + ) + ); + } + + set( + documentRef: DocumentReference, + data: Partial, + options: PublicSetOptions + ): Transaction; + set(documentRef: DocumentReference, data: T): Transaction; + set( + documentRef: PublicDocumentReference, + data: T | Partial, + options?: PublicSetOptions + ): Transaction { + const ref = castReference(documentRef); + if (options) { + validateSetOptions('Transaction.set', options); + this._delegate.set(ref, data as PartialWithFieldValue, options); + } else { + this._delegate.set(ref, data as WithFieldValue); + } + return this; + } + + update( + documentRef: PublicDocumentReference, + data: PublicUpdateData + ): Transaction; + update( + documentRef: PublicDocumentReference, + field: string | PublicFieldPath, + value: unknown, + ...moreFieldsAndValues: unknown[] + ): Transaction; + update( + documentRef: PublicDocumentReference, + dataOrField: unknown, + value?: unknown, + ...moreFieldsAndValues: unknown[] + ): Transaction { + const ref = castReference(documentRef); + if (arguments.length === 2) { + this._delegate.update(ref, dataOrField as PublicUpdateData); + } else { + this._delegate.update( + ref, + dataOrField as string | ExpFieldPath, + value, + ...moreFieldsAndValues + ); + } + + return this; + } + + delete(documentRef: PublicDocumentReference): Transaction { + const ref = castReference(documentRef); + this._delegate.delete(ref); + return this; + } +} + +export class WriteBatch implements PublicWriteBatch, Compat { + constructor(readonly _delegate: ExpWriteBatch) {} + set( + documentRef: DocumentReference, + data: Partial, + options: PublicSetOptions + ): WriteBatch; + set(documentRef: DocumentReference, data: T): WriteBatch; + set( + documentRef: PublicDocumentReference, + data: T | Partial, + options?: PublicSetOptions + ): WriteBatch { + const ref = castReference(documentRef); + if (options) { + validateSetOptions('WriteBatch.set', options); + this._delegate.set(ref, data as PartialWithFieldValue, options); + } else { + this._delegate.set(ref, data as WithFieldValue); + } + return this; + } + + update( + documentRef: PublicDocumentReference, + data: PublicUpdateData + ): WriteBatch; + update( + documentRef: PublicDocumentReference, + field: string | PublicFieldPath, + value: unknown, + ...moreFieldsAndValues: unknown[] + ): WriteBatch; + update( + documentRef: PublicDocumentReference, + dataOrField: string | PublicFieldPath | PublicUpdateData, + value?: unknown, + ...moreFieldsAndValues: unknown[] + ): WriteBatch { + const ref = castReference(documentRef); + if (arguments.length === 2) { + this._delegate.update(ref, dataOrField as PublicUpdateData); + } else { + this._delegate.update( + ref, + dataOrField as string | ExpFieldPath, + value, + ...moreFieldsAndValues + ); + } + return this; + } + + delete(documentRef: PublicDocumentReference): WriteBatch { + const ref = castReference(documentRef); + this._delegate.delete(ref); + return this; + } + + commit(): Promise { + return this._delegate.commit(); + } +} + +/** + * Wraps a `PublicFirestoreDataConverter` translating the types from the + * experimental SDK into corresponding types from the Classic SDK before passing + * them to the wrapped converter. + */ +class FirestoreDataConverter + implements + ModularFirestoreDataConverter, + Compat> +{ + private static readonly INSTANCES = new WeakMap(); + + private constructor( + private readonly _firestore: Firestore, + private readonly _userDataWriter: UserDataWriter, + readonly _delegate: PublicFirestoreDataConverter + ) {} + + fromFirestore( + snapshot: ExpQueryDocumentSnapshot, + options?: PublicSnapshotOptions + ): U { + const expSnapshot = new ExpQueryDocumentSnapshot( + this._firestore._delegate, + this._userDataWriter, + snapshot._key, + snapshot._document, + snapshot.metadata, + /* converter= */ null + ); + return this._delegate.fromFirestore( + new QueryDocumentSnapshot(this._firestore, expSnapshot), + options ?? {} + ); + } + + toFirestore(modelObject: WithFieldValue): PublicDocumentData; + toFirestore( + modelObject: PartialWithFieldValue, + options: PublicSetOptions + ): PublicDocumentData; + toFirestore( + modelObject: WithFieldValue | PartialWithFieldValue, + options?: PublicSetOptions + ): PublicDocumentData { + if (!options) { + return this._delegate.toFirestore(modelObject as U); + } else { + return this._delegate.toFirestore(modelObject as Partial, options); + } + } + + // Use the same instance of `FirestoreDataConverter` for the given instances + // of `Firestore` and `PublicFirestoreDataConverter` so that isEqual() will + // compare equal for two objects created with the same converter instance. + static getInstance( + firestore: Firestore, + converter: PublicFirestoreDataConverter + ): FirestoreDataConverter { + const converterMapByFirestore = FirestoreDataConverter.INSTANCES; + let untypedConverterByConverter = converterMapByFirestore.get(firestore); + if (!untypedConverterByConverter) { + untypedConverterByConverter = new WeakMap(); + converterMapByFirestore.set(firestore, untypedConverterByConverter); + } + + let instance = untypedConverterByConverter.get(converter); + if (!instance) { + instance = new FirestoreDataConverter( + firestore, + new UserDataWriter(firestore), + converter + ); + untypedConverterByConverter.set(converter, instance); + } + + return instance; + } +} + +/** + * A reference to a particular document in a collection in the database. + */ +export class DocumentReference + implements PublicDocumentReference, Compat> +{ + private _userDataWriter: UserDataWriter; + + constructor( + readonly firestore: Firestore, + readonly _delegate: ExpDocumentReference + ) { + this._userDataWriter = new UserDataWriter(firestore); + } + + static forPath( + path: _ResourcePath, + firestore: Firestore, + converter: ModularFirestoreDataConverter | null + ): DocumentReference { + if (path.length % 2 !== 0) { + throw new FirestoreError( + 'invalid-argument', + 'Invalid document reference. Document ' + + 'references must have an even number of segments, but ' + + `${path.canonicalString()} has ${path.length}` + ); + } + return new DocumentReference( + firestore, + new ExpDocumentReference( + firestore._delegate, + converter, + new _DocumentKey(path) + ) + ); + } + + static forKey( + key: _DocumentKey, + firestore: Firestore, + converter: ModularFirestoreDataConverter | null + ): DocumentReference { + return new DocumentReference( + firestore, + new ExpDocumentReference(firestore._delegate, converter, key) + ); + } + + get id(): string { + return this._delegate.id; + } + + get parent(): PublicCollectionReference { + return new CollectionReference(this.firestore, this._delegate.parent); + } + + get path(): string { + return this._delegate.path; + } + + collection( + pathString: string + ): PublicCollectionReference { + try { + return new CollectionReference( + this.firestore, + collection(this._delegate, pathString) + ); + } catch (e) { + throw replaceFunctionName( + e, + 'collection()', + 'DocumentReference.collection()' + ); + } + } + + isEqual(other: PublicDocumentReference): boolean { + other = getModularInstance>(other); + + if (!(other instanceof ExpDocumentReference)) { + return false; + } + return refEqual(this._delegate, other); + } + + set(value: Partial, options: PublicSetOptions): Promise; + set(value: T): Promise; + set(value: T | Partial, options?: PublicSetOptions): Promise { + options = validateSetOptions('DocumentReference.set', options); + try { + if (options) { + return setDoc( + this._delegate, + value as PartialWithFieldValue, + options + ); + } else { + return setDoc(this._delegate, value as WithFieldValue); + } + } catch (e) { + throw replaceFunctionName(e, 'setDoc()', 'DocumentReference.set()'); + } + } + + update(value: PublicUpdateData): Promise; + update( + field: string | PublicFieldPath, + value: unknown, + ...moreFieldsAndValues: unknown[] + ): Promise; + update( + fieldOrUpdateData: string | PublicFieldPath | PublicUpdateData, + value?: unknown, + ...moreFieldsAndValues: unknown[] + ): Promise { + try { + if (arguments.length === 1) { + return updateDoc(this._delegate, fieldOrUpdateData as PublicUpdateData); + } else { + return updateDoc( + this._delegate, + fieldOrUpdateData as string | ExpFieldPath, + value, + ...moreFieldsAndValues + ); + } + } catch (e) { + throw replaceFunctionName(e, 'updateDoc()', 'DocumentReference.update()'); + } + } + + delete(): Promise { + return deleteDoc(this._delegate); + } + + onSnapshot(observer: PartialObserver>): Unsubscribe; + onSnapshot( + options: PublicSnapshotListenOptions, + observer: PartialObserver> + ): Unsubscribe; + onSnapshot( + onNext: NextFn>, + onError?: ErrorFn, + onCompletion?: CompleteFn + ): Unsubscribe; + onSnapshot( + options: PublicSnapshotListenOptions, + onNext: NextFn>, + onError?: ErrorFn, + onCompletion?: CompleteFn + ): Unsubscribe; + + onSnapshot(...args: unknown[]): Unsubscribe { + const options = extractSnapshotOptions(args); + const observer = wrapObserver, ExpDocumentSnapshot>( + args, + result => + new DocumentSnapshot( + this.firestore, + new ExpDocumentSnapshot( + this.firestore._delegate, + this._userDataWriter, + result._key, + result._document, + result.metadata, + this._delegate.converter + ) + ) + ); + return onSnapshot(this._delegate, options, observer); + } + + get(options?: PublicGetOptions): Promise> { + let snap: Promise>; + if (options?.source === 'cache') { + snap = getDocFromCache(this._delegate); + } else if (options?.source === 'server') { + snap = getDocFromServer(this._delegate); + } else { + snap = getDoc(this._delegate); + } + + return snap.then( + result => + new DocumentSnapshot( + this.firestore, + new ExpDocumentSnapshot( + this.firestore._delegate, + this._userDataWriter, + result._key, + result._document, + result.metadata, + this._delegate.converter + ) + ) + ); + } + + withConverter(converter: null): PublicDocumentReference; + withConverter( + converter: PublicFirestoreDataConverter + ): PublicDocumentReference; + withConverter( + converter: PublicFirestoreDataConverter | null + ): PublicDocumentReference { + return new DocumentReference( + this.firestore, + converter + ? this._delegate.withConverter( + FirestoreDataConverter.getInstance(this.firestore, converter) + ) + : (this._delegate.withConverter(null) as ExpDocumentReference) + ); + } +} + +/** + * Replaces the function name in an error thrown by the firestore-exp API + * with the function names used in the classic API. + */ +function replaceFunctionName( + e: Error, + original: string | RegExp, + updated: string +): Error { + e.message = e.message.replace(original, updated); + return e; +} + +/** + * Iterates the list of arguments from an `onSnapshot` call and returns the + * first argument that may be an `SnapshotListenOptions` object. Returns an + * empty object if none is found. + */ +export function extractSnapshotOptions( + args: unknown[] +): PublicSnapshotListenOptions { + for (const arg of args) { + if (typeof arg === 'object' && !isPartialObserver(arg)) { + return arg as PublicSnapshotListenOptions; + } + } + return {}; +} + +/** + * Creates an observer that can be passed to the firestore-exp SDK. The + * observer converts all observed values into the format expected by the classic + * SDK. + * + * @param args - The list of arguments from an `onSnapshot` call. + * @param wrapper - The function that converts the firestore-exp type into the + * type used by this shim. + */ +export function wrapObserver( + args: unknown[], + wrapper: (val: ExpType) => CompatType +): PartialObserver { + let userObserver: PartialObserver; + if (isPartialObserver(args[0])) { + userObserver = args[0] as PartialObserver; + } else if (isPartialObserver(args[1])) { + userObserver = args[1]; + } else if (typeof args[0] === 'function') { + userObserver = { + next: args[0] as NextFn | undefined, + error: args[1] as ErrorFn | undefined, + complete: args[2] as CompleteFn | undefined + }; + } else { + userObserver = { + next: args[1] as NextFn | undefined, + error: args[2] as ErrorFn | undefined, + complete: args[3] as CompleteFn | undefined + }; + } + + return { + next: val => { + if (userObserver!.next) { + userObserver!.next(wrapper(val)); + } + }, + error: userObserver.error?.bind(userObserver), + complete: userObserver.complete?.bind(userObserver) + }; +} + +/** + * Options interface that can be provided to configure the deserialization of + * DocumentSnapshots. + */ +export interface SnapshotOptions extends PublicSnapshotOptions {} + +export class DocumentSnapshot + implements PublicDocumentSnapshot, Compat> +{ + constructor( + private readonly _firestore: Firestore, + readonly _delegate: ExpDocumentSnapshot + ) {} + + get ref(): DocumentReference { + return new DocumentReference(this._firestore, this._delegate.ref); + } + + get id(): string { + return this._delegate.id; + } + + get metadata(): SnapshotMetadata { + return this._delegate.metadata; + } + + get exists(): boolean { + return this._delegate.exists(); + } + + data(options?: PublicSnapshotOptions): T | undefined { + return this._delegate.data(options); + } + + get( + fieldPath: string | PublicFieldPath, + options?: PublicSnapshotOptions + // We are using `any` here to avoid an explicit cast by our users. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ): any { + return this._delegate.get(fieldPath as string | ExpFieldPath, options); + } + + isEqual(other: DocumentSnapshot): boolean { + return snapshotEqual(this._delegate, other._delegate); + } +} + +export class QueryDocumentSnapshot + extends DocumentSnapshot + implements PublicQueryDocumentSnapshot +{ + data(options?: PublicSnapshotOptions): T { + const data = this._delegate.data(options); + _debugAssert( + data !== undefined, + 'Document in a QueryDocumentSnapshot should exist' + ); + return data; + } +} + +export class Query + implements PublicQuery, Compat> +{ + private readonly _userDataWriter: UserDataWriter; + + constructor(readonly firestore: Firestore, readonly _delegate: ExpQuery) { + this._userDataWriter = new UserDataWriter(firestore); + } + + where( + fieldPath: string | _FieldPath, + opStr: PublicWhereFilterOp, + value: unknown + ): Query { + try { + // The "as string" cast is a little bit of a hack. `where` accepts the + // FieldPath Compat type as input, but is not typed as such in order to + // not expose this via our public typings file. + return new Query( + this.firestore, + query(this._delegate, where(fieldPath as string, opStr, value)) + ); + } catch (e) { + throw replaceFunctionName(e, /(orderBy|where)\(\)/, 'Query.$1()'); + } + } + + orderBy( + fieldPath: string | _FieldPath, + directionStr?: PublicOrderByDirection + ): Query { + try { + // The "as string" cast is a little bit of a hack. `orderBy` accepts the + // FieldPath Compat type as input, but is not typed as such in order to + // not expose this via our public typings file. + return new Query( + this.firestore, + query(this._delegate, orderBy(fieldPath as string, directionStr)) + ); + } catch (e) { + throw replaceFunctionName(e, /(orderBy|where)\(\)/, 'Query.$1()'); + } + } + + limit(n: number): Query { + try { + return new Query(this.firestore, query(this._delegate, limit(n))); + } catch (e) { + throw replaceFunctionName(e, 'limit()', 'Query.limit()'); + } + } + + limitToLast(n: number): Query { + try { + return new Query( + this.firestore, + query(this._delegate, limitToLast(n)) + ); + } catch (e) { + throw replaceFunctionName(e, 'limitToLast()', 'Query.limitToLast()'); + } + } + + startAt(...args: any[]): Query { + try { + return new Query(this.firestore, query(this._delegate, startAt(...args))); + } catch (e) { + throw replaceFunctionName(e, 'startAt()', 'Query.startAt()'); + } + } + + startAfter(...args: any[]): Query { + try { + return new Query( + this.firestore, + query(this._delegate, startAfter(...args)) + ); + } catch (e) { + throw replaceFunctionName(e, 'startAfter()', 'Query.startAfter()'); + } + } + + endBefore(...args: any[]): Query { + try { + return new Query( + this.firestore, + query(this._delegate, endBefore(...args)) + ); + } catch (e) { + throw replaceFunctionName(e, 'endBefore()', 'Query.endBefore()'); + } + } + + endAt(...args: any[]): Query { + try { + return new Query(this.firestore, query(this._delegate, endAt(...args))); + } catch (e) { + throw replaceFunctionName(e, 'endAt()', 'Query.endAt()'); + } + } + + isEqual(other: PublicQuery): boolean { + return queryEqual(this._delegate, (other as Query)._delegate); + } + + get(options?: PublicGetOptions): Promise> { + let query: Promise>; + if (options?.source === 'cache') { + query = getDocsFromCache(this._delegate); + } else if (options?.source === 'server') { + query = getDocsFromServer(this._delegate); + } else { + query = getDocs(this._delegate); + } + return query.then( + result => + new QuerySnapshot( + this.firestore, + new ExpQuerySnapshot( + this.firestore._delegate, + this._userDataWriter, + this._delegate, + result._snapshot + ) + ) + ); + } + + onSnapshot(observer: PartialObserver>): Unsubscribe; + onSnapshot( + options: PublicSnapshotListenOptions, + observer: PartialObserver> + ): Unsubscribe; + onSnapshot( + onNext: NextFn>, + onError?: ErrorFn, + onCompletion?: CompleteFn + ): Unsubscribe; + onSnapshot( + options: PublicSnapshotListenOptions, + onNext: NextFn>, + onError?: ErrorFn, + onCompletion?: CompleteFn + ): Unsubscribe; + + onSnapshot(...args: unknown[]): Unsubscribe { + const options = extractSnapshotOptions(args); + const observer = wrapObserver, ExpQuerySnapshot>( + args, + snap => + new QuerySnapshot( + this.firestore, + new ExpQuerySnapshot( + this.firestore._delegate, + this._userDataWriter, + this._delegate, + snap._snapshot + ) + ) + ); + return onSnapshot(this._delegate, options, observer); + } + + withConverter(converter: null): Query; + withConverter(converter: PublicFirestoreDataConverter): Query; + withConverter( + converter: PublicFirestoreDataConverter | null + ): Query { + return new Query( + this.firestore, + converter + ? this._delegate.withConverter( + FirestoreDataConverter.getInstance(this.firestore, converter) + ) + : (this._delegate.withConverter(null) as ExpQuery) + ); + } +} + +export class DocumentChange + implements PublicDocumentChange, Compat> +{ + constructor( + private readonly _firestore: Firestore, + readonly _delegate: ExpDocumentChange + ) {} + + get type(): PublicDocumentChangeType { + return this._delegate.type; + } + + get doc(): QueryDocumentSnapshot { + return new QueryDocumentSnapshot(this._firestore, this._delegate.doc); + } + + get oldIndex(): number { + return this._delegate.oldIndex; + } + + get newIndex(): number { + return this._delegate.newIndex; + } +} + +export class QuerySnapshot + implements PublicQuerySnapshot, Compat> +{ + constructor( + readonly _firestore: Firestore, + readonly _delegate: ExpQuerySnapshot + ) {} + + get query(): Query { + return new Query(this._firestore, this._delegate.query); + } + + get metadata(): SnapshotMetadata { + return this._delegate.metadata; + } + + get size(): number { + return this._delegate.size; + } + + get empty(): boolean { + return this._delegate.empty; + } + + get docs(): Array> { + return this._delegate.docs.map( + doc => new QueryDocumentSnapshot(this._firestore, doc) + ); + } + + docChanges( + options?: PublicSnapshotListenOptions + ): Array> { + return this._delegate + .docChanges(options) + .map(docChange => new DocumentChange(this._firestore, docChange)); + } + + forEach( + callback: (result: QueryDocumentSnapshot) => void, + thisArg?: unknown + ): void { + this._delegate.forEach(snapshot => { + callback.call( + thisArg, + new QueryDocumentSnapshot(this._firestore, snapshot) + ); + }); + } + + isEqual(other: QuerySnapshot): boolean { + return snapshotEqual(this._delegate, other._delegate); + } +} + +export class CollectionReference + extends Query + implements PublicCollectionReference +{ + constructor( + readonly firestore: Firestore, + readonly _delegate: ExpCollectionReference + ) { + super(firestore, _delegate); + } + + get id(): string { + return this._delegate.id; + } + + get path(): string { + return this._delegate.path; + } + + get parent(): DocumentReference | null { + const docRef = this._delegate.parent; + return docRef ? new DocumentReference(this.firestore, docRef) : null; + } + + doc(documentPath?: string): DocumentReference { + try { + if (documentPath === undefined) { + // Call `doc` without `documentPath` if `documentPath` is `undefined` + // as `doc` validates the number of arguments to prevent users from + // accidentally passing `undefined`. + return new DocumentReference(this.firestore, doc(this._delegate)); + } else { + return new DocumentReference( + this.firestore, + doc(this._delegate, documentPath) + ); + } + } catch (e) { + throw replaceFunctionName(e, 'doc()', 'CollectionReference.doc()'); + } + } + + add(data: T): Promise> { + return addDoc(this._delegate, data as WithFieldValue).then( + docRef => new DocumentReference(this.firestore, docRef) + ); + } + + isEqual(other: CollectionReference): boolean { + return refEqual(this._delegate, other._delegate); + } + + withConverter(converter: null): CollectionReference; + withConverter( + converter: PublicFirestoreDataConverter + ): CollectionReference; + withConverter( + converter: PublicFirestoreDataConverter | null + ): CollectionReference { + return new CollectionReference( + this.firestore, + converter + ? this._delegate.withConverter( + FirestoreDataConverter.getInstance(this.firestore, converter) + ) + : (this._delegate.withConverter(null) as ExpCollectionReference) + ); + } +} + +function castReference( + documentRef: PublicDocumentReference +): ExpDocumentReference { + return _cast>(documentRef, ExpDocumentReference); +} diff --git a/packages/firestore-compat/src/api/field_path.ts b/packages/firestore-compat/src/api/field_path.ts new file mode 100644 index 00000000000..a236077d496 --- /dev/null +++ b/packages/firestore-compat/src/api/field_path.ts @@ -0,0 +1,64 @@ +/** + * @license + * Copyright 2017 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 { + FieldPath as ExpFieldPath, + _FieldPath as InternalFieldPath +} from '@firebase/firestore'; +import { FieldPath as PublicFieldPath } from '@firebase/firestore-types'; +import { Compat, getModularInstance } from '@firebase/util'; + +// The objects that are a part of this API are exposed to third-parties as +// compiled javascript so we want to flag our private members with a leading +// underscore to discourage their use. + +/** + * A `FieldPath` refers to a field in a document. The path may consist of a + * single field name (referring to a top-level field in the document), or a list + * of field names (referring to a nested field in the document). + */ +export class FieldPath implements PublicFieldPath, Compat { + readonly _delegate: ExpFieldPath; + /** + * Creates a FieldPath from the provided field names. If more than one field + * name is provided, the path will point to a nested field in a document. + * + * @param fieldNames - A list of field names. + */ + constructor(...fieldNames: string[]) { + this._delegate = new ExpFieldPath(...fieldNames); + } + + static documentId(): FieldPath { + /** + * Internal Note: The backend doesn't technically support querying by + * document ID. Instead it queries by the entire document name (full path + * included), but in the cases we currently support documentId(), the net + * effect is the same. + */ + return new FieldPath(InternalFieldPath.keyField().canonicalString()); + } + + isEqual(other: PublicFieldPath): boolean { + other = getModularInstance(other); + + if (!(other instanceof ExpFieldPath)) { + return false; + } + return this._delegate._internalPath.isEqual(other._internalPath); + } +} diff --git a/packages/firestore-compat/src/api/field_value.ts b/packages/firestore-compat/src/api/field_value.ts new file mode 100644 index 00000000000..25cb4c81242 --- /dev/null +++ b/packages/firestore-compat/src/api/field_value.ts @@ -0,0 +1,65 @@ +/** + * @license + * Copyright 2017 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 { + arrayRemove, + arrayUnion, + deleteField, + FieldValue as FieldValue1, + increment, + serverTimestamp +} from '@firebase/firestore'; +import { FieldValue as PublicFieldValue } from '@firebase/firestore-types'; +import { Compat } from '@firebase/util'; + +export class FieldValue implements PublicFieldValue, Compat { + static serverTimestamp(): FieldValue { + const delegate = serverTimestamp(); + delegate._methodName = 'FieldValue.serverTimestamp'; + return new FieldValue(delegate); + } + + static delete(): FieldValue { + const delegate = deleteField(); + delegate._methodName = 'FieldValue.delete'; + return new FieldValue(delegate); + } + + static arrayUnion(...elements: unknown[]): FieldValue { + const delegate = arrayUnion(...elements); + delegate._methodName = 'FieldValue.arrayUnion'; + return new FieldValue(delegate); + } + + static arrayRemove(...elements: unknown[]): FieldValue { + const delegate = arrayRemove(...elements); + delegate._methodName = 'FieldValue.arrayRemove'; + return new FieldValue(delegate); + } + + static increment(n: number): FieldValue { + const delegate = increment(n); + delegate._methodName = 'FieldValue.increment'; + return new FieldValue(delegate); + } + + constructor(readonly _delegate: FieldValue1) {} + + isEqual(other: FieldValue): boolean { + return this._delegate.isEqual(other._delegate); + } +} diff --git a/packages/firestore/src/exp/geo_point.ts b/packages/firestore-compat/src/api/geo_point.ts similarity index 88% rename from packages/firestore/src/exp/geo_point.ts rename to packages/firestore-compat/src/api/geo_point.ts index ffcd008a807..2831e048330 100644 --- a/packages/firestore/src/exp/geo_point.ts +++ b/packages/firestore-compat/src/api/geo_point.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2020 Google LLC + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,4 +15,4 @@ * limitations under the License. */ -export { GeoPoint } from '../lite/geo_point'; +export { GeoPoint } from '@firebase/firestore'; diff --git a/packages/firestore-compat/src/api/observer.ts b/packages/firestore-compat/src/api/observer.ts new file mode 100644 index 00000000000..93218010028 --- /dev/null +++ b/packages/firestore-compat/src/api/observer.ts @@ -0,0 +1,54 @@ +/** + * @license + * Copyright 2017 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 { FirestoreError } from '@firebase/firestore'; + +/** + * Observer/Subscribe interfaces. + */ +export type NextFn = (value: T) => void; +export type ErrorFn = (error: FirestoreError) => void; +export type CompleteFn = () => void; + +// Allow for any of the Observer methods to be undefined. +export interface PartialObserver { + next?: NextFn; + error?: ErrorFn; + complete?: CompleteFn; +} + +export function isPartialObserver(obj: unknown): obj is PartialObserver { + return implementsAnyMethods(obj, ['next', 'error', 'complete']); +} + +/** + * Returns true if obj is an object and contains at least one of the specified + * methods. + */ +function implementsAnyMethods(obj: unknown, methods: string[]): boolean { + if (typeof obj !== 'object' || obj === null) { + return false; + } + + const object = obj as Record; + for (const method of methods) { + if (method in object && typeof object[method] === 'function') { + return true; + } + } + return false; +} diff --git a/packages/firestore/src/exp/timestamp.ts b/packages/firestore-compat/src/api/timestamp.ts similarity index 88% rename from packages/firestore/src/exp/timestamp.ts rename to packages/firestore-compat/src/api/timestamp.ts index f0de0029048..e983fbbe466 100644 --- a/packages/firestore/src/exp/timestamp.ts +++ b/packages/firestore-compat/src/api/timestamp.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2020 Google LLC + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,4 +15,4 @@ * limitations under the License. */ -export { Timestamp } from '../lite/timestamp'; +export { Timestamp } from '@firebase/firestore'; diff --git a/packages/firestore/compat/config.ts b/packages/firestore-compat/src/config.ts similarity index 80% rename from packages/firestore/compat/config.ts rename to packages/firestore-compat/src/config.ts index 295ecdc8ecc..29dea55d0a8 100644 --- a/packages/firestore/compat/config.ts +++ b/packages/firestore-compat/src/config.ts @@ -20,14 +20,14 @@ import { FirebaseApp } from '@firebase/app-compat'; import { FirebaseNamespace } from '@firebase/app-types'; import { _FirebaseNamespace } from '@firebase/app-types/private'; import { Component, ComponentType } from '@firebase/component'; - import { - Firestore as FirebaseFirestore, + Firestore as ModularFirestore, CACHE_SIZE_UNLIMITED, GeoPoint, Timestamp -} from '../exp/index'; // import from the exp public API -import { Blob } from '../src/api/blob'; +} from '@firebase/firestore'; // import from the exp public API + +import { Blob } from './api/blob'; import { Firestore, Transaction, @@ -39,9 +39,9 @@ import { QuerySnapshot, WriteBatch, setLogLevel -} from '../src/api/database'; -import { FieldPath } from '../src/api/field_path'; -import { FieldValue } from '../src/api/field_value'; +} from './api/database'; +import { FieldPath } from './api/field_path'; +import { FieldValue } from './api/field_value'; const firestoreNamespace = { Firestore, @@ -62,12 +62,6 @@ const firestoreNamespace = { CACHE_SIZE_UNLIMITED }; -declare module '@firebase/component' { - interface NameServiceMapping { - 'firestore-compat': Firestore; - } -} - /** * Configures Firestore as part of the Firebase SDK by calling registerComponent. * @@ -79,7 +73,7 @@ export function configureForFirebase( firebase: FirebaseNamespace, firestoreFactory: ( app: FirebaseApp, - firestoreExp: FirebaseFirestore + firestoreExp: ModularFirestore ) => Firestore ): void { (firebase as _FirebaseNamespace).INTERNAL.registerComponent( @@ -87,9 +81,7 @@ export function configureForFirebase( 'firestore-compat', container => { const app = container.getProvider('app-compat').getImmediate()!; - const firestoreExp = container - .getProvider('firestore-exp') - .getImmediate()!; + const firestoreExp = container.getProvider('firestore').getImmediate()!; return firestoreFactory(app, firestoreExp); }, ComponentType.PUBLIC diff --git a/packages/firestore/index.console.ts b/packages/firestore-compat/src/index.console.ts similarity index 72% rename from packages/firestore/index.console.ts rename to packages/firestore-compat/src/index.console.ts index cf2c827ad5d..8edf89aa317 100644 --- a/packages/firestore/index.console.ts +++ b/packages/firestore-compat/src/index.console.ts @@ -17,26 +17,29 @@ import { FirebaseAuthInternalName } from '@firebase/auth-interop-types'; import { Provider } from '@firebase/component'; +import { + _DatabaseId, + Firestore as FirestoreExp, + FirestoreError +} from '@firebase/firestore'; import { Firestore as FirestoreCompat, MemoryPersistenceProvider -} from './src/api/database'; -import { DatabaseId } from './src/core/database_info'; -import { Firestore as FirestoreExp } from './src/exp/database'; -import { Code, FirestoreError } from './src/util/error'; +} from './api/database'; +import { EmptyCredentialsProvider } from './src/api/credentials'; + export { CollectionReference, DocumentReference, DocumentSnapshot, QuerySnapshot -} from './src/api/database'; - -export { Blob } from './src/api/blob'; -export { GeoPoint } from './src/api/geo_point'; -export { FieldPath } from './src/api/field_path'; -export { FieldValue } from './src/api/field_value'; -export { Timestamp } from './src/api/timestamp'; +} from './api/database'; +export { Blob } from './api/blob'; +export { GeoPoint } from './api/geo_point'; +export { FieldPath } from './api/field_path'; +export { FieldValue } from './api/field_value'; +export { Timestamp } from './api/timestamp'; export interface FirestoreDatabase { projectId: string; @@ -53,7 +56,7 @@ export class Firestore extends FirestoreCompat { databaseIdFromFirestoreDatabase(firestoreDatabase), new FirestoreExp( databaseIdFromFirestoreDatabase(firestoreDatabase), - authProvider + new EmptyCredentialsProvider() ), new MemoryPersistenceProvider() ); @@ -62,11 +65,11 @@ export class Firestore extends FirestoreCompat { function databaseIdFromFirestoreDatabase( firestoreDatabase: FirestoreDatabase -): DatabaseId { +): _DatabaseId { if (!firestoreDatabase.projectId) { - throw new FirestoreError(Code.INVALID_ARGUMENT, 'Must provide projectId'); + throw new FirestoreError('invalid-argument', 'Must provide projectId'); } - return new DatabaseId( + return new _DatabaseId( firestoreDatabase.projectId, firestoreDatabase.database ); diff --git a/packages/firestore/compat/index.node.ts b/packages/firestore-compat/src/index.node.ts similarity index 78% rename from packages/firestore/compat/index.node.ts rename to packages/firestore-compat/src/index.node.ts index 21ecd90dbf1..38c7954bf8c 100644 --- a/packages/firestore/compat/index.node.ts +++ b/packages/firestore-compat/src/index.node.ts @@ -19,19 +19,16 @@ import firebase from '@firebase/app-compat'; import { FirebaseNamespace } from '@firebase/app-types'; -import { Firestore, IndexedDbPersistenceProvider } from '../src/api/database'; -import { setSDKVersion } from '../src/core/version'; +import { name, version } from '../package.json'; -import { registerBundle } from './bundle'; +import { Firestore, IndexedDbPersistenceProvider } from './api/database'; import { configureForFirebase } from './config'; -import { name, version } from './package.json'; /** * Registers the main Firestore Node build with the components framework. * Persistence can be enabled via `firebase.firestore().enablePersistence()`. */ export function registerFirestore(instance: FirebaseNamespace): void { - setSDKVersion(instance.SDK_VERSION); configureForFirebase( instance, (app, firestoreExp) => @@ -40,5 +37,4 @@ export function registerFirestore(instance: FirebaseNamespace): void { instance.registerVersion(name, version, 'node'); } -registerFirestore((firebase as unknown) as FirebaseNamespace); -registerBundle(Firestore); +registerFirestore(firebase as unknown as FirebaseNamespace); diff --git a/packages/firestore/compat/index.rn.ts b/packages/firestore-compat/src/index.rn.ts similarity index 78% rename from packages/firestore/compat/index.rn.ts rename to packages/firestore-compat/src/index.rn.ts index 32f2e0dab4a..6b28e6c5e94 100644 --- a/packages/firestore/compat/index.rn.ts +++ b/packages/firestore-compat/src/index.rn.ts @@ -19,18 +19,15 @@ import firebase from '@firebase/app-compat'; import { FirebaseNamespace } from '@firebase/app-types'; -import { Firestore, IndexedDbPersistenceProvider } from '../src/api/database'; -import { setSDKVersion } from '../src/core/version'; +import { name, version } from '../package.json'; -import { registerBundle } from './bundle'; +import { Firestore, IndexedDbPersistenceProvider } from './api/database'; import { configureForFirebase } from './config'; -import { name, version } from './package.json'; /** * Registers the main Firestore ReactNative build with the components framework. * Persistence can be enabled via `firebase.firestore().enablePersistence()`. */ export function registerFirestore(instance: FirebaseNamespace): void { - setSDKVersion(instance.SDK_VERSION); configureForFirebase( instance, (app, firestoreExp) => @@ -39,5 +36,4 @@ export function registerFirestore(instance: FirebaseNamespace): void { instance.registerVersion(name, version, 'rn'); } -registerFirestore((firebase as unknown) as FirebaseNamespace); -registerBundle(Firestore); +registerFirestore(firebase as unknown as FirebaseNamespace); diff --git a/packages/firestore/compat/index.ts b/packages/firestore-compat/src/index.ts similarity index 85% rename from packages/firestore/compat/index.ts rename to packages/firestore-compat/src/index.ts index 9084fe34150..5b1eedd5524 100644 --- a/packages/firestore/compat/index.ts +++ b/packages/firestore-compat/src/index.ts @@ -20,21 +20,18 @@ import firebase from '@firebase/app-compat'; import { FirebaseNamespace } from '@firebase/app-types'; import * as types from '@firebase/firestore-types'; -import { Firestore, IndexedDbPersistenceProvider } from '../src/api/database'; -import { setSDKVersion } from '../src/core/version'; +import { name, version } from '../package.json'; -import { registerBundle } from './bundle'; +import { Firestore, IndexedDbPersistenceProvider } from './api/database'; import { configureForFirebase } from './config'; -import { name, version } from './package.json'; -import '../register-module'; +import './register-module'; /** * Registers the main Firestore build with the components framework. * Persistence can be enabled via `firebase.firestore().enablePersistence()`. */ export function registerFirestore(instance: FirebaseNamespace): void { - setSDKVersion(instance.SDK_VERSION); configureForFirebase( instance, (app, firestoreExp) => @@ -43,8 +40,7 @@ export function registerFirestore(instance: FirebaseNamespace): void { instance.registerVersion(name, version); } -registerFirestore((firebase as unknown) as FirebaseNamespace); -registerBundle(Firestore); +registerFirestore(firebase as unknown as FirebaseNamespace); declare module '@firebase/app-compat' { interface FirebaseNamespace { diff --git a/packages/firestore/register-module.ts b/packages/firestore-compat/src/register-module.ts similarity index 97% rename from packages/firestore/register-module.ts rename to packages/firestore-compat/src/register-module.ts index f76fabd5a1d..7bf6e087ce4 100644 --- a/packages/firestore/register-module.ts +++ b/packages/firestore-compat/src/register-module.ts @@ -17,7 +17,7 @@ import * as types from '@firebase/firestore-types'; -declare module '@firebase/app-types' { +declare module '@firebase/app-compat' { interface FirebaseNamespace { firestore: { (app?: FirebaseApp): types.FirebaseFirestore; diff --git a/packages/firestore-compat/src/util/input_validation.ts b/packages/firestore-compat/src/util/input_validation.ts new file mode 100644 index 00000000000..8a60a6955f9 --- /dev/null +++ b/packages/firestore-compat/src/util/input_validation.ts @@ -0,0 +1,40 @@ +/** + * @license + * Copyright 2021 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 { FirestoreError } from '@firebase/firestore'; +import { SetOptions } from '@firebase/firestore-types'; + +export function validateSetOptions( + methodName: string, + options: SetOptions | undefined +): SetOptions { + if (options === undefined) { + return { + merge: false + }; + } + + if (options.mergeFields !== undefined && options.merge !== undefined) { + throw new FirestoreError( + 'invalid-argument', + `Invalid options passed to function ${methodName}(): You cannot ` + + 'specify both "merge" and "mergeFields".' + ); + } + + return options; +} diff --git a/packages/firestore/tools/console.build.js b/packages/firestore-compat/tools/console.build.js similarity index 56% rename from packages/firestore/tools/console.build.js rename to packages/firestore-compat/tools/console.build.js index 2a7e6de38c1..706cc19438e 100644 --- a/packages/firestore/tools/console.build.js +++ b/packages/firestore-compat/tools/console.build.js @@ -1,6 +1,6 @@ /** * @license - * Copyright 2019 Google LLC + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,49 +22,37 @@ const rollup = require('rollup'); const { uglify } = require('rollup-plugin-uglify'); const { nodeResolve } = require('@rollup/plugin-node-resolve'); +const typescriptPlugin = require('rollup-plugin-typescript2'); +const typescript = require('typescript'); +const json = require('@rollup/plugin-json'); const fs = require('fs'); const util = require('util'); const fs_writeFile = util.promisify(fs.writeFile); - -const rollupUtil = require('../rollup.shared'); +const rollupUtil = require('../../firestore/rollup.shared'); const EXPORTNAME = '__firestore_exports__'; +const OUTPUT_FOLDER = 'dist'; +const OUTPUT_FILE = 'standalone.js'; -const esm2017OutputFile = 'dist/standalone.esm2017.js'; -const esm5OutputFile = 'dist/standalone.js'; - -const es2017InputOptions = { - input: 'index.console.ts', - plugins: rollupUtil.es2017Plugins('browser', /* mangled= */ true), - external: rollupUtil.resolveBrowserExterns, - treeshake: { - moduleSideEffects: false - } -}; - -const es2017OutputOptions = { - file: esm2017OutputFile, - format: 'es' -}; - -const es2017toEs5InputOptions = { - input: esm2017OutputFile, +const es5InputOptions = { + input: 'src/index.console.ts', plugins: [ nodeResolve(), - ...rollupUtil.es2017ToEs5Plugins(/* mangled= */ true), + typescriptPlugin({ + typescript, + transformers: [rollupUtil.removeAssertTransformer] + }), + json({ preferConst: true }), uglify({ output: { ascii_only: true // escape unicode chars } }) - ], - treeshake: { - moduleSideEffects: false - } -}; + ] +} -const es2017toEs5OutputOptions = { - file: esm5OutputFile, +const es5OutputOptions = { + file: `${OUTPUT_FOLDER}/${OUTPUT_FILE}`, name: EXPORTNAME, format: 'iife' }; @@ -76,18 +64,18 @@ exports = eval(`; const POSTFIX = ` + '${EXPORTNAME};');`; async function build() { - // Create an ES2017 bundle - const es2017Bundle = await rollup.rollup(es2017InputOptions); - await es2017Bundle.write(es2017OutputOptions); - - // Transpile down to ES5 - const es5Bundle = await rollup.rollup(es2017toEs5InputOptions); + const es5Bundle = await rollup.rollup(es5InputOptions); const { output: [{ code }] - } = await es5Bundle.generate(es2017toEs5OutputOptions); + } = await es5Bundle.generate(es5OutputOptions); const output = `${PREFIX}${JSON.stringify(String(code))}${POSTFIX}`; - await fs_writeFile(es2017toEs5OutputOptions.file, output, 'utf-8'); + + if (!fs.existsSync(OUTPUT_FOLDER)) { + fs.mkdirSync(OUTPUT_FOLDER); + } + + await fs_writeFile(es5OutputOptions.file, output, 'utf-8'); } build(); diff --git a/packages/firestore-compat/tsconfig.json b/packages/firestore-compat/tsconfig.json new file mode 100644 index 00000000000..09f747b4d46 --- /dev/null +++ b/packages/firestore-compat/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../config/tsconfig.base.json", + "compilerOptions": { + "outDir": "dist" + }, + "exclude": [ + "dist/**/*" + ] +} diff --git a/packages/firestore-types/index.d.ts b/packages/firestore-types/index.d.ts index 7085d4eaef7..d198fd78866 100644 --- a/packages/firestore-types/index.d.ts +++ b/packages/firestore-types/index.d.ts @@ -507,6 +507,6 @@ export interface FirestoreError { declare module '@firebase/component' { interface NameServiceMapping { - 'firestore': FirebaseFirestore; + 'firestore-compat': FirebaseFirestore; } } diff --git a/packages/firestore/.eslintrc.js b/packages/firestore/.eslintrc.js index 4d43fc80013..5dd443333d9 100644 --- a/packages/firestore/.eslintrc.js +++ b/packages/firestore/.eslintrc.js @@ -24,6 +24,7 @@ module.exports = { tsconfigRootDir: __dirname }, plugins: ['import'], + ignorePatterns: ['compat/*'], rules: { 'no-console': ['error', { allow: ['warn', 'error'] }], '@typescript-eslint/no-unused-vars': [ @@ -82,20 +83,6 @@ module.exports = { 'import/no-extraneous-dependencies': 'off', '@typescript-eslint/no-require-imports': 'off' } - }, - // TODO(firestorelite): Remove this exception when app-exp is published - { - files: ['lite/**/*.ts'], - rules: { - 'import/no-extraneous-dependencies': 'off' - } - }, - // TODO(firestoreexp): Remove this exception when app-exp is published - { - files: ['exp/**/*.ts'], - rules: { - 'import/no-extraneous-dependencies': 'off' - } } ] }; diff --git a/packages/firestore/.gitignore b/packages/firestore/.gitignore new file mode 100644 index 00000000000..cb7b960ce00 --- /dev/null +++ b/packages/firestore/.gitignore @@ -0,0 +1 @@ +/compat \ No newline at end of file diff --git a/packages/firestore/.idea/runConfigurations/All_Tests__Emulator_.xml b/packages/firestore/.idea/runConfigurations/All_Tests__Emulator_.xml index 92d7cc57670..86a66f0f39e 100644 --- a/packages/firestore/.idea/runConfigurations/All_Tests__Emulator_.xml +++ b/packages/firestore/.idea/runConfigurations/All_Tests__Emulator_.xml @@ -11,9 +11,9 @@ bdd - --require ts-node/register/type-check --require index.node.ts --timeout 5000 + --require ts-node/register/type-check --require test/register.ts --timeout 5000 PATTERN test/{,!(browser|lite)/**/}*.test.ts - \ No newline at end of file + diff --git a/packages/firestore/.idea/runConfigurations/All_Tests__Emulator_w__Mock_Persistence_.xml b/packages/firestore/.idea/runConfigurations/All_Tests__Emulator_w__Mock_Persistence_.xml index c2181e91517..5dcd1e701c2 100644 --- a/packages/firestore/.idea/runConfigurations/All_Tests__Emulator_w__Mock_Persistence_.xml +++ b/packages/firestore/.idea/runConfigurations/All_Tests__Emulator_w__Mock_Persistence_.xml @@ -12,9 +12,9 @@ bdd - --require ts-node/register/type-check --require index.node.ts --require test/util/node_persistence.ts --timeout 5000 + --require ts-node/register/type-check --require test/register.ts --require test/util/node_persistence.ts --timeout 5000 PATTERN test/{,!(browser|lite)/**/}*.test.ts - \ No newline at end of file + diff --git a/packages/firestore/.idea/runConfigurations/Integration_Tests__Emulator_.xml b/packages/firestore/.idea/runConfigurations/Integration_Tests__Emulator_.xml index 1ea460ca6e5..421038182f0 100644 --- a/packages/firestore/.idea/runConfigurations/Integration_Tests__Emulator_.xml +++ b/packages/firestore/.idea/runConfigurations/Integration_Tests__Emulator_.xml @@ -11,9 +11,9 @@ bdd - --require ts-node/register/type-check --require index.node.ts --timeout 5000 + --require ts-node/register/type-check --require test/register.ts --require compat/index.node.ts --timeout 5000 PATTERN test/integration/{,!(browser|lite)/**/}*.test.ts - \ No newline at end of file + diff --git a/packages/firestore/.idea/runConfigurations/Integration_Tests__Emulator_w__Mock_Persistence_.xml b/packages/firestore/.idea/runConfigurations/Integration_Tests__Emulator_w__Mock_Persistence_.xml index 6d9215345e1..828749694c3 100644 --- a/packages/firestore/.idea/runConfigurations/Integration_Tests__Emulator_w__Mock_Persistence_.xml +++ b/packages/firestore/.idea/runConfigurations/Integration_Tests__Emulator_w__Mock_Persistence_.xml @@ -12,9 +12,9 @@ bdd - --require ts-node/register/type-check --require index.node.ts --require test/util/node_persistence.ts --timeout 5000 + --require ts-node/register/type-check --require test/register.ts --require test/util/node_persistence.ts --timeout 5000 PATTERN test/integration/{,!(browser|lite)/**/}*.test.ts - \ No newline at end of file + diff --git a/packages/firestore/.idea/runConfigurations/Unit_Tests.xml b/packages/firestore/.idea/runConfigurations/Unit_Tests.xml index 5d0dcda754a..4c525bfb4fa 100644 --- a/packages/firestore/.idea/runConfigurations/Unit_Tests.xml +++ b/packages/firestore/.idea/runConfigurations/Unit_Tests.xml @@ -9,9 +9,9 @@ bdd - --require ts-node/register/type-check --require index.node.ts + --require ts-node/register/type-check --require test/register.ts PATTERN test/unit/{,!(browser|lite)/**/}*.test.ts - \ No newline at end of file + diff --git a/packages/firestore/.idea/runConfigurations/Unit_Tests__w__Mock_Persistence_.xml b/packages/firestore/.idea/runConfigurations/Unit_Tests__w__Mock_Persistence_.xml index f62d34dcd93..759a10bfda1 100644 --- a/packages/firestore/.idea/runConfigurations/Unit_Tests__w__Mock_Persistence_.xml +++ b/packages/firestore/.idea/runConfigurations/Unit_Tests__w__Mock_Persistence_.xml @@ -10,9 +10,9 @@ bdd - --require ts-node/register/type-check --require index.node.ts --require test/util/node_persistence.ts + --require ts-node/register/type-check --require test/register.ts --require test/util/node_persistence.ts PATTERN test/unit/{,!(browser|lite)/**/}*.test.ts - \ No newline at end of file + diff --git a/packages/firestore/api-extractor.json b/packages/firestore/api-extractor.json index a81e7c54b52..ed10a0d62ac 100644 --- a/packages/firestore/api-extractor.json +++ b/packages/firestore/api-extractor.json @@ -5,7 +5,7 @@ */ "extends": "../../config/api-extractor.json", // Point it to your entry point d.ts file. - "mainEntryPointFilePath": "/dist/exp/index.d.ts", + "mainEntryPointFilePath": "/dist/index.d.ts", "additionalEntryPoints": [ { "modulePath": "lite", diff --git a/packages/firestore/bundle/package.json b/packages/firestore/bundle/package.json deleted file mode 100644 index df3c12d9f3f..00000000000 --- a/packages/firestore/bundle/package.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "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/compat/bundle.ts b/packages/firestore/compat/bundle.ts deleted file mode 100644 index 427c2e50680..00000000000 --- a/packages/firestore/compat/bundle.ts +++ /dev/null @@ -1,55 +0,0 @@ -/** - * @license - * Copyright 2021 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 { - loadBundle as expLoadBundle, - namedQuery as expNamedQuery, - LoadBundleTask -} from '../exp/index'; -import { Firestore, Query } from '../src/api/database'; - -export function loadBundle( - this: Firestore, - data: ArrayBuffer | ReadableStream | string -): LoadBundleTask { - return expLoadBundle(this._delegate, data); -} - -export function namedQuery( - this: Firestore, - queryName: string -): Promise { - return expNamedQuery(this._delegate, queryName).then(expQuery => { - if (!expQuery) { - return null; - } - return new Query( - this, - // We can pass `expQuery` here directly since named queries don't have a UserDataConverter. - // Otherwise, we would have to create a new ExpQuery and pass the old UserDataConverter. - expQuery - ); - }); -} - -/** - * Prototype patches bundle loading to Firestore. - */ -export function registerBundle(instance: typeof Firestore): void { - instance.prototype.loadBundle = loadBundle; - instance.prototype.namedQuery = namedQuery; -} diff --git a/packages/firestore/compat/package.json b/packages/firestore/compat/package.json deleted file mode 100644 index 180edcd7f0c..00000000000 --- a/packages/firestore/compat/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "@firebase/firestore-compat", - "version": "0.0.900", - "description": "The Cloud Firestore component of the Firebase JS SDK.", - "author": "Firebase (https://firebase.google.com/)", - "main": "../dist/compat/node-cjs/index.js", - "main-esm2017": "../dist/compat/node-esm2017/index.js", - "react-native": "../dist/compat/rn/index.js", - "browser": "../dist/compat/esm2017/index.js", - "module": "../dist/compat/esm2017/index.js", - "esm5": "../dist/compat/esm5/index.js", - "license": "Apache-2.0", - "typings": "../dist/compat/esm2017/firestore/compat/index.d.ts" - } - \ No newline at end of file diff --git a/packages/firestore/exp/package.json b/packages/firestore/exp/package.json deleted file mode 100644 index 375bed2ae15..00000000000 --- a/packages/firestore/exp/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "@firebase/firestore-exp", - "description": "A tree-shakeable version of the Firestore SDK", - "main": "../dist/exp/index.node.cjs.js", - "main-esm": "../dist/exp/index.node.esm2017.js", - "module": "../dist/exp/index.browser.esm2017.js", - "browser": "../dist/exp/index.browser.esm2017.js", - "react-native": "../dist/exp/index.rn.esm2017.js", - "esm5": "../dist/exp/index.browser.esm5.js", - "typings": "../dist/exp/index.d.ts", - "private": true -} diff --git a/packages/firestore/export.ts b/packages/firestore/export.ts deleted file mode 100644 index 4e97cd52b8d..00000000000 --- a/packages/firestore/export.ts +++ /dev/null @@ -1,69 +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 { Firestore, Query } from './src/api/database'; -import { LoadBundleTask } from './src/exp/bundle'; -import { - loadBundle as expLoadBundle, - namedQuery as expNamedQuery -} from './src/exp/database'; - -export { Blob } from './src/api/blob'; -export { - CollectionReference, - DocumentReference, - DocumentSnapshot, - Firestore, - Query, - QueryDocumentSnapshot, - QuerySnapshot, - IndexedDbPersistenceProvider, - MemoryPersistenceProvider, - Transaction, - WriteBatch, - setLogLevel -} from './src/api/database'; -export { CACHE_SIZE_UNLIMITED } from './src/exp/database'; -export { GeoPoint } from './src/api/geo_point'; -export { FieldPath } from './src/api/field_path'; -export { FieldValue } from './src/api/field_value'; -export { Timestamp } from './src/api/timestamp'; -export { Firestore as ExpFirestore } from './src/exp/database'; - -export function loadBundle( - this: Firestore, - data: ArrayBuffer | ReadableStream | string -): LoadBundleTask { - return expLoadBundle(this._delegate, data); -} - -export function namedQuery( - this: Firestore, - queryName: string -): Promise { - return expNamedQuery(this._delegate, queryName).then(expQuery => { - if (!expQuery) { - return null; - } - return new Query( - this, - // We can pass `expQuery` here directly since named queries don't have a UserDataConverter. - // Otherwise, we would have to create a new ExpQuery and pass the old UserDataConverter. - expQuery - ); - }); -} diff --git a/packages/firestore/externs.json b/packages/firestore/externs.json index a5f2044fa30..fbbdae36636 100644 --- a/packages/firestore/externs.json +++ b/packages/firestore/externs.json @@ -17,7 +17,7 @@ "packages/app/dist/app.d.ts", "packages/auth-interop-types/index.d.ts", "packages/firestore/dist/lite/internal.d.ts", - "packages/firestore/dist/exp/internal.d.ts", + "packages/firestore/dist/internal.d.ts", "packages/firestore-types/index.d.ts", "packages/firebase/compat/index.d.ts", "packages/component/dist/src/component.d.ts", @@ -29,7 +29,7 @@ "packages/util/dist/src/emulator.d.ts", "packages/util/dist/src/environment.d.ts", "packages/util/dist/src/compat.d.ts", - "packages/firestore/export.ts", + "packages/util/dist/src/obj.d.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.memory.ts b/packages/firestore/index.memory.ts deleted file mode 100644 index bc37d4281e4..00000000000 --- a/packages/firestore/index.memory.ts +++ /dev/null @@ -1,43 +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 firebase from '@firebase/app'; -import { FirebaseNamespace } from '@firebase/app-types'; - -import { Firestore, MemoryPersistenceProvider, ExpFirestore } from './export'; -import { name, version } from './package.json'; -import { configureForFirebase } from './src/config'; - -import './register-module'; - -/** - * Registers the memory-only Firestore build with the components framework. - */ -export function registerFirestore(instance: FirebaseNamespace): void { - configureForFirebase( - instance, - (app, auth) => - new Firestore( - app, - new ExpFirestore(app, auth), - new MemoryPersistenceProvider() - ) - ); - instance.registerVersion(name, version); -} - -registerFirestore(firebase); diff --git a/packages/firestore/index.node.memory.ts b/packages/firestore/index.node.memory.ts deleted file mode 100644 index 3f01a3607ca..00000000000 --- a/packages/firestore/index.node.memory.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. - */ - -import firebase from '@firebase/app'; -import { FirebaseNamespace } from '@firebase/app-types'; - -import { name, version } from './package.json'; -import { Firestore, MemoryPersistenceProvider } from './src/api/database'; -import { configureForFirebase } from './src/config'; -import { Firestore as ExpFirestore } from './src/exp/database'; - -import './register-module'; - -/** - * Registers the memory-only Firestore build for Node with the components - * framework. - */ -export function registerFirestore(instance: FirebaseNamespace): void { - configureForFirebase( - instance, - (app, auth) => - new Firestore( - app, - new ExpFirestore(app, auth), - new MemoryPersistenceProvider() - ) - ); - instance.registerVersion(name, version, 'node'); -} - -registerFirestore(firebase); diff --git a/packages/firestore/index.node.ts b/packages/firestore/index.node.ts deleted file mode 100644 index f6eaeb8cba8..00000000000 --- a/packages/firestore/index.node.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * @license - * Copyright 2017 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 firebase from '@firebase/app'; -import { FirebaseNamespace } from '@firebase/app-types'; - -import { name, version } from './package.json'; -import { Firestore, IndexedDbPersistenceProvider } from './src/api/database'; -import { configureForFirebase } from './src/config'; -import { Firestore as ExpFirestore } from './src/exp/database'; - -import './register-module'; - -/** - * Registers the main Firestore Node build with the components framework. - * Persistence can be enabled via `firebase.firestore().enablePersistence()`. - */ -export function registerFirestore(instance: FirebaseNamespace): void { - configureForFirebase( - instance, - (app, auth) => - new Firestore( - app, - new ExpFirestore(app, auth), - new IndexedDbPersistenceProvider() - ) - ); - instance.registerVersion(name, version, 'node'); -} - -registerFirestore(firebase); diff --git a/packages/firestore/index.rn.memory.ts b/packages/firestore/index.rn.memory.ts deleted file mode 100644 index 6b0dd3d8c4b..00000000000 --- a/packages/firestore/index.rn.memory.ts +++ /dev/null @@ -1,44 +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 firebase from '@firebase/app'; -import { FirebaseNamespace } from '@firebase/app-types'; - -import { Firestore, MemoryPersistenceProvider, ExpFirestore } from './export'; -import { name, version } from './package.json'; -import { configureForFirebase } from './src/config'; - -import './register-module'; - -/** - * Registers the memory-only Firestore build for ReactNative with the components - * framework. - */ -export function registerFirestore(instance: FirebaseNamespace): void { - configureForFirebase( - instance, - (app, auth) => - new Firestore( - app, - new ExpFirestore(app, auth), - new MemoryPersistenceProvider() - ) - ); - instance.registerVersion(name, version, 'rn'); -} - -registerFirestore(firebase); diff --git a/packages/firestore/index.rn.ts b/packages/firestore/index.rn.ts deleted file mode 100644 index 3f843fe5be1..00000000000 --- a/packages/firestore/index.rn.ts +++ /dev/null @@ -1,47 +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 firebase from '@firebase/app'; -import { FirebaseNamespace } from '@firebase/app-types'; - -import { - Firestore, - IndexedDbPersistenceProvider, - ExpFirestore -} from './export'; -import { name, version } from './package.json'; -import { configureForFirebase } from './src/config'; - -import './register-module'; - -/** - * Registers the main Firestore ReactNative build with the components framework. - * Persistence can be enabled via `firebase.firestore().enablePersistence()`. - */ -export function registerFirestore(instance: FirebaseNamespace): void { - configureForFirebase( - instance, - (app, auth) => - new Firestore( - app, - new ExpFirestore(app, auth), - new IndexedDbPersistenceProvider() - ) - ); - instance.registerVersion(name, version, 'rn'); -} - -registerFirestore(firebase); diff --git a/packages/firestore/index.ts b/packages/firestore/index.ts deleted file mode 100644 index f970abb1431..00000000000 --- a/packages/firestore/index.ts +++ /dev/null @@ -1,48 +0,0 @@ -/** - * @license - * Copyright 2017 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 firebase from '@firebase/app'; -import { FirebaseNamespace } from '@firebase/app-types'; - -import { - Firestore, - IndexedDbPersistenceProvider, - ExpFirestore -} from './export'; -import { name, version } from './package.json'; -import { configureForFirebase } from './src/config'; - -import './register-module'; - -/** - * Registers the main Firestore build with the components framework. - * Persistence can be enabled via `firebase.firestore().enablePersistence()`. - */ -export function registerFirestore(instance: FirebaseNamespace): void { - configureForFirebase( - instance, - (app, auth) => - new Firestore( - app, - new ExpFirestore(app, auth), - new IndexedDbPersistenceProvider() - ) - ); - instance.registerVersion(name, version); -} - -registerFirestore(firebase); diff --git a/packages/firestore/lite/index.ts b/packages/firestore/lite/index.ts index eb4ceef313a..81f7318ec2a 100644 --- a/packages/firestore/lite/index.ts +++ b/packages/firestore/lite/index.ts @@ -27,15 +27,16 @@ import { registerFirestore } from './register'; registerFirestore(); -export { FirestoreSettings as Settings } from '../src/lite/settings'; +export { FirestoreSettings as Settings } from '../src/lite-api/settings'; export { Firestore as Firestore, + EmulatorMockTokenOptions, initializeFirestore, getFirestore, terminate, connectFirestoreEmulator -} from '../src/lite/database'; +} from '../src/lite-api/database'; export { DocumentData, @@ -51,7 +52,7 @@ export { doc, refEqual, queryEqual -} from '../src/lite/reference'; +} from '../src/lite-api/reference'; export { endAt, @@ -67,7 +68,7 @@ export { query, QueryConstraint, QueryConstraintType -} from '../src/lite/query'; +} from '../src/lite-api/query'; export { addDoc, @@ -76,20 +77,20 @@ export { setDoc, getDoc, getDocs -} from '../src/lite/reference_impl'; +} from '../src/lite-api/reference_impl'; export { Primitive, NestedUpdateFields, AddPrefixToKeys, UnionToIntersection -} from '../src/lite/types'; +} from '../src/lite-api/types'; // TOOD(firestorelite): Add tests when Queries are usable -export { FieldPath, documentId } from '../src/lite/field_path'; +export { FieldPath, documentId } from '../src/lite-api/field_path'; // TOOD(firestorelite): Add tests when setDoc() is available -export { FieldValue } from '../src/lite/field_value'; +export { FieldValue } from '../src/lite-api/field_value'; export { increment, @@ -97,7 +98,7 @@ export { arrayUnion, serverTimestamp, deleteField -} from '../src/lite/field_value_impl'; +} from '../src/lite-api/field_value_impl'; export { FirestoreDataConverter, @@ -105,18 +106,18 @@ export { QueryDocumentSnapshot, QuerySnapshot, snapshotEqual -} from '../src/lite/snapshot'; +} from '../src/lite-api/snapshot'; -export { WriteBatch, writeBatch } from '../src/lite/write_batch'; +export { WriteBatch, writeBatch } from '../src/lite-api/write_batch'; -export { Transaction, runTransaction } from '../src/lite/transaction'; +export { Transaction, runTransaction } from '../src/lite-api/transaction'; export { setLogLevel, LogLevelString as LogLevel } from '../src/util/log'; -export { Bytes } from '../src/lite/bytes'; +export { Bytes } from '../src/lite-api/bytes'; -export { GeoPoint } from '../src/lite/geo_point'; +export { GeoPoint } from '../src/lite-api/geo_point'; -export { Timestamp } from '../src/lite/timestamp'; +export { Timestamp } from '../src/lite-api/timestamp'; export { FirestoreErrorCode, FirestoreError } from '../src/util/error'; diff --git a/packages/firestore/lite/register.ts b/packages/firestore/lite/register.ts index 24a73b56556..50a05aef5ed 100644 --- a/packages/firestore/lite/register.ts +++ b/packages/firestore/lite/register.ts @@ -19,13 +19,14 @@ import { _registerComponent, registerVersion, SDK_VERSION -} from '@firebase/app-exp'; +} from '@firebase/app'; import { Component, ComponentType } from '@firebase/component'; import { version } from '../package.json'; +import { LiteCredentialsProvider } from '../src/api/credentials'; import { setSDKVersion } from '../src/core/version'; -import { Firestore } from '../src/lite/database'; -import { FirestoreSettings } from '../src/lite/settings'; +import { Firestore } from '../src/lite-api/database'; +import { FirestoreSettings } from '../src/lite-api/settings'; declare module '@firebase/component' { interface NameServiceMapping { @@ -39,10 +40,10 @@ export function registerFirestore(): void { new Component( 'firestore/lite', (container, { options: settings }: { options?: FirestoreSettings }) => { - const app = container.getProvider('app-exp').getImmediate()!; + const app = container.getProvider('app').getImmediate()!; const firestoreInstance = new Firestore( app, - container.getProvider('auth-internal') + new LiteCredentialsProvider(container.getProvider('auth-internal')) ); if (settings) { firestoreInstance._setSettings(settings); diff --git a/packages/firestore/memory-bundle/package.json b/packages/firestore/memory-bundle/package.json deleted file mode 100644 index b29c357c34e..00000000000 --- a/packages/firestore/memory-bundle/package.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "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 deleted file mode 100644 index 0879ff8fa39..00000000000 --- a/packages/firestore/memory/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "@firebase/firestore/memory", - "description": "A memory-only build of the Cloud Firestore JS SDK.", - "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 8f371295599..e19044d7b29 100644 --- a/packages/firestore/package.json +++ b/packages/firestore/package.json @@ -9,65 +9,55 @@ "scripts": { "bundle": "rollup -c", "prebuild": "tsc --emitDeclarationOnly --declaration -p tsconfig.json; yarn api-report", - "build": "run-p 'bundle rollup.config.browser.js' 'bundle rollup.config.node.js' 'bundle rollup.config.rn.js' build:lite build:exp && yarn build:compat", + "build": "run-p build:lite build:main", + "build:release": "yarn build && yarn typings:public", "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.node.js' 'bundle rollup.config.rn.js'", "build:deps": "lerna run --scope @firebase/firestore --include-dependencies build", - "build:compat": "run-p 'bundle ./rollup.config.browser.compat.js' 'bundle ./rollup.config.node.compat.js' 'bundle ./rollup.config.rn.compat.js' && yarn add-compat-overloads", - "build:console": "node tools/console.build.js", - "build:exp": "rollup -c rollup.config.exp.js", + "build:main": "rollup -c rollup.config.js", "build:lite": "rollup -c rollup.config.lite.js", - "build:exp:release": "yarn build:exp && yarn build:lite && yarn build:compat", - "postbuild:exp:release": "node ../../scripts/exp/remove-exp.js dist/exp/index.d.ts && node ../../scripts/exp/remove-exp.js dist/lite/index.d.ts", - "build:browser": "rollup -c rollup.config.browser.js", "predev": "yarn prebuild", "dev": "rollup -c -w", "lint": "eslint -c .eslintrc.js '**/*.ts' --ignore-path '../../.gitignore'", "lint:fix": "eslint --fix -c .eslintrc.js '**/*.ts' --ignore-path '../../.gitignore'", - "prettier": "prettier --write '*.js' '*.ts' '@(lite|exp|src|test)/**/*.ts'", - "pregendeps:exp": "yarn api-report && node scripts/build-bundle.js --input ./exp/index.ts --output ./dist/exp/tmp.js", - "gendeps:exp": "../../scripts/exp/extract-deps.sh --types ./dist/exp/index.d.ts --bundle ./dist/exp/tmp.js --output ./dist/exp/dependencies.json", - "pregendeps:lite": "yarn api-report && node scripts/build-bundle.js --input ./lite/index.ts --output ./dist/lite/tmp.js", - "gendeps:lite": "../../scripts/exp/extract-deps.sh --types ./dist/lite/index.d.ts --bundle ./dist/lite/tmp.js --output ./dist/lite/dependencies.json", + "prettier": "prettier --write '*.js' '*.ts' '@(lite|src|test)/**/*.ts'", "test:lite": "node ./scripts/run-tests.js --emulator --platform node_lite --main=lite/index.ts 'test/lite/**/*.test.ts'", "test:lite:prod": "node ./scripts/run-tests.js --platform node_lite --main=lite/index.ts 'test/lite/**/*.test.ts'", "test:lite:browser": "karma start --single-run --lite", "test:lite:browser:debug": "karma start --browsers=Chrome --lite --auto-watch", + "pretest": "yarn test:prepare", + "pretest:ci": "yarn pretest", "test": "run-s lint test:all", "test:ci": "node ../../scripts/run_tests_in_ci.js -s test:all", "test:all": "run-p test:browser test:lite:browser test:travis test:minified", "test:browser": "karma start --single-run", "test:browser:debug": "karma start --browsers=Chrome --auto-watch", - "test:node": "node ./scripts/run-tests.js --main=index.node.ts --emulator 'test/{,!(browser|lite)/**/}*.test.ts'", - "test:node:prod": "node ./scripts/run-tests.js --main=index.node.ts 'test/{,!(browser|lite)/**/}*.test.ts'", - "test:node:persistence": "node ./scripts/run-tests.js --main=index.node.ts --persistence --emulator 'test/{,!(browser|lite)/**/}*.test.ts'", - "test:node:persistence:prod": "node ./scripts/run-tests.js --main=index.node.ts --persistence 'test/{,!(browser|lite)/**/}*.test.ts'", + "test:node": "node ./scripts/run-tests.js --main=test/register.ts --emulator 'test/{,!(browser|lite)/**/}*.test.ts'", + "test:node:prod": "ts-node-script ./scripts/run-tests.ts --main=test/register.ts 'test/{,!(browser|lite)/**/}*.test.ts'", + "test:node:persistence": "node ./scripts/run-tests.js --main=test/register.ts --persistence --emulator 'test/{,!(browser|lite)/**/}*.test.ts'", + "test:node:persistence:prod": "node ./scripts/run-tests.js --main=test/register.ts --persistence 'test/{,!(browser|lite)/**/}*.test.ts'", "test:travis": "ts-node --compiler-options='{\"module\":\"commonjs\"}' ../../scripts/emulator-testing/firestore-test-runner.ts", "test:minified": "(cd ../../integration/firestore ; yarn test)", - "api-report:exp": "TS_NODE_COMPILER_OPTIONS='{\"module\":\"commonjs\"}' ts-node ../../repo-scripts/prune-dts/extract-public-api.ts --package firestore --packageRoot . --typescriptDts ./dist/firestore/exp/index.d.ts --rollupDts ./dist/exp/private.d.ts --untrimmedRollupDts ./dist/exp/internal.d.ts --publicDts ./dist/exp/index.d.ts", + "api-report:main": "TS_NODE_COMPILER_OPTIONS='{\"module\":\"commonjs\"}' ts-node ../../repo-scripts/prune-dts/extract-public-api.ts --package firestore --packageRoot . --typescriptDts ./dist/firestore/src/index.d.ts --rollupDts ./dist/private.d.ts --untrimmedRollupDts ./dist/internal.d.ts --publicDts ./dist/index.d.ts", "api-report:lite": "TS_NODE_COMPILER_OPTIONS='{\"module\":\"commonjs\"}' ts-node ../../repo-scripts/prune-dts/extract-public-api.ts --package firestore-lite --packageRoot . --typescriptDts ./dist/firestore/lite/index.d.ts --rollupDts ./dist/lite/private.d.ts --untrimmedRollupDts ./dist/lite/internal.d.ts --publicDts ./dist/lite/index.d.ts", "api-report:api-json": "rm -rf temp && api-extractor run --local --verbose", - "api-report": "run-s api-report:exp api-report:lite && yarn api-report:api-json", - "predoc": "node ../../scripts/exp/remove-exp.js temp", + "api-report": "run-s api-report:main api-report:lite && yarn api-report:api-json", "doc": "api-documenter markdown --input temp --output docs", - "add-compat-overloads": "ts-node-script ../../scripts/exp/create-overloads.ts -i dist/exp/index.d.ts -o dist/compat/esm2017/firestore/compat/index.d.ts -a -r FirebaseFirestore:types.FirebaseFirestore -r CollectionReference:types.CollectionReference -r DocumentReference:types.DocumentReference -r Query:types.Query -r FirebaseApp:FirebaseAppCompat --moduleToEnhance @firebase/firestore" + "test:prepare": "node ./scripts/prepare-test.js", + "typings:public": "node ../../scripts/exp/use_typings.js ./dist/index.d.ts" }, - "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", + "main": "dist/index.node.cjs.js", + "main-esm": "dist/index.node.cjs.esm2017.js", + "react-native": "dist/index.rn.js", + "browser": "dist/index.esm2017.js", + "module": "dist/index.esm2017.js", + "esm5": "dist/index.esm5.js", "license": "Apache-2.0", "files": [ "dist", - "memory/package.json", - "bundle/package.json", - "memory-bundle/package.json" + "lite/package.json" ], "dependencies": { "@firebase/component": "0.5.6", - "@firebase/firestore-types": "2.4.0", "@firebase/logger": "0.2.6", "@firebase/util": "1.3.0", "@firebase/webchannel-wrapper": "0.5.1", @@ -77,14 +67,13 @@ "tslib": "^2.1.0" }, "peerDependencies": { - "@firebase/app": "0.x", - "@firebase/app-types": "0.x" + "@firebase/app": "0.x" }, "devDependencies": { "@firebase/app": "0.6.30", + "@firebase/app-compat": "0.0.900", "@rollup/plugin-alias": "3.1.2", "@rollup/plugin-json": "4.1.0", - "@rollup/plugin-node-resolve": "11.2.0", "@types/eslint": "7.2.10", "@types/json-stable-stringify": "1.0.32", "json-stable-stringify": "1.0.1", @@ -107,7 +96,7 @@ "bugs": { "url": "https://github.com/firebase/firebase-js-sdk/issues" }, - "typings": "dist/firestore/index.d.ts", + "typings": "dist/firestore/src/index.d.ts", "nyc": { "extension": [ ".ts" diff --git a/packages/firestore/rollup.config.browser.compat.js b/packages/firestore/rollup.config.browser.compat.js deleted file mode 100644 index 94384ddf8a6..00000000000 --- a/packages/firestore/rollup.config.browser.compat.js +++ /dev/null @@ -1,69 +0,0 @@ -import pkg from './compat/package.json'; -import path from 'path'; -import { getImportPathTransformer } from '../../scripts/exp/ts-transform-import-path'; - -/** - * @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. - */ - -const util = require('./rollup.shared'); - -export default [ - // Create main build - { - input: { - index: './compat/index.ts' - }, - output: { - dir: 'dist/compat/esm2017', - format: 'es', - sourcemap: true - }, - plugins: [ - ...util.es2017PluginsCompat( - 'browser', - getImportPathTransformer({ - // ../../exp/index - pattern: /^.*exp\/index$/, - template: ['@firebase/firestore'] - }), - /* mangled= */ false - ) - ], - external: util.resolveBrowserExterns, - treeshake: { - moduleSideEffects: false - } - }, - // Convert main build to ES5 - { - input: { - index: path.resolve('./compat', pkg['browser']) - }, - output: [ - { - dir: 'dist/compat/esm5', - format: 'es', - sourcemap: true - } - ], - plugins: util.es2017ToEs5Plugins(/* mangled= */ true), - external: util.resolveBrowserExterns, - treeshake: { - moduleSideEffects: false - } - } -]; diff --git a/packages/firestore/rollup.config.browser.js b/packages/firestore/rollup.config.browser.js deleted file mode 100644 index fbfe1af418f..00000000000 --- a/packages/firestore/rollup.config.browser.js +++ /dev/null @@ -1,120 +0,0 @@ -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 - * 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. - */ - -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/prebuilt.js', - format: 'es', - sourcemap: true - }, - plugins: util.es2017Plugins('browser', /* mangled= */ true), - external: util.resolveBrowserExterns, - treeshake: { - moduleSideEffects: false - }, - onwarn: util.onwarn - }, - // Create main build - { - input: { - index: 'index.ts', - bundle: 'index.bundle.ts' - }, - output: { - dir: 'dist/esm2017', - format: 'es', - sourcemap: true - }, - plugins: [ - util.applyPrebuilt(), - ...util.es2017Plugins('browser', /* mangled= */ false) - ], - external: util.resolveBrowserExterns, - treeshake: { - moduleSideEffects: false - } - }, - // Convert main build to ES5 - { - input: { - index: pkg['esm2017'], - bundle: path.resolve('./bundle', bundlePkg['esm2017']) - }, - output: [ - { - dir: 'dist/esm5', - format: 'es', - sourcemap: true - } - ], - 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.exp.js b/packages/firestore/rollup.config.js similarity index 79% rename from packages/firestore/rollup.config.exp.js rename to packages/firestore/rollup.config.js index 1367380c3e7..d34960c310f 100644 --- a/packages/firestore/rollup.config.exp.js +++ b/packages/firestore/rollup.config.js @@ -20,13 +20,11 @@ import json from '@rollup/plugin-json'; import alias from '@rollup/plugin-alias'; import typescriptPlugin from 'rollup-plugin-typescript2'; import typescript from 'typescript'; -import path from 'path'; import replace from 'rollup-plugin-replace'; import copy from 'rollup-plugin-copy'; import { terser } from 'rollup-plugin-terser'; -import { importPathTransformer } from '../../scripts/exp/ts-transform-import-path'; -import pkg from './exp/package.json'; +import pkg from './package.json'; const util = require('./rollup.shared'); @@ -41,7 +39,7 @@ const nodePlugins = function () { }, cacheDir: tmp.dirSync(), abortOnError: false, - transformers: [util.removeAssertTransformer, importPathTransformer] + transformers: [util.removeAssertTransformer] }), json({ preferConst: true }), // Needed as we also use the *.proto files @@ -49,7 +47,7 @@ const nodePlugins = function () { targets: [ { src: 'src/protos', - dest: 'dist/exp/src' + dest: 'dist/src' } ] }), @@ -71,10 +69,7 @@ const browserPlugins = function () { cacheDir: tmp.dirSync(), clean: true, abortOnError: false, - transformers: [ - util.removeAssertAndPrefixInternalTransformer, - importPathTransformer - ] + transformers: [util.removeAssertAndPrefixInternalTransformer] }), json({ preferConst: true }), terser(util.manglePrivatePropertiesOptions) @@ -84,9 +79,9 @@ const browserPlugins = function () { const allBuilds = [ // Node ESM build { - input: './exp/index.node.ts', + input: './src/index.node.ts', output: { - file: path.resolve('./exp', pkg['main-esm']), + file: pkg['main-esm'], format: 'es', sourcemap: true }, @@ -99,9 +94,9 @@ const allBuilds = [ }, // Node CJS build { - input: path.resolve('./exp', pkg['main-esm']), + input: pkg['main-esm'], output: { - file: path.resolve('./exp', pkg.main), + file: pkg.main, format: 'cjs', sourcemap: true }, @@ -113,9 +108,9 @@ const allBuilds = [ }, // Browser build { - input: './exp/index.ts', + input: './src/index.ts', output: { - file: path.resolve('./exp', pkg.browser), + file: pkg.browser, format: 'es', sourcemap: true }, @@ -127,10 +122,10 @@ const allBuilds = [ }, // Convert es2017 build to ES5 { - input: path.resolve('./exp', pkg['browser']), + input: pkg['browser'], output: [ { - file: path.resolve('./exp', pkg['esm5']), + file: pkg['esm5'], format: 'es', sourcemap: true } @@ -143,9 +138,9 @@ const allBuilds = [ }, // RN build { - input: './exp/index.rn.ts', + input: './src/index.rn.ts', output: { - file: path.resolve('./exp', pkg['react-native']), + file: pkg['react-native'], format: 'es', sourcemap: true }, diff --git a/packages/firestore/rollup.config.node.compat.js b/packages/firestore/rollup.config.node.compat.js deleted file mode 100644 index b7d8d0ff791..00000000000 --- a/packages/firestore/rollup.config.node.compat.js +++ /dev/null @@ -1,75 +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 replace from 'rollup-plugin-replace'; -import copy from 'rollup-plugin-copy'; -import pkg from './compat/package.json'; -import path from 'path'; -import { getImportPathTransformer } from '../../scripts/exp/ts-transform-import-path'; - -const util = require('./rollup.shared'); - -export default [ - { - input: { - index: './compat/index.node.ts' - }, - output: { - dir: 'dist/compat/node-esm2017', - format: 'es', - sourcemap: true - }, - plugins: [ - ...util.es2017PluginsCompat( - 'node', - getImportPathTransformer({ - // ../../exp/index - pattern: /^.*exp\/index$/, - template: ['@firebase/firestore'] - }) - ), - replace({ - 'process.env.FIRESTORE_PROTO_ROOT': JSON.stringify('../protos') - }), - copy({ - targets: [{ src: 'src/protos', dest: 'dist' }] - }) - ], - external: util.resolveNodeExterns, - treeshake: { - moduleSideEffects: false - }, - onwarn: util.onwarn - }, - { - input: { - index: path.resolve('./compat', pkg['main-esm2017']) - }, - output: [ - { - dir: 'dist/compat/node-cjs', - format: 'cjs', - sourcemap: true - } - ], - plugins: util.es2017ToEs5Plugins(), - external: util.resolveNodeExterns, - treeshake: { - moduleSideEffects: false - } - } -]; diff --git a/packages/firestore/rollup.config.node.js b/packages/firestore/rollup.config.node.js deleted file mode 100644 index 6554fb1a629..00000000000 --- a/packages/firestore/rollup.config.node.js +++ /dev/null @@ -1,113 +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 replace from 'rollup-plugin-replace'; -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'); - -export default [ - { - input: { - index: 'index.node.ts', - bundle: 'index.bundle.ts' - }, - output: { - dir: 'dist/node-esm2017', - format: 'es', - sourcemap: true - }, - plugins: [ - ...util.es2017Plugins('node'), - replace({ - 'process.env.FIRESTORE_PROTO_ROOT': JSON.stringify('../protos') - }), - copy({ - targets: [{ src: 'src/protos', dest: 'dist' }] - }) - ], - external: util.resolveNodeExterns, - treeshake: { - moduleSideEffects: false - }, - onwarn: util.onwarn - }, - { - input: { - index: pkg['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: { - 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') - }), - copy({ - targets: [{ src: 'src/protos', dest: 'dist/memory' }] - }) - ], - 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.compat.js b/packages/firestore/rollup.config.rn.compat.js deleted file mode 100644 index 3aab7b7fb37..00000000000 --- a/packages/firestore/rollup.config.rn.compat.js +++ /dev/null @@ -1,48 +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. - */ - -const util = require('./rollup.shared'); -import { getImportPathTransformer } from '../../scripts/exp/ts-transform-import-path'; - -export default [ - // Create main build - { - input: { - index: './compat/index.ts' - }, - output: { - dir: 'dist/compat/rn', - format: 'es', - sourcemap: true - }, - plugins: [ - ...util.es2017PluginsCompat( - 'rn', - getImportPathTransformer({ - // ../../exp/index - pattern: /^.*exp\/index$/, - template: ['@firebase/firestore'] - }), - /* mangled= */ false - ) - ], - external: util.resolveBrowserExterns, - treeshake: { - moduleSideEffects: false - } - } -]; diff --git a/packages/firestore/rollup.config.rn.js b/packages/firestore/rollup.config.rn.js deleted file mode 100644 index b89adc481bf..00000000000 --- a/packages/firestore/rollup.config.rn.js +++ /dev/null @@ -1,76 +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. - */ - -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/prebuilt.rn.js', - format: 'es', - sourcemap: true - }, - plugins: util.es2017Plugins('rn', /* mangled= */ true), - external: util.resolveBrowserExterns, - treeshake: { - moduleSideEffects: false - }, - onwarn: util.onwarn - }, - // Create main build - { - input: { - index: 'index.ts', - bundle: 'index.bundle.ts' - }, - output: { - dir: 'dist/rn', - format: 'es', - sourcemap: true - }, - plugins: [ - 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/memory/rn', - format: 'es', - sourcemap: true - }, - plugins: [ - util.applyPrebuilt('prebuilt.rn.js'), - ...util.es2017Plugins('rn', /* mangled= */ false) - ], - external: util.resolveBrowserExterns, - treeshake: { - moduleSideEffects: false - } - } -]; diff --git a/packages/firestore/scripts/prepare-test.js b/packages/firestore/scripts/prepare-test.js new file mode 100644 index 00000000000..208647deed1 --- /dev/null +++ b/packages/firestore/scripts/prepare-test.js @@ -0,0 +1,18 @@ +/** + * @license + * Copyright 2021 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. + */ + +"use strict";var __spreadArray=this&&this.__spreadArray||function(to,from){for(var i=0,il=from.length,j=to.length;i Promise; * listening for changes. */ export interface CredentialsProvider { + /** + * Starts the credentials provider and specifies a listener to be notified of + * credential changes (sign-in / sign-out, token changes). It is immediately + * called once with the initial user. + * + * The change listener is invoked on the provided AsyncQueue. + */ + start(asyncQueue: AsyncQueue, changeListener: CredentialChangeListener): void; + /** Requests a token for the current user. */ getToken(): Promise; @@ -96,53 +105,26 @@ export interface CredentialsProvider { */ invalidateToken(): void; - /** - * Specifies a listener to be notified of credential changes - * (sign-in / sign-out, token changes). It is immediately called once with the - * initial user. - * - * The change listener is invoked on the provided AsyncQueue. - */ - setChangeListener( - asyncQueue: AsyncQueue, - changeListener: CredentialChangeListener - ): void; - - /** Removes the previously-set change listener. */ - removeChangeListener(): void; + shutdown(): void; } /** A CredentialsProvider that always yields an empty token. */ export class EmptyCredentialsProvider implements CredentialsProvider { - /** - * Stores the listener registered with setChangeListener() - * This isn't actually necessary since the UID never changes, but we use this - * to verify the listen contract is adhered to in tests. - */ - private changeListener: CredentialChangeListener | null = null; - getToken(): Promise { return Promise.resolve(null); } invalidateToken(): void {} - setChangeListener( + start( asyncQueue: AsyncQueue, changeListener: CredentialChangeListener ): void { - debugAssert( - !this.changeListener, - 'Can only call setChangeListener() once.' - ); - this.changeListener = changeListener; // Fire with initial user. asyncQueue.enqueueRetryable(() => changeListener(User.UNAUTHENTICATED)); } - removeChangeListener(): void { - this.changeListener = null; - } + shutdown(): void {} } /** @@ -165,7 +147,7 @@ export class EmulatorCredentialsProvider implements CredentialsProvider { invalidateToken(): void {} - setChangeListener( + start( asyncQueue: AsyncQueue, changeListener: CredentialChangeListener ): void { @@ -178,80 +160,148 @@ export class EmulatorCredentialsProvider implements CredentialsProvider { asyncQueue.enqueueRetryable(() => changeListener(this.token.user)); } - removeChangeListener(): void { + shutdown(): void { this.changeListener = null; } } +/** Credential provider for the Lite SDK. */ +export class LiteCredentialsProvider implements CredentialsProvider { + private auth: FirebaseAuthInternal | null = null; + + constructor(authProvider: Provider) { + authProvider.onInit(auth => { + this.auth = auth; + }); + } + + getToken(): Promise { + if (!this.auth) { + return Promise.resolve(null); + } + + return this.auth.getToken().then(tokenData => { + if (tokenData) { + hardAssert( + typeof tokenData.accessToken === 'string', + 'Invalid tokenData returned from getToken():' + tokenData + ); + return new OAuthToken( + tokenData.accessToken, + new User(this.auth!.getUid()) + ); + } else { + return null; + } + }); + } + + invalidateToken(): void {} + + start( + asyncQueue: AsyncQueue, + changeListener: CredentialChangeListener + ): void {} + + shutdown(): void {} +} + export class FirebaseCredentialsProvider implements CredentialsProvider { /** * The auth token listener registered with FirebaseApp, retained here so we * can unregister it. */ - private tokenListener: () => void; + private tokenListener!: () => void; /** Tracks the current User. */ private currentUser: User = User.UNAUTHENTICATED; - /** Promise that allows blocking on the initialization of Firebase Auth. */ - private authDeferred = new Deferred(); - /** * Counter used to detect if the token changed while a getToken request was * outstanding. */ private tokenCounter = 0; - /** The listener registered with setChangeListener(). */ - private changeListener?: CredentialChangeListener; - private forceRefresh = false; private auth: FirebaseAuthInternal | null = null; - private asyncQueue: AsyncQueue | null = null; + constructor(private authProvider: Provider) {} + + start( + asyncQueue: AsyncQueue, + changeListener: CredentialChangeListener + ): void { + let lastTokenId = this.tokenCounter; + + // A change listener that prevents double-firing for the same token change. + const guardedChangeListener: (user: User) => Promise = user => { + if (this.tokenCounter !== lastTokenId) { + lastTokenId = this.tokenCounter; + return changeListener(user); + } else { + return Promise.resolve(); + } + }; + + // A promise that can be waited on to block on the next token change. + // This promise is re-created after each change. + let nextToken = new Deferred(); - constructor(authProvider: Provider) { this.tokenListener = () => { this.tokenCounter++; this.currentUser = this.getUser(); - this.authDeferred.resolve(); - if (this.changeListener) { - this.asyncQueue!.enqueueRetryable(() => - this.changeListener!(this.currentUser) - ); - } + nextToken.resolve(); + nextToken = new Deferred(); + asyncQueue.enqueueRetryable(() => + guardedChangeListener(this.currentUser) + ); }; const registerAuth = (auth: FirebaseAuthInternal): void => { - logDebug('FirebaseCredentialsProvider', 'Auth detected'); - this.auth = auth; - this.auth.addAuthTokenListener(this.tokenListener); + asyncQueue.enqueueRetryable(async () => { + logDebug('FirebaseCredentialsProvider', 'Auth detected'); + this.auth = auth; + this.auth.addAuthTokenListener(this.tokenListener); + + // Call the change listener inline to block on the user change. + await nextToken.promise; + await guardedChangeListener(this.currentUser); + }); }; - authProvider.onInit(auth => registerAuth(auth)); + this.authProvider.onInit(auth => registerAuth(auth)); // Our users can initialize Auth right after Firestore, so we give it // a chance to register itself with the component framework before we // determine whether to start up in unauthenticated mode. setTimeout(() => { if (!this.auth) { - const auth = authProvider.getImmediate({ optional: true }); + const auth = this.authProvider.getImmediate({ optional: true }); if (auth) { registerAuth(auth); } else { // If auth is still not available, proceed with `null` user logDebug('FirebaseCredentialsProvider', 'Auth not yet detected'); - this.authDeferred.resolve(); + nextToken.resolve(); + nextToken = new Deferred(); } } }, 0); + + asyncQueue.enqueueRetryable(async () => { + // If we have not received a token, wait for the first one. + if (this.tokenCounter === 0) { + await nextToken.promise; + await guardedChangeListener(this.currentUser); + } + }); } getToken(): Promise { debugAssert( this.tokenListener != null, - 'getToken cannot be called after listener removed.' + 'FirebaseCredentialsProvider not started.' ); // Take note of the current value of the tokenCounter so that this method @@ -293,26 +343,10 @@ export class FirebaseCredentialsProvider implements CredentialsProvider { this.forceRefresh = true; } - setChangeListener( - asyncQueue: AsyncQueue, - changeListener: CredentialChangeListener - ): void { - debugAssert(!this.asyncQueue, 'Can only call setChangeListener() once.'); - this.asyncQueue = asyncQueue; - - // Blocks the AsyncQueue until the next user is available. - this.asyncQueue!.enqueueRetryable(async () => { - await this.authDeferred.promise; - await changeListener(this.currentUser); - this.changeListener = changeListener; - }); - } - - removeChangeListener(): void { + shutdown(): void { if (this.auth) { this.auth.removeAuthTokenListener(this.tokenListener!); } - this.changeListener = () => Promise.resolve(); } // Auth.getUid() can return null even with a user logged in. It is because @@ -389,7 +423,7 @@ export class FirstPartyCredentialsProvider implements CredentialsProvider { ); } - setChangeListener( + start( asyncQueue: AsyncQueue, changeListener: CredentialChangeListener ): void { @@ -397,7 +431,7 @@ export class FirstPartyCredentialsProvider implements CredentialsProvider { asyncQueue.enqueueRetryable(() => changeListener(User.FIRST_PARTY)); } - removeChangeListener(): void {} + shutdown(): void {} invalidateToken(): void {} } diff --git a/packages/firestore/src/api/database.ts b/packages/firestore/src/api/database.ts index d8e4847233e..c4a74e4e541 100644 --- a/packages/firestore/src/api/database.ts +++ b/packages/firestore/src/api/database.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2017 Google LLC + * 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. @@ -15,1316 +15,540 @@ * limitations under the License. */ -import { FirebaseApp } from '@firebase/app-types'; -import { _FirebaseApp, FirebaseService } from '@firebase/app-types/private'; +// eslint-disable-next-line import/no-extraneous-dependencies import { - CollectionReference as PublicCollectionReference, - DocumentChange as PublicDocumentChange, - DocumentChangeType as PublicDocumentChangeType, - DocumentData, - DocumentData as PublicDocumentData, - DocumentReference as PublicDocumentReference, - DocumentSnapshot as PublicDocumentSnapshot, - FieldPath as PublicFieldPath, - FirebaseFirestore as PublicFirestore, - FirestoreDataConverter as PublicFirestoreDataConverter, - GetOptions as PublicGetOptions, - LogLevel as PublicLogLevel, - OrderByDirection as PublicOrderByDirection, - PersistenceSettings as PublicPersistenceSettings, - Query as PublicQuery, - QueryDocumentSnapshot as PublicQueryDocumentSnapshot, - QuerySnapshot as PublicQuerySnapshot, - SetOptions as PublicSetOptions, - Settings as PublicSettings, - SnapshotListenOptions as PublicSnapshotListenOptions, - SnapshotOptions as PublicSnapshotOptions, - Transaction as PublicTransaction, - UpdateData as PublicUpdateData, - WhereFilterOp as PublicWhereFilterOp, - WriteBatch as PublicWriteBatch -} from '@firebase/firestore-types'; -import { - Compat, - EmulatorMockTokenOptions, - getModularInstance -} from '@firebase/util'; + _getProvider, + _removeServiceInstance, + FirebaseApp, + getApp +} from '@firebase/app'; +import { deepEqual } from '@firebase/util'; import { - LoadBundleTask, - Bytes, - clearIndexedDbPersistence, - disableNetwork, - enableIndexedDbPersistence, - enableMultiTabIndexedDbPersistence, - enableNetwork, - ensureFirestoreConfigured, - Firestore as ExpFirestore, - connectFirestoreEmulator, - waitForPendingWrites, - FieldPath as ExpFieldPath, - limit, - limitToLast, - where, - orderBy, - startAfter, - startAt, - query, - endBefore, - endAt, - doc, - collection, - collectionGroup, - queryEqual, - Query as ExpQuery, - CollectionReference as ExpCollectionReference, - DocumentReference as ExpDocumentReference, - refEqual, - addDoc, - deleteDoc, - executeWrite, - getDoc, - getDocFromCache, - getDocFromServer, - getDocs, - getDocsFromCache, - getDocsFromServer, - onSnapshot, - onSnapshotsInSync, - setDoc, - updateDoc, - Unsubscribe, - DocumentChange as ExpDocumentChange, - DocumentSnapshot as ExpDocumentSnapshot, - QueryDocumentSnapshot as ExpQueryDocumentSnapshot, - QuerySnapshot as ExpQuerySnapshot, - snapshotEqual, - SnapshotMetadata, - runTransaction, - Transaction as ExpTransaction, - WriteBatch as ExpWriteBatch, - AbstractUserDataWriter -} from '../../exp/index'; // import from the exp public API + IndexedDbOfflineComponentProvider, + MultiTabOfflineComponentProvider, + OfflineComponentProvider, + OnlineComponentProvider +} from '../core/component_provider'; import { DatabaseId } from '../core/database_info'; -import { PartialWithFieldValue, WithFieldValue } from '../lite/reference'; -import { UntypedFirestoreDataConverter } from '../lite/user_data_reader'; -import { DocumentKey } from '../model/document_key'; -import { FieldPath, ResourcePath } from '../model/path'; +import { + FirestoreClient, + firestoreClientDisableNetwork, + firestoreClientEnableNetwork, + firestoreClientGetNamedQuery, + firestoreClientLoadBundle, + firestoreClientWaitForPendingWrites, + setOfflineComponentProvider, + setOnlineComponentProvider +} from '../core/firestore_client'; +import { makeDatabaseInfo } from '../lite-api/components'; +import { Firestore as LiteFirestore } from '../lite-api/database'; +import { Query } from '../lite-api/reference'; +import { + indexedDbClearPersistence, + indexedDbStoragePrefix +} from '../local/indexeddb_persistence'; +import { LRU_COLLECTION_DISABLED } from '../local/lru_garbage_collector'; +import { LRU_MINIMUM_CACHE_SIZE_BYTES } from '../local/lru_garbage_collector_impl'; import { debugAssert } from '../util/assert'; -import { ByteString } from '../util/byte_string'; +import { AsyncQueue } from '../util/async_queue'; +import { newAsyncQueue } from '../util/async_queue_impl'; import { Code, FirestoreError } from '../util/error'; -import { - cast, - validateIsNotUsedTogether, - validateSetOptions -} from '../util/input_validation'; -import { logWarn, setLogLevel as setClientLogLevel } from '../util/log'; +import { cast } from '../util/input_validation'; +import { Deferred } from '../util/promise'; -import { Blob } from './blob'; -import { - CompleteFn, - ErrorFn, - isPartialObserver, - NextFn, - PartialObserver -} from './observer'; +import { LoadBundleTask } from './bundle'; +import { CredentialsProvider } from './credentials'; +import { PersistenceSettings, FirestoreSettings } from './settings'; +export { + connectFirestoreEmulator, + EmulatorMockTokenOptions +} from '../lite-api/database'; -/** - * A persistence provider for either memory-only or IndexedDB persistence. - * Mainly used to allow optional inclusion of IndexedDB code. - */ -export interface PersistenceProvider { - enableIndexedDbPersistence( - firestore: Firestore, - forceOwnership: boolean - ): Promise; - enableMultiTabIndexedDbPersistence(firestore: Firestore): Promise; - clearIndexedDbPersistence(firestore: Firestore): Promise; +declare module '@firebase/component' { + interface NameServiceMapping { + 'firestore': Firestore; + } } -const MEMORY_ONLY_PERSISTENCE_ERROR_MESSAGE = - 'You are using the memory-only build of Firestore. Persistence support is ' + - 'only available via the @firebase/firestore bundle or the ' + - 'firebase-firestore.js build.'; +/** DOMException error code constants. */ +const DOM_EXCEPTION_INVALID_STATE = 11; +const DOM_EXCEPTION_ABORTED = 20; +const DOM_EXCEPTION_QUOTA_EXCEEDED = 22; /** - * The persistence provider included with the memory-only SDK. This provider - * errors for all attempts to access persistence. + * Constant used to indicate the LRU garbage collection should be disabled. + * Set this value as the `cacheSizeBytes` on the settings passed to the + * {@link Firestore} instance. */ -export class MemoryPersistenceProvider implements PersistenceProvider { - enableIndexedDbPersistence( - firestore: Firestore, - forceOwnership: boolean - ): Promise { - throw new FirestoreError( - Code.FAILED_PRECONDITION, - MEMORY_ONLY_PERSISTENCE_ERROR_MESSAGE - ); - } - - enableMultiTabIndexedDbPersistence(firestore: Firestore): Promise { - throw new FirestoreError( - Code.FAILED_PRECONDITION, - MEMORY_ONLY_PERSISTENCE_ERROR_MESSAGE - ); - } - - clearIndexedDbPersistence(firestore: Firestore): Promise { - throw new FirestoreError( - Code.FAILED_PRECONDITION, - MEMORY_ONLY_PERSISTENCE_ERROR_MESSAGE - ); - } -} +export const CACHE_SIZE_UNLIMITED = LRU_COLLECTION_DISABLED; /** - * The persistence provider included with the full Firestore SDK. + * The Cloud Firestore service interface. + * + * Do not call this constructor directly. Instead, use {@link getFirestore}. */ -export class IndexedDbPersistenceProvider implements PersistenceProvider { - enableIndexedDbPersistence( - firestore: Firestore, - forceOwnership: boolean - ): Promise { - return enableIndexedDbPersistence(firestore._delegate, { forceOwnership }); - } - enableMultiTabIndexedDbPersistence(firestore: Firestore): Promise { - return enableMultiTabIndexedDbPersistence(firestore._delegate); - } - clearIndexedDbPersistence(firestore: Firestore): Promise { - return clearIndexedDbPersistence(firestore._delegate); - } -} +export class Firestore extends LiteFirestore { + /** + * Whether it's a {@link Firestore} or Firestore Lite instance. + */ + type: 'firestore-lite' | 'firestore' = 'firestore'; -/** - * Compat class for Firestore. Exposes Firestore Legacy API, but delegates - * to the functional API of firestore-exp. - */ -export class Firestore - implements PublicFirestore, FirebaseService, Compat -{ - _appCompat?: FirebaseApp; + readonly _queue: AsyncQueue = newAsyncQueue(); + readonly _persistenceKey: string; + + _firestoreClient: FirestoreClient | undefined; + + /** @hideconstructor */ constructor( databaseIdOrApp: DatabaseId | FirebaseApp, - readonly _delegate: ExpFirestore, - private _persistenceProvider: PersistenceProvider + credentialsProvider: CredentialsProvider ) { - if (!(databaseIdOrApp instanceof DatabaseId)) { - this._appCompat = databaseIdOrApp as FirebaseApp; - } - } - - get _databaseId(): DatabaseId { - return this._delegate._databaseId; - } - - settings(settingsLiteral: PublicSettings): void { - const currentSettings = this._delegate._getSettings(); - if ( - !settingsLiteral.merge && - currentSettings.host !== settingsLiteral.host - ) { - logWarn( - 'You are overriding the original host. If you did not intend ' + - 'to override your settings, use {merge: true}.' - ); - } - - if (settingsLiteral.merge) { - settingsLiteral = { - ...currentSettings, - ...settingsLiteral - }; - // Remove the property from the settings once the merge is completed - delete settingsLiteral.merge; - } - - this._delegate._setSettings(settingsLiteral); - } - - useEmulator( - host: string, - port: number, - options: { - mockUserToken?: EmulatorMockTokenOptions | string; - } = {} - ): void { - connectFirestoreEmulator(this._delegate, host, port, options); - } - - enableNetwork(): Promise { - return enableNetwork(this._delegate); + super(databaseIdOrApp, credentialsProvider); + this._persistenceKey = + 'name' in databaseIdOrApp ? databaseIdOrApp.name : '[DEFAULT]'; } - disableNetwork(): Promise { - return disableNetwork(this._delegate); - } - - enablePersistence(settings?: PublicPersistenceSettings): Promise { - let synchronizeTabs = false; - let experimentalForceOwningTab = false; - - if (settings) { - synchronizeTabs = !!settings.synchronizeTabs; - experimentalForceOwningTab = !!settings.experimentalForceOwningTab; - - validateIsNotUsedTogether( - 'synchronizeTabs', - synchronizeTabs, - 'experimentalForceOwningTab', - experimentalForceOwningTab - ); - } - - return synchronizeTabs - ? this._persistenceProvider.enableMultiTabIndexedDbPersistence(this) - : this._persistenceProvider.enableIndexedDbPersistence( - this, - experimentalForceOwningTab - ); - } - - clearPersistence(): Promise { - return this._persistenceProvider.clearIndexedDbPersistence(this); - } - - terminate(): Promise { - if (this._appCompat) { - (this._appCompat as _FirebaseApp)._removeServiceInstance('firestore'); - (this._appCompat as _FirebaseApp)._removeServiceInstance('firestore-exp'); + _terminate(): Promise { + if (!this._firestoreClient) { + // The client must be initialized to ensure that all subsequent API + // usage throws an exception. + configureFirestore(this); } - return this._delegate._delete(); - } - - waitForPendingWrites(): Promise { - return waitForPendingWrites(this._delegate); - } - - onSnapshotsInSync(observer: PartialObserver): Unsubscribe; - onSnapshotsInSync(onSync: () => void): Unsubscribe; - onSnapshotsInSync(arg: unknown): Unsubscribe { - return onSnapshotsInSync(this._delegate, arg as PartialObserver); + return this._firestoreClient!.terminate(); } +} - get app(): FirebaseApp { - if (!this._appCompat) { +/** + * Initializes a new instance of {@link Firestore} with the provided settings. + * Can only be called before any other function, including + * {@link getFirestore}. If the custom settings are empty, this function is + * equivalent to calling {@link getFirestore}. + * + * @param app - The {@link @firebase/app#FirebaseApp} with which the {@link Firestore} instance will + * be associated. + * @param settings - A settings object to configure the {@link Firestore} instance. + * @returns A newly initialized {@link Firestore} instance. + */ +export function initializeFirestore( + app: FirebaseApp, + settings: FirestoreSettings +): Firestore { + const provider = _getProvider(app, 'firestore'); + + if (provider.isInitialized()) { + const existingInstance = provider.getImmediate(); + const initialSettings = provider.getOptions() as FirestoreSettings; + if (deepEqual(initialSettings, settings)) { + return existingInstance; + } else { throw new FirestoreError( Code.FAILED_PRECONDITION, - "Firestore was not initialized using the Firebase SDK. 'app' is " + - 'not available' - ); - } - return this._appCompat as FirebaseApp; - } - - INTERNAL = { - delete: () => this.terminate() - }; - - collection(pathString: string): PublicCollectionReference { - try { - return new CollectionReference( - this, - collection(this._delegate, pathString) - ); - } catch (e) { - throw replaceFunctionName(e, 'collection()', 'Firestore.collection()'); - } - } - - doc(pathString: string): PublicDocumentReference { - try { - return new DocumentReference(this, doc(this._delegate, pathString)); - } catch (e) { - throw replaceFunctionName(e, 'doc()', 'Firestore.doc()'); - } - } - - collectionGroup(collectionId: string): PublicQuery { - try { - return new Query(this, collectionGroup(this._delegate, collectionId)); - } catch (e) { - throw replaceFunctionName( - e, - 'collectionGroup()', - 'Firestore.collectionGroup()' + 'initializeFirestore() has already been called with ' + + 'different options. To avoid this error, call initializeFirestore() with the ' + + 'same options as when it was originally called, or call getFirestore() to return the' + + ' already initialized instance.' ); } } - runTransaction( - updateFunction: (transaction: PublicTransaction) => Promise - ): Promise { - return runTransaction(this._delegate, transaction => - updateFunction(new Transaction(this, transaction)) - ); - } - - batch(): PublicWriteBatch { - ensureFirestoreConfigured(this._delegate); - return new WriteBatch( - new ExpWriteBatch(this._delegate, mutations => - executeWrite(this._delegate, mutations) - ) - ); - } - - loadBundle( - bundleData: ArrayBuffer | ReadableStream | string - ): LoadBundleTask { + if ( + settings.cacheSizeBytes !== undefined && + settings.cacheSizeBytes !== CACHE_SIZE_UNLIMITED && + settings.cacheSizeBytes < LRU_MINIMUM_CACHE_SIZE_BYTES + ) { throw new FirestoreError( - Code.FAILED_PRECONDITION, - '"loadBundle()" does not exist, have you imported "firebase/firestore/bundle"?' + Code.INVALID_ARGUMENT, + `cacheSizeBytes must be at least ${LRU_MINIMUM_CACHE_SIZE_BYTES}` ); } - namedQuery(name: string): Promise | null> { - throw new FirestoreError( - Code.FAILED_PRECONDITION, - '"namedQuery()" does not exist, have you imported "firebase/firestore/bundle"?' - ); - } + return provider.initialize({ options: settings }); } -export class UserDataWriter extends AbstractUserDataWriter { - constructor(protected firestore: Firestore) { - super(); - } - - protected convertBytes(bytes: ByteString): Blob { - return new Blob(new Bytes(bytes)); - } +/** + * Returns the existing {@link Firestore} instance that is associated with the + * provided {@link @firebase/app#FirebaseApp}. If no instance exists, initializes a new + * instance with default settings. + * + * @param app - The {@link @firebase/app#FirebaseApp} instance that the returned {@link Firestore} + * instance is associated with. + * @returns The {@link Firestore} instance of the provided app. + */ +export function getFirestore(app: FirebaseApp = getApp()): Firestore { + return _getProvider(app, 'firestore').getImmediate() as Firestore; +} - protected convertReference(name: string): DocumentReference { - const key = this.convertDocumentKey(name, this.firestore._databaseId); - return DocumentReference.forKey(key, this.firestore, /* converter= */ null); - } +/** + * @internal + */ +export function ensureFirestoreConfigured( + firestore: Firestore +): FirestoreClient { + if (!firestore._firestoreClient) { + configureFirestore(firestore); + } + firestore._firestoreClient!.verifyNotTerminated(); + return firestore._firestoreClient as FirestoreClient; } -export function setLogLevel(level: PublicLogLevel): void { - setClientLogLevel(level); +export function configureFirestore(firestore: Firestore): void { + const settings = firestore._freezeSettings(); + debugAssert(!!settings.host, 'FirestoreSettings.host is not set'); + debugAssert( + !firestore._firestoreClient, + 'configureFirestore() called multiple times' + ); + + const databaseInfo = makeDatabaseInfo( + firestore._databaseId, + firestore._app?.options.appId || '', + firestore._persistenceKey, + settings + ); + firestore._firestoreClient = new FirestoreClient( + firestore._credentials, + firestore._queue, + databaseInfo + ); } /** - * A reference to a transaction. + * Attempts to enable persistent storage, if possible. + * + * Must be called before any other functions (other than + * {@link initializeFirestore}, {@link getFirestore} or + * {@link clearIndexedDbPersistence}. + * + * If this fails, `enableIndexedDbPersistence()` will reject the promise it + * returns. Note that even after this failure, the {@link Firestore} instance will + * remain usable, however offline persistence will be disabled. + * + * There are several reasons why this can fail, which can be identified by + * the `code` on the error. + * + * * failed-precondition: The app is already open in another browser tab. + * * unimplemented: The browser is incompatible with the offline + * persistence implementation. + * + * @param firestore - The {@link Firestore} instance to enable persistence for. + * @param persistenceSettings - Optional settings object to configure + * persistence. + * @returns A `Promise` that represents successfully enabling persistent storage. */ -export class Transaction implements PublicTransaction, Compat { - private _userDataWriter: UserDataWriter; - - constructor( - private readonly _firestore: Firestore, - readonly _delegate: ExpTransaction - ) { - this._userDataWriter = new UserDataWriter(_firestore); - } - - get( - documentRef: PublicDocumentReference - ): Promise> { - const ref = castReference(documentRef); - return this._delegate - .get(ref) - .then( - result => - new DocumentSnapshot( - this._firestore, - new ExpDocumentSnapshot( - this._firestore._delegate, - this._userDataWriter, - result._key, - result._document, - result.metadata, - ref.converter - ) - ) - ); - } - - set( - documentRef: DocumentReference, - data: Partial, - options: PublicSetOptions - ): Transaction; - set(documentRef: DocumentReference, data: T): Transaction; - set( - documentRef: PublicDocumentReference, - data: T | Partial, - options?: PublicSetOptions - ): Transaction { - const ref = castReference(documentRef); - if (options) { - validateSetOptions('Transaction.set', options); - this._delegate.set(ref, data as PartialWithFieldValue, options); - } else { - this._delegate.set(ref, data as WithFieldValue); - } - return this; - } - - update( - documentRef: PublicDocumentReference, - data: PublicUpdateData - ): Transaction; - update( - documentRef: PublicDocumentReference, - field: string | PublicFieldPath, - value: unknown, - ...moreFieldsAndValues: unknown[] - ): Transaction; - update( - documentRef: PublicDocumentReference, - dataOrField: unknown, - value?: unknown, - ...moreFieldsAndValues: unknown[] - ): Transaction { - const ref = castReference(documentRef); - if (arguments.length === 2) { - this._delegate.update(ref, dataOrField as PublicUpdateData); - } else { - this._delegate.update( - ref, - dataOrField as string | ExpFieldPath, - value, - ...moreFieldsAndValues - ); - } - - return this; - } - - delete(documentRef: PublicDocumentReference): Transaction { - const ref = castReference(documentRef); - this._delegate.delete(ref); - return this; - } +export function enableIndexedDbPersistence( + firestore: Firestore, + persistenceSettings?: PersistenceSettings +): Promise { + firestore = cast(firestore, Firestore); + verifyNotInitialized(firestore); + + const client = ensureFirestoreConfigured(firestore); + const settings = firestore._freezeSettings(); + + const onlineComponentProvider = new OnlineComponentProvider(); + const offlineComponentProvider = new IndexedDbOfflineComponentProvider( + onlineComponentProvider, + settings.cacheSizeBytes, + persistenceSettings?.forceOwnership + ); + return setPersistenceProviders( + client, + onlineComponentProvider, + offlineComponentProvider + ); } -export class WriteBatch implements PublicWriteBatch, Compat { - constructor(readonly _delegate: ExpWriteBatch) {} - set( - documentRef: DocumentReference, - data: Partial, - options: PublicSetOptions - ): WriteBatch; - set(documentRef: DocumentReference, data: T): WriteBatch; - set( - documentRef: PublicDocumentReference, - data: T | Partial, - options?: PublicSetOptions - ): WriteBatch { - const ref = castReference(documentRef); - if (options) { - validateSetOptions('WriteBatch.set', options); - this._delegate.set(ref, data as PartialWithFieldValue, options); - } else { - this._delegate.set(ref, data as WithFieldValue); - } - return this; - } - - update( - documentRef: PublicDocumentReference, - data: PublicUpdateData - ): WriteBatch; - update( - documentRef: PublicDocumentReference, - field: string | PublicFieldPath, - value: unknown, - ...moreFieldsAndValues: unknown[] - ): WriteBatch; - update( - documentRef: PublicDocumentReference, - dataOrField: string | PublicFieldPath | PublicUpdateData, - value?: unknown, - ...moreFieldsAndValues: unknown[] - ): WriteBatch { - const ref = castReference(documentRef); - if (arguments.length === 2) { - this._delegate.update(ref, dataOrField as PublicUpdateData); - } else { - this._delegate.update( - ref, - dataOrField as string | ExpFieldPath, - value, - ...moreFieldsAndValues - ); - } - return this; - } - - delete(documentRef: PublicDocumentReference): WriteBatch { - const ref = castReference(documentRef); - this._delegate.delete(ref); - return this; - } - - commit(): Promise { - return this._delegate.commit(); - } +/** + * Attempts to enable multi-tab persistent storage, if possible. If enabled + * across all tabs, all operations share access to local persistence, including + * shared execution of queries and latency-compensated local document updates + * across all connected instances. + * + * If this fails, `enableMultiTabIndexedDbPersistence()` will reject the promise + * it returns. Note that even after this failure, the {@link Firestore} instance will + * remain usable, however offline persistence will be disabled. + * + * There are several reasons why this can fail, which can be identified by + * the `code` on the error. + * + * * failed-precondition: The app is already open in another browser tab and + * multi-tab is not enabled. + * * unimplemented: The browser is incompatible with the offline + * persistence implementation. + * + * @param firestore - The {@link Firestore} instance to enable persistence for. + * @returns A `Promise` that represents successfully enabling persistent + * storage. + */ +export function enableMultiTabIndexedDbPersistence( + firestore: Firestore +): Promise { + firestore = cast(firestore, Firestore); + verifyNotInitialized(firestore); + + const client = ensureFirestoreConfigured(firestore); + const settings = firestore._freezeSettings(); + + const onlineComponentProvider = new OnlineComponentProvider(); + const offlineComponentProvider = new MultiTabOfflineComponentProvider( + onlineComponentProvider, + settings.cacheSizeBytes + ); + return setPersistenceProviders( + client, + onlineComponentProvider, + offlineComponentProvider + ); } /** - * Wraps a `PublicFirestoreDataConverter` translating the types from the - * experimental SDK into corresponding types from the Classic SDK before passing - * them to the wrapped converter. + * Registers both the `OfflineComponentProvider` and `OnlineComponentProvider`. + * If the operation fails with a recoverable error (see + * `canRecoverFromIndexedDbError()` below), the returned Promise is rejected + * but the client remains usable. */ -class FirestoreDataConverter - implements - UntypedFirestoreDataConverter, - Compat> -{ - private static readonly INSTANCES = new WeakMap(); - - private constructor( - private readonly _firestore: Firestore, - private readonly _userDataWriter: UserDataWriter, - readonly _delegate: PublicFirestoreDataConverter - ) {} +function setPersistenceProviders( + client: FirestoreClient, + onlineComponentProvider: OnlineComponentProvider, + offlineComponentProvider: OfflineComponentProvider +): Promise { + const persistenceResult = new Deferred(); + return client.asyncQueue + .enqueue(async () => { + try { + await setOfflineComponentProvider(client, offlineComponentProvider); + await setOnlineComponentProvider(client, onlineComponentProvider); + persistenceResult.resolve(); + } catch (e) { + if (!canFallbackFromIndexedDbError(e)) { + throw e; + } + console.warn( + 'Error enabling offline persistence. Falling back to ' + + 'persistence disabled: ' + + e + ); + persistenceResult.reject(e); + } + }) + .then(() => persistenceResult.promise); +} - fromFirestore( - snapshot: ExpQueryDocumentSnapshot, - options?: PublicSnapshotOptions - ): U { - const expSnapshot = new ExpQueryDocumentSnapshot( - this._firestore._delegate, - this._userDataWriter, - snapshot._key, - snapshot._document, - snapshot.metadata, - /* converter= */ null +/** + * Decides whether the provided error allows us to gracefully disable + * persistence (as opposed to crashing the client). + */ +function canFallbackFromIndexedDbError( + error: FirestoreError | DOMException +): boolean { + if (error.name === 'FirebaseError') { + return ( + error.code === Code.FAILED_PRECONDITION || + error.code === Code.UNIMPLEMENTED ); - return this._delegate.fromFirestore( - new QueryDocumentSnapshot(this._firestore, expSnapshot), - options ?? {} + } else if ( + typeof DOMException !== 'undefined' && + error instanceof DOMException + ) { + // There are a few known circumstances where we can open IndexedDb but + // trying to read/write will fail (e.g. quota exceeded). For + // well-understood cases, we attempt to detect these and then gracefully + // fall back to memory persistence. + // NOTE: Rather than continue to add to this list, we could decide to + // always fall back, with the risk that we might accidentally hide errors + // representing actual SDK bugs. + return ( + // When the browser is out of quota we could get either quota exceeded + // or an aborted error depending on whether the error happened during + // schema migration. + error.code === DOM_EXCEPTION_QUOTA_EXCEEDED || + error.code === DOM_EXCEPTION_ABORTED || + // Firefox Private Browsing mode disables IndexedDb and returns + // INVALID_STATE for any usage. + error.code === DOM_EXCEPTION_INVALID_STATE ); } - toFirestore(modelObject: WithFieldValue): PublicDocumentData; - toFirestore( - modelObject: PartialWithFieldValue, - options: PublicSetOptions - ): PublicDocumentData; - toFirestore( - modelObject: WithFieldValue | PartialWithFieldValue, - options?: PublicSetOptions - ): PublicDocumentData { - if (!options) { - return this._delegate.toFirestore(modelObject as U); - } else { - return this._delegate.toFirestore(modelObject as Partial, options); - } - } - - // Use the same instance of `FirestoreDataConverter` for the given instances - // of `Firestore` and `PublicFirestoreDataConverter` so that isEqual() will - // compare equal for two objects created with the same converter instance. - static getInstance( - firestore: Firestore, - converter: PublicFirestoreDataConverter - ): FirestoreDataConverter { - const converterMapByFirestore = FirestoreDataConverter.INSTANCES; - let untypedConverterByConverter = converterMapByFirestore.get(firestore); - if (!untypedConverterByConverter) { - untypedConverterByConverter = new WeakMap(); - converterMapByFirestore.set(firestore, untypedConverterByConverter); - } - - let instance = untypedConverterByConverter.get(converter); - if (!instance) { - instance = new FirestoreDataConverter( - firestore, - new UserDataWriter(firestore), - converter - ); - untypedConverterByConverter.set(converter, instance); - } - - return instance; - } + return true; } /** - * A reference to a particular document in a collection in the database. + * Clears the persistent storage. This includes pending writes and cached + * documents. + * + * Must be called while the {@link Firestore} instance is not started (after the app is + * terminated or when the app is first initialized). On startup, this function + * must be called before other functions (other than {@link + * initializeFirestore} or {@link getFirestore})). If the {@link Firestore} + * instance is still running, the promise will be rejected with the error code + * of `failed-precondition`. + * + * Note: `clearIndexedDbPersistence()` is primarily intended to help write + * reliable tests that use Cloud Firestore. It uses an efficient mechanism for + * dropping existing data but does not attempt to securely overwrite or + * otherwise make cached data unrecoverable. For applications that are sensitive + * to the disclosure of cached data in between user sessions, we strongly + * recommend not enabling persistence at all. + * + * @param firestore - The {@link Firestore} instance to clear persistence for. + * @returns A `Promise` that is resolved when the persistent storage is + * cleared. Otherwise, the promise is rejected with an error. */ -export class DocumentReference - implements PublicDocumentReference, Compat> -{ - private _userDataWriter: UserDataWriter; - - constructor( - readonly firestore: Firestore, - readonly _delegate: ExpDocumentReference - ) { - this._userDataWriter = new UserDataWriter(firestore); - } - - static forPath( - path: ResourcePath, - firestore: Firestore, - converter: UntypedFirestoreDataConverter | null - ): DocumentReference { - if (path.length % 2 !== 0) { - throw new FirestoreError( - Code.INVALID_ARGUMENT, - 'Invalid document reference. Document ' + - 'references must have an even number of segments, but ' + - `${path.canonicalString()} has ${path.length}` - ); - } - return new DocumentReference( - firestore, - new ExpDocumentReference( - firestore._delegate, - converter, - new DocumentKey(path) - ) - ); - } - - static forKey( - key: DocumentKey, - firestore: Firestore, - converter: UntypedFirestoreDataConverter | null - ): DocumentReference { - return new DocumentReference( - firestore, - new ExpDocumentReference(firestore._delegate, converter, key) +export function clearIndexedDbPersistence(firestore: Firestore): Promise { + if (firestore._initialized && !firestore._terminated) { + throw new FirestoreError( + Code.FAILED_PRECONDITION, + 'Persistence can only be cleared before a Firestore instance is ' + + 'initialized or after it is terminated.' ); } - get id(): string { - return this._delegate.id; - } - - get parent(): PublicCollectionReference { - return new CollectionReference(this.firestore, this._delegate.parent); - } - - get path(): string { - return this._delegate.path; - } - - collection( - pathString: string - ): PublicCollectionReference { + const deferred = new Deferred(); + firestore._queue.enqueueAndForgetEvenWhileRestricted(async () => { try { - return new CollectionReference( - this.firestore, - collection(this._delegate, pathString) + await indexedDbClearPersistence( + indexedDbStoragePrefix(firestore._databaseId, firestore._persistenceKey) ); + deferred.resolve(); } catch (e) { - throw replaceFunctionName( - e, - 'collection()', - 'DocumentReference.collection()' - ); + deferred.reject(e); } - } - - isEqual(other: PublicDocumentReference): boolean { - other = getModularInstance>(other); - - if (!(other instanceof ExpDocumentReference)) { - return false; - } - return refEqual(this._delegate, other); - } - - set(value: Partial, options: PublicSetOptions): Promise; - set(value: T): Promise; - set(value: T | Partial, options?: PublicSetOptions): Promise { - options = validateSetOptions('DocumentReference.set', options); - try { - if (options) { - return setDoc( - this._delegate, - value as PartialWithFieldValue, - options - ); - } else { - return setDoc(this._delegate, value as WithFieldValue); - } - } catch (e) { - throw replaceFunctionName(e, 'setDoc()', 'DocumentReference.set()'); - } - } - - update(value: PublicUpdateData): Promise; - update( - field: string | PublicFieldPath, - value: unknown, - ...moreFieldsAndValues: unknown[] - ): Promise; - update( - fieldOrUpdateData: string | PublicFieldPath | PublicUpdateData, - value?: unknown, - ...moreFieldsAndValues: unknown[] - ): Promise { - try { - if (arguments.length === 1) { - return updateDoc(this._delegate, fieldOrUpdateData as PublicUpdateData); - } else { - return updateDoc( - this._delegate, - fieldOrUpdateData as string | ExpFieldPath, - value, - ...moreFieldsAndValues - ); - } - } catch (e) { - throw replaceFunctionName(e, 'updateDoc()', 'DocumentReference.update()'); - } - } - - delete(): Promise { - return deleteDoc(this._delegate); - } - - onSnapshot(observer: PartialObserver>): Unsubscribe; - onSnapshot( - options: PublicSnapshotListenOptions, - observer: PartialObserver> - ): Unsubscribe; - onSnapshot( - onNext: NextFn>, - onError?: ErrorFn, - onCompletion?: CompleteFn - ): Unsubscribe; - onSnapshot( - options: PublicSnapshotListenOptions, - onNext: NextFn>, - onError?: ErrorFn, - onCompletion?: CompleteFn - ): Unsubscribe; - - onSnapshot(...args: unknown[]): Unsubscribe { - const options = extractSnapshotOptions(args); - const observer = wrapObserver, ExpDocumentSnapshot>( - args, - result => - new DocumentSnapshot( - this.firestore, - new ExpDocumentSnapshot( - this.firestore._delegate, - this._userDataWriter, - result._key, - result._document, - result.metadata, - this._delegate.converter - ) - ) - ); - return onSnapshot(this._delegate, options, observer); - } - - get(options?: PublicGetOptions): Promise> { - let snap: Promise>; - if (options?.source === 'cache') { - snap = getDocFromCache(this._delegate); - } else if (options?.source === 'server') { - snap = getDocFromServer(this._delegate); - } else { - snap = getDoc(this._delegate); - } - - return snap.then( - result => - new DocumentSnapshot( - this.firestore, - new ExpDocumentSnapshot( - this.firestore._delegate, - this._userDataWriter, - result._key, - result._document, - result.metadata, - this._delegate.converter as UntypedFirestoreDataConverter - ) - ) - ); - } - - withConverter(converter: null): PublicDocumentReference; - withConverter( - converter: PublicFirestoreDataConverter - ): PublicDocumentReference; - withConverter( - converter: PublicFirestoreDataConverter | null - ): PublicDocumentReference { - return new DocumentReference( - this.firestore, - converter - ? this._delegate.withConverter( - FirestoreDataConverter.getInstance(this.firestore, converter) - ) - : (this._delegate.withConverter(null) as ExpDocumentReference) - ); - } + }); + return deferred.promise; } /** - * Replaces the function name in an error thrown by the firestore-exp API - * with the function names used in the classic API. + * Waits until all currently pending writes for the active user have been + * acknowledged by the backend. + * + * The returned promise resolves immediately if there are no outstanding writes. + * Otherwise, the promise waits for all previously issued writes (including + * those written in a previous app session), but it does not wait for writes + * that were added after the function is called. If you want to wait for + * additional writes, call `waitForPendingWrites()` again. + * + * Any outstanding `waitForPendingWrites()` promises are rejected during user + * changes. + * + * @returns A `Promise` which resolves when all currently pending writes have been + * acknowledged by the backend. */ -function replaceFunctionName( - e: Error, - original: string | RegExp, - updated: string -): Error { - e.message = e.message.replace(original, updated); - return e; +export function waitForPendingWrites(firestore: Firestore): Promise { + firestore = cast(firestore, Firestore); + const client = ensureFirestoreConfigured(firestore); + return firestoreClientWaitForPendingWrites(client); } /** - * Iterates the list of arguments from an `onSnapshot` call and returns the - * first argument that may be an `SnapshotListenOptions` object. Returns an - * empty object if none is found. + * Re-enables use of the network for this {@link Firestore} instance after a prior + * call to {@link disableNetwork}. + * + * @returns A `Promise` that is resolved once the network has been enabled. */ -export function extractSnapshotOptions( - args: unknown[] -): PublicSnapshotListenOptions { - for (const arg of args) { - if (typeof arg === 'object' && !isPartialObserver(arg)) { - return arg as PublicSnapshotListenOptions; - } - } - return {}; +export function enableNetwork(firestore: Firestore): Promise { + firestore = cast(firestore, Firestore); + const client = ensureFirestoreConfigured(firestore); + return firestoreClientEnableNetwork(client); } /** - * Creates an observer that can be passed to the firestore-exp SDK. The - * observer converts all observed values into the format expected by the classic - * SDK. + * Disables network usage for this instance. It can be re-enabled via {@link + * enableNetwork}. While the network is disabled, any snapshot listeners, + * `getDoc()` or `getDocs()` calls will return results from cache, and any write + * operations will be queued until the network is restored. * - * @param args - The list of arguments from an `onSnapshot` call. - * @param wrapper - The function that converts the firestore-exp type into the - * type used by this shim. + * @returns A `Promise` that is resolved once the network has been disabled. */ -export function wrapObserver( - args: unknown[], - wrapper: (val: ExpType) => CompatType -): PartialObserver { - let userObserver: PartialObserver; - if (isPartialObserver(args[0])) { - userObserver = args[0] as PartialObserver; - } else if (isPartialObserver(args[1])) { - userObserver = args[1]; - } else if (typeof args[0] === 'function') { - userObserver = { - next: args[0] as NextFn | undefined, - error: args[1] as ErrorFn | undefined, - complete: args[2] as CompleteFn | undefined - }; - } else { - userObserver = { - next: args[1] as NextFn | undefined, - error: args[2] as ErrorFn | undefined, - complete: args[3] as CompleteFn | undefined - }; - } - - return { - next: val => { - if (userObserver!.next) { - userObserver!.next(wrapper(val)); - } - }, - error: userObserver.error?.bind(userObserver), - complete: userObserver.complete?.bind(userObserver) - }; +export function disableNetwork(firestore: Firestore): Promise { + firestore = cast(firestore, Firestore); + const client = ensureFirestoreConfigured(firestore); + return firestoreClientDisableNetwork(client); } /** - * Options interface that can be provided to configure the deserialization of - * DocumentSnapshots. + * Terminates the provided {@link Firestore} instance. + * + * After calling `terminate()` only the `clearIndexedDbPersistence()` function + * may be used. Any other function will throw a `FirestoreError`. + * + * To restart after termination, create a new instance of FirebaseFirestore with + * {@link getFirestore}. + * + * Termination does not cancel any pending writes, and any promises that are + * awaiting a response from the server will not be resolved. If you have + * persistence enabled, the next time you start this instance, it will resume + * sending these writes to the server. + * + * Note: Under normal circumstances, calling `terminate()` is not required. This + * function is useful only when you want to force this instance to release all + * of its resources or in combination with `clearIndexedDbPersistence()` to + * ensure that all local state is destroyed between test runs. + * + * @returns A `Promise` that is resolved when the instance has been successfully + * terminated. */ -export interface SnapshotOptions extends PublicSnapshotOptions {} - -export class DocumentSnapshot - implements PublicDocumentSnapshot, Compat> -{ - constructor( - private readonly _firestore: Firestore, - readonly _delegate: ExpDocumentSnapshot - ) {} - - get ref(): DocumentReference { - return new DocumentReference(this._firestore, this._delegate.ref); - } - - get id(): string { - return this._delegate.id; - } - - get metadata(): SnapshotMetadata { - return this._delegate.metadata; - } - - get exists(): boolean { - return this._delegate.exists(); - } - - data(options?: PublicSnapshotOptions): T | undefined { - return this._delegate.data(options); - } - - get( - fieldPath: string | PublicFieldPath, - options?: PublicSnapshotOptions - // We are using `any` here to avoid an explicit cast by our users. - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ): any { - return this._delegate.get(fieldPath as string | ExpFieldPath, options); - } - - isEqual(other: DocumentSnapshot): boolean { - return snapshotEqual(this._delegate, other._delegate); - } -} - -export class QueryDocumentSnapshot - extends DocumentSnapshot - implements PublicQueryDocumentSnapshot -{ - data(options?: PublicSnapshotOptions): T { - const data = this._delegate.data(options); - debugAssert( - data !== undefined, - 'Document in a QueryDocumentSnapshot should exist' - ); - return data; - } +export function terminate(firestore: Firestore): Promise { + _removeServiceInstance(firestore.app, 'firestore'); + return firestore._delete(); } -export class Query - implements PublicQuery, Compat> -{ - private readonly _userDataWriter: UserDataWriter; - - constructor(readonly firestore: Firestore, readonly _delegate: ExpQuery) { - this._userDataWriter = new UserDataWriter(firestore); - } - - where( - fieldPath: string | FieldPath, - opStr: PublicWhereFilterOp, - value: unknown - ): Query { - try { - // The "as string" cast is a little bit of a hack. `where` accepts the - // FieldPath Compat type as input, but is not typed as such in order to - // not expose this via our public typings file. - return new Query( - this.firestore, - query(this._delegate, where(fieldPath as string, opStr, value)) - ); - } catch (e) { - throw replaceFunctionName(e, /(orderBy|where)\(\)/, 'Query.$1()'); - } - } - - orderBy( - fieldPath: string | FieldPath, - directionStr?: PublicOrderByDirection - ): Query { - try { - // The "as string" cast is a little bit of a hack. `orderBy` accepts the - // FieldPath Compat type as input, but is not typed as such in order to - // not expose this via our public typings file. - return new Query( - this.firestore, - query(this._delegate, orderBy(fieldPath as string, directionStr)) - ); - } catch (e) { - throw replaceFunctionName(e, /(orderBy|where)\(\)/, 'Query.$1()'); - } - } - - limit(n: number): Query { - try { - return new Query(this.firestore, query(this._delegate, limit(n))); - } catch (e) { - throw replaceFunctionName(e, 'limit()', 'Query.limit()'); - } - } - - limitToLast(n: number): Query { - try { - return new Query( - this.firestore, - query(this._delegate, limitToLast(n)) - ); - } catch (e) { - throw replaceFunctionName(e, 'limitToLast()', 'Query.limitToLast()'); - } - } - - startAt(...args: any[]): Query { - try { - return new Query(this.firestore, query(this._delegate, startAt(...args))); - } catch (e) { - throw replaceFunctionName(e, 'startAt()', 'Query.startAt()'); - } - } - - startAfter(...args: any[]): Query { - try { - return new Query( - this.firestore, - query(this._delegate, startAfter(...args)) - ); - } catch (e) { - throw replaceFunctionName(e, 'startAfter()', 'Query.startAfter()'); - } - } - - endBefore(...args: any[]): Query { - try { - return new Query( - this.firestore, - query(this._delegate, endBefore(...args)) - ); - } catch (e) { - throw replaceFunctionName(e, 'endBefore()', 'Query.endBefore()'); - } - } - - endAt(...args: any[]): Query { - try { - return new Query(this.firestore, query(this._delegate, endAt(...args))); - } catch (e) { - throw replaceFunctionName(e, 'endAt()', 'Query.endAt()'); - } - } - - isEqual(other: PublicQuery): boolean { - return queryEqual(this._delegate, (other as Query)._delegate); - } - - get(options?: PublicGetOptions): Promise> { - let query: Promise>; - if (options?.source === 'cache') { - query = getDocsFromCache(this._delegate); - } else if (options?.source === 'server') { - query = getDocsFromServer(this._delegate); - } else { - query = getDocs(this._delegate); - } - return query.then( - result => - new QuerySnapshot( - this.firestore, - new ExpQuerySnapshot( - this.firestore._delegate, - this._userDataWriter, - this._delegate, - result._snapshot - ) - ) - ); - } - - onSnapshot(observer: PartialObserver>): Unsubscribe; - onSnapshot( - options: PublicSnapshotListenOptions, - observer: PartialObserver> - ): Unsubscribe; - onSnapshot( - onNext: NextFn>, - onError?: ErrorFn, - onCompletion?: CompleteFn - ): Unsubscribe; - onSnapshot( - options: PublicSnapshotListenOptions, - onNext: NextFn>, - onError?: ErrorFn, - onCompletion?: CompleteFn - ): Unsubscribe; - - onSnapshot(...args: unknown[]): Unsubscribe { - const options = extractSnapshotOptions(args); - const observer = wrapObserver, ExpQuerySnapshot>( - args, - snap => - new QuerySnapshot( - this.firestore, - new ExpQuerySnapshot( - this.firestore._delegate, - this._userDataWriter, - this._delegate, - snap._snapshot - ) - ) - ); - return onSnapshot(this._delegate, options, observer); - } - - withConverter(converter: null): Query; - withConverter(converter: PublicFirestoreDataConverter): Query; - withConverter( - converter: PublicFirestoreDataConverter | null - ): Query { - return new Query( - this.firestore, - converter - ? this._delegate.withConverter( - FirestoreDataConverter.getInstance(this.firestore, converter) - ) - : (this._delegate.withConverter(null) as ExpQuery) - ); - } -} - -export class DocumentChange - implements PublicDocumentChange, Compat> -{ - constructor( - private readonly _firestore: Firestore, - readonly _delegate: ExpDocumentChange - ) {} - - get type(): PublicDocumentChangeType { - return this._delegate.type; - } - - get doc(): QueryDocumentSnapshot { - return new QueryDocumentSnapshot(this._firestore, this._delegate.doc); - } - - get oldIndex(): number { - return this._delegate.oldIndex; - } - - get newIndex(): number { - return this._delegate.newIndex; - } +/** + * Loads a Firestore bundle into the local cache. + * + * @param firestore - The {@link Firestore} instance to load bundles for for. + * @param bundleData - An object representing the bundle to be loaded. Valid objects are + * `ArrayBuffer`, `ReadableStream` or `string`. + * + * @returns + * A `LoadBundleTask` object, which notifies callers with progress updates, and completion + * or error events. It can be used as a `Promise`. + */ +export function loadBundle( + firestore: Firestore, + bundleData: ReadableStream | ArrayBuffer | string +): LoadBundleTask { + firestore = cast(firestore, Firestore); + const client = ensureFirestoreConfigured(firestore); + const resultTask = new LoadBundleTask(); + firestoreClientLoadBundle( + client, + firestore._databaseId, + bundleData, + resultTask + ); + return resultTask; } -export class QuerySnapshot - implements PublicQuerySnapshot, Compat> -{ - constructor( - readonly _firestore: Firestore, - readonly _delegate: ExpQuerySnapshot - ) {} - - get query(): Query { - return new Query(this._firestore, this._delegate.query); - } - - get metadata(): SnapshotMetadata { - return this._delegate.metadata; - } - - get size(): number { - return this._delegate.size; - } - - get empty(): boolean { - return this._delegate.empty; - } - - get docs(): Array> { - return this._delegate.docs.map( - doc => new QueryDocumentSnapshot(this._firestore, doc) - ); - } - - docChanges( - options?: PublicSnapshotListenOptions - ): Array> { - return this._delegate - .docChanges(options) - .map(docChange => new DocumentChange(this._firestore, docChange)); - } - - forEach( - callback: (result: QueryDocumentSnapshot) => void, - thisArg?: unknown - ): void { - this._delegate.forEach(snapshot => { - callback.call( - thisArg, - new QueryDocumentSnapshot(this._firestore, snapshot) - ); - }); - } - - isEqual(other: QuerySnapshot): boolean { - return snapshotEqual(this._delegate, other._delegate); - } +/** + * Reads a Firestore {@link Query} from local cache, identified by the given name. + * + * The named queries are packaged into bundles on the server side (along + * with resulting documents), and loaded to local cache using `loadBundle`. Once in local + * cache, use this method to extract a {@link Query} by name. + */ +export function namedQuery( + firestore: Firestore, + name: string +): Promise { + firestore = cast(firestore, Firestore); + const client = ensureFirestoreConfigured(firestore); + return firestoreClientGetNamedQuery(client, name).then(namedQuery => { + if (!namedQuery) { + return null; + } + + return new Query(firestore, null, namedQuery.query); + }); } -export class CollectionReference - extends Query - implements PublicCollectionReference -{ - constructor( - readonly firestore: Firestore, - readonly _delegate: ExpCollectionReference - ) { - super(firestore, _delegate); - } - - get id(): string { - return this._delegate.id; - } - - get path(): string { - return this._delegate.path; - } - - get parent(): DocumentReference | null { - const docRef = this._delegate.parent; - return docRef ? new DocumentReference(this.firestore, docRef) : null; - } - - doc(documentPath?: string): DocumentReference { - try { - if (documentPath === undefined) { - // Call `doc` without `documentPath` if `documentPath` is `undefined` - // as `doc` validates the number of arguments to prevent users from - // accidentally passing `undefined`. - return new DocumentReference(this.firestore, doc(this._delegate)); - } else { - return new DocumentReference( - this.firestore, - doc(this._delegate, documentPath) - ); - } - } catch (e) { - throw replaceFunctionName(e, 'doc()', 'CollectionReference.doc()'); - } - } - - add(data: T): Promise> { - return addDoc(this._delegate, data as WithFieldValue).then( - docRef => new DocumentReference(this.firestore, docRef) - ); - } - - isEqual(other: CollectionReference): boolean { - return refEqual(this._delegate, other._delegate); - } - - withConverter(converter: null): CollectionReference; - withConverter( - converter: PublicFirestoreDataConverter - ): CollectionReference; - withConverter( - converter: PublicFirestoreDataConverter | null - ): CollectionReference { - return new CollectionReference( - this.firestore, - converter - ? this._delegate.withConverter( - FirestoreDataConverter.getInstance(this.firestore, converter) - ) - : (this._delegate.withConverter(null) as ExpCollectionReference) +function verifyNotInitialized(firestore: Firestore): void { + if (firestore._initialized || firestore._terminated) { + throw new FirestoreError( + Code.FAILED_PRECONDITION, + 'Firestore has already been started and persistence can no longer be ' + + 'enabled. You can only enable persistence before calling any other ' + + 'methods on a Firestore object.' ); } } - -function castReference( - documentRef: PublicDocumentReference -): ExpDocumentReference { - return cast>(documentRef, ExpDocumentReference); -} diff --git a/packages/firestore/src/api/field_path.ts b/packages/firestore/src/api/field_path.ts index 2b1ef4442fc..7c14d757aae 100644 --- a/packages/firestore/src/api/field_path.ts +++ b/packages/firestore/src/api/field_path.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2017 Google LLC + * 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. @@ -15,49 +15,4 @@ * limitations under the License. */ -import { FieldPath as PublicFieldPath } from '@firebase/firestore-types'; -import { Compat, getModularInstance } from '@firebase/util'; - -import { FieldPath as ExpFieldPath } from '../../exp/index'; -import { FieldPath as InternalFieldPath } from '../model/path'; - -// The objects that are a part of this API are exposed to third-parties as -// compiled javascript so we want to flag our private members with a leading -// underscore to discourage their use. - -/** - * A `FieldPath` refers to a field in a document. The path may consist of a - * single field name (referring to a top-level field in the document), or a list - * of field names (referring to a nested field in the document). - */ -export class FieldPath implements PublicFieldPath, Compat { - readonly _delegate: ExpFieldPath; - /** - * Creates a FieldPath from the provided field names. If more than one field - * name is provided, the path will point to a nested field in a document. - * - * @param fieldNames - A list of field names. - */ - constructor(...fieldNames: string[]) { - this._delegate = new ExpFieldPath(...fieldNames); - } - - static documentId(): FieldPath { - /** - * Internal Note: The backend doesn't technically support querying by - * document ID. Instead it queries by the entire document name (full path - * included), but in the cases we currently support documentId(), the net - * effect is the same. - */ - return new FieldPath(InternalFieldPath.keyField().canonicalString()); - } - - isEqual(other: PublicFieldPath): boolean { - other = getModularInstance(other); - - if (!(other instanceof ExpFieldPath)) { - return false; - } - return this._delegate._internalPath.isEqual(other._internalPath); - } -} +export { FieldPath, documentId } from '../lite-api/field_path'; diff --git a/packages/firestore/src/api/field_value.ts b/packages/firestore/src/api/field_value.ts index 6e13c0e438b..d7d20d51cbb 100644 --- a/packages/firestore/src/api/field_value.ts +++ b/packages/firestore/src/api/field_value.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2017 Google LLC + * 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. @@ -15,52 +15,4 @@ * limitations under the License. */ -import { FieldValue as PublicFieldValue } from '@firebase/firestore-types'; -import { Compat } from '@firebase/util'; - -import { - arrayRemove, - arrayUnion, - deleteField, - FieldValue as FieldValue1, - increment, - serverTimestamp -} from '../../exp/index'; - -export class FieldValue implements PublicFieldValue, Compat { - static serverTimestamp(): FieldValue { - const delegate = serverTimestamp(); - delegate._methodName = 'FieldValue.serverTimestamp'; - return new FieldValue(delegate); - } - - static delete(): FieldValue { - const delegate = deleteField(); - delegate._methodName = 'FieldValue.delete'; - return new FieldValue(delegate); - } - - static arrayUnion(...elements: unknown[]): FieldValue { - const delegate = arrayUnion(...elements); - delegate._methodName = 'FieldValue.arrayUnion'; - return new FieldValue(delegate); - } - - static arrayRemove(...elements: unknown[]): FieldValue { - const delegate = arrayRemove(...elements); - delegate._methodName = 'FieldValue.arrayRemove'; - return new FieldValue(delegate); - } - - static increment(n: number): FieldValue { - const delegate = increment(n); - delegate._methodName = 'FieldValue.increment'; - return new FieldValue(delegate); - } - - constructor(readonly _delegate: FieldValue1) {} - - isEqual(other: FieldValue): boolean { - return this._delegate.isEqual(other._delegate); - } -} +export { FieldValue } from '../lite-api/field_value'; diff --git a/packages/firestore/src/exp/field_value_impl.ts b/packages/firestore/src/api/field_value_impl.ts similarity index 94% rename from packages/firestore/src/exp/field_value_impl.ts rename to packages/firestore/src/api/field_value_impl.ts index 0259fdd8e13..4689352f840 100644 --- a/packages/firestore/src/exp/field_value_impl.ts +++ b/packages/firestore/src/api/field_value_impl.ts @@ -21,4 +21,4 @@ export { arrayUnion, serverTimestamp, deleteField -} from '../lite/field_value_impl'; +} from '../lite-api/field_value_impl'; diff --git a/packages/firestore/src/api/geo_point.ts b/packages/firestore/src/api/geo_point.ts index a0709fabf30..672e7dffeff 100644 --- a/packages/firestore/src/api/geo_point.ts +++ b/packages/firestore/src/api/geo_point.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2017 Google LLC + * 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. @@ -15,4 +15,4 @@ * limitations under the License. */ -export { GeoPoint } from '../../exp/index'; +export { GeoPoint } from '../lite-api/geo_point'; diff --git a/packages/firestore/src/exp/query.ts b/packages/firestore/src/api/query.ts similarity index 96% rename from packages/firestore/src/exp/query.ts rename to packages/firestore/src/api/query.ts index 6ba2d791075..b8a7cc90720 100644 --- a/packages/firestore/src/exp/query.ts +++ b/packages/firestore/src/api/query.ts @@ -29,4 +29,4 @@ export { query, QueryConstraint, QueryConstraintType -} from '../lite/query'; +} from '../lite-api/query'; diff --git a/packages/firestore/src/exp/reference.ts b/packages/firestore/src/api/reference.ts similarity index 96% rename from packages/firestore/src/exp/reference.ts rename to packages/firestore/src/api/reference.ts index 735075fae76..b9dd0b078cc 100644 --- a/packages/firestore/src/exp/reference.ts +++ b/packages/firestore/src/api/reference.ts @@ -29,4 +29,4 @@ export { WithFieldValue, PartialWithFieldValue, refEqual -} from '../lite/reference'; +} from '../lite-api/reference'; diff --git a/packages/firestore/src/exp/reference_impl.ts b/packages/firestore/src/api/reference_impl.ts similarity index 98% rename from packages/firestore/src/exp/reference_impl.ts rename to packages/firestore/src/api/reference_impl.ts index 53779d13313..dbefef93d26 100644 --- a/packages/firestore/src/exp/reference_impl.ts +++ b/packages/firestore/src/api/reference_impl.ts @@ -35,9 +35,9 @@ import { } from '../core/firestore_client'; import { newQueryForPath, Query as InternalQuery } from '../core/query'; import { ViewSnapshot } from '../core/view_snapshot'; -import { Bytes } from '../lite/bytes'; -import { FieldPath } from '../lite/field_path'; -import { validateHasExplicitOrderByForLimitToLast } from '../lite/query'; +import { Bytes } from '../lite-api/bytes'; +import { FieldPath } from '../lite-api/field_path'; +import { validateHasExplicitOrderByForLimitToLast } from '../lite-api/query'; import { CollectionReference, doc, @@ -47,16 +47,16 @@ import { SetOptions, UpdateData, WithFieldValue -} from '../lite/reference'; -import { applyFirestoreDataConverter } from '../lite/reference_impl'; +} from '../lite-api/reference'; +import { applyFirestoreDataConverter } from '../lite-api/reference_impl'; import { newUserDataReader, ParsedUpdateData, parseSetData, parseUpdateData, parseUpdateVarargs -} from '../lite/user_data_reader'; -import { AbstractUserDataWriter } from '../lite/user_data_writer'; +} from '../lite-api/user_data_reader'; +import { AbstractUserDataWriter } from '../lite-api/user_data_writer'; import { DeleteMutation, Mutation, Precondition } from '../model/mutation'; import { debugAssert } from '../util/assert'; import { ByteString } from '../util/byte_string'; diff --git a/packages/firestore/src/exp/settings.ts b/packages/firestore/src/api/settings.ts similarity index 96% rename from packages/firestore/src/exp/settings.ts rename to packages/firestore/src/api/settings.ts index 83d87082301..f6e92854495 100644 --- a/packages/firestore/src/exp/settings.ts +++ b/packages/firestore/src/api/settings.ts @@ -15,9 +15,9 @@ * limitations under the License. */ -import { FirestoreSettings as LiteSettings } from '../lite/settings'; +import { FirestoreSettings as LiteSettings } from '../lite-api/settings'; -export { DEFAULT_HOST } from '../lite/settings'; +export { DEFAULT_HOST } from '../lite-api/settings'; /** * Settings that can be passed to `enableIndexedDbPersistence()` to configure diff --git a/packages/firestore/src/exp/snapshot.ts b/packages/firestore/src/api/snapshot.ts similarity index 98% rename from packages/firestore/src/exp/snapshot.ts rename to packages/firestore/src/api/snapshot.ts index b10c721ebca..0a0f5ec0eda 100644 --- a/packages/firestore/src/exp/snapshot.ts +++ b/packages/firestore/src/api/snapshot.ts @@ -17,7 +17,7 @@ import { newQueryComparator } from '../core/query'; import { ChangeType, ViewSnapshot } from '../core/view_snapshot'; -import { FieldPath } from '../lite/field_path'; +import { FieldPath } from '../lite-api/field_path'; import { DocumentData, PartialWithFieldValue, @@ -25,14 +25,14 @@ import { queryEqual, SetOptions, WithFieldValue -} from '../lite/reference'; +} from '../lite-api/reference'; import { DocumentSnapshot as LiteDocumentSnapshot, fieldPathFromArgument, FirestoreDataConverter as LiteFirestoreDataConverter -} from '../lite/snapshot'; -import { UntypedFirestoreDataConverter } from '../lite/user_data_reader'; -import { AbstractUserDataWriter } from '../lite/user_data_writer'; +} from '../lite-api/snapshot'; +import { UntypedFirestoreDataConverter } from '../lite-api/user_data_reader'; +import { AbstractUserDataWriter } from '../lite-api/user_data_writer'; import { Document } from '../model/document'; import { DocumentKey } from '../model/document_key'; import { debugAssert, fail } from '../util/assert'; diff --git a/packages/firestore/src/api/timestamp.ts b/packages/firestore/src/api/timestamp.ts index 959b2390297..58e12707560 100644 --- a/packages/firestore/src/api/timestamp.ts +++ b/packages/firestore/src/api/timestamp.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2017 Google LLC + * 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. @@ -15,4 +15,4 @@ * limitations under the License. */ -export { Timestamp } from '../../exp/index'; +export { Timestamp } from '../lite-api/timestamp'; diff --git a/packages/firestore/src/exp/transaction.ts b/packages/firestore/src/api/transaction.ts similarity index 94% rename from packages/firestore/src/exp/transaction.ts rename to packages/firestore/src/api/transaction.ts index 802dc38101c..c16574fed66 100644 --- a/packages/firestore/src/exp/transaction.ts +++ b/packages/firestore/src/api/transaction.ts @@ -17,9 +17,9 @@ import { firestoreClientTransaction } from '../core/firestore_client'; import { Transaction as InternalTransaction } from '../core/transaction'; -import { DocumentReference } from '../lite/reference'; -import { Transaction as LiteTransaction } from '../lite/transaction'; -import { validateReference } from '../lite/write_batch'; +import { DocumentReference } from '../lite-api/reference'; +import { Transaction as LiteTransaction } from '../lite-api/transaction'; +import { validateReference } from '../lite-api/write_batch'; import { ensureFirestoreConfigured, Firestore } from './database'; import { ExpUserDataWriter } from './reference_impl'; diff --git a/packages/firestore/src/exp/write_batch.ts b/packages/firestore/src/api/write_batch.ts similarity index 96% rename from packages/firestore/src/exp/write_batch.ts rename to packages/firestore/src/api/write_batch.ts index 09f91e36bb7..0d7b6e2bde9 100644 --- a/packages/firestore/src/exp/write_batch.ts +++ b/packages/firestore/src/api/write_batch.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { WriteBatch } from '../lite/write_batch'; +import { WriteBatch } from '../lite-api/write_batch'; import { cast } from '../util/input_validation'; import { ensureFirestoreConfigured, Firestore } from './database'; diff --git a/packages/firestore/src/config.ts b/packages/firestore/src/config.ts deleted file mode 100644 index 22866845806..00000000000 --- a/packages/firestore/src/config.ts +++ /dev/null @@ -1,85 +0,0 @@ -/** - * @license - * Copyright 2017 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 { FirebaseApp, FirebaseNamespace } from '@firebase/app-types'; -import { _FirebaseNamespace } from '@firebase/app-types/private'; -import { FirebaseAuthInternalName } from '@firebase/auth-interop-types'; -import { Component, ComponentType, Provider } from '@firebase/component'; - -import { - CACHE_SIZE_UNLIMITED, - CollectionReference, - DocumentReference, - DocumentSnapshot, - Firestore, - Query, - QueryDocumentSnapshot, - QuerySnapshot, - Transaction, - WriteBatch, - setLogLevel, - Blob, - FieldPath, - GeoPoint, - Timestamp, - FieldValue -} from '../export'; - -const firestoreNamespace = { - Firestore, - GeoPoint, - Timestamp, - Blob, - Transaction, - WriteBatch, - DocumentReference, - DocumentSnapshot, - Query, - QueryDocumentSnapshot, - QuerySnapshot, - CollectionReference, - FieldPath, - FieldValue, - setLogLevel, - CACHE_SIZE_UNLIMITED -}; - -/** - * Configures Firestore as part of the Firebase SDK by calling registerService. - * - * @param firebase - The FirebaseNamespace to register Firestore with - * @param firestoreFactory - A factory function that returns a new Firestore - * instance. - */ -export function configureForFirebase( - firebase: FirebaseNamespace, - firestoreFactory: ( - app: FirebaseApp, - auth: Provider - ) => Firestore -): void { - (firebase as _FirebaseNamespace).INTERNAL.registerComponent( - new Component( - 'firestore', - container => { - const app = container.getProvider('app').getImmediate()!; - return firestoreFactory(app, container.getProvider('auth-internal')); - }, - ComponentType.PUBLIC - ).setServiceProps({ ...firestoreNamespace }) - ); -} diff --git a/packages/firestore/src/core/component_provider.ts b/packages/firestore/src/core/component_provider.ts index 2bf13fc33e0..9c847ffd34c 100644 --- a/packages/firestore/src/core/component_provider.ts +++ b/packages/firestore/src/core/component_provider.ts @@ -187,7 +187,7 @@ export class IndexedDbOfflineComponentProvider extends MemoryOfflineComponentPro // set it after localStore / remoteStore are started. await this.persistence.setPrimaryStateListener(() => { if (this.gcScheduler && !this.gcScheduler.started) { - this.gcScheduler.start(this.localStore); + this.gcScheduler.start(this.localStore); } return Promise.resolve(); }); diff --git a/packages/firestore/src/core/database_info.ts b/packages/firestore/src/core/database_info.ts index 218e0626d80..d88a8b7fc2d 100644 --- a/packages/firestore/src/core/database_info.ts +++ b/packages/firestore/src/core/database_info.ts @@ -48,7 +48,10 @@ export class DatabaseInfo { /** The default database name for a project. */ const DEFAULT_DATABASE_NAME = '(default)'; -/** Represents the database ID a Firestore client is associated with. */ +/** + * Represents the database ID a Firestore client is associated with. + * @internal + */ export class DatabaseId { readonly database: string; constructor(readonly projectId: string, database?: string) { diff --git a/packages/firestore/src/core/firestore_client.ts b/packages/firestore/src/core/firestore_client.ts index 575c2ceb5a0..d8e5b04840a 100644 --- a/packages/firestore/src/core/firestore_client.ts +++ b/packages/firestore/src/core/firestore_client.ts @@ -17,12 +17,12 @@ import { GetOptions } from '@firebase/firestore-types'; +import { LoadBundleTask } from '../api/bundle'; import { CredentialChangeListener, CredentialsProvider } from '../api/credentials'; import { User } from '../auth/user'; -import { LoadBundleTask } from '../exp/bundle'; import { LocalStore } from '../local/local_store'; import { localStoreExecuteQuery, @@ -116,7 +116,7 @@ export class FirestoreClient { public asyncQueue: AsyncQueue, private databaseInfo: DatabaseInfo ) { - this.credentials.setChangeListener(asyncQueue, async user => { + this.credentials.start(asyncQueue, async user => { logDebug(LOG_TAG, 'Received user=', user.uid); await this.credentialListener(user); this.user = user; @@ -163,10 +163,10 @@ export class FirestoreClient { await this.offlineComponents.terminate(); } - // `removeChangeListener` must be called after shutting down the - // RemoteStore as it will prevent the RemoteStore from retrieving - // auth tokens. - this.credentials.removeChangeListener(); + // The credentials provider must be terminated after shutting down the + // RemoteStore as it will prevent the RemoteStore from retrieving auth + // tokens. + this.credentials.shutdown(); deferred.resolve(); } catch (e) { const firestoreError = wrapInUserErrorIfRecoverable( diff --git a/packages/firestore/src/core/snapshot_version.ts b/packages/firestore/src/core/snapshot_version.ts index df6bf0c841e..9f2e5581a7f 100644 --- a/packages/firestore/src/core/snapshot_version.ts +++ b/packages/firestore/src/core/snapshot_version.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Timestamp } from '../lite/timestamp'; +import { Timestamp } from '../lite-api/timestamp'; /** * A version of a document in Firestore. This corresponds to the version diff --git a/packages/firestore/src/core/sync_engine_impl.ts b/packages/firestore/src/core/sync_engine_impl.ts index da272ec0ac7..1eb42cca1ba 100644 --- a/packages/firestore/src/core/sync_engine_impl.ts +++ b/packages/firestore/src/core/sync_engine_impl.ts @@ -15,8 +15,8 @@ * limitations under the License. */ +import { LoadBundleTask } from '../api/bundle'; import { User } from '../auth/user'; -import { LoadBundleTask } from '../exp/bundle'; import { ignoreIfPrimaryLeaseLoss, LocalStore } from '../local/local_store'; import { localStoreAcknowledgeBatch, diff --git a/packages/firestore/src/core/transaction.ts b/packages/firestore/src/core/transaction.ts index 760e4174f6b..f50cde580b4 100644 --- a/packages/firestore/src/core/transaction.ts +++ b/packages/firestore/src/core/transaction.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { ParsedSetData, ParsedUpdateData } from '../lite/user_data_reader'; +import { ParsedSetData, ParsedUpdateData } from '../lite-api/user_data_reader'; import { Document } from '../model/document'; import { DocumentKey } from '../model/document_key'; import { diff --git a/packages/firestore/src/exp/database.ts b/packages/firestore/src/exp/database.ts deleted file mode 100644 index e49d8870897..00000000000 --- a/packages/firestore/src/exp/database.ts +++ /dev/null @@ -1,546 +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. - */ - -// eslint-disable-next-line import/no-extraneous-dependencies -import { - _getProvider, - _removeServiceInstance, - FirebaseApp, - getApp -} from '@firebase/app-exp'; -import { FirebaseAuthInternalName } from '@firebase/auth-interop-types'; -import { Provider } from '@firebase/component'; -import { deepEqual } from '@firebase/util'; - -import { - IndexedDbOfflineComponentProvider, - MultiTabOfflineComponentProvider, - OfflineComponentProvider, - OnlineComponentProvider -} from '../core/component_provider'; -import { DatabaseId } from '../core/database_info'; -import { - FirestoreClient, - firestoreClientDisableNetwork, - firestoreClientEnableNetwork, - firestoreClientGetNamedQuery, - firestoreClientLoadBundle, - firestoreClientWaitForPendingWrites, - setOfflineComponentProvider, - setOnlineComponentProvider -} from '../core/firestore_client'; -import { makeDatabaseInfo } from '../lite/components'; -import { Firestore as LiteFirestore } from '../lite/database'; -import { Query } from '../lite/reference'; -import { - indexedDbClearPersistence, - indexedDbStoragePrefix -} from '../local/indexeddb_persistence'; -import { LRU_COLLECTION_DISABLED } from '../local/lru_garbage_collector'; -import { LRU_MINIMUM_CACHE_SIZE_BYTES } from '../local/lru_garbage_collector_impl'; -import { debugAssert } from '../util/assert'; -import { AsyncQueue } from '../util/async_queue'; -import { newAsyncQueue } from '../util/async_queue_impl'; -import { Code, FirestoreError } from '../util/error'; -import { cast } from '../util/input_validation'; -import { Deferred } from '../util/promise'; - -import { LoadBundleTask } from './bundle'; -import { PersistenceSettings, FirestoreSettings } from './settings'; -export { connectFirestoreEmulator } from '../lite/database'; - -/** DOMException error code constants. */ -const DOM_EXCEPTION_INVALID_STATE = 11; -const DOM_EXCEPTION_ABORTED = 20; -const DOM_EXCEPTION_QUOTA_EXCEEDED = 22; - -/** - * Constant used to indicate the LRU garbage collection should be disabled. - * Set this value as the `cacheSizeBytes` on the settings passed to the - * {@link Firestore} instance. - */ -export const CACHE_SIZE_UNLIMITED = LRU_COLLECTION_DISABLED; - -/** - * The Cloud Firestore service interface. - * - * Do not call this constructor directly. Instead, use {@link getFirestore}. - */ -export class Firestore extends LiteFirestore { - /** - * Whether it's a {@link Firestore} or Firestore Lite instance. - */ - type: 'firestore-lite' | 'firestore' = 'firestore'; - - readonly _queue: AsyncQueue = newAsyncQueue(); - readonly _persistenceKey: string; - - _firestoreClient: FirestoreClient | undefined; - - /** @hideconstructor */ - constructor( - databaseIdOrApp: DatabaseId | FirebaseApp, - authProvider: Provider - ) { - super(databaseIdOrApp, authProvider); - this._persistenceKey = - 'name' in databaseIdOrApp ? databaseIdOrApp.name : '[DEFAULT]'; - } - - _terminate(): Promise { - if (!this._firestoreClient) { - // The client must be initialized to ensure that all subsequent API - // usage throws an exception. - configureFirestore(this); - } - return this._firestoreClient!.terminate(); - } -} - -/** - * Initializes a new instance of {@link Firestore} with the provided settings. - * Can only be called before any other function, including - * {@link getFirestore}. If the custom settings are empty, this function is - * equivalent to calling {@link getFirestore}. - * - * @param app - The {@link @firebase/app#FirebaseApp} with which the {@link Firestore} instance will - * be associated. - * @param settings - A settings object to configure the {@link Firestore} instance. - * @returns A newly initialized {@link Firestore} instance. - */ -export function initializeFirestore( - app: FirebaseApp, - settings: FirestoreSettings -): Firestore { - const provider = _getProvider(app, 'firestore-exp'); - - if (provider.isInitialized()) { - const existingInstance = provider.getImmediate(); - const initialSettings = provider.getOptions() as FirestoreSettings; - if (deepEqual(initialSettings, settings)) { - return existingInstance; - } else { - throw new FirestoreError( - Code.FAILED_PRECONDITION, - 'initializeFirestore() has already been called with ' + - 'different options. To avoid this error, call initializeFirestore() with the ' + - 'same options as when it was originally called, or call getFirestore() to return the' + - ' already initialized instance.' - ); - } - } - - if ( - settings.cacheSizeBytes !== undefined && - settings.cacheSizeBytes !== CACHE_SIZE_UNLIMITED && - settings.cacheSizeBytes < LRU_MINIMUM_CACHE_SIZE_BYTES - ) { - throw new FirestoreError( - Code.INVALID_ARGUMENT, - `cacheSizeBytes must be at least ${LRU_MINIMUM_CACHE_SIZE_BYTES}` - ); - } - - return provider.initialize({ options: settings }); -} - -/** - * Returns the existing {@link Firestore} instance that is associated with the - * provided {@link @firebase/app#FirebaseApp}. If no instance exists, initializes a new - * instance with default settings. - * - * @param app - The {@link @firebase/app#FirebaseApp} instance that the returned {@link Firestore} - * instance is associated with. - * @returns The {@link Firestore} instance of the provided app. - */ -export function getFirestore(app: FirebaseApp = getApp()): Firestore { - return _getProvider(app, 'firestore-exp').getImmediate() as Firestore; -} - -/** - * @internal - */ -export function ensureFirestoreConfigured( - firestore: Firestore -): FirestoreClient { - if (!firestore._firestoreClient) { - configureFirestore(firestore); - } - firestore._firestoreClient!.verifyNotTerminated(); - return firestore._firestoreClient as FirestoreClient; -} - -export function configureFirestore(firestore: Firestore): void { - const settings = firestore._freezeSettings(); - debugAssert(!!settings.host, 'FirestoreSettings.host is not set'); - debugAssert( - !firestore._firestoreClient, - 'configureFirestore() called multiple times' - ); - - const databaseInfo = makeDatabaseInfo( - firestore._databaseId, - firestore._app?.options.appId || '', - firestore._persistenceKey, - settings - ); - firestore._firestoreClient = new FirestoreClient( - firestore._credentials, - firestore._queue, - databaseInfo - ); -} - -/** - * Attempts to enable persistent storage, if possible. - * - * Must be called before any other functions (other than - * {@link initializeFirestore}, {@link getFirestore} or - * {@link clearIndexedDbPersistence}. - * - * If this fails, `enableIndexedDbPersistence()` will reject the promise it - * returns. Note that even after this failure, the {@link Firestore} instance will - * remain usable, however offline persistence will be disabled. - * - * There are several reasons why this can fail, which can be identified by - * the `code` on the error. - * - * * failed-precondition: The app is already open in another browser tab. - * * unimplemented: The browser is incompatible with the offline - * persistence implementation. - * - * @param firestore - The {@link Firestore} instance to enable persistence for. - * @param persistenceSettings - Optional settings object to configure - * persistence. - * @returns A `Promise` that represents successfully enabling persistent storage. - */ -export function enableIndexedDbPersistence( - firestore: Firestore, - persistenceSettings?: PersistenceSettings -): Promise { - firestore = cast(firestore, Firestore); - verifyNotInitialized(firestore); - - const client = ensureFirestoreConfigured(firestore); - const settings = firestore._freezeSettings(); - - const onlineComponentProvider = new OnlineComponentProvider(); - const offlineComponentProvider = new IndexedDbOfflineComponentProvider( - onlineComponentProvider, - settings.cacheSizeBytes, - persistenceSettings?.forceOwnership - ); - return setPersistenceProviders( - client, - onlineComponentProvider, - offlineComponentProvider - ); -} - -/** - * Attempts to enable multi-tab persistent storage, if possible. If enabled - * across all tabs, all operations share access to local persistence, including - * shared execution of queries and latency-compensated local document updates - * across all connected instances. - * - * If this fails, `enableMultiTabIndexedDbPersistence()` will reject the promise - * it returns. Note that even after this failure, the {@link Firestore} instance will - * remain usable, however offline persistence will be disabled. - * - * There are several reasons why this can fail, which can be identified by - * the `code` on the error. - * - * * failed-precondition: The app is already open in another browser tab and - * multi-tab is not enabled. - * * unimplemented: The browser is incompatible with the offline - * persistence implementation. - * - * @param firestore - The {@link Firestore} instance to enable persistence for. - * @returns A `Promise` that represents successfully enabling persistent - * storage. - */ -export function enableMultiTabIndexedDbPersistence( - firestore: Firestore -): Promise { - firestore = cast(firestore, Firestore); - verifyNotInitialized(firestore); - - const client = ensureFirestoreConfigured(firestore); - const settings = firestore._freezeSettings(); - - const onlineComponentProvider = new OnlineComponentProvider(); - const offlineComponentProvider = new MultiTabOfflineComponentProvider( - onlineComponentProvider, - settings.cacheSizeBytes - ); - return setPersistenceProviders( - client, - onlineComponentProvider, - offlineComponentProvider - ); -} - -/** - * Registers both the `OfflineComponentProvider` and `OnlineComponentProvider`. - * If the operation fails with a recoverable error (see - * `canRecoverFromIndexedDbError()` below), the returned Promise is rejected - * but the client remains usable. - */ -function setPersistenceProviders( - client: FirestoreClient, - onlineComponentProvider: OnlineComponentProvider, - offlineComponentProvider: OfflineComponentProvider -): Promise { - const persistenceResult = new Deferred(); - return client.asyncQueue - .enqueue(async () => { - try { - await setOfflineComponentProvider(client, offlineComponentProvider); - await setOnlineComponentProvider(client, onlineComponentProvider); - persistenceResult.resolve(); - } catch (e) { - if (!canFallbackFromIndexedDbError(e)) { - throw e; - } - console.warn( - 'Error enabling offline persistence. Falling back to ' + - 'persistence disabled: ' + - e - ); - persistenceResult.reject(e); - } - }) - .then(() => persistenceResult.promise); -} - -/** - * Decides whether the provided error allows us to gracefully disable - * persistence (as opposed to crashing the client). - */ -function canFallbackFromIndexedDbError( - error: FirestoreError | DOMException -): boolean { - if (error.name === 'FirebaseError') { - return ( - error.code === Code.FAILED_PRECONDITION || - error.code === Code.UNIMPLEMENTED - ); - } else if ( - typeof DOMException !== 'undefined' && - error instanceof DOMException - ) { - // There are a few known circumstances where we can open IndexedDb but - // trying to read/write will fail (e.g. quota exceeded). For - // well-understood cases, we attempt to detect these and then gracefully - // fall back to memory persistence. - // NOTE: Rather than continue to add to this list, we could decide to - // always fall back, with the risk that we might accidentally hide errors - // representing actual SDK bugs. - return ( - // When the browser is out of quota we could get either quota exceeded - // or an aborted error depending on whether the error happened during - // schema migration. - error.code === DOM_EXCEPTION_QUOTA_EXCEEDED || - error.code === DOM_EXCEPTION_ABORTED || - // Firefox Private Browsing mode disables IndexedDb and returns - // INVALID_STATE for any usage. - error.code === DOM_EXCEPTION_INVALID_STATE - ); - } - - return true; -} - -/** - * Clears the persistent storage. This includes pending writes and cached - * documents. - * - * Must be called while the {@link Firestore} instance is not started (after the app is - * terminated or when the app is first initialized). On startup, this function - * must be called before other functions (other than {@link - * initializeFirestore} or {@link getFirestore})). If the {@link Firestore} - * instance is still running, the promise will be rejected with the error code - * of `failed-precondition`. - * - * Note: `clearIndexedDbPersistence()` is primarily intended to help write - * reliable tests that use Cloud Firestore. It uses an efficient mechanism for - * dropping existing data but does not attempt to securely overwrite or - * otherwise make cached data unrecoverable. For applications that are sensitive - * to the disclosure of cached data in between user sessions, we strongly - * recommend not enabling persistence at all. - * - * @param firestore - The {@link Firestore} instance to clear persistence for. - * @returns A `Promise` that is resolved when the persistent storage is - * cleared. Otherwise, the promise is rejected with an error. - */ -export function clearIndexedDbPersistence(firestore: Firestore): Promise { - if (firestore._initialized && !firestore._terminated) { - throw new FirestoreError( - Code.FAILED_PRECONDITION, - 'Persistence can only be cleared before a Firestore instance is ' + - 'initialized or after it is terminated.' - ); - } - - const deferred = new Deferred(); - firestore._queue.enqueueAndForgetEvenWhileRestricted(async () => { - try { - await indexedDbClearPersistence( - indexedDbStoragePrefix(firestore._databaseId, firestore._persistenceKey) - ); - deferred.resolve(); - } catch (e) { - deferred.reject(e); - } - }); - return deferred.promise; -} - -/** - * Waits until all currently pending writes for the active user have been - * acknowledged by the backend. - * - * The returned promise resolves immediately if there are no outstanding writes. - * Otherwise, the promise waits for all previously issued writes (including - * those written in a previous app session), but it does not wait for writes - * that were added after the function is called. If you want to wait for - * additional writes, call `waitForPendingWrites()` again. - * - * Any outstanding `waitForPendingWrites()` promises are rejected during user - * changes. - * - * @returns A `Promise` which resolves when all currently pending writes have been - * acknowledged by the backend. - */ -export function waitForPendingWrites(firestore: Firestore): Promise { - firestore = cast(firestore, Firestore); - const client = ensureFirestoreConfigured(firestore); - return firestoreClientWaitForPendingWrites(client); -} - -/** - * Re-enables use of the network for this {@link Firestore} instance after a prior - * call to {@link disableNetwork}. - * - * @returns A `Promise` that is resolved once the network has been enabled. - */ -export function enableNetwork(firestore: Firestore): Promise { - firestore = cast(firestore, Firestore); - const client = ensureFirestoreConfigured(firestore); - return firestoreClientEnableNetwork(client); -} - -/** - * Disables network usage for this instance. It can be re-enabled via {@link - * enableNetwork}. While the network is disabled, any snapshot listeners, - * `getDoc()` or `getDocs()` calls will return results from cache, and any write - * operations will be queued until the network is restored. - * - * @returns A `Promise` that is resolved once the network has been disabled. - */ -export function disableNetwork(firestore: Firestore): Promise { - firestore = cast(firestore, Firestore); - const client = ensureFirestoreConfigured(firestore); - return firestoreClientDisableNetwork(client); -} - -/** - * Terminates the provided {@link Firestore} instance. - * - * After calling `terminate()` only the `clearIndexedDbPersistence()` function - * may be used. Any other function will throw a `FirestoreError`. - * - * To restart after termination, create a new instance of FirebaseFirestore with - * {@link getFirestore}. - * - * Termination does not cancel any pending writes, and any promises that are - * awaiting a response from the server will not be resolved. If you have - * persistence enabled, the next time you start this instance, it will resume - * sending these writes to the server. - * - * Note: Under normal circumstances, calling `terminate()` is not required. This - * function is useful only when you want to force this instance to release all - * of its resources or in combination with `clearIndexedDbPersistence()` to - * ensure that all local state is destroyed between test runs. - * - * @returns A `Promise` that is resolved when the instance has been successfully - * terminated. - */ -export function terminate(firestore: Firestore): Promise { - _removeServiceInstance(firestore.app, 'firestore-exp'); - return firestore._delete(); -} - -/** - * Loads a Firestore bundle into the local cache. - * - * @param firestore - The {@link Firestore} instance to load bundles for for. - * @param bundleData - An object representing the bundle to be loaded. Valid objects are - * `ArrayBuffer`, `ReadableStream` or `string`. - * - * @returns - * A `LoadBundleTask` object, which notifies callers with progress updates, and completion - * or error events. It can be used as a `Promise`. - */ -export function loadBundle( - firestore: Firestore, - bundleData: ReadableStream | ArrayBuffer | string -): LoadBundleTask { - firestore = cast(firestore, Firestore); - const client = ensureFirestoreConfigured(firestore); - const resultTask = new LoadBundleTask(); - firestoreClientLoadBundle( - client, - firestore._databaseId, - bundleData, - resultTask - ); - return resultTask; -} - -/** - * Reads a Firestore {@link Query} from local cache, identified by the given name. - * - * The named queries are packaged into bundles on the server side (along - * with resulting documents), and loaded to local cache using `loadBundle`. Once in local - * cache, use this method to extract a {@link Query} by name. - */ -export function namedQuery( - firestore: Firestore, - name: string -): Promise { - firestore = cast(firestore, Firestore); - const client = ensureFirestoreConfigured(firestore); - return firestoreClientGetNamedQuery(client, name).then(namedQuery => { - if (!namedQuery) { - return null; - } - - return new Query(firestore, null, namedQuery.query); - }); -} - -function verifyNotInitialized(firestore: Firestore): void { - if (firestore._initialized || firestore._terminated) { - throw new FirestoreError( - Code.FAILED_PRECONDITION, - 'Firestore has already been started and persistence can no longer be ' + - 'enabled. You can only enable persistence before calling any other ' + - 'methods on a Firestore object.' - ); - } -} diff --git a/packages/firestore/src/exp/field_path.ts b/packages/firestore/src/exp/field_path.ts deleted file mode 100644 index 04047bfa792..00000000000 --- a/packages/firestore/src/exp/field_path.ts +++ /dev/null @@ -1,18 +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. - */ - -export { FieldPath, documentId } from '../lite/field_path'; diff --git a/packages/firestore/src/exp/field_value.ts b/packages/firestore/src/exp/field_value.ts deleted file mode 100644 index 9ebc601869c..00000000000 --- a/packages/firestore/src/exp/field_value.ts +++ /dev/null @@ -1,18 +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. - */ - -export { FieldValue } from '../lite/field_value'; diff --git a/packages/firestore/exp/index.node.ts b/packages/firestore/src/index.node.ts similarity index 100% rename from packages/firestore/exp/index.node.ts rename to packages/firestore/src/index.node.ts diff --git a/packages/firestore/exp/index.rn.ts b/packages/firestore/src/index.rn.ts similarity index 100% rename from packages/firestore/exp/index.rn.ts rename to packages/firestore/src/index.rn.ts diff --git a/packages/firestore/exp/index.ts b/packages/firestore/src/index.ts similarity index 83% rename from packages/firestore/exp/index.ts rename to packages/firestore/src/index.ts index aab859b67a7..5babf0ef726 100644 --- a/packages/firestore/exp/index.ts +++ b/packages/firestore/src/index.ts @@ -21,8 +21,15 @@ * limitations under the License. */ +import { Firestore } from './api/database'; import { registerFirestore } from './register'; registerFirestore(); export * from './api'; + +declare module '@firebase/component' { + interface NameServiceMapping { + 'firestore': Firestore; + } +} diff --git a/packages/firestore/src/lite/bytes.ts b/packages/firestore/src/lite-api/bytes.ts similarity index 100% rename from packages/firestore/src/lite/bytes.ts rename to packages/firestore/src/lite-api/bytes.ts diff --git a/packages/firestore/src/lite/components.ts b/packages/firestore/src/lite-api/components.ts similarity index 98% rename from packages/firestore/src/lite/components.ts rename to packages/firestore/src/lite-api/components.ts index 24179eaf319..8dfaa32d1b9 100644 --- a/packages/firestore/src/lite/components.ts +++ b/packages/firestore/src/lite-api/components.ts @@ -16,7 +16,7 @@ */ // eslint-disable-next-line import/no-extraneous-dependencies -import { _FirebaseService } from '@firebase/app-exp'; +import { _FirebaseService } from '@firebase/app'; import { CredentialsProvider } from '../api/credentials'; import { DatabaseId, DatabaseInfo } from '../core/database_info'; diff --git a/packages/firestore/src/lite/database.ts b/packages/firestore/src/lite-api/database.ts similarity index 95% rename from packages/firestore/src/lite/database.ts rename to packages/firestore/src/lite-api/database.ts index 536877fe672..a082af975d3 100644 --- a/packages/firestore/src/lite/database.ts +++ b/packages/firestore/src/lite-api/database.ts @@ -21,16 +21,12 @@ import { _removeServiceInstance, FirebaseApp, getApp -} from '@firebase/app-exp'; -import { FirebaseAuthInternalName } from '@firebase/auth-interop-types'; -import { Provider } from '@firebase/component'; +} from '@firebase/app'; import { createMockUserToken, EmulatorMockTokenOptions } from '@firebase/util'; import { CredentialsProvider, - EmptyCredentialsProvider, EmulatorCredentialsProvider, - FirebaseCredentialsProvider, makeCredentialsProvider, OAuthToken } from '../api/credentials'; @@ -67,7 +63,6 @@ export class Firestore implements FirestoreService { readonly _databaseId: DatabaseId; readonly _persistenceKey: string = '(lite)'; - _credentials: CredentialsProvider; private _settings = new FirestoreSettingsImpl({}); private _settingsFrozen = false; @@ -81,15 +76,13 @@ export class Firestore implements FirestoreService { /** @hideconstructor */ constructor( databaseIdOrApp: DatabaseId | FirebaseApp, - authProvider: Provider + public _credentials: CredentialsProvider ) { if (databaseIdOrApp instanceof DatabaseId) { this._databaseId = databaseIdOrApp; - this._credentials = new EmptyCredentialsProvider(); } else { this._app = databaseIdOrApp as FirebaseApp; this._databaseId = databaseIdFromApp(databaseIdOrApp as FirebaseApp); - this._credentials = new FirebaseCredentialsProvider(authProvider); } } @@ -220,6 +213,7 @@ export function getFirestore(app: FirebaseApp = getApp()): Firestore { return _getProvider(app, 'firestore/lite').getImmediate() as Firestore; } +export { EmulatorMockTokenOptions } from '@firebase/util'; /** * Modify this instance to communicate with the Cloud Firestore emulator. * diff --git a/packages/firestore/src/lite/field_path.ts b/packages/firestore/src/lite-api/field_path.ts similarity index 100% rename from packages/firestore/src/lite/field_path.ts rename to packages/firestore/src/lite-api/field_path.ts diff --git a/packages/firestore/src/lite/field_value.ts b/packages/firestore/src/lite-api/field_value.ts similarity index 100% rename from packages/firestore/src/lite/field_value.ts rename to packages/firestore/src/lite-api/field_value.ts diff --git a/packages/firestore/src/lite/field_value_impl.ts b/packages/firestore/src/lite-api/field_value_impl.ts similarity index 100% rename from packages/firestore/src/lite/field_value_impl.ts rename to packages/firestore/src/lite-api/field_value_impl.ts diff --git a/packages/firestore/src/lite/geo_point.ts b/packages/firestore/src/lite-api/geo_point.ts similarity index 100% rename from packages/firestore/src/lite/geo_point.ts rename to packages/firestore/src/lite-api/geo_point.ts diff --git a/packages/firestore/src/lite/query.ts b/packages/firestore/src/lite-api/query.ts similarity index 100% rename from packages/firestore/src/lite/query.ts rename to packages/firestore/src/lite-api/query.ts diff --git a/packages/firestore/src/lite/reference.ts b/packages/firestore/src/lite-api/reference.ts similarity index 100% rename from packages/firestore/src/lite/reference.ts rename to packages/firestore/src/lite-api/reference.ts diff --git a/packages/firestore/src/lite/reference_impl.ts b/packages/firestore/src/lite-api/reference_impl.ts similarity index 100% rename from packages/firestore/src/lite/reference_impl.ts rename to packages/firestore/src/lite-api/reference_impl.ts diff --git a/packages/firestore/src/lite/settings.ts b/packages/firestore/src/lite-api/settings.ts similarity index 100% rename from packages/firestore/src/lite/settings.ts rename to packages/firestore/src/lite-api/settings.ts diff --git a/packages/firestore/src/lite/snapshot.ts b/packages/firestore/src/lite-api/snapshot.ts similarity index 100% rename from packages/firestore/src/lite/snapshot.ts rename to packages/firestore/src/lite-api/snapshot.ts diff --git a/packages/firestore/src/lite/timestamp.ts b/packages/firestore/src/lite-api/timestamp.ts similarity index 100% rename from packages/firestore/src/lite/timestamp.ts rename to packages/firestore/src/lite-api/timestamp.ts diff --git a/packages/firestore/src/lite/transaction.ts b/packages/firestore/src/lite-api/transaction.ts similarity index 100% rename from packages/firestore/src/lite/transaction.ts rename to packages/firestore/src/lite-api/transaction.ts diff --git a/packages/firestore/src/lite/types.ts b/packages/firestore/src/lite-api/types.ts similarity index 100% rename from packages/firestore/src/lite/types.ts rename to packages/firestore/src/lite-api/types.ts diff --git a/packages/firestore/src/lite/user_data_reader.ts b/packages/firestore/src/lite-api/user_data_reader.ts similarity index 100% rename from packages/firestore/src/lite/user_data_reader.ts rename to packages/firestore/src/lite-api/user_data_reader.ts diff --git a/packages/firestore/src/lite/user_data_writer.ts b/packages/firestore/src/lite-api/user_data_writer.ts similarity index 98% rename from packages/firestore/src/lite/user_data_writer.ts rename to packages/firestore/src/lite-api/user_data_writer.ts index 2b30b970573..7f2e2b86301 100644 --- a/packages/firestore/src/lite/user_data_writer.ts +++ b/packages/firestore/src/lite-api/user_data_writer.ts @@ -18,8 +18,6 @@ import { DocumentData } from '@firebase/firestore-types'; import { DatabaseId } from '../core/database_info'; -import { GeoPoint } from '../lite/geo_point'; -import { Timestamp } from '../lite/timestamp'; import { DocumentKey } from '../model/document_key'; import { normalizeByteString, @@ -46,6 +44,9 @@ import { ByteString } from '../util/byte_string'; import { logError } from '../util/log'; import { forEach } from '../util/obj'; +import { GeoPoint } from './geo_point'; +import { Timestamp } from './timestamp'; + export type ServerTimestampBehavior = 'estimate' | 'previous' | 'none'; /** diff --git a/packages/firestore/src/lite/write_batch.ts b/packages/firestore/src/lite-api/write_batch.ts similarity index 100% rename from packages/firestore/src/lite/write_batch.ts rename to packages/firestore/src/lite-api/write_batch.ts diff --git a/packages/firestore/src/local/indexeddb_mutation_queue.ts b/packages/firestore/src/local/indexeddb_mutation_queue.ts index 5ceb0d9943d..cb9b8dd21ee 100644 --- a/packages/firestore/src/local/indexeddb_mutation_queue.ts +++ b/packages/firestore/src/local/indexeddb_mutation_queue.ts @@ -18,7 +18,7 @@ import { User } from '../auth/user'; import { isCollectionGroupQuery, isDocumentQuery, Query } from '../core/query'; import { BatchId } from '../core/types'; -import { Timestamp } from '../lite/timestamp'; +import { Timestamp } from '../lite-api/timestamp'; import { DocumentKeySet } from '../model/collections'; import { DocumentKey } from '../model/document_key'; import { Mutation } from '../model/mutation'; diff --git a/packages/firestore/src/local/indexeddb_target_cache.ts b/packages/firestore/src/local/indexeddb_target_cache.ts index 39d268ec0e4..c864e5afde7 100644 --- a/packages/firestore/src/local/indexeddb_target_cache.ts +++ b/packages/firestore/src/local/indexeddb_target_cache.ts @@ -19,7 +19,7 @@ import { SnapshotVersion } from '../core/snapshot_version'; import { canonifyTarget, Target, targetEquals } from '../core/target'; import { TargetIdGenerator } from '../core/target_id_generator'; import { ListenSequenceNumber, TargetId } from '../core/types'; -import { Timestamp } from '../lite/timestamp'; +import { Timestamp } from '../lite-api/timestamp'; import { DocumentKeySet, documentKeySet } from '../model/collections'; import { DocumentKey } from '../model/document_key'; import { hardAssert } from '../util/assert'; diff --git a/packages/firestore/src/local/local_serializer.ts b/packages/firestore/src/local/local_serializer.ts index 290a5664a36..dabb8c24c58 100644 --- a/packages/firestore/src/local/local_serializer.ts +++ b/packages/firestore/src/local/local_serializer.ts @@ -15,11 +15,11 @@ * limitations under the License. */ +import { Timestamp } from '../api/timestamp'; import { BundleMetadata, NamedQuery } from '../core/bundle'; import { LimitType, Query, queryWithLimit } from '../core/query'; import { SnapshotVersion } from '../core/snapshot_version'; import { canonifyTarget, isDocumentTarget, Target } from '../core/target'; -import { Timestamp } from '../exp/timestamp'; import { MutableDocument } from '../model/document'; import { DocumentKey } from '../model/document_key'; import { MutationBatch } from '../model/mutation_batch'; diff --git a/packages/firestore/src/local/local_store_impl.ts b/packages/firestore/src/local/local_store_impl.ts index f34385f50b9..9cb0a3ea9bc 100644 --- a/packages/firestore/src/local/local_store_impl.ts +++ b/packages/firestore/src/local/local_store_impl.ts @@ -21,7 +21,7 @@ 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'; -import { Timestamp } from '../lite/timestamp'; +import { Timestamp } from '../lite-api/timestamp'; import { documentKeySet, DocumentKeySet, diff --git a/packages/firestore/src/local/memory_mutation_queue.ts b/packages/firestore/src/local/memory_mutation_queue.ts index 490f6b9175d..91f51d8c5d6 100644 --- a/packages/firestore/src/local/memory_mutation_queue.ts +++ b/packages/firestore/src/local/memory_mutation_queue.ts @@ -17,7 +17,7 @@ import { isCollectionGroupQuery, Query } from '../core/query'; import { BatchId } from '../core/types'; -import { Timestamp } from '../lite/timestamp'; +import { Timestamp } from '../lite-api/timestamp'; import { DocumentKey } from '../model/document_key'; import { Mutation } from '../model/mutation'; import { MutationBatch } from '../model/mutation_batch'; diff --git a/packages/firestore/src/local/mutation_queue.ts b/packages/firestore/src/local/mutation_queue.ts index 04ce858b9d6..299ad3d6113 100644 --- a/packages/firestore/src/local/mutation_queue.ts +++ b/packages/firestore/src/local/mutation_queue.ts @@ -17,7 +17,7 @@ import { Query } from '../core/query'; import { BatchId } from '../core/types'; -import { Timestamp } from '../lite/timestamp'; +import { Timestamp } from '../lite-api/timestamp'; import { DocumentKey } from '../model/document_key'; import { Mutation } from '../model/mutation'; import { MutationBatch } from '../model/mutation_batch'; diff --git a/packages/firestore/src/model/document_key.ts b/packages/firestore/src/model/document_key.ts index 989890c185d..e40b7c0c565 100644 --- a/packages/firestore/src/model/document_key.ts +++ b/packages/firestore/src/model/document_key.ts @@ -19,6 +19,9 @@ import { debugAssert } from '../util/assert'; import { ResourcePath } from './path'; +/** + * @internal + */ export class DocumentKey { constructor(readonly path: ResourcePath) { debugAssert( diff --git a/packages/firestore/src/model/mutation.ts b/packages/firestore/src/model/mutation.ts index eecac8048cf..5d04eea4be8 100644 --- a/packages/firestore/src/model/mutation.ts +++ b/packages/firestore/src/model/mutation.ts @@ -16,7 +16,7 @@ */ import { SnapshotVersion } from '../core/snapshot_version'; -import { Timestamp } from '../lite/timestamp'; +import { Timestamp } from '../lite-api/timestamp'; import { Value as ProtoValue } from '../protos/firestore_proto_api'; import { debugAssert, hardAssert } from '../util/assert'; import { arrayEquals } from '../util/misc'; diff --git a/packages/firestore/src/model/mutation_batch.ts b/packages/firestore/src/model/mutation_batch.ts index 49ab8b21176..b05b2c7cfb2 100644 --- a/packages/firestore/src/model/mutation_batch.ts +++ b/packages/firestore/src/model/mutation_batch.ts @@ -17,7 +17,7 @@ import { SnapshotVersion } from '../core/snapshot_version'; import { BatchId } from '../core/types'; -import { Timestamp } from '../lite/timestamp'; +import { Timestamp } from '../lite-api/timestamp'; import { debugAssert, hardAssert } from '../util/assert'; import { arrayEquals } from '../util/misc'; diff --git a/packages/firestore/src/model/path.ts b/packages/firestore/src/model/path.ts index edf58d25a5f..8ae933eb355 100644 --- a/packages/firestore/src/model/path.ts +++ b/packages/firestore/src/model/path.ts @@ -190,6 +190,8 @@ abstract class BasePath> { /** * A slash-separated path for navigating resources (documents and collections) * within Firestore. + * + * @internal */ export class ResourcePath extends BasePath { protected construct( @@ -244,7 +246,10 @@ export class ResourcePath extends BasePath { const identifierRegExp = /^[_a-zA-Z][_a-zA-Z0-9]*$/; -/** A dot-separated path for navigating sub-objects within a document. */ +/** + * A dot-separated path for navigating sub-objects within a document. + * @internal + */ export class FieldPath extends BasePath { protected construct( segments: string[], diff --git a/packages/firestore/src/model/server_timestamps.ts b/packages/firestore/src/model/server_timestamps.ts index 532da6a17b5..b2e59c105b9 100644 --- a/packages/firestore/src/model/server_timestamps.ts +++ b/packages/firestore/src/model/server_timestamps.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Timestamp } from '../lite/timestamp'; +import { Timestamp } from '../lite-api/timestamp'; import { Value as ProtoValue, MapValue as ProtoMapValue diff --git a/packages/firestore/src/model/transform_operation.ts b/packages/firestore/src/model/transform_operation.ts index af8c8f23efe..07f6df94366 100644 --- a/packages/firestore/src/model/transform_operation.ts +++ b/packages/firestore/src/model/transform_operation.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Timestamp } from '../lite/timestamp'; +import { Timestamp } from '../lite-api/timestamp'; import { Value as ProtoValue } from '../protos/firestore_proto_api'; import { Serializer, toDouble, toInteger } from '../remote/number_serializer'; import { debugAssert } from '../util/assert'; diff --git a/packages/firestore/src/platform/base64.ts b/packages/firestore/src/platform/base64.ts index b794158d909..877a78c732b 100644 --- a/packages/firestore/src/platform/base64.ts +++ b/packages/firestore/src/platform/base64.ts @@ -29,7 +29,10 @@ export function encodeBase64(raw: string): string { return platform.encodeBase64(raw); } -/** True if and only if the Base64 conversion functions are available. */ +/** + * True if and only if the Base64 conversion functions are available. + * @internal + */ export function isBase64Available(): boolean { return platform.isBase64Available(); } diff --git a/packages/firestore/exp/register.ts b/packages/firestore/src/register.ts similarity index 77% rename from packages/firestore/exp/register.ts rename to packages/firestore/src/register.ts index f6d18e67b98..44f027f8986 100644 --- a/packages/firestore/exp/register.ts +++ b/packages/firestore/src/register.ts @@ -19,30 +19,28 @@ import { _registerComponent, registerVersion, SDK_VERSION -} from '@firebase/app-exp'; +} from '@firebase/app'; import { Component, ComponentType } from '@firebase/component'; import { name, version } from '../package.json'; +import { FirebaseCredentialsProvider } from '../src/api/credentials'; import { setSDKVersion } from '../src/core/version'; -import { Firestore } from '../src/exp/database'; -import { PrivateSettings } from '../src/lite/settings'; -declare module '@firebase/component' { - interface NameServiceMapping { - 'firestore-exp': Firestore; - } -} +import { Firestore } from './api/database'; +import { PrivateSettings } from './lite-api/settings'; export function registerFirestore(variant?: string): void { setSDKVersion(SDK_VERSION); _registerComponent( new Component( - 'firestore-exp', + 'firestore', (container, { options: settings }: { options?: PrivateSettings }) => { - const app = container.getProvider('app-exp').getImmediate()!; + const app = container.getProvider('app').getImmediate()!; const firestoreInstance = new Firestore( app, - container.getProvider('auth-internal') + new FirebaseCredentialsProvider( + container.getProvider('auth-internal') + ) ); settings = { useFetchStreams: true, ...settings }; firestoreInstance._setSettings(settings); diff --git a/packages/firestore/src/remote/serializer.ts b/packages/firestore/src/remote/serializer.ts index 7f4b71e8f2b..c43ac066ad7 100644 --- a/packages/firestore/src/remote/serializer.ts +++ b/packages/firestore/src/remote/serializer.ts @@ -35,7 +35,7 @@ import { Target } from '../core/target'; import { TargetId } from '../core/types'; -import { Timestamp } from '../lite/timestamp'; +import { Timestamp } from '../lite-api/timestamp'; import { TargetData, TargetPurpose } from '../local/target_data'; import { MutableDocument } from '../model/document'; import { DocumentKey } from '../model/document_key'; diff --git a/packages/firestore/src/util/assert.ts b/packages/firestore/src/util/assert.ts index 4a73566583a..6d65e6cd19b 100644 --- a/packages/firestore/src/util/assert.ts +++ b/packages/firestore/src/util/assert.ts @@ -62,6 +62,8 @@ export function hardAssert( * The code of callsites invoking this function are stripped out in production * builds. Any side-effects of code within the debugAssert() invocation will not * happen in this case. + * + * @internal */ export function debugAssert( assertion: boolean, diff --git a/packages/firestore/src/util/byte_stream.ts b/packages/firestore/src/util/byte_stream.ts index 36904956ce1..edb69c460c6 100644 --- a/packages/firestore/src/util/byte_stream.ts +++ b/packages/firestore/src/util/byte_stream.ts @@ -39,8 +39,13 @@ export function toByteStreamReaderHelper( `toByteStreamReader expects positive bytesPerRead, but got ${bytesPerRead}` ); let readFrom = 0; - const reader: ReadableStreamReader = { - async read(): Promise> { + // The TypeScript definition for ReadableStreamReader changed. We use + // `any` here to allow this code to compile with different versions. + // See https://github.com/microsoft/TypeScript/issues/42970 + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const reader: any = { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + async read(): Promise { if (readFrom < source.byteLength) { const result = { value: source.slice(readFrom, readFrom + bytesPerRead), diff --git a/packages/firestore/src/util/byte_string.ts b/packages/firestore/src/util/byte_string.ts index 7ed6fb2b554..f34efb44927 100644 --- a/packages/firestore/src/util/byte_string.ts +++ b/packages/firestore/src/util/byte_string.ts @@ -26,6 +26,7 @@ import { primitiveComparator } from './misc'; * sent on the wire. This class abstracts away this differentiation by holding * the proto byte string in a common class that must be converted into a string * before being sent as a proto. + * @internal */ export class ByteString { static readonly EMPTY_BYTE_STRING = new ByteString(''); diff --git a/packages/firestore/src/util/input_validation.ts b/packages/firestore/src/util/input_validation.ts index 3e81c6d9a9a..47289823f27 100644 --- a/packages/firestore/src/util/input_validation.ts +++ b/packages/firestore/src/util/input_validation.ts @@ -15,8 +15,6 @@ * limitations under the License. */ -import { SetOptions } from '@firebase/firestore-types'; - import { DocumentKey } from '../model/document_key'; import { ResourcePath } from '../model/path'; @@ -46,29 +44,9 @@ export function validateNonEmptyArgument( } } -export function validateSetOptions( - methodName: string, - options: SetOptions | undefined -): SetOptions { - if (options === undefined) { - return { - merge: false - }; - } - - if (options.mergeFields !== undefined && options.merge !== undefined) { - throw new FirestoreError( - Code.INVALID_ARGUMENT, - `Invalid options passed to function ${methodName}(): You cannot ` + - 'specify both "merge" and "mergeFields".' - ); - } - - return options; -} - /** * Validates that two boolean options are not set at the same time. + * @internal */ export function validateIsNotUsedTogether( optionName1: string, @@ -172,6 +150,7 @@ export function tryGetCustomObjectType(input: object): string | null { * * This cast is used in the Lite and Full SDK to verify instance types for * arguments passed to the public API. + * @internal */ export function cast( obj: object, diff --git a/packages/firestore/src/util/log.ts b/packages/firestore/src/util/log.ts index 0a6999dec61..cbdbad38ecf 100644 --- a/packages/firestore/src/util/log.ts +++ b/packages/firestore/src/util/log.ts @@ -60,6 +60,9 @@ export function logError(msg: string, ...obj: unknown[]): void { } } +/** + * @internal + */ export function logWarn(msg: string, ...obj: unknown[]): void { if (logClient.logLevel <= LogLevel.WARN) { const args = obj.map(argToString); diff --git a/packages/firestore/test/integration/api/database.test.ts b/packages/firestore/test/integration/api/database.test.ts index 35a85f85b9f..be5e529c70a 100644 --- a/packages/firestore/test/integration/api/database.test.ts +++ b/packages/firestore/test/integration/api/database.test.ts @@ -42,10 +42,6 @@ const FieldValue = firebaseExport.FieldValue; const DocumentReference = firebaseExport.DocumentReference; const QueryDocumentSnapshot = firebaseExport.QueryDocumentSnapshot; -const MEMORY_ONLY_BUILD = - typeof process !== 'undefined' && - process.env?.INCLUDE_FIRESTORE_PERSISTENCE === 'false'; - apiDescribe('Database', (persistence: boolean) => { it('can set a document', () => { return withTestDoc(persistence, docRef => { @@ -1093,33 +1089,6 @@ apiDescribe('Database', (persistence: boolean) => { }); }); - // eslint-disable-next-line no-restricted-properties - (MEMORY_ONLY_BUILD ? it : it.skip)( - 'recovers when persistence is missing', - async () => { - await withTestDbs(/* persistence */ false, 2, async dbs => { - for (let i = 0; i < 2; ++i) { - const db = dbs[i]; - - try { - await (i === 0 ? db.enablePersistence() : db.clearPersistence()); - expect.fail( - 'Persistence operation should fail for memory-only build' - ); - } catch (err) { - expect(err.code).to.equal('failed-precondition'); - expect(err.message).to.match( - /You are using the memory-only build of Firestore./ - ); - } - - // Verify client functionality after failed call - await db.doc('coll/doc').get(); - } - }); - } - ); - // eslint-disable-next-line no-restricted-properties (persistence ? it : it.skip)( 'maintains persistence after restarting app', diff --git a/packages/firestore/test/integration/bootstrap.ts b/packages/firestore/test/integration/bootstrap.ts index a38829ab6b9..3b333395fc1 100644 --- a/packages/firestore/test/integration/bootstrap.ts +++ b/packages/firestore/test/integration/bootstrap.ts @@ -15,7 +15,8 @@ * limitations under the License. */ -import '../../index'; +import '../../src/index'; +import '../../compat/index'; /** * This will include all of the test files and compile them as needed diff --git a/packages/firestore/test/integration/util/firebase_export.ts b/packages/firestore/test/integration/util/firebase_export.ts index b6d825c900b..26f77b8b78a 100644 --- a/packages/firestore/test/integration/util/firebase_export.ts +++ b/packages/firestore/test/integration/util/firebase_export.ts @@ -20,22 +20,20 @@ // reference to the minified sources. If you change any exports in this file, // you need to also adjust "integration/firestore/firebase_export.ts". -import firebase from '@firebase/app'; +import firebase from '@firebase/app-compat'; import { FirebaseApp } from '@firebase/app-types'; import * as firestore from '@firebase/firestore-types'; -import { Blob } from '../../../src/api/blob'; +import { Blob } from '../../../compat/api/blob'; import { Firestore, DocumentReference, QueryDocumentSnapshot -} from '../../../src/api/database'; -import { FieldPath } from '../../../src/api/field_path'; -import { FieldValue } from '../../../src/api/field_value'; -import { GeoPoint } from '../../../src/api/geo_point'; -import { Timestamp } from '../../../src/api/timestamp'; -// Import to trigger prototype patching of bundle loading. -import '../../../index.bundle'; +} from '../../../compat/api/database'; +import { FieldPath } from '../../../compat/api/field_path'; +import { FieldValue } from '../../../compat/api/field_value'; +import { GeoPoint } from '../../../compat/api/geo_point'; +import { Timestamp } from '../../../compat/api/timestamp'; // 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. @@ -64,8 +62,9 @@ export function newTestFirestore( nameOrApp ) : nameOrApp; - const firestore = firebase.firestore(app); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const firestore = (firebase as any).firestore(app); if (settings) { firestore.settings(settings); } diff --git a/packages/firestore/test/integration/util/internal_helpers.ts b/packages/firestore/test/integration/util/internal_helpers.ts index 9495fb71c79..5c2962dec1f 100644 --- a/packages/firestore/test/integration/util/internal_helpers.ts +++ b/packages/firestore/test/integration/util/internal_helpers.ts @@ -17,12 +17,12 @@ import * as firestore from '@firebase/firestore-types'; +import { Firestore } from '../../../compat/api/database'; import { CredentialChangeListener, CredentialsProvider, EmptyCredentialsProvider } from '../../../src/api/credentials'; -import { Firestore } from '../../../src/api/database'; import { User } from '../../../src/auth/user'; import { DatabaseId, DatabaseInfo } from '../../../src/core/database_info'; import { newConnection } from '../../../src/platform/connection'; @@ -73,11 +73,8 @@ export class MockCredentialsProvider extends EmptyCredentialsProvider { this.asyncQueue!.enqueueRetryable(async () => this.listener!(newUser)); } - setChangeListener( - asyncQueue: AsyncQueue, - listener: CredentialChangeListener - ): void { - super.setChangeListener(asyncQueue, listener); + start(asyncQueue: AsyncQueue, listener: CredentialChangeListener): void { + super.start(asyncQueue, listener); this.asyncQueue = asyncQueue; this.listener = listener; } diff --git a/packages/firestore/test/lite/helpers.ts b/packages/firestore/test/lite/helpers.ts index 964592f5621..e51c7c606ab 100644 --- a/packages/firestore/test/lite/helpers.ts +++ b/packages/firestore/test/lite/helpers.ts @@ -16,10 +16,10 @@ */ // eslint-disable-next-line import/no-extraneous-dependencies -import { initializeApp } from '@firebase/app-exp'; +import { initializeApp } from '@firebase/app'; import { expect } from 'chai'; -import { initializeFirestore, Firestore } from '../../src/lite/database'; +import { initializeFirestore, Firestore } from '../../src/lite-api/database'; import { doc, collection, @@ -28,10 +28,10 @@ import { DocumentReference, SetOptions, PartialWithFieldValue -} from '../../src/lite/reference'; -import { setDoc } from '../../src/lite/reference_impl'; -import { FirestoreSettings } from '../../src/lite/settings'; -import { QueryDocumentSnapshot } from '../../src/lite/snapshot'; +} from '../../src/lite-api/reference'; +import { setDoc } from '../../src/lite-api/reference_impl'; +import { FirestoreSettings } from '../../src/lite-api/settings'; +import { QueryDocumentSnapshot } from '../../src/lite-api/snapshot'; import { AutoId } from '../../src/util/misc'; import { DEFAULT_PROJECT_ID, diff --git a/packages/firestore/test/lite/integration.test.ts b/packages/firestore/test/lite/integration.test.ts index a0a803d6b13..a5035d58d3b 100644 --- a/packages/firestore/test/lite/integration.test.ts +++ b/packages/firestore/test/lite/integration.test.ts @@ -16,26 +16,26 @@ */ // eslint-disable-next-line import/no-extraneous-dependencies -import { initializeApp } from '@firebase/app-exp'; +import { initializeApp } from '@firebase/app'; import { expect, use } from 'chai'; import * as chaiAsPromised from 'chai-as-promised'; -import { Bytes } from '../../src/lite/bytes'; +import { Bytes } from '../../src/lite-api/bytes'; import { Firestore, getFirestore, initializeFirestore, terminate -} from '../../src/lite/database'; -import { FieldPath } from '../../src/lite/field_path'; -import { FieldValue } from '../../src/lite/field_value'; +} from '../../src/lite-api/database'; +import { FieldPath } from '../../src/lite-api/field_path'; +import { FieldValue } from '../../src/lite-api/field_value'; import { arrayRemove, arrayUnion, deleteField, increment, serverTimestamp -} from '../../src/lite/field_value_impl'; +} from '../../src/lite-api/field_value_impl'; import { endAt, endBefore, @@ -46,7 +46,7 @@ import { startAfter, startAt, where -} from '../../src/lite/query'; +} from '../../src/lite-api/query'; import { collection, CollectionReference, @@ -60,7 +60,7 @@ import { WithFieldValue, PartialWithFieldValue, UpdateData -} from '../../src/lite/reference'; +} from '../../src/lite-api/reference'; import { addDoc, deleteDoc, @@ -68,15 +68,15 @@ import { getDocs, setDoc, updateDoc -} from '../../src/lite/reference_impl'; +} from '../../src/lite-api/reference_impl'; import { snapshotEqual, QuerySnapshot, QueryDocumentSnapshot -} from '../../src/lite/snapshot'; -import { Timestamp } from '../../src/lite/timestamp'; -import { runTransaction } from '../../src/lite/transaction'; -import { writeBatch } from '../../src/lite/write_batch'; +} from '../../src/lite-api/snapshot'; +import { Timestamp } from '../../src/lite-api/timestamp'; +import { runTransaction } from '../../src/lite-api/transaction'; +import { writeBatch } from '../../src/lite-api/write_batch'; import { DEFAULT_PROJECT_ID, DEFAULT_SETTINGS diff --git a/packages/firestore/index.bundle.ts b/packages/firestore/test/register.ts similarity index 62% rename from packages/firestore/index.bundle.ts rename to packages/firestore/test/register.ts index 368c41bd8d4..be93ecdccc2 100644 --- a/packages/firestore/index.bundle.ts +++ b/packages/firestore/test/register.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2020 Google LLC + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,14 +15,11 @@ * limitations under the License. */ -import { Firestore, loadBundle, namedQuery } from './export'; +import firebase from '@firebase/app-compat'; +import { FirebaseNamespace } from '@firebase/app-types'; -/** - * Prototype patches bundle loading to Firestore. - */ -export function registerBundle(instance: typeof Firestore): void { - instance.prototype.loadBundle = loadBundle; - instance.prototype.namedQuery = namedQuery; -} +import { registerFirestore as registerFirestoreCompat } from '../compat'; +import { registerFirestore } from '../src/register'; -registerBundle(Firestore); +registerFirestore(); +registerFirestoreCompat(firebase as unknown as FirebaseNamespace); diff --git a/packages/firestore/test/unit/api/blob.test.ts b/packages/firestore/test/unit/api/blob.test.ts index 164eea702c4..ae85ebde898 100644 --- a/packages/firestore/test/unit/api/blob.test.ts +++ b/packages/firestore/test/unit/api/blob.test.ts @@ -17,7 +17,7 @@ import { expect } from 'chai'; -import { Blob } from '../../../src/api/blob'; +import { Blob } from '../../../compat/api/blob'; import { blob, expectEqual, expectNotEqual } from '../../util/helpers'; describe('Blob', () => { diff --git a/packages/firestore/test/unit/api/document_change.test.ts b/packages/firestore/test/unit/api/document_change.test.ts index 0d167efe07d..15445d2d01d 100644 --- a/packages/firestore/test/unit/api/document_change.test.ts +++ b/packages/firestore/test/unit/api/document_change.test.ts @@ -17,11 +17,11 @@ import { expect } from 'chai'; +import { Query } from '../../../src/api/reference'; +import { ExpUserDataWriter } from '../../../src/api/reference_impl'; +import { QuerySnapshot } from '../../../src/api/snapshot'; import { Query as InternalQuery } from '../../../src/core/query'; import { View } from '../../../src/core/view'; -import { Query } from '../../../src/exp/reference'; -import { ExpUserDataWriter } from '../../../src/exp/reference_impl'; -import { QuerySnapshot } from '../../../src/exp/snapshot'; import { documentKeySet } from '../../../src/model/collections'; import { MutableDocument } from '../../../src/model/document'; import { DocumentKey } from '../../../src/model/document_key'; diff --git a/packages/firestore/test/unit/api/field_value.test.ts b/packages/firestore/test/unit/api/field_value.test.ts index 5c437446633..9ba1b0585a1 100644 --- a/packages/firestore/test/unit/api/field_value.test.ts +++ b/packages/firestore/test/unit/api/field_value.test.ts @@ -17,7 +17,7 @@ import { expect } from 'chai'; -import { FieldValue } from '../../../src/api/field_value'; +import { FieldValue } from '../../../compat/api/field_value'; import { expectEqual, expectNotEqual } from '../../util/helpers'; describe('FieldValue', () => { diff --git a/packages/firestore/test/unit/core/query.test.ts b/packages/firestore/test/unit/core/query.test.ts index 0c9a30885e0..248db631b3d 100644 --- a/packages/firestore/test/unit/core/query.test.ts +++ b/packages/firestore/test/unit/core/query.test.ts @@ -17,7 +17,7 @@ import { expect } from 'chai'; -import { Blob } from '../../../src/api/blob'; +import { Blob } from '../../../compat/api/blob'; import { GeoPoint } from '../../../src/api/geo_point'; import { Timestamp } from '../../../src/api/timestamp'; import { diff --git a/packages/firestore/test/unit/local/local_store.test.ts b/packages/firestore/test/unit/local/local_store.test.ts index 9685fa71d20..71e0ab862cd 100644 --- a/packages/firestore/test/unit/local/local_store.test.ts +++ b/packages/firestore/test/unit/local/local_store.test.ts @@ -17,7 +17,7 @@ import { expect } from 'chai'; -import { FieldValue } from '../../../src/api/field_value'; +import { FieldValue } from '../../../compat/api/field_value'; import { Timestamp } from '../../../src/api/timestamp'; import { User } from '../../../src/auth/user'; import { BundledDocuments, NamedQuery } from '../../../src/core/bundle'; diff --git a/packages/firestore/test/unit/model/mutation.test.ts b/packages/firestore/test/unit/model/mutation.test.ts index d4b4d718be1..03bb4aa706a 100644 --- a/packages/firestore/test/unit/model/mutation.test.ts +++ b/packages/firestore/test/unit/model/mutation.test.ts @@ -17,7 +17,7 @@ import { expect } from 'chai'; -import { FieldValue } from '../../../src/api/field_value'; +import { FieldValue } from '../../../compat/api/field_value'; import { Timestamp } from '../../../src/api/timestamp'; import { MutableDocument } from '../../../src/model/document'; import { diff --git a/packages/firestore/test/unit/remote/serializer.helper.ts b/packages/firestore/test/unit/remote/serializer.helper.ts index 43458d2aae2..bee01fbdaa6 100644 --- a/packages/firestore/test/unit/remote/serializer.helper.ts +++ b/packages/firestore/test/unit/remote/serializer.helper.ts @@ -17,9 +17,12 @@ import { expect } from 'chai'; -import { Blob } from '../../../src/api/blob'; -import { DocumentReference, UserDataWriter } from '../../../src/api/database'; -import { FieldValue } from '../../../src/api/field_value'; +import { Blob } from '../../../compat/api/blob'; +import { + DocumentReference, + UserDataWriter +} from '../../../compat/api/database'; +import { FieldValue } from '../../../compat/api/field_value'; import { GeoPoint } from '../../../src/api/geo_point'; import { Timestamp } from '../../../src/api/timestamp'; import { DatabaseId } from '../../../src/core/database_info'; @@ -46,7 +49,7 @@ import { targetEquals, TargetImpl } from '../../../src/core/target'; -import { parseQueryValue } from '../../../src/lite/user_data_reader'; +import { parseQueryValue } from '../../../src/lite-api/user_data_reader'; import { TargetData, TargetPurpose } from '../../../src/local/target_data'; import { FieldMask } from '../../../src/model/field_mask'; import { diff --git a/packages/firestore/test/unit/specs/spec_builder.ts b/packages/firestore/test/unit/specs/spec_builder.ts index 4e4b9b268b5..1f36c1a9cf7 100644 --- a/packages/firestore/test/unit/specs/spec_builder.ts +++ b/packages/firestore/test/unit/specs/spec_builder.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { UserDataWriter } from '../../../src/api/database'; +import { UserDataWriter } from '../../../compat/api/database'; import { hasLimitToFirst, hasLimitToLast, diff --git a/packages/firestore/test/unit/specs/spec_test_runner.ts b/packages/firestore/test/unit/specs/spec_test_runner.ts index 785ca80de30..3ba157871dc 100644 --- a/packages/firestore/test/unit/specs/spec_test_runner.ts +++ b/packages/firestore/test/unit/specs/spec_test_runner.ts @@ -17,6 +17,7 @@ import { expect } from 'chai'; +import { LoadBundleTask } from '../../../src/api/bundle'; import { EmptyCredentialsProvider } from '../../../src/api/credentials'; import { User } from '../../../src/auth/user'; import { ComponentConfiguration } from '../../../src/core/component_provider'; @@ -57,7 +58,6 @@ import { ChangeType, DocumentViewChange } from '../../../src/core/view_snapshot'; -import { LoadBundleTask } from '../../../src/exp/bundle'; import { IndexedDbPersistence } from '../../../src/local/indexeddb_persistence'; import { DbPrimaryClient, diff --git a/packages/firestore/test/util/api_helpers.ts b/packages/firestore/test/util/api_helpers.ts index 35f89e11c85..432f3fe4666 100644 --- a/packages/firestore/test/util/api_helpers.ts +++ b/packages/firestore/test/util/api_helpers.ts @@ -18,8 +18,6 @@ // Helpers here mock Firestore in order to unit-test API types. Do NOT use // these in any integration test, where we expect working Firestore object. -import { Provider, ComponentContainer } from '@firebase/component'; - import { CollectionReference, DocumentReference, @@ -29,28 +27,29 @@ import { Query, QuerySnapshot, UserDataWriter -} from '../../src/api/database'; -import { DatabaseId } from '../../src/core/database_info'; -import { newQueryForPath, Query as InternalQuery } from '../../src/core/query'; -import { - ChangeType, - DocumentViewChange, - ViewSnapshot -} from '../../src/core/view_snapshot'; +} from '../../compat/api/database'; +import { EmptyCredentialsProvider } from '../../src/api/credentials'; import { ensureFirestoreConfigured, Firestore as ExpFirestore -} from '../../src/exp/database'; +} from '../../src/api/database'; import { Query as ExpQuery, CollectionReference as ExpCollectionReference -} from '../../src/exp/reference'; -import { ExpUserDataWriter } from '../../src/exp/reference_impl'; +} from '../../src/api/reference'; +import { ExpUserDataWriter } from '../../src/api/reference_impl'; import { QuerySnapshot as ExpQuerySnapshot, DocumentSnapshot as ExpDocumentSnapshot, SnapshotMetadata -} from '../../src/exp/snapshot'; +} from '../../src/api/snapshot'; +import { DatabaseId } from '../../src/core/database_info'; +import { newQueryForPath, Query as InternalQuery } from '../../src/core/query'; +import { + ChangeType, + DocumentViewChange, + ViewSnapshot +} from '../../src/core/view_snapshot'; import { DocumentKeySet } from '../../src/model/collections'; import { DocumentSet } from '../../src/model/document_set'; import { JsonObject } from '../../src/model/object_value'; @@ -70,10 +69,7 @@ export function firestore(): Firestore { export function newTestFirestore(projectId = 'new-project'): Firestore { return new Firestore( new DatabaseId(projectId), - new ExpFirestore( - new DatabaseId(projectId), - new Provider('auth-internal', new ComponentContainer('default')) - ), + new ExpFirestore(new DatabaseId(projectId), new EmptyCredentialsProvider()), new IndexedDbPersistenceProvider() ); } diff --git a/packages/firestore/test/util/helpers.ts b/packages/firestore/test/util/helpers.ts index 5d1cb3caf99..a1ab260288e 100644 --- a/packages/firestore/test/util/helpers.ts +++ b/packages/firestore/test/util/helpers.ts @@ -18,8 +18,8 @@ import * as firestore from '@firebase/firestore-types'; import { expect } from 'chai'; -import { Blob } from '../../src/api/blob'; -import { DocumentReference } from '../../src/api/database'; +import { Blob } from '../../compat/api/blob'; +import { DocumentReference } from '../../compat/api/database'; import { Timestamp } from '../../src/api/timestamp'; import { BundledDocuments } from '../../src/core/bundle'; import { DatabaseId } from '../../src/core/database_info'; @@ -53,7 +53,7 @@ import { parseSetData, parseUpdateData, UserDataReader -} from '../../src/lite/user_data_reader'; +} from '../../src/lite-api/user_data_reader'; import { LocalViewChanges } from '../../src/local/local_view_changes'; import { TargetData, TargetPurpose } from '../../src/local/target_data'; import { diff --git a/scripts/ci-test/testConfig.ts b/scripts/ci-test/testConfig.ts index 4cd6fe7d859..06c3774b376 100644 --- a/scripts/ci-test/testConfig.ts +++ b/scripts/ci-test/testConfig.ts @@ -44,7 +44,7 @@ export const testConfig: { ] }, 'firestore': { - 'onlyIncludePackages': ['@firebase/firestore'] + 'onlyIncludePackages': ['@firebase/firestore', '@firebase/firestore-compat'] }, 'firestore-integration': { 'onlyIncludePackages': ['firebase-firestore-integration-test']