44 type ResolvedFunctionConfig as ManifestFunctionConfig ,
55} from "@supabase/config/effect" ;
66import { edgeRuntimeNofileUlimit } from "../stack-constants.ts" ;
7- import { mkdir , readFile , rm , writeFile } from "node:fs/promises" ;
8- import { isAbsolute , join , resolve } from "node:path" ;
7+ import { mkdir , readFile , realpath , rm , writeFile } from "node:fs/promises" ;
8+ import { isAbsolute , join , relative , resolve , sep } from "node:path" ;
99import { Effect , Option , Redacted , Stream } from "effect" ;
1010import { parseDotEnv } from "../../legacy/shared/legacy-dotenv.ts" ;
1111import { Output } from "../output/output.service.ts" ;
@@ -318,8 +318,21 @@ const parseFunctionEnvFile = Effect.fnUntraced(function* (pathname: string) {
318318function toFunctionContainerConfig (
319319 config : ResolvedDeployFunctionConfig ,
320320 envFile : Readonly < Record < string , string > > ,
321+ pathMappings : ReadonlyArray < { readonly hostRoot : string ; readonly containerRoot : string } > ,
321322) : ServeFunctionContainerConfig {
322- const toContainerPath = ( pathname : string ) => toDockerPath ( resolve ( pathname ) ) ;
323+ const toContainerPath = ( pathname : string ) => {
324+ const resolvedPath = resolve ( pathname ) ;
325+ for ( const mapping of pathMappings ) {
326+ const relativePath = relative ( mapping . hostRoot , resolvedPath ) ;
327+ if (
328+ relativePath === "" ||
329+ ( ! isAbsolute ( relativePath ) && relativePath !== ".." && ! relativePath . startsWith ( `..${ sep } ` ) )
330+ ) {
331+ return join ( mapping . containerRoot , relativePath ) . replaceAll ( "\\" , "/" ) ;
332+ }
333+ }
334+ return toDockerPath ( resolvedPath ) ;
335+ } ;
323336
324337 return {
325338 // The Go serve path defaults verifyJWT to true when verify_jwt is not set in
@@ -649,6 +662,17 @@ export const startEdgeRuntimeContainer = Effect.fn("functions.startEdgeRuntimeCo
649662 ) ;
650663
651664 const functionsDir = join ( input . projectRoot , functionsDirName ) ;
665+ const functionsRoot = resolve ( functionsDir ) ;
666+ const containerFunctionsRoot = toDockerPath ( functionsRoot ) ;
667+ const canonicalFunctionsRoot = yield * Effect . promise ( ( ) =>
668+ realpath ( functionsRoot ) . catch ( ( ) => functionsRoot ) ,
669+ ) ;
670+ const pathMappings = [
671+ { hostRoot : functionsRoot , containerRoot : containerFunctionsRoot } ,
672+ ...( canonicalFunctionsRoot === functionsRoot
673+ ? [ ]
674+ : [ { hostRoot : canonicalFunctionsRoot , containerRoot : containerFunctionsRoot } ] ) ,
675+ ] ;
652676 const functionBinds = new Map < string , DockerBind > ( ) ;
653677 const emittedScopeWarnings = new Set < string > ( ) ;
654678 const functionsConfig : Record < string , ServeFunctionContainerConfig > = { } ;
@@ -692,7 +716,7 @@ export const startEdgeRuntimeContainer = Effect.fn("functions.startEdgeRuntimeCo
692716 input . discoverFunctionEnvFiles && Option . isNone ( input . envFile )
693717 ? yield * parseFunctionEnvFile ( join ( functionsDir , config . slug , ".env" ) )
694718 : { } ;
695- functionsConfig [ config . slug ] = toFunctionContainerConfig ( config , functionEnv ) ;
719+ functionsConfig [ config . slug ] = toFunctionContainerConfig ( config , functionEnv , pathMappings ) ;
696720 }
697721
698722 const binds = [ ...functionBinds . values ( ) ] ;
@@ -716,7 +740,7 @@ export const startEdgeRuntimeContainer = Effect.fn("functions.startEdgeRuntimeCo
716740 `SUPABASE_INTERNAL_JWT_SECRET=${ input . authArtifacts . jwtSecret } ` ,
717741 `SUPABASE_JWKS=${ input . authArtifacts . jwks } ` ,
718742 `SUPABASE_INTERNAL_HOST_PORT=${ input . config . apiPort } ` ,
719- `SUPABASE_INTERNAL_FUNCTIONS_ROOT=${ toDockerPath ( functionsDir ) } ` ,
743+ `SUPABASE_INTERNAL_FUNCTIONS_ROOT=${ containerFunctionsRoot } ` ,
720744 `SUPABASE_INTERNAL_FUNCTIONS_CONFIG=${ JSON . stringify ( functionsConfig ) } ` ,
721745 ...( input . debug ? [ "SUPABASE_INTERNAL_DEBUG=true" ] : [ ] ) ,
722746 ] ;
0 commit comments