Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/app/index.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

import { FirebaseNamespace } from '@firebase/app-types';
import { _FirebaseNamespace } from '@firebase/app-types/private';
import { createFirebaseNamespace } from './src/firebaseApp';
import { createFirebaseNamespace } from './src/firebaseNamespace';
import Storage from 'dom-storage';
import { XMLHttpRequest } from 'xmlhttprequest';

Expand Down
2 changes: 1 addition & 1 deletion packages/app/index.rn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

import { FirebaseNamespace } from '@firebase/app-types';
import { _FirebaseNamespace } from '@firebase/app-types/private';
import { createFirebaseNamespace } from './src/firebaseApp';
import { createFirebaseNamespace } from './src/firebaseNamespace';

/**
* To avoid having to include the @types/react-native package, which breaks
Expand Down
2 changes: 1 addition & 1 deletion packages/app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*/

import { FirebaseNamespace } from '@firebase/app-types';
import { createFirebaseNamespace } from './src/firebaseApp';
import { createFirebaseNamespace } from './src/firebaseNamespace';

// Node detection logic from: https://github.com/iliakan/detect-node/
let isNode = false;
Expand Down
47 changes: 47 additions & 0 deletions packages/app/src/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* @license
* Copyright 2019 Google Inc.
*
* 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 { ErrorFactory } from '@firebase/util';

export const enum AppError {
NO_APP = 'no-app',
BAD_APP_NAME = 'bad-app-name',
DUPLICATE_APP = 'duplicate-app',
APP_DELETED = 'app-deleted',
DUPLICATE_SERVICE = 'duplicate-service',
INVALID_APP_ARGUMENT = 'invalid-app-argument'
}

const errors: { readonly [code in AppError]: string } = {
[AppError.NO_APP]:
"No Firebase App '{$name}' has been created - " +
'call Firebase App.initializeApp()',
[AppError.BAD_APP_NAME]: "Illegal App name: '{$name}",
[AppError.DUPLICATE_APP]: "Firebase App named '{$name}' already exists",
[AppError.APP_DELETED]: "Firebase App named '{$name}' already deleted",
[AppError.DUPLICATE_SERVICE]:
"Firebase service named '{$name}' already registered",
[AppError.INVALID_APP_ARGUMENT]:
'firebase.{$name}() takes either no argument or a ' +
'Firebase App instance.'
};

let appErrors = new ErrorFactory<AppError>('app', 'Firebase', errors);

export function error(code: AppError, args?: { [name: string]: any }) {
throw appErrors.create(code, args);
}
286 changes: 6 additions & 280 deletions packages/app/src/firebaseApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,27 +24,12 @@ import {
import {
_FirebaseApp,
_FirebaseNamespace,
FirebaseService,
FirebaseServiceFactory,
FirebaseServiceNamespace,
AppHook
FirebaseService
} from '@firebase/app-types/private';
import {
createSubscribe,
deepCopy,
deepExtend,
ErrorFactory,
FirebaseError,
Observer,
patchProperty,
Subscribe
} from '@firebase/util';

const contains = function(obj, key) {
return Object.prototype.hasOwnProperty.call(obj, key);
};
import { deepCopy, deepExtend } from '@firebase/util';
import { error, AppError } from './errors';

const DEFAULT_ENTRY_NAME = '[DEFAULT]';
export const DEFAULT_ENTRY_NAME = '[DEFAULT]';

// An array to capture listeners before the true auth functions
// exist
Expand All @@ -54,7 +39,7 @@ let tokenListeners: any[] = [];
* Global context object for a collection of services using
* a shared authentication state.
*/
class FirebaseAppImpl implements FirebaseApp {
export class FirebaseAppImpl implements FirebaseApp {
private options_: FirebaseOptions;
private name_: string;
private isDeleted_ = false;
Expand Down Expand Up @@ -212,7 +197,7 @@ class FirebaseAppImpl implements FirebaseApp {
*/
private checkDestroyed_(): void {
if (this.isDeleted_) {
error('app-deleted', { name: this.name_ });
error(AppError.APP_DELETED, { name: this.name_ });
}
}
}
Expand All @@ -222,262 +207,3 @@ class FirebaseAppImpl implements FirebaseApp {
(FirebaseAppImpl.prototype.name && FirebaseAppImpl.prototype.options) ||
FirebaseAppImpl.prototype.delete ||
console.log('dc');

/**
* Return a firebase namespace object.
*
* In production, this will be called exactly once and the result
* assigned to the 'firebase' global. It may be called multiple times
* in unit tests.
*/
export function createFirebaseNamespace(): FirebaseNamespace {
let apps_: { [name: string]: FirebaseApp } = {};
let factories: { [service: string]: FirebaseServiceFactory } = {};
let appHooks: { [service: string]: AppHook } = {};

// A namespace is a plain JavaScript Object.
let namespace = {
// Hack to prevent Babel from modifying the object returned
// as the firebase namespace.
__esModule: true,
initializeApp: initializeApp,
app: app as any,
apps: null as any,
Promise: Promise,
SDK_VERSION: '${JSCORE_VERSION}',
INTERNAL: {
registerService: registerService,
createFirebaseNamespace: createFirebaseNamespace,
extendNamespace: extendNamespace,
createSubscribe: createSubscribe,
ErrorFactory: ErrorFactory,
removeApp: removeApp,
factories: factories,
useAsService: useAsService,
Promise: Promise,
deepExtend: deepExtend
}
};

// Inject a circular default export to allow Babel users who were previously
// using:
//
// import firebase from 'firebase';
// which becomes: var firebase = require('firebase').default;
//
// instead of
//
// import * as firebase from 'firebase';
// which becomes: var firebase = require('firebase');
patchProperty(namespace, 'default', namespace);

// firebase.apps is a read-only getter.
Object.defineProperty(namespace, 'apps', {
get: getApps
});

/**
* Called by App.delete() - but before any services associated with the App
* are deleted.
*/
function removeApp(name: string): void {
let app = apps_[name];
callAppHooks(app, 'delete');
delete apps_[name];
}

/**
* Get the App object for a given name (or DEFAULT).
*/
function app(name?: string): FirebaseApp {
name = name || DEFAULT_ENTRY_NAME;
if (!contains(apps_, name)) {
error('no-app', { name: name });
}
return apps_[name];
}

patchProperty(app, 'App', FirebaseAppImpl);

/**
* Create a new App instance (name must be unique).
*/
function initializeApp(
options: FirebaseOptions,
config?: FirebaseAppConfig
): FirebaseApp;
function initializeApp(options: FirebaseOptions, name?: string): FirebaseApp;
function initializeApp(options: FirebaseOptions, rawConfig = {}) {
if (typeof rawConfig !== 'object' || rawConfig === null) {
const name = rawConfig;
rawConfig = { name };
}

const config = rawConfig as FirebaseAppConfig;

if (config.name === undefined) {
config.name = DEFAULT_ENTRY_NAME;
}

const { name } = config;

if (typeof name !== 'string' || !name) {
error('bad-app-name', { name: name + '' });
}

if (contains(apps_, name)) {
error('duplicate-app', { name: name });
}

let app = new FirebaseAppImpl(
options,
config!,
namespace as FirebaseNamespace
);

apps_[name!] = app;
callAppHooks(app, 'create');

return app;
}

/*
* Return an array of all the non-deleted FirebaseApps.
*/
function getApps(): FirebaseApp[] {
// Make a copy so caller cannot mutate the apps list.
return Object.keys(apps_).map(name => apps_[name]);
}

/*
* Register a Firebase Service.
*
* firebase.INTERNAL.registerService()
*
* TODO: Implement serviceProperties.
*/
function registerService(
name: string,
createService: FirebaseServiceFactory,
serviceProperties?: { [prop: string]: any },
appHook?: AppHook,
allowMultipleInstances?: boolean
): FirebaseServiceNamespace<FirebaseService> {
// Cannot re-register a service that already exists
if (factories[name]) {
error('duplicate-service', { name: name });
}

// Capture the service factory for later service instantiation
factories[name] = createService;

// Capture the appHook, if passed
if (appHook) {
appHooks[name] = appHook;

// Run the **new** app hook on all existing apps
getApps().forEach(app => {
appHook('create', app);
});
}

// The Service namespace is an accessor function ...
const serviceNamespace = (appArg: FirebaseApp = app()) => {
if (typeof (appArg as any)[name] !== 'function') {
// Invalid argument.
// This happens in the following case: firebase.storage('gs:/')
error('invalid-app-argument', { name: name });
}

// Forward service instance lookup to the FirebaseApp.
return (appArg as any)[name]();
};

// ... and a container for service-level properties.
if (serviceProperties !== undefined) {
deepExtend(serviceNamespace, serviceProperties);
}

// Monkey-patch the serviceNamespace onto the firebase namespace
(namespace as any)[name] = serviceNamespace;

// Patch the FirebaseAppImpl prototype
FirebaseAppImpl.prototype[name] = function(...args) {
const serviceFxn = this._getService.bind(this, name);
return serviceFxn.apply(this, allowMultipleInstances ? args : []);
};

return serviceNamespace;
}

/**
* Patch the top-level firebase namespace with additional properties.
*
* firebase.INTERNAL.extendNamespace()
*/
function extendNamespace(props: { [prop: string]: any }): void {
deepExtend(namespace, props);
}

function callAppHooks(app: FirebaseApp, eventName: string) {
Object.keys(factories).forEach(serviceName => {
// Ignore virtual services
let factoryName = useAsService(app, serviceName);
if (factoryName === null) {
return;
}

if (appHooks[factoryName]) {
appHooks[factoryName](eventName, app);
}
});
}

// Map the requested service to a registered service name
// (used to map auth to serverAuth service when needed).
function useAsService(app: FirebaseApp, name: string): string | null {
if (name === 'serverAuth') {
return null;
}

let useService = name;
let options = app.options;

return useService;
}

return (namespace as any) as FirebaseNamespace;
}

type AppError =
| 'no-app'
| 'bad-app-name'
| 'duplicate-app'
| 'app-deleted'
| 'duplicate-service'
| 'sa-not-supported'
| 'invalid-app-argument';

function error(code: AppError, args?: { [name: string]: any }) {
throw appErrors.create(code, args);
}

const errors: { readonly [code in AppError]: string } = {
'no-app':
"No Firebase App '{$name}' has been created - " +
'call Firebase App.initializeApp()',
'bad-app-name': "Illegal App name: '{$name}",
'duplicate-app': "Firebase App named '{$name}' already exists",
'app-deleted': "Firebase App named '{$name}' already deleted",
'duplicate-service': "Firebase service named '{$name}' already registered",
'sa-not-supported':
'Initializing the Firebase SDK with a service ' +
'account is only allowed in a Node.js environment. On client ' +
'devices, you should instead initialize the SDK with an api key and ' +
'auth domain',
'invalid-app-argument':
'firebase.{$name}() takes either no argument or a ' +
'Firebase App instance.'
};

let appErrors = new ErrorFactory<AppError>('app', 'Firebase', errors);
Loading