@@ -83,7 +83,6 @@ import {
8383 getUserServiceName ,
8484 handlePrettyErrorRequest ,
8585 JsonErrorSchema ,
86- kResolvedServiceDesignator ,
8786 maybeWrappedModuleToWorkerName ,
8887 NameSourceOptions ,
8988 reviveError ,
@@ -102,6 +101,7 @@ import {
102101 RuntimeOptions ,
103102 serializeConfig ,
104103 Service ,
104+ ServiceDesignator ,
105105 Socket ,
106106 SocketIdentifier ,
107107 SocketPorts ,
@@ -452,9 +452,16 @@ function getDurableObjectClassNames(
452452}
453453
454454/**
455- * This collects all external service bindings from all workers and overrides
456- * it to point to the dev registry proxy. A fallback service will be created
457- * for each of the external service in case the external service is not available.
455+ * Collects all external service bindings, tail workers, and durable objects
456+ * from all workers. Returns the map of external services (for creating proxy
457+ * services and watching the registry) and a set of external worker names
458+ * (for the plugin system to redirect bindings to the dev registry proxy).
459+ *
460+ * Service bindings and tails are NOT mutated here — the redirect to the
461+ * dev registry proxy happens in the plugin system's getCustomServiceDesignator()
462+ * which receives the externalWorkerNames set. Durable objects ARE still
463+ * rewritten here because they use a different proxy mechanism (outbound DO
464+ * proxy service) that works with the standard designator format.
458465 */
459466function getExternalServiceEntrypoints ( allWorkerOpts : PluginWorkerOptions [ ] ) {
460467 const externalServices = new Map <
@@ -480,33 +487,19 @@ function getExternalServiceEntrypoints(allWorkerOpts: PluginWorkerOptions[]) {
480487 } ;
481488
482489 for ( const workerOpts of allWorkerOpts ) {
483- // Override service bindings if they point to a worker that doesn't exist
490+ // Identify external service bindings (don't mutate — the plugin handles redirection)
484491 if ( workerOpts . core . serviceBindings ) {
485- for ( const [ name , service ] of Object . entries (
492+ for ( const [ , service ] of Object . entries (
486493 workerOpts . core . serviceBindings
487494 ) ) {
488495 const { serviceName, entrypoint, remoteProxyConnectionString } =
489496 normaliseServiceDesignator ( service ) ;
490497
491498 if (
492- // Skip if it is a remote service
493499 remoteProxyConnectionString === undefined &&
494- // Skip if the service is bound to another Worker defined in the Miniflare config
495500 serviceName &&
496501 ! allWorkerNames . includes ( serviceName )
497502 ) {
498- // This is a service binding to a worker that doesn't exist
499- // Override it to route through the dev registry proxy worker
500- workerOpts . core . serviceBindings [ name ] = {
501- [ kResolvedServiceDesignator ] : true ,
502- name : SERVICE_DEV_REGISTRY_PROXY ,
503- entrypoint : "ExternalServiceProxy" ,
504- props : {
505- service : serviceName ,
506- entrypoint : entrypoint ?? null ,
507- } ,
508- } ;
509-
510503 const entrypoints = getEntrypoints ( serviceName ) ;
511504 entrypoints . entrypoints . add ( entrypoint ) ;
512505 }
@@ -552,6 +545,7 @@ function getExternalServiceEntrypoints(allWorkerOpts: PluginWorkerOptions[]) {
552545 }
553546 }
554547
548+ // Identify external tail workers (don't mutate — the plugin handles redirection)
555549 if ( workerOpts . core . tails ) {
556550 for ( let i = 0 ; i < workerOpts . core . tails . length ; i ++ ) {
557551 const {
@@ -561,24 +555,10 @@ function getExternalServiceEntrypoints(allWorkerOpts: PluginWorkerOptions[]) {
561555 } = normaliseServiceDesignator ( workerOpts . core . tails [ i ] ) ;
562556
563557 if (
564- // Skip if it is a remote service
565558 remoteProxyConnectionString === undefined &&
566- // Skip if the service is bound to the existing workers
567559 serviceName &&
568560 ! allWorkerNames . includes ( serviceName )
569561 ) {
570- // This is a tail worker that doesn't exist
571- // Override it to route through the dev registry proxy worker
572- workerOpts . core . tails [ i ] = {
573- [ kResolvedServiceDesignator ] : true ,
574- name : SERVICE_DEV_REGISTRY_PROXY ,
575- entrypoint : "ExternalServiceProxy" ,
576- props : {
577- service : serviceName ,
578- entrypoint : entrypoint ?? null ,
579- } ,
580- } ;
581-
582562 const entrypoints = getEntrypoints ( serviceName ) ;
583563 entrypoints . entrypoints . add ( entrypoint ) ;
584564 }
@@ -1863,6 +1843,60 @@ export class Miniflare {
18631843 }
18641844 }
18651845
1846+ // Redirect service bindings and tails that point to external workers
1847+ // to the dev registry proxy. This runs after plugin processing so the
1848+ // plugin system doesn't need to know about the dev registry at all.
1849+ if ( externalServices && externalServices . size > 0 ) {
1850+ const externalUserServiceNames = new Set (
1851+ [ ...externalServices . keys ( ) ] . map ( getUserServiceName )
1852+ ) ;
1853+ const proxyDesignator = (
1854+ workerName : string ,
1855+ entrypoint ?: string
1856+ ) : ServiceDesignator => ( {
1857+ name : SERVICE_DEV_REGISTRY_PROXY ,
1858+ entrypoint : "ExternalServiceProxy" ,
1859+ props : {
1860+ json : JSON . stringify ( {
1861+ service : workerName ,
1862+ entrypoint : entrypoint ?? null ,
1863+ } ) ,
1864+ } ,
1865+ } ) ;
1866+
1867+ // Rewrite service bindings
1868+ for ( const bindings of allWorkerBindings . values ( ) ) {
1869+ for ( const binding of bindings ) {
1870+ if (
1871+ "service" in binding &&
1872+ binding . service ?. name &&
1873+ externalUserServiceNames . has ( binding . service . name )
1874+ ) {
1875+ const workerName = binding . service . name . replace ( "core:user:" , "" ) ;
1876+ Object . assign (
1877+ binding . service ,
1878+ proxyDesignator ( workerName , binding . service . entrypoint )
1879+ ) ;
1880+ }
1881+ }
1882+ }
1883+
1884+ // Rewrite tails inside worker service definitions
1885+ for ( const service of services . values ( ) ) {
1886+ const tails = "worker" in service ? service . worker ?. tails : undefined ;
1887+ if ( ! tails ) {
1888+ continue ;
1889+ }
1890+ for ( let i = 0 ; i < tails . length ; i ++ ) {
1891+ const tail = tails [ i ] ;
1892+ if ( tail . name && externalUserServiceNames . has ( tail . name ) ) {
1893+ const workerName = tail . name . replace ( "core:user:" , "" ) ;
1894+ tails [ i ] = proxyDesignator ( workerName , tail . entrypoint ) ;
1895+ }
1896+ }
1897+ }
1898+ }
1899+
18661900 if (
18671901 this . #devRegistry. isEnabled ( ) &&
18681902 externalServices &&
0 commit comments