Skip to content

Commit f3ac6bd

Browse files
mack-erelclaude
andcommitted
refactor(hyperdrive): prepare edge credentials in the remote proxy session
Per review: `maybeStartOrUpdateRemoteProxySession()` is already awaited by every consumer of a remote session, so it is a better home for the seeding step than `buildMiniflareOptions`. It now fetches each remote Hyperdrive binding's edge connection string once per session and returns them on `RemoteProxySessionData`. `wrangler dev` (single- and multi-worker) and `getPlatformProxy()` pass them straight through to the binding builder, and the Vite plugin and vitest-pool-workers — which already await the same call — can pick them up the same way. This also takes the work off the config-reload path: seeding used to run inside `buildMiniflareOptions`, which every reload goes through. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015GDyTfEa62t3bSe5wWn8Mt
1 parent 680b589 commit f3ac6bd

5 files changed

Lines changed: 44 additions & 46 deletions

File tree

packages/remote-bindings/src/maybe-start-or-update-session.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import assert from "node:assert";
22
import { getBindingLocalSupport } from "@cloudflare/workers-utils";
33
import { getRemoteBindingsAuthHook } from "./auth";
4+
import { seedRemoteHyperdriveBindings } from "./seed-hyperdrive-bindings";
45
import { startRemoteProxySession } from "./start-remote-proxy-session";
56
import type { RemoteBindingsLogger } from "./logger";
67
import type { RemoteProxySession } from "./start-remote-proxy-session";
@@ -45,6 +46,15 @@ export type RemoteProxySessionData = {
4546
session: RemoteProxySession;
4647
remoteBindings: Record<string, Binding>;
4748
auth?: AsyncHook<CfAccount>;
49+
/**
50+
* Edge connection strings for remote Hyperdrive bindings, keyed by binding
51+
* name. The edge mints per-session credentials, so a database client has to
52+
* present *these* to authenticate through the proxy. Fetched here, once per
53+
* session, so that every consumer of a session — `wrangler dev`,
54+
* `getPlatformProxy()`, the Vite plugin, `vitest-pool-workers` — receives
55+
* usable credentials without repeating the setup.
56+
*/
57+
hyperdriveConnectionStrings: Map<string, string>;
4858
};
4959

5060
export type RemoteBindingsContext = {
@@ -123,6 +133,10 @@ export async function maybeStartOrUpdateRemoteProxySession(
123133
session: remoteProxySession,
124134
remoteBindings,
125135
auth,
136+
hyperdriveConnectionStrings: await seedRemoteHyperdriveBindings(
137+
workerConfigObject.bindings,
138+
remoteProxySession.remoteProxyConnectionString
139+
),
126140
};
127141
}
128142

packages/wrangler/src/api/integrations/platform/index.ts

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,7 @@ import { logger } from "../../../logger";
2222
import { getSiteAssetPaths } from "../../../sites";
2323
import { dedent } from "../../../utils/dedent";
2424
import { getZoneFromRoute } from "../../../zones";
25-
import {
26-
maybeStartOrUpdateRemoteProxySession,
27-
seedRemoteHyperdriveBindings,
28-
} from "../../remoteBindings";
25+
import { maybeStartOrUpdateRemoteProxySession } from "../../remoteBindings";
2926
import { CacheStorage } from "./caches";
3027
import { ExecutionContext } from "./executionContext";
3128
// TODO: import from `@cloudflare/workers-utils` after migrating to `tsdown`
@@ -36,7 +33,10 @@ import type {
3633
RawConfig,
3734
RawEnvironment,
3835
} from "../../../../../workers-utils/src";
39-
import type { RemoteProxySession } from "../../remoteBindings";
36+
import type {
37+
RemoteProxySession,
38+
RemoteProxySessionData,
39+
} from "../../remoteBindings";
4040
import type { IncomingRequestCfProperties } from "@cloudflare/workers-types/experimental";
4141
import type {
4242
RemoteProxyConnectionString,
@@ -183,21 +183,24 @@ export async function getPlatformProxy<
183183
env,
184184
});
185185

186-
let remoteProxySession: RemoteProxySession | undefined = undefined;
186+
let remoteProxySessionData: RemoteProxySessionData | null = null;
187187
if (config.configPath && options.remoteBindings !== false) {
188-
remoteProxySession = (
188+
remoteProxySessionData =
189189
(await maybeStartOrUpdateRemoteProxySession({
190190
path: config.configPath,
191191
environment: env,
192-
})) ?? {}
193-
).session;
192+
})) ?? null;
194193
}
194+
const remoteProxySession: RemoteProxySession | undefined =
195+
remoteProxySessionData?.session;
195196

196197
const miniflareOptions = await getMiniflareOptionsFromConfig({
197198
config,
198199
options,
199200
remoteProxyConnectionString:
200201
remoteProxySession?.remoteProxyConnectionString,
202+
hyperdriveConnectionStrings:
203+
remoteProxySessionData?.hyperdriveConnectionStrings,
201204
});
202205

203206
const mf = new Miniflare(convertV4MiniflareOptions(miniflareOptions));
@@ -233,6 +236,11 @@ async function getMiniflareOptionsFromConfig(args: {
233236
config: Config;
234237
options: GetPlatformProxyOptions;
235238
remoteProxyConnectionString?: RemoteProxyConnectionString;
239+
/**
240+
* Edge credentials for remote Hyperdrive bindings, prepared once when the
241+
* remote proxy session started.
242+
*/
243+
hyperdriveConnectionStrings?: ReadonlyMap<string, string>;
236244
}): Promise<V4MiniflareOptions> {
237245
const { config, options, remoteProxyConnectionString } = args;
238246

@@ -303,9 +311,9 @@ async function getMiniflareOptionsFromConfig(args: {
303311
enableContainers: config.dev.enable_containers,
304312
},
305313
remoteProxyConnectionString,
306-
// Remote Hyperdrive bindings authenticate with the edge session's
307-
// credentials, which have to be fetched before this synchronous builder.
308-
await seedRemoteHyperdriveBindings(bindings, remoteProxyConnectionString)
314+
// Edge credentials for remote Hyperdrive bindings, prepared once when the
315+
// remote proxy session started.
316+
args.hyperdriveConnectionStrings
309317
);
310318

311319
let processedAssetOptions: AssetsOptions | undefined;

packages/wrangler/src/api/startDevWorker/LocalRuntimeController.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,8 @@ export class LocalRuntimeController extends RuntimeController {
417417
type: "devRegistryUpdate",
418418
registry,
419419
});
420-
}
420+
},
421+
this.#remoteProxySessionData?.hyperdriveConnectionStrings
421422
);
422423
options.handleUncaughtError = this.dispatchRuntimeError;
423424

packages/wrangler/src/api/startDevWorker/MultiworkerRuntimeController.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,9 @@ export class MultiworkerRuntimeController extends LocalRuntimeController {
235235
type: "devRegistryUpdate",
236236
registry,
237237
});
238-
}
238+
},
239+
this.#remoteProxySessionsData.get(data.config.name)
240+
?.hyperdriveConnectionStrings
239241
);
240242

241243
// `handleUncaughtError` is a shared Miniflare option, and the

packages/wrangler/src/dev/miniflare/index.ts

Lines changed: 5 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -343,19 +343,6 @@ function pipelineEntry(
343343
throw new Error("Pipeline must have either a stream");
344344
}
345345
}
346-
/**
347-
* Whether any binding opts into a remote Hyperdrive configuration. Used to keep
348-
* the (async) credential seeding off the reload path of configs that have no
349-
* such binding — which is every config that does not use this feature.
350-
*/
351-
function hasRemoteHyperdriveBinding(
352-
bindings: StartDevWorkerInput["bindings"]
353-
): boolean {
354-
return Object.values(bindings ?? {}).some(
355-
(binding) => binding.type === "hyperdrive" && binding.remote === true
356-
);
357-
}
358-
359346
function hyperdriveEntry(
360347
hyperdrive: CfHyperdrive,
361348
remoteProxyConnectionString?: RemoteProxyConnectionString,
@@ -1210,35 +1197,21 @@ export async function buildMiniflareOptions(
12101197
config: Omit<ConfigBundle, "rules">,
12111198
proxyToUserWorkerAuthenticationSecret: UUID,
12121199
remoteProxyConnectionString: RemoteProxyConnectionString | undefined,
1213-
onDevRegistryUpdate?: (registry: WorkerRegistry) => void
1200+
onDevRegistryUpdate?: (registry: WorkerRegistry) => void,
1201+
// Edge credentials for remote Hyperdrive bindings, prepared once per remote
1202+
// proxy session (see `maybeStartOrUpdateRemoteProxySession`).
1203+
hyperdriveConnectionStrings?: ReadonlyMap<string, string>
12141204
): Promise<Options> {
12151205
const upstream =
12161206
typeof config.localUpstream === "string"
12171207
? `${config.upstreamProtocol}://${config.localUpstream}`
12181208
: undefined;
12191209

12201210
const { sourceOptions } = await buildSourceOptions(config);
1221-
// Remote Hyperdrive bindings need the edge session's credentials before the
1222-
// (synchronous) binding builder runs. Doing it here covers every dev path
1223-
// that goes through this function.
1224-
//
1225-
// This runs on every reload, so the guard is a cheap synchronous check that
1226-
// stays out of the way of configs this feature has nothing to do with: no
1227-
// remote Hyperdrive binding means no seeding, no lazy module load (the import
1228-
// is lazy to avoid a circular dependency), and no extra await here.
1229-
const seededHyperdriveConnectionStrings =
1230-
remoteProxyConnectionString && hasRemoteHyperdriveBinding(config.bindings)
1231-
? await import("../../api/remoteBindings").then((m) =>
1232-
m.seedRemoteHyperdriveBindings(
1233-
config.bindings ?? undefined,
1234-
remoteProxyConnectionString
1235-
)
1236-
)
1237-
: undefined;
12381211
const { bindingOptions, externalWorkers } = buildMiniflareBindingOptions(
12391212
config,
12401213
remoteProxyConnectionString,
1241-
seededHyperdriveConnectionStrings
1214+
hyperdriveConnectionStrings
12421215
);
12431216
if (bindingOptions.browserRendering && getBrowserRenderingHeadfulFromEnv()) {
12441217
bindingOptions.browserRendering.headful = true;

0 commit comments

Comments
 (0)