@@ -11,8 +11,10 @@ import {
1111 PluginContext ,
1212 RequestContext ,
1313 SetupResult ,
14+ UsageModel ,
1415 WranglerServiceConfig ,
1516 getRequestContext ,
17+ usageModelExternalSubrequestLimit ,
1618 viewToBuffer ,
1719} from "@miniflare/shared" ;
1820import dotenv from "dotenv" ;
@@ -28,6 +30,14 @@ export type _CoreMount = Mount<Request, Response>; // yuck :(
2830// some other custom way (e.g. Cloudflare Pages' `env.PAGES` asset handler)
2931export type FetcherFetch = ( request : Request ) => Awaitable < Response > ;
3032
33+ export interface FetcherFetchWithUsageModel {
34+ fetch : FetcherFetch ;
35+ // Usage model required as mount might have different usage model,
36+ // and therefore different subrequest limits.
37+ // We need to know these when creating the request context.
38+ usageModel ?: UsageModel ;
39+ }
40+
3141export type ServiceBindingsOptions = Record <
3242 string ,
3343 | string // Just service name, environment defaults to "production"
@@ -54,34 +64,48 @@ export interface BindingsOptions {
5464
5565export class Fetcher {
5666 readonly #service: string | FetcherFetch ;
57- readonly #getServiceFetch: ( name : string ) => Promise < FetcherFetch > ;
67+ readonly #getServiceFetch: (
68+ name : string
69+ ) => Promise < FetcherFetchWithUsageModel > ;
70+ readonly #defaultUsageModel?: UsageModel ;
5871
5972 constructor (
6073 service : string | FetcherFetch ,
61- getServiceFetch : ( name : string ) => Promise < FetcherFetch >
74+ getServiceFetch : ( name : string ) => Promise < FetcherFetchWithUsageModel > ,
75+ defaultUsageModel ?: UsageModel
6276 ) {
6377 this . #service = service ;
6478 this . #getServiceFetch = getServiceFetch ;
79+ this . #defaultUsageModel = defaultUsageModel ;
6580 }
6681
6782 async fetch ( input : RequestInfo , init ?: RequestInit ) : Promise < Response > {
83+ // Always create new Request instance, so clean object passed to services
84+ const req = new Request ( input , init ) ;
85+
86+ // If we're using a custom fetch handler, call that or wait for the service
87+ // fetch handler to be available
88+ let fetch : FetcherFetch ;
89+ let usageModel = this . #defaultUsageModel;
90+ if ( typeof this . #service === "function" ) {
91+ fetch = this . #service;
92+ } else {
93+ const serviceFetch = await this . #getServiceFetch( this . #service) ;
94+ fetch = serviceFetch . fetch ;
95+ usageModel = serviceFetch . usageModel ;
96+ }
97+
6898 // Check we're not too deep, should throw in the caller and NOT return a
6999 // 500 Internal Server Error Response from this function
70100 const parentCtx = getRequestContext ( ) ;
71101 const requestDepth = parentCtx ?. requestDepth ?? 1 ;
72102 const pipelineDepth = ( parentCtx ?. pipelineDepth ?? 0 ) + 1 ;
73103 // NOTE: `new RequestContext` throws if too deep
74- const ctx = new RequestContext ( { requestDepth, pipelineDepth } ) ;
75-
76- // Always create new Request instance, so clean object passed to services
77- const req = new Request ( input , init ) ;
78-
79- // If we're using a custom fetch handler, call that or wait for the service
80- // fetch handler to be available
81- const fetch =
82- typeof this . #service === "function"
83- ? this . #service
84- : await this . #getServiceFetch( this . #service) ;
104+ const ctx = new RequestContext ( {
105+ requestDepth,
106+ pipelineDepth,
107+ externalSubrequestLimit : usageModelExternalSubrequestLimit ( usageModel ) ,
108+ } ) ;
85109
86110 // Cloudflare Workers currently don't propagate errors thrown by the service
87111 // when handling the request. Instead a 500 Internal Server Error Response
@@ -256,7 +280,9 @@ export class BindingsPlugin
256280 }
257281 }
258282
259- #getServiceFetch = async ( service : string ) : Promise < FetcherFetch > => {
283+ #getServiceFetch = async (
284+ service : string
285+ ) : Promise < FetcherFetchWithUsageModel > => {
260286 // Wait for mounts
261287 assert (
262288 this . #contextPromise,
@@ -266,9 +292,9 @@ export class BindingsPlugin
266292
267293 // Should've thrown error earlier in reload if service not found and
268294 // dispatchFetch should always be set, it's optional to make testing easier.
269- const fetch = this . #mounts?. get ( service ) ?. dispatchFetch ;
270- assert ( fetch ) ;
271- return fetch ;
295+ const mount = this . #mounts?. get ( service ) ;
296+ assert ( mount ?. dispatchFetch ) ;
297+ return { fetch : mount . dispatchFetch , usageModel : mount . usageModel } ;
272298 } ;
273299
274300 async setup ( ) : Promise < SetupResult > {
@@ -336,7 +362,11 @@ export class BindingsPlugin
336362
337363 // 6) Load service bindings
338364 for ( const { name, service } of this . #processedServiceBindings) {
339- bindings [ name ] = new Fetcher ( service , this . #getServiceFetch) ;
365+ bindings [ name ] = new Fetcher (
366+ service ,
367+ this . #getServiceFetch,
368+ this . ctx . usageModel
369+ ) ;
340370 }
341371
342372 // 7) Copy user's arbitrary bindings
0 commit comments