diff --git a/packages-exp/functions-exp/package.json b/packages-exp/functions-exp/package.json index 10f486000a6..76b7dbc435c 100644 --- a/packages-exp/functions-exp/package.json +++ b/packages-exp/functions-exp/package.json @@ -54,7 +54,7 @@ "@firebase/functions-types-exp": "0.0.800", "@firebase/messaging-types": "0.5.0", "@firebase/util": "0.3.2", - "isomorphic-fetch": "2.2.1", + "node-fetch": "2.6.1", "tslib": "^1.11.1" }, "nyc": { diff --git a/packages-exp/functions-exp/src/config.ts b/packages-exp/functions-exp/src/config.ts index b963b38c53c..021235d6483 100644 --- a/packages-exp/functions-exp/src/config.ts +++ b/packages-exp/functions-exp/src/config.ts @@ -27,28 +27,31 @@ import { FUNCTIONS_TYPE } from './constants'; export const DEFAULT_REGION = 'us-central1'; -const factory: InstanceFactory<'functions'> = ( - container: ComponentContainer, - region?: string -) => { - // Dependencies - const app = container.getProvider('app-exp').getImmediate(); - const authProvider = container.getProvider('auth-internal'); - const messagingProvider = container.getProvider('messaging'); +export function registerFunctions(fetchImpl: typeof fetch): void { + const factory: InstanceFactory<'functions'> = ( + container: ComponentContainer, + region?: string + ) => { + // Dependencies + const app = container.getProvider('app-exp').getImmediate(); + const authProvider = container.getProvider('auth-internal'); + const messagingProvider = container.getProvider('messaging'); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return new FunctionsService(app, authProvider, messagingProvider, region); -}; - -export function registerFunctions(): void { - const namespaceExports = { - // no-inline - Functions: FunctionsService + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return new FunctionsService( + app, + authProvider, + messagingProvider, + region, + fetchImpl + ); }; _registerComponent( - new Component(FUNCTIONS_TYPE, factory, ComponentType.PUBLIC) - .setServiceProps(namespaceExports) - .setMultipleInstances(true) + new Component( + FUNCTIONS_TYPE, + factory, + ComponentType.PUBLIC + ).setMultipleInstances(true) ); } diff --git a/packages-exp/functions-exp/src/index.node.ts b/packages-exp/functions-exp/src/index.node.ts index df2c852866c..5955172f350 100644 --- a/packages-exp/functions-exp/src/index.node.ts +++ b/packages-exp/functions-exp/src/index.node.ts @@ -16,11 +16,12 @@ */ import { registerVersion } from '@firebase/app-exp'; import { registerFunctions } from './config'; -import 'isomorphic-fetch'; +import nodeFetch from 'node-fetch'; import { name, version } from '../package.json'; export * from './api'; -registerFunctions(); +// eslint-disable-next-line @typescript-eslint/no-explicit-any +registerFunctions(nodeFetch as any); registerVersion(name, version, 'node'); diff --git a/packages-exp/functions-exp/src/index.ts b/packages-exp/functions-exp/src/index.ts index fe76bde3af4..3ebaeb7034a 100644 --- a/packages-exp/functions-exp/src/index.ts +++ b/packages-exp/functions-exp/src/index.ts @@ -21,5 +21,5 @@ import { name, version } from '../package.json'; export * from './api'; -registerFunctions(); +registerFunctions(fetch); registerVersion(name, version); diff --git a/packages-exp/functions-exp/src/service.ts b/packages-exp/functions-exp/src/service.ts index 233896842fb..f51b838a0e0 100644 --- a/packages-exp/functions-exp/src/service.ts +++ b/packages-exp/functions-exp/src/service.ts @@ -84,7 +84,8 @@ export class FunctionsService implements _FirebaseService { readonly app: FirebaseApp, authProvider: Provider, messagingProvider: Provider, - readonly region: string = DEFAULT_REGION + readonly region: string = DEFAULT_REGION, + readonly fetchImpl: typeof fetch ) { this.contextProvider = new ContextProvider(authProvider, messagingProvider); // Cancels all ongoing requests when resolved. @@ -155,13 +156,14 @@ export function httpsCallable( async function postJSON( url: string, body: unknown, - headers: Headers + headers: { [key: string]: string }, + fetchImpl: typeof fetch ): Promise { - headers.append('Content-Type', 'application/json'); + headers['Content-Type'] = 'application/json'; let response: Response; try { - response = await fetch(url, { + response = await fetchImpl(url, { method: 'POST', body: JSON.stringify(body), headers @@ -206,20 +208,20 @@ async function call( const body = { data }; // Add a header for the authToken. - const headers = new Headers(); + const headers: { [key: string]: string } = {}; const context = await functionsInstance.contextProvider.getContext(); if (context.authToken) { - headers.append('Authorization', 'Bearer ' + context.authToken); + headers['Authorization'] = 'Bearer ' + context.authToken; } if (context.messagingToken) { - headers.append('Firebase-Instance-ID-Token', context.messagingToken); + headers['Firebase-Instance-ID-Token'] = context.messagingToken; } // Default timeout to 70s, but let the options override it. const timeout = options.timeout || 70000; const response = await Promise.race([ - postJSON(url, body, headers), + postJSON(url, body, headers, functionsInstance.fetchImpl), failAfter(timeout), functionsInstance.cancelAllRequests ]); diff --git a/packages-exp/functions-exp/test/utils.ts b/packages-exp/functions-exp/test/utils.ts index ae168ea7496..659016c01df 100644 --- a/packages-exp/functions-exp/test/utils.ts +++ b/packages-exp/functions-exp/test/utils.ts @@ -21,6 +21,7 @@ import { FirebaseAuthInternalName } from '@firebase/auth-interop-types'; import { FirebaseMessagingName } from '@firebase/messaging-types'; import { FunctionsService } from '../src/service'; import { useFunctionsEmulator } from '../src/api'; +import nodeFetch from 'node-fetch'; export function makeFakeApp(options: FirebaseOptions = {}): FirebaseApp { options = { @@ -52,11 +53,14 @@ export function createTestService( new ComponentContainer('test') ) ): FunctionsService { + const fetchImpl: typeof fetch = + typeof window !== 'undefined' ? fetch.bind(window) : (nodeFetch as any); const functions = new FunctionsService( app, authProvider, messagingProvider, - region + region, + fetchImpl ); const useEmulator = !!process.env.FIREBASE_FUNCTIONS_EMULATOR_ORIGIN; if (useEmulator) { diff --git a/packages/functions/index.node.ts b/packages/functions/index.node.ts index e2f7ee80a7e..dab160f9bfe 100644 --- a/packages/functions/index.node.ts +++ b/packages/functions/index.node.ts @@ -17,9 +17,10 @@ import firebase from '@firebase/app'; import { _FirebaseNamespace } from '@firebase/app-types/private'; import { registerFunctions } from './src/config'; -import 'isomorphic-fetch'; +import nodeFetch from 'node-fetch'; import { name, version } from './package.json'; -registerFunctions(firebase as _FirebaseNamespace); +// eslint-disable-next-line @typescript-eslint/no-explicit-any +registerFunctions(firebase as _FirebaseNamespace, nodeFetch as any); firebase.registerVersion(name, version, 'node'); diff --git a/packages/functions/index.ts b/packages/functions/index.ts index 9100a2d1850..6a797b92029 100644 --- a/packages/functions/index.ts +++ b/packages/functions/index.ts @@ -21,7 +21,7 @@ import { registerFunctions } from './src/config'; import { name, version } from './package.json'; -registerFunctions(firebase as _FirebaseNamespace); +registerFunctions(firebase as _FirebaseNamespace, fetch); firebase.registerVersion(name, version); declare module '@firebase/app-types' { diff --git a/packages/functions/package.json b/packages/functions/package.json index b8fb6080cd1..f75e2f02ce6 100644 --- a/packages/functions/package.json +++ b/packages/functions/package.json @@ -7,7 +7,9 @@ "browser": "dist/index.cjs.js", "module": "dist/index.esm.js", "esm2017": "dist/index.esm2017.js", - "files": ["dist"], + "files": [ + "dist" + ], "scripts": { "lint": "eslint -c .eslintrc.js '**/*.ts' --ignore-path '../../.gitignore'", "lint:fix": "eslint --fix -c .eslintrc.js '**/*.ts' --ignore-path '../../.gitignore'", @@ -45,10 +47,10 @@ }, "typings": "dist/index.d.ts", "dependencies": { + "@firebase/component": "0.1.19", "@firebase/functions-types": "0.3.17", "@firebase/messaging-types": "0.5.0", - "@firebase/component": "0.1.19", - "isomorphic-fetch": "2.2.1", + "node-fetch": "2.6.1", "tslib": "^1.11.1" }, "nyc": { diff --git a/packages/functions/src/api/service.ts b/packages/functions/src/api/service.ts index 1aa65cb1b46..601f365344b 100644 --- a/packages/functions/src/api/service.ts +++ b/packages/functions/src/api/service.ts @@ -96,7 +96,8 @@ export class Service implements FirebaseFunctions, FirebaseService { private app_: FirebaseApp, authProvider: Provider, messagingProvider: Provider, - private region_: string = 'us-central1' + private region_: string = 'us-central1', + readonly fetchImpl: typeof fetch ) { this.contextProvider = new ContextProvider(authProvider, messagingProvider); // Cancels all ongoing requests when resolved. @@ -162,13 +163,13 @@ export class Service implements FirebaseFunctions, FirebaseService { private async postJSON( url: string, body: {}, - headers: Headers + headers: { [key: string]: string } ): Promise { - headers.append('Content-Type', 'application/json'); + headers['Content-Type'] = 'application/json'; let response: Response; try { - response = await fetch(url, { + response = await this.fetchImpl(url, { method: 'POST', body: JSON.stringify(body), headers @@ -212,13 +213,13 @@ export class Service implements FirebaseFunctions, FirebaseService { const body = { data }; // Add a header for the authToken. - const headers = new Headers(); + const headers: { [key: string]: string } = {}; const context = await this.contextProvider.getContext(); if (context.authToken) { - headers.append('Authorization', 'Bearer ' + context.authToken); + headers['Authorization'] = 'Bearer ' + context.authToken; } if (context.instanceIdToken) { - headers.append('Firebase-Instance-ID-Token', context.instanceIdToken); + headers['Firebase-Instance-ID-Token'] = context.instanceIdToken; } // Default timeout to 70s, but let the options override it. diff --git a/packages/functions/src/config.ts b/packages/functions/src/config.ts index 31d3925c410..c1cfdef7dac 100644 --- a/packages/functions/src/config.ts +++ b/packages/functions/src/config.ts @@ -28,21 +28,24 @@ import { _FirebaseNamespace } from '@firebase/app-types/private'; */ const FUNCTIONS_TYPE = 'functions'; -function factory(container: ComponentContainer, region?: string): Service { - // Dependencies - const app = container.getProvider('app').getImmediate(); - const authProvider = container.getProvider('auth-internal'); - const messagingProvider = container.getProvider('messaging'); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return new Service(app, authProvider, messagingProvider, region); -} - -export function registerFunctions(instance: _FirebaseNamespace): void { +export function registerFunctions( + instance: _FirebaseNamespace, + fetchImpl: typeof fetch +): void { const namespaceExports = { // no-inline Functions: Service }; + + function factory(container: ComponentContainer, region?: string): Service { + // Dependencies + const app = container.getProvider('app').getImmediate(); + const authProvider = container.getProvider('auth-internal'); + const messagingProvider = container.getProvider('messaging'); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return new Service(app, authProvider, messagingProvider, region, fetchImpl); + } instance.INTERNAL.registerComponent( new Component(FUNCTIONS_TYPE, factory, ComponentType.PUBLIC) .setServiceProps(namespaceExports) diff --git a/packages/functions/test/utils.ts b/packages/functions/test/utils.ts index 94e343e7137..56c6f52201f 100644 --- a/packages/functions/test/utils.ts +++ b/packages/functions/test/utils.ts @@ -20,6 +20,7 @@ import { Provider, ComponentContainer } from '@firebase/component'; import { FirebaseAuthInternalName } from '@firebase/auth-interop-types'; import { FirebaseMessagingName } from '@firebase/messaging-types'; import { Service } from '../src/api/service'; +import nodeFetch from 'node-fetch'; export function makeFakeApp(options: FirebaseOptions = {}): FirebaseApp { options = { @@ -52,7 +53,15 @@ export function createTestService( new ComponentContainer('test') ) ): Service { - const functions = new Service(app, authProvider, messagingProvider, region); + const fetchImpl: typeof fetch = + typeof window !== 'undefined' ? fetch.bind(window) : (nodeFetch as any); + const functions = new Service( + app, + authProvider, + messagingProvider, + region, + fetchImpl + ); const useEmulator = !!process.env.FIREBASE_FUNCTIONS_EMULATOR_ORIGIN; if (useEmulator) { functions.useFunctionsEmulator( diff --git a/yarn.lock b/yarn.lock index d1cb51a20a1..21e31f09d0f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8934,7 +8934,7 @@ is-stream-ended@^0.1.4: resolved "https://registry.npmjs.org/is-stream-ended/-/is-stream-ended-0.1.4.tgz#f50224e95e06bce0e356d440a4827cd35b267eda" integrity sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw== -is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: +is-stream@^1.0.0, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= @@ -9075,14 +9075,6 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= -isomorphic-fetch@2.2.1: - version "2.2.1" - resolved "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" - integrity sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk= - dependencies: - node-fetch "^1.0.1" - whatwg-fetch ">=0.10.0" - isstream@~0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -11100,14 +11092,6 @@ node-fetch@2.6.1, node-fetch@^2.2.0, node-fetch@^2.3.0, node-fetch@^2.5.0, node- resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== -node-fetch@^1.0.1: - version "1.7.3" - resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" - integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ== - dependencies: - encoding "^0.1.11" - is-stream "^1.0.1" - node-forge@^0.10.0: version "0.10.0" resolved "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" @@ -14361,7 +14345,6 @@ symbol-observable@^1.1.0: "sync-promise@git+https://github.com/brettz9/sync-promise.git#full-sync-missing-promise-features": version "1.0.1" - uid "25845a49a00aa2d2c985a5149b97c86a1fcdc75a" resolved "git+https://github.com/brettz9/sync-promise.git#25845a49a00aa2d2c985a5149b97c86a1fcdc75a" table@^5.2.3: @@ -15670,7 +15653,6 @@ websocket-extensions@>=0.1.1: "websql@git+https://github.com/brettz9/node-websql.git#configurable-secure2": version "1.0.0" - uid "5149bc0763376ca757fc32dc74345ada0467bfbb" resolved "git+https://github.com/brettz9/node-websql.git#5149bc0763376ca757fc32dc74345ada0467bfbb" dependencies: argsarray "^0.0.1" @@ -15684,11 +15666,6 @@ whatwg-fetch@2.0.4: resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" integrity sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng== -whatwg-fetch@>=0.10.0: - version "3.4.1" - resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.4.1.tgz#e5f871572d6879663fa5674c8f833f15a8425ab3" - integrity sha512-sofZVzE1wKwO+EYPbWfiwzaKovWiZXf4coEzjGP9b2GBVgQRLQUZ2QcuPpQExGDAW5GItpEm6Tl4OU5mywnAoQ== - whatwg-mimetype@^2.3.0: version "2.3.0" resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"