1- import * as fs from "fs" ;
2-
3- import { CloudFunction , DeploymentOptions , https } from "firebase-functions" ;
1+ import { CloudFunction , DeploymentOptions } from "firebase-functions" ;
42import * as express from "express" ;
53import * as path from "path" ;
64import * as admin from "firebase-admin" ;
@@ -10,12 +8,7 @@ import * as _ from "lodash";
108
119import { EmulatorLog } from "./types" ;
1210import { Constants } from "./constants" ;
13- import {
14- findModuleRoot ,
15- FunctionsRuntimeBundle ,
16- HttpConstants ,
17- SignatureType ,
18- } from "./functionsEmulatorShared" ;
11+ import { findModuleRoot , FunctionsRuntimeBundle , SignatureType } from "./functionsEmulatorShared" ;
1912import { compareVersionStrings , isLocalHost } from "./functionsEmulatorUtils" ;
2013import { EventUtils } from "./events/types" ;
2114
@@ -252,7 +245,7 @@ async function assertResolveDeveloperNodeModule(name: string): Promise<Successfu
252245async function verifyDeveloperNodeModules ( ) : Promise < boolean > {
253246 const modBundles = [
254247 { name : "firebase-admin" , isDev : false , minVersion : "8.9.0" } ,
255- { name : "firebase-functions" , isDev : false , minVersion : "3.13.1 " } ,
248+ { name : "firebase-functions" , isDev : false , minVersion : "3.16.0 " } ,
256249 ] ;
257250
258251 for ( const modBundle of modBundles ) {
@@ -383,7 +376,6 @@ function initializeNetworkFiltering(): void {
383376 logDebug ( "Outgoing network have been stubbed." , results ) ;
384377}
385378
386- type CallableHandler = ( data : any , context : https . CallableContext ) => any | Promise < any > ;
387379type HttpsHandler = ( req : Request , resp : Response ) => void ;
388380
389381/*
@@ -431,116 +423,12 @@ async function initializeFirebaseFunctionsStubs(): Promise<void> {
431423 httpsProvider . onRequest = ( handler : HttpsHandler ) => {
432424 return httpsProvider [ onRequestInnerMethodName ] ( handler , { } ) ;
433425 } ;
434-
435- // Mocking https.onCall is very similar to onRequest
436- const onCallInnerMethodName = "_onCallWithOptions" ;
437- const onCallMethodOriginal = httpsProvider [ onCallInnerMethodName ] ;
438-
439- // Newer versions of the firebase-functions package's _onCallWithOptions method expects 3 arguments.
440- if ( onCallMethodOriginal . length === 3 ) {
441- httpsProvider [ onCallInnerMethodName ] = (
442- opts : any ,
443- handler : any ,
444- deployOpts : DeploymentOptions ,
445- ) => {
446- const wrapped = wrapCallableHandler ( handler ) ;
447- const cf = onCallMethodOriginal ( opts , wrapped , deployOpts ) ;
448- return cf ;
449- } ;
450- } else {
451- httpsProvider [ onCallInnerMethodName ] = ( handler : any , opts : DeploymentOptions ) => {
452- const wrapped = wrapCallableHandler ( handler ) ;
453- const cf = onCallMethodOriginal ( wrapped , opts ) ;
454- return cf ;
455- } ;
456- }
457-
458- // Newer versions of the firebase-functions package's onCall method can accept upto 2 arguments.
459- httpsProvider . onCall = function ( optsOrHandler : any , handler : CallableHandler ) {
460- if ( onCallMethodOriginal . length === 3 ) {
461- let opts ;
462- if ( arguments . length === 1 ) {
463- opts = { } ;
464- handler = optsOrHandler as CallableHandler ;
465- } else {
466- opts = optsOrHandler ;
467- }
468- return httpsProvider [ onCallInnerMethodName ] ( opts , handler , { } ) ;
469- } else {
470- return httpsProvider [ onCallInnerMethodName ] ( optsOrHandler , { } ) ;
471- }
472- } ;
473- }
474-
475- /**
476- * Wrap a callable functions handler with an outer method that extracts a special authorization
477- * header used to mock auth in the emulator.
478- */
479- function wrapCallableHandler ( handler : CallableHandler ) : CallableHandler {
480- const newHandler = ( data : any , context : https . CallableContext ) => {
481- if ( context . rawRequest ) {
482- const authContext = context . rawRequest . header ( HttpConstants . CALLABLE_AUTH_HEADER ) ;
483- if ( authContext ) {
484- logDebug ( "Callable functions auth override" , {
485- key : HttpConstants . CALLABLE_AUTH_HEADER ,
486- value : authContext ,
487- } ) ;
488- context . auth = JSON . parse ( decodeURIComponent ( authContext ) ) ;
489- delete context . rawRequest . headers [ HttpConstants . CALLABLE_AUTH_HEADER ] ;
490- } else {
491- logDebug ( "No callable functions auth found" ) ;
492- }
493-
494- // Restore the original auth header in case the code relies on parsing it (for
495- // example, the code could forward it to another function or server).
496- const originalAuth = context . rawRequest . header ( HttpConstants . ORIGINAL_AUTH_HEADER ) ;
497- if ( originalAuth ) {
498- context . rawRequest . headers [ "authorization" ] = originalAuth ;
499- delete context . rawRequest . headers [ HttpConstants . ORIGINAL_AUTH_HEADER ] ;
500- }
501- }
502- return handler ( data , context ) ;
503- } ;
504-
505- return newHandler ;
506426}
507427
508428function getDefaultConfig ( ) : any {
509429 return JSON . parse ( process . env . FIREBASE_CONFIG || "{}" ) ;
510430}
511431
512- function initializeRuntimeConfig ( ) {
513- // Most recent version of Firebase Functions SDK automatically picks up locally
514- // stored .runtimeconfig.json to populate the config entries.
515- // However, due to a bug in some older version of the Function SDK, this process may fail.
516- //
517- // See the following issues for more detail:
518- // https://github.com/firebase/firebase-tools/issues/3793
519- // https://github.com/firebase/firebase-functions/issues/877
520- //
521- // As a workaround, the emulator runtime will load the contents of the .runtimeconfig.json
522- // to the CLOUD_RUNTIME_CONFIG environment variable IF the env var is unused.
523- // In the future, we will bump up the minimum version of the Firebase Functions SDK
524- // required to run the functions emulator to v3.15.1 and get rid of this workaround.
525- if ( ! process . env . CLOUD_RUNTIME_CONFIG ) {
526- const configPath = `${ process . cwd ( ) } /.runtimeconfig.json` ;
527- try {
528- const configContent = fs . readFileSync ( configPath , "utf8" ) ;
529- if ( configContent ) {
530- try {
531- JSON . parse ( configContent . toString ( ) ) ;
532- logDebug ( `Found local functions config: ${ configPath } ` ) ;
533- process . env . CLOUD_RUNTIME_CONFIG = configContent . toString ( ) ;
534- } catch ( e ) {
535- new EmulatorLog ( "SYSTEM" , "function-runtimeconfig-json-invalid" , "" ) . log ( ) ;
536- }
537- }
538- } catch ( e ) {
539- // Ignore, config is optional
540- }
541- }
542- }
543-
544432/**
545433 * This stub is the most important and one of the only non-optional stubs.This feature redirects
546434 * writes from the admin SDK back into emulated resources.
@@ -709,48 +597,6 @@ function warnAboutStorageProd(): void {
709597 ) . log ( ) ;
710598}
711599
712- async function initializeFunctionsConfigHelper ( ) : Promise < void > {
713- const functionsResolution = await assertResolveDeveloperNodeModule ( "firebase-functions" ) ;
714- const localFunctionsModule = require ( functionsResolution . resolution ) ;
715-
716- logDebug ( "Checked functions.config()" , {
717- config : localFunctionsModule . config ( ) ,
718- } ) ;
719-
720- const originalConfig = localFunctionsModule . config ( ) ;
721- const proxiedConfig = new Proxied ( originalConfig )
722- . any ( ( parentConfig , parentKey ) => {
723- const isInternal = parentKey . startsWith ( "Symbol(" ) || parentKey . startsWith ( "inspect" ) ;
724- if ( ! parentConfig [ parentKey ] && ! isInternal ) {
725- new EmulatorLog ( "SYSTEM" , "functions-config-missing-value" , "" , {
726- key : parentKey ,
727- } ) . log ( ) ;
728- }
729-
730- return parentConfig [ parentKey ] ;
731- } )
732- . finalize ( ) ;
733-
734- const functionsModuleProxy = new Proxied < typeof localFunctionsModule > ( localFunctionsModule ) ;
735- const proxiedFunctionsModule = functionsModuleProxy
736- . when ( "config" , ( ) => ( ) => {
737- return proxiedConfig ;
738- } )
739- . finalize ( ) ;
740-
741- // Stub the functions module in the require cache
742- const v = require . cache [ functionsResolution . resolution ] ;
743- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- this is not precedent.
744- require . cache [ functionsResolution . resolution ] = Object . assign ( v ! , {
745- exports : proxiedFunctionsModule ,
746- path : path . dirname ( functionsResolution . resolution ) ,
747- } ) ;
748-
749- logDebug ( "firebase-functions has been stubbed." , {
750- functionsResolution,
751- } ) ;
752- }
753-
754600/*
755601 Retains a reference to the raw body buffer to allow access to the raw body for things like request
756602 signature validation. This is used as the "verify" function in body-parser options.
@@ -897,9 +743,7 @@ async function initializeRuntime(): Promise<void> {
897743 return ;
898744 }
899745
900- initializeRuntimeConfig ( ) ;
901746 initializeNetworkFiltering ( ) ;
902- await initializeFunctionsConfigHelper ( ) ;
903747 await initializeFirebaseFunctionsStubs ( ) ;
904748 await initializeFirebaseAdminStubs ( ) ;
905749}
0 commit comments