Skip to content

Commit 4c37d41

Browse files
committed
refactor: remove kResolvedServiceDesignator, post-process external bindings instead
Instead of mutating workerOpts with symbol-tagged designators before plugin processing (requiring every consumer to special-case them), we now let the plugin system process all bindings normally, then rewrite external service bindings and tails to point at the dev registry proxy afterward. This follows the same post-processing pattern already used for assets at line 1673. Removes kResolvedServiceDesignator, ResolvedServiceDesignator, the zod validator, and checks in getCustomServiceDesignator, maybeGetCustomServiceService, and normaliseServiceDesignator.
1 parent 028b2c7 commit 4c37d41

File tree

4 files changed

+69
-87
lines changed

4 files changed

+69
-87
lines changed

packages/miniflare/src/index.ts

Lines changed: 68 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -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
*/
459466
function 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 &&

packages/miniflare/src/plugins/core/index.ts

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@ import { PROXY_SECRET } from "./proxy";
7575
import {
7676
CustomFetchServiceSchema,
7777
kCurrentWorker,
78-
kResolvedServiceDesignator,
7978
ServiceDesignatorSchema,
8079
} from "./services";
8180
import type { WorkerRegistry } from "../../shared/dev-registry";
@@ -368,22 +367,6 @@ function getCustomServiceDesignator(
368367
service: z.infer<typeof ServiceDesignatorSchema>,
369368
hasAssetsAndIsVitest: boolean = false
370369
): ServiceDesignator {
371-
// Pre-resolved designator: used by the dev registry proxy to point bindings
372-
// directly at internal core services without getUserServiceName() wrapping
373-
if (
374-
typeof service === "object" &&
375-
kResolvedServiceDesignator in service &&
376-
service[kResolvedServiceDesignator] === true
377-
) {
378-
return {
379-
name: service.name,
380-
entrypoint: service.entrypoint,
381-
props: service.props
382-
? { json: JSON.stringify(service.props) }
383-
: undefined,
384-
};
385-
}
386-
387370
let serviceName: string;
388371
let entrypoint: string | undefined;
389372
let props: { json: string } | undefined;
@@ -432,14 +415,6 @@ function maybeGetCustomServiceService(
432415
name: string,
433416
service: z.infer<typeof ServiceDesignatorSchema>
434417
): Service | undefined {
435-
// Pre-resolved designator: the target service is defined elsewhere
436-
if (
437-
typeof service === "object" &&
438-
kResolvedServiceDesignator in service &&
439-
service[kResolvedServiceDesignator] === true
440-
) {
441-
return undefined;
442-
}
443418
if (typeof service === "function") {
444419
// Custom `fetch` function
445420
return {

packages/miniflare/src/plugins/core/services.ts

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,6 @@ import type * as http from "node:http";
2222
// `miniflare` are loaded (e.g. when configuring Vitest and when running pool)
2323
export const kCurrentWorker = Symbol.for("miniflare.kCurrentWorker");
2424

25-
// Marker for a pre-resolved ServiceDesignator that should be used as-is,
26-
// without passing through getUserServiceName(). Used by the dev registry
27-
// proxy to point bindings directly at internal core services.
28-
export const kResolvedServiceDesignator = Symbol.for(
29-
"miniflare.kResolvedServiceDesignator"
30-
);
31-
32-
export interface ResolvedServiceDesignator {
33-
[kResolvedServiceDesignator]: true;
34-
/** The already-resolved service name (not wrapped in getUserServiceName) */
35-
name: string;
36-
entrypoint?: string;
37-
props?: Record<string, unknown>;
38-
}
39-
4025
export const HttpOptionsHeaderSchema = z.object({
4126
name: z.string(), // name should be required
4227
value: z.ostring(), // If omitted, the header will be removed
@@ -115,13 +100,6 @@ export const CustomFetchServiceSchema = z.custom<
115100
export const ServiceDesignatorSchema = z.union([
116101
z.string(),
117102
z.literal(kCurrentWorker),
118-
z.custom<ResolvedServiceDesignator>(
119-
(v) =>
120-
typeof v === "object" &&
121-
v !== null &&
122-
kResolvedServiceDesignator in v &&
123-
v[kResolvedServiceDesignator] === true
124-
),
125103
z.object({
126104
name: z.union([z.string(), z.literal(kCurrentWorker)]),
127105
entrypoint: z.ostring(),

packages/miniflare/src/shared/external-service.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import {
55
kCurrentWorker,
66
ServiceDesignatorSchema,
77
} from "../plugins/core";
8-
import { kResolvedServiceDesignator } from "../plugins/core/services";
98
import { RemoteProxyConnectionString } from "../plugins/shared";
109
import { WORKER_BINDING_DEV_REGISTRY_DISK } from "../plugins/shared/constants";
1110
import { kVoid, Service, Worker_DurableObjectNamespace } from "../runtime";
@@ -23,11 +22,7 @@ export function normaliseServiceDesignator(
2322

2423
if (typeof service === "string") {
2524
serviceName = service;
26-
} else if (
27-
typeof service === "object" &&
28-
"name" in service &&
29-
!(kResolvedServiceDesignator in service)
30-
) {
25+
} else if (typeof service === "object" && "name" in service) {
3126
serviceName = service.name !== kCurrentWorker ? service.name : undefined;
3227
entrypoint = service.entrypoint;
3328
remoteProxyConnectionString = service.remoteProxyConnectionString;

0 commit comments

Comments
 (0)