Skip to content

Commit 1ee90c7

Browse files
committed
Fresh branch for jamesdaniels_ssr_autoconfig
1 parent c6ba6fc commit 1ee90c7

File tree

13 files changed

+235
-30
lines changed

13 files changed

+235
-30
lines changed

config/.eslintrc.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ module.exports = {
129129
{
130130
// Check dependencies from both local package.json
131131
// and from root package.json.
132-
'packageDir': [path.join(__dirname, '../'), './'],
132+
'packageDir': [context.getFilename(), path.join(__dirname, '../'), './'],
133133
'devDependencies': [
134134
'**/*.test.ts',
135135
'**/test/**/*.ts',

packages/app/src/api.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import {
3939
LogOptions,
4040
setUserLogHandler
4141
} from '@firebase/logger';
42-
import { deepEqual } from '@firebase/util';
42+
import { deepEqual, getDefaultAppConfig } from '@firebase/util';
4343

4444
export { FirebaseError } from '@firebase/util';
4545

@@ -110,10 +110,18 @@ export function initializeApp(
110110
options: FirebaseOptions,
111111
config?: FirebaseAppSettings
112112
): FirebaseApp;
113+
/**
114+
* Creates and initializes a FirebaseApp instance.
115+
*
116+
* @public
117+
*/
118+
export function initializeApp(): FirebaseApp;
113119
export function initializeApp(
114-
options: FirebaseOptions,
120+
_options?: FirebaseOptions,
115121
rawConfig = {}
116122
): FirebaseApp {
123+
let options = _options;
124+
117125
if (typeof rawConfig !== 'object') {
118126
const name = rawConfig;
119127
rawConfig = { name };
@@ -132,6 +140,12 @@ export function initializeApp(
132140
});
133141
}
134142

143+
options ||= getDefaultAppConfig();
144+
145+
if (!options) {
146+
throw ERROR_FACTORY.create(AppError.NO_OPTIONS);
147+
}
148+
135149
const existingApp = _apps.get(name) as FirebaseAppImpl;
136150
if (existingApp) {
137151
// return the existing app if options and config deep equal the ones in the existing app.
@@ -188,6 +202,9 @@ export function initializeApp(
188202
*/
189203
export function getApp(name: string = DEFAULT_ENTRY_NAME): FirebaseApp {
190204
const app = _apps.get(name);
205+
if (!app && name === DEFAULT_ENTRY_NAME) {
206+
return initializeApp();
207+
}
191208
if (!app) {
192209
throw ERROR_FACTORY.create(AppError.NO_APP, { appName: name });
193210
}

packages/app/src/errors.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export const enum AppError {
2222
BAD_APP_NAME = 'bad-app-name',
2323
DUPLICATE_APP = 'duplicate-app',
2424
APP_DELETED = 'app-deleted',
25+
NO_OPTIONS = 'no-options',
2526
INVALID_APP_ARGUMENT = 'invalid-app-argument',
2627
INVALID_LOG_ARGUMENT = 'invalid-log-argument',
2728
IDB_OPEN = 'idb-open',
@@ -38,6 +39,7 @@ const ERRORS: ErrorMap<AppError> = {
3839
[AppError.DUPLICATE_APP]:
3940
"Firebase App named '{$appName}' already exists with different options or config",
4041
[AppError.APP_DELETED]: "Firebase App named '{$appName}' already deleted",
42+
[AppError.NO_OPTIONS]: "Need to provide options, when not being deployed to hosting via source.",
4143
[AppError.INVALID_APP_ARGUMENT]:
4244
'firebase.{$appName}() takes either no argument or a ' +
4345
'Firebase App instance.',

packages/auth/src/platform_browser/index.ts

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,36 @@
1717

1818
import { FirebaseApp, getApp, _getProvider } from '@firebase/app';
1919

20-
import { initializeAuth } from '..';
20+
import { initializeAuth, beforeAuthStateChanged, onIdTokenChanged, connectAuthEmulator } from '..';
2121
import { registerAuth } from '../core/auth/register';
2222
import { ClientPlatform } from '../core/util/version';
2323
import { browserLocalPersistence } from './persistence/local_storage';
2424
import { browserSessionPersistence } from './persistence/session_storage';
2525
import { indexedDBLocalPersistence } from './persistence/indexed_db';
2626
import { browserPopupRedirectResolver } from './popup_redirect';
27-
import { Auth } from '../model/public_types';
27+
import { Auth, User } from '../model/public_types';
28+
import { getDefaultEmulatorHost, getExperimentalSetting } from '@firebase/util';
29+
30+
const DEFAULT_ID_TOKEN_MAX_AGE = 5 * 60;
31+
const authIdTokenMaxAge = getExperimentalSetting('authIdTokenMaxAge') || DEFAULT_ID_TOKEN_MAX_AGE;
32+
33+
let lastPostedIdToken: string|undefined|null = null;
34+
35+
const mintCookieFactory = (url:string) => async (user: User|null) => {
36+
const idTokenResult = user && await user.getIdTokenResult();
37+
const idTokenAge = idTokenResult && (new Date().getTime() - Date.parse(idTokenResult.issuedAtTime)) / 1_000;
38+
if (idTokenAge && idTokenAge > authIdTokenMaxAge) return;
39+
// Specifically trip null => undefined when logged out, to delete any existing cookie
40+
const idToken = idTokenResult?.token;
41+
if (lastPostedIdToken === idToken) return;
42+
lastPostedIdToken = idToken;
43+
await fetch(url, {
44+
method: idToken ? 'POST' : 'DELETE',
45+
headers: idToken ? {
46+
'Authorization': `Bearer ${idToken}`,
47+
} : {}
48+
});
49+
};
2850

2951
/**
3052
* Returns the Auth instance associated with the provided {@link @firebase/app#FirebaseApp}.
@@ -41,14 +63,30 @@ export function getAuth(app: FirebaseApp = getApp()): Auth {
4163
return provider.getImmediate();
4264
}
4365

44-
return initializeAuth(app, {
66+
const auth = initializeAuth(app, {
4567
popupRedirectResolver: browserPopupRedirectResolver,
4668
persistence: [
4769
indexedDBLocalPersistence,
4870
browserLocalPersistence,
4971
browserSessionPersistence
5072
]
5173
});
74+
75+
const authTokenSyncUrl = getExperimentalSetting('authTokenSyncURL');
76+
if (authTokenSyncUrl) {
77+
const mintCookie = mintCookieFactory(authTokenSyncUrl);
78+
beforeAuthStateChanged(auth, mintCookie, () => {
79+
mintCookie(auth.currentUser)
80+
});
81+
onIdTokenChanged(auth, user => mintCookie(user));
82+
}
83+
84+
const authEmulatorHost = getDefaultEmulatorHost('auth');
85+
if (authEmulatorHost) {
86+
connectAuthEmulator(auth, `http://${authEmulatorHost}`);
87+
}
88+
89+
return auth;
5290
}
5391

5492
registerAuth(ClientPlatform.BROWSER);

packages/auth/src/platform_node/index.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,14 @@ import { _createError } from '../core/util/assert';
2121
import { FirebaseApp, getApp, _getProvider } from '@firebase/app';
2222
import { Auth } from '../model/public_types';
2323

24-
import { initializeAuth, inMemoryPersistence } from '..';
24+
import { initializeAuth, inMemoryPersistence, connectAuthEmulator } from '..';
2525
import { registerAuth } from '../core/auth/register';
2626
import { ClientPlatform } from '../core/util/version';
2727
import { AuthImpl } from '../core/auth/auth_impl';
2828

2929
import { FetchProvider } from '../core/util/fetch_provider';
3030
import * as fetchImpl from 'node-fetch';
31+
import { getDefaultEmulatorHost } from '@firebase/util';
3132

3233
// Initialize the fetch polyfill, the types are slightly off so just cast and hope for the best
3334
FetchProvider.initialize(
@@ -46,7 +47,14 @@ export function getAuth(app: FirebaseApp = getApp()): Auth {
4647
return provider.getImmediate();
4748
}
4849

49-
return initializeAuth(app);
50+
const auth = initializeAuth(app);
51+
52+
const authEmulatorHost = getDefaultEmulatorHost('auth');
53+
if (authEmulatorHost) {
54+
connectAuthEmulator(auth, `http://${authEmulatorHost}`);
55+
}
56+
57+
return auth;
5058
}
5159

5260
registerAuth(ClientPlatform.NODE);

packages/database/src/api/Database.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ import { Provider } from '@firebase/component';
2727
import {
2828
getModularInstance,
2929
createMockUserToken,
30-
EmulatorMockTokenOptions
30+
EmulatorMockTokenOptions,
31+
getDefaultEmulatorHost
3132
} from '@firebase/util';
3233

3334
import { AppCheckTokenProvider } from '../core/AppCheckTokenProvider';
@@ -53,7 +54,10 @@ import { WebSocketConnection } from '../realtime/WebSocketConnection';
5354

5455
import { ReferenceImpl } from './Reference_impl';
5556

56-
export { EmulatorMockTokenOptions } from '@firebase/util';
57+
export {
58+
EmulatorMockTokenOptions,
59+
getDefaultEmulatorHost
60+
} from '@firebase/util';
5761
/**
5862
* This variable is also defined in the firebase Node.js Admin SDK. Before
5963
* modifying this definition, consult the definition in:
@@ -316,9 +320,15 @@ export function getDatabase(
316320
app: FirebaseApp = getApp(),
317321
url?: string
318322
): Database {
319-
return _getProvider(app, 'database').getImmediate({
323+
const db = _getProvider(app, 'database').getImmediate({
320324
identifier: url
321325
}) as Database;
326+
const databaseEmulatorHost = getDefaultEmulatorHost('database');
327+
if (databaseEmulatorHost) {
328+
const [host, port] = databaseEmulatorHost.split(':');
329+
connectDatabaseEmulator(db, host, parseInt(port, 10));
330+
}
331+
return db;
322332
}
323333

324334
/**

packages/firestore/src/api/database.ts

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,13 @@
1515
* limitations under the License.
1616
*/
1717

18-
// eslint-disable-next-line import/no-extraneous-dependencies
1918
import {
2019
_getProvider,
2120
_removeServiceInstance,
2221
FirebaseApp,
2322
getApp
2423
} from '@firebase/app';
25-
import { deepEqual } from '@firebase/util';
24+
import { deepEqual, getDefaultEmulatorHost } from '@firebase/util';
2625

2726
import { User } from '../auth/user';
2827
import {
@@ -43,7 +42,10 @@ import {
4342
setOnlineComponentProvider
4443
} from '../core/firestore_client';
4544
import { makeDatabaseInfo } from '../lite-api/components';
46-
import { Firestore as LiteFirestore } from '../lite-api/database';
45+
import {
46+
Firestore as LiteFirestore,
47+
connectFirestoreEmulator
48+
} from '../lite-api/database';
4749
import { Query } from '../lite-api/reference';
4850
import {
4951
indexedDbClearPersistence,
@@ -162,9 +164,9 @@ export function initializeFirestore(
162164
throw new FirestoreError(
163165
Code.FAILED_PRECONDITION,
164166
'initializeFirestore() has already been called with ' +
165-
'different options. To avoid this error, call initializeFirestore() with the ' +
166-
'same options as when it was originally called, or call getFirestore() to return the' +
167-
' already initialized instance.'
167+
'different options. To avoid this error, call initializeFirestore() with the ' +
168+
'same options as when it was originally called, or call getFirestore() to return the' +
169+
' already initialized instance.'
168170
);
169171
}
170172
}
@@ -215,7 +217,15 @@ export function getFirestore(app: FirebaseApp): Firestore;
215217
*/
216218
export function getFirestore(databaseId: string): Firestore;
217219
/**
218-
* Returns the existing {@link Firestore} instance that is associated with the
220+
* Returns the existing default {@link Firestore} instance that is associated with the
221+
* default {@link @firebase/app#FirebaseApp}. If no instance exists, initializes a new
222+
* instance with default settings.
223+
*
224+
* @returns The {@link Firestore} instance of the provided app.
225+
*/
226+
export function getFirestore(): Firestore;
227+
/**
228+
* Returns the existing default {@link Firestore} instance that is associated with the
219229
* provided {@link @firebase/app#FirebaseApp}. If no instance exists, initializes a new
220230
* instance with default settings.
221231
*
@@ -236,9 +246,17 @@ export function getFirestore(
236246
typeof appOrDatabaseId === 'string'
237247
? appOrDatabaseId
238248
: optionalDatabaseId || DEFAULT_DATABASE_NAME;
239-
return _getProvider(app, 'firestore').getImmediate({
249+
const db = _getProvider(app, 'firestore').getImmediate({
240250
identifier: databaseId
241251
}) as Firestore;
252+
if (!db._initialized) {
253+
const firestoreEmulatorHost = getDefaultEmulatorHost('firestore');
254+
if (firestoreEmulatorHost) {
255+
const [host, port] = firestoreEmulatorHost.split(':');
256+
connectFirestoreEmulator(db, host, parseInt(port, 10));
257+
}
258+
}
259+
return db;
242260
}
243261

244262
/**
@@ -390,8 +408,8 @@ function setPersistenceProviders(
390408
}
391409
logWarn(
392410
'Error enabling offline persistence. Falling back to ' +
393-
'persistence disabled: ' +
394-
error
411+
'persistence disabled: ' +
412+
error
395413
);
396414
persistenceResult.reject(error);
397415
}
@@ -464,7 +482,7 @@ export function clearIndexedDbPersistence(firestore: Firestore): Promise<void> {
464482
throw new FirestoreError(
465483
Code.FAILED_PRECONDITION,
466484
'Persistence can only be cleared before a Firestore instance is ' +
467-
'initialized or after it is terminated.'
485+
'initialized or after it is terminated.'
468486
);
469487
}
470488

@@ -620,8 +638,8 @@ function verifyNotInitialized(firestore: Firestore): void {
620638
throw new FirestoreError(
621639
Code.FAILED_PRECONDITION,
622640
'Firestore has already been started and persistence can no longer be ' +
623-
'enabled. You can only enable persistence before calling any other ' +
624-
'methods on a Firestore object.'
641+
'enabled. You can only enable persistence before calling any other ' +
642+
'methods on a Firestore object.'
625643
);
626644
}
627645
}

packages/firestore/src/lite-api/database.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ import {
2222
FirebaseApp,
2323
getApp
2424
} from '@firebase/app';
25-
import { createMockUserToken, EmulatorMockTokenOptions } from '@firebase/util';
25+
import {
26+
createMockUserToken,
27+
EmulatorMockTokenOptions,
28+
getDefaultEmulatorHost
29+
} from '@firebase/util';
2630

2731
import {
2832
CredentialsProvider,
@@ -44,6 +48,8 @@ import {
4448
FirestoreSettings
4549
} from './settings';
4650

51+
export { EmulatorMockTokenOptions } from '@firebase/util';
52+
4753
declare module '@firebase/component' {
4854
interface NameServiceMapping {
4955
'firestore/lite': Firestore;
@@ -260,12 +266,19 @@ export function getFirestore(
260266
typeof appOrDatabaseId === 'string'
261267
? appOrDatabaseId
262268
: optionalDatabaseId || '(default)';
263-
return _getProvider(app, 'firestore/lite').getImmediate({
269+
const db = _getProvider(app, 'firestore/lite').getImmediate({
264270
identifier: databaseId
265271
}) as Firestore;
272+
if (!db._initialized) {
273+
const firestoreEmulatorHost = getDefaultEmulatorHost('firestore');
274+
if (firestoreEmulatorHost) {
275+
const [host, port] = firestoreEmulatorHost.split(':');
276+
connectFirestoreEmulator(db, host, parseInt(port, 10));
277+
}
278+
}
279+
return db;
266280
}
267281

268-
export { EmulatorMockTokenOptions } from '@firebase/util';
269282
/**
270283
* Modify this instance to communicate with the Cloud Firestore emulator.
271284
*

packages/functions/src/api.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {
2727
httpsCallable as _httpsCallable,
2828
httpsCallableFromURL as _httpsCallableFromURL
2929
} from './service';
30-
import { getModularInstance } from '@firebase/util';
30+
import { getModularInstance, getDefaultEmulatorHost } from '@firebase/util';
3131

3232
export * from './public-types';
3333

@@ -51,6 +51,12 @@ export function getFunctions(
5151
const functionsInstance = functionsProvider.getImmediate({
5252
identifier: regionOrCustomDomain
5353
});
54+
const functionsEmulatorHost = getDefaultEmulatorHost('functions');
55+
if (functionsEmulatorHost) {
56+
const [host, port] = functionsEmulatorHost.split(':');
57+
// eslint-disable-next-line no-restricted-globals
58+
connectFunctionsEmulator(functionsInstance, host, parseInt(port, 10));
59+
}
5460
return functionsInstance;
5561
}
5662

0 commit comments

Comments
 (0)