Skip to content

Commit c86b4f7

Browse files
CarmenPopoviciupenalosa
authored andcommitted
feat(wrangler): Add remote mode support for Workers + Assets (#7380)
1 parent d40cf82 commit c86b4f7

File tree

8 files changed

+183
-57
lines changed

8 files changed

+183
-57
lines changed

.changeset/strange-tips-lick.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"wrangler": minor
3+
---
4+
5+
Add Workers + Assets support in `wrangler dev --remote`

packages/wrangler/e2e/__snapshots__/dev.test.ts.snap

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@ exports[`basic js dev: 'wrangler dev --remote' > --test-scheduled works with wra
44

55
exports[`basic js dev: 'wrangler dev --remote' > --test-scheduled works with wrangler dev --remote > no custom build 1`] = `"Ran scheduled event"`;
66

7+
exports[`basic js dev: 'wrangler dev --remote' > Workers + Assets > can modify User Worker during wrangler dev --remote 1`] = `"Hello World!"`;
8+
9+
exports[`basic js dev: 'wrangler dev --remote' > Workers + Assets > can modify User Worker during wrangler dev --remote 2`] = `"Updated Worker!"`;
10+
11+
exports[`basic js dev: 'wrangler dev --remote' > Workers + Assets > can modify assets during wrangler dev --remote 1`] = `"Hello World!"`;
12+
13+
exports[`basic js dev: 'wrangler dev --remote' > Workers + Assets > can modify assets during wrangler dev --remote 2`] = `"Hello World!"`;
14+
15+
exports[`basic js dev: 'wrangler dev --remote' > can modify Worker during wrangler dev --remote 1`] = `"Hello World!"`;
16+
17+
exports[`basic js dev: 'wrangler dev --remote' > can modify Worker during wrangler dev --remote 2`] = `"Updated Worker! value"`;
18+
719
exports[`basic js dev: 'wrangler dev --remote' > can modify worker during wrangler dev --remote 1`] = `"Hello World!"`;
820

921
exports[`basic js dev: 'wrangler dev --remote' > can modify worker during wrangler dev --remote 2`] = `"Updated Worker! value"`;
@@ -14,6 +26,18 @@ exports[`basic js dev: 'wrangler dev' > --test-scheduled works with wrangler dev
1426

1527
exports[`basic js dev: 'wrangler dev' > --test-scheduled works with wrangler dev > no custom build 1`] = `"Ran scheduled event"`;
1628

29+
exports[`basic js dev: 'wrangler dev' > Workers + Assets > can modify User Worker during wrangler dev 1`] = `"Hello World!"`;
30+
31+
exports[`basic js dev: 'wrangler dev' > Workers + Assets > can modify User Worker during wrangler dev 2`] = `"Updated Worker!"`;
32+
33+
exports[`basic js dev: 'wrangler dev' > Workers + Assets > can modify assets during wrangler dev 1`] = `"Hello World!"`;
34+
35+
exports[`basic js dev: 'wrangler dev' > Workers + Assets > can modify assets during wrangler dev 2`] = `"Hello World!"`;
36+
37+
exports[`basic js dev: 'wrangler dev' > can modify Worker during wrangler dev 1`] = `"Hello World!"`;
38+
39+
exports[`basic js dev: 'wrangler dev' > can modify Worker during wrangler dev 2`] = `"Updated Worker! value"`;
40+
1741
exports[`basic js dev: 'wrangler dev' > can modify worker during wrangler dev 1`] = `"Hello World!"`;
1842

1943
exports[`basic js dev: 'wrangler dev' > can modify worker during wrangler dev 2`] = `"Updated Worker! value"`;

packages/wrangler/e2e/dev.test.ts

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ it("can import URL from 'url' in node_compat mode", async () => {
5555
describe.each([{ cmd: "wrangler dev" }, { cmd: "wrangler dev --remote" }])(
5656
"basic js dev: $cmd",
5757
({ cmd }) => {
58-
it(`can modify worker during ${cmd}`, async () => {
58+
it(`can modify Worker during ${cmd}`, async () => {
5959
const helper = new WranglerE2ETestHelper();
6060
await helper.seed({
6161
"wrangler.toml": dedent`
@@ -207,6 +207,104 @@ describe.each([{ cmd: "wrangler dev" }, { cmd: "wrangler dev --remote" }])(
207207
await worker.readUntil(/Event triggered/);
208208
});
209209
});
210+
211+
describe("Workers + Assets", () => {
212+
it(`can modify User Worker during ${cmd}`, async () => {
213+
const helper = new WranglerE2ETestHelper();
214+
await helper.seed({
215+
"wrangler.toml": dedent`
216+
name = "${workerName}"
217+
main = "src/index.ts"
218+
compatibility_date = "2023-01-01"
219+
compatibility_flags = ["nodejs_compat"]
220+
221+
[assets]
222+
directory = "public"
223+
`,
224+
"src/index.ts": dedent`
225+
export default {
226+
fetch(request) {
227+
return new Response("Hello World!")
228+
}
229+
}`,
230+
"public/readme.md": dedent`
231+
Welcome to Workers + Assets readme!`,
232+
"package.json": dedent`
233+
{
234+
"name": "worker",
235+
"version": "0.0.0",
236+
"private": true
237+
}
238+
`,
239+
});
240+
const worker = helper.runLongLived(cmd);
241+
242+
const { url } = await worker.waitForReady();
243+
244+
await expect(
245+
fetch(url).then((r) => r.text())
246+
).resolves.toMatchSnapshot();
247+
248+
await helper.seed({
249+
"src/index.ts": dedent`
250+
export default {
251+
fetch(request, env) {
252+
return new Response("Updated Worker!")
253+
}
254+
}`,
255+
});
256+
257+
await worker.waitForReload();
258+
259+
await expect(fetchText(url)).resolves.toMatchSnapshot();
260+
});
261+
262+
it(`can modify assets during ${cmd}`, async () => {
263+
const helper = new WranglerE2ETestHelper();
264+
await helper.seed({
265+
"wrangler.toml": dedent`
266+
name = "${workerName}"
267+
main = "src/index.ts"
268+
compatibility_date = "2023-01-01"
269+
compatibility_flags = ["nodejs_compat"]
270+
271+
[assets]
272+
directory = "public"
273+
`,
274+
"src/index.ts": dedent`
275+
export default {
276+
fetch(request) {
277+
return new Response("Hello World!")
278+
}
279+
}`,
280+
"public/readme.md": dedent`
281+
Welcome to Workers + Assets readme!`,
282+
"package.json": dedent`
283+
{
284+
"name": "worker",
285+
"version": "0.0.0",
286+
"private": true
287+
}
288+
`,
289+
});
290+
const worker = helper.runLongLived(cmd);
291+
292+
const { url } = await worker.waitForReady();
293+
294+
await expect(
295+
fetch(url).then((r) => r.text())
296+
).resolves.toMatchSnapshot();
297+
298+
await helper.seed({
299+
"public/readme.md": dedent`
300+
Welcome to updated Workers + Assets readme!`,
301+
});
302+
303+
await worker.waitForReload();
304+
305+
await expect(fetchText(url)).resolves.toMatchSnapshot();
306+
});
307+
});
210308
}
211309
);
212310

packages/wrangler/src/__tests__/dev.test.ts

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1732,27 +1732,6 @@ describe.sequential("wrangler dev", () => {
17321732
)
17331733
);
17341734
});
1735-
1736-
it("should error if --assets and --remote are used together", async () => {
1737-
fs.mkdirSync("public");
1738-
await expect(
1739-
runWrangler("dev --assets public --remote")
1740-
).rejects.toThrowErrorMatchingInlineSnapshot(
1741-
`[Error: Cannot use assets in remote mode. Workers with assets are only supported in local mode. Please use \`wrangler dev\`.]`
1742-
);
1743-
});
1744-
1745-
it("should error if config.assets and --remote are used together", async () => {
1746-
writeWranglerConfig({
1747-
assets: { directory: "./public" },
1748-
});
1749-
fs.mkdirSync("public");
1750-
await expect(
1751-
runWrangler("dev --remote")
1752-
).rejects.toThrowErrorMatchingInlineSnapshot(
1753-
`[Error: Cannot use assets in remote mode. Workers with assets are only supported in local mode. Please use \`wrangler dev\`.]`
1754-
);
1755-
});
17561735
});
17571736

17581737
describe("--inspect", () => {

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

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -314,12 +314,6 @@ async function resolveConfig(
314314
);
315315
}
316316

317-
if (resolved.assets && resolved.dev.remote) {
318-
throw new UserError(
319-
"Cannot use assets in remote mode. Workers with assets are only supported in local mode. Please use `wrangler dev`."
320-
);
321-
}
322-
323317
validateAssetsArgsAndConfig(resolved);
324318

325319
const services = extractBindingsOfType("service", resolved.bindings);

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

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import type {
3030
} from "./events";
3131
import type { Trigger } from "./types";
3232

33+
type CreateRemoteWorkerInitProps = Parameters<typeof createRemoteWorkerInit>[0];
34+
3335
export class RemoteRuntimeController extends RuntimeController {
3436
#abortController = new AbortController();
3537

@@ -60,40 +62,49 @@ export class RemoteRuntimeController extends RuntimeController {
6062
}
6163

6264
async #previewToken(
63-
props: Parameters<typeof createRemoteWorkerInit>[0] &
65+
props: Omit<CreateRemoteWorkerInitProps, "name"> &
66+
Partial<Pick<CreateRemoteWorkerInitProps, "name">> &
6467
Parameters<typeof getWorkerAccountAndContext>[0]
6568
): Promise<CfPreviewToken | undefined> {
69+
if (!this.#session) {
70+
return;
71+
}
72+
6673
try {
74+
const { workerAccount, workerContext } = await getWorkerAccountAndContext(
75+
{
76+
accountId: props.accountId,
77+
env: props.env,
78+
legacyEnv: props.legacyEnv,
79+
host: props.host,
80+
routes: props.routes,
81+
sendMetrics: props.sendMetrics,
82+
configPath: props.configPath,
83+
}
84+
);
85+
86+
const scriptId =
87+
props.name ||
88+
(workerContext.zone
89+
? this.#session.id
90+
: this.#session.host.split(".")[0]);
91+
6792
const init = await createRemoteWorkerInit({
6893
bundle: props.bundle,
6994
modules: props.modules,
7095
accountId: props.accountId,
71-
name: props.name,
96+
name: scriptId,
7297
legacyEnv: props.legacyEnv,
7398
env: props.env,
7499
isWorkersSite: props.isWorkersSite,
100+
assets: props.assets,
75101
legacyAssetPaths: props.legacyAssetPaths,
76102
format: props.format,
77103
bindings: props.bindings,
78104
compatibilityDate: props.compatibilityDate,
79105
compatibilityFlags: props.compatibilityFlags,
80106
});
81107

82-
const { workerAccount, workerContext } = await getWorkerAccountAndContext(
83-
{
84-
accountId: props.accountId,
85-
env: props.env,
86-
legacyEnv: props.legacyEnv,
87-
host: props.host,
88-
routes: props.routes,
89-
sendMetrics: props.sendMetrics,
90-
configPath: props.configPath,
91-
}
92-
);
93-
if (!this.#session) {
94-
return;
95-
}
96-
97108
const workerPreviewToken = await createWorkerPreview(
98109
init,
99110
workerAccount,
@@ -172,6 +183,7 @@ export class RemoteRuntimeController extends RuntimeController {
172183
legacyEnv: !config.legacy?.enableServiceEnvironments,
173184
env: config.env,
174185
isWorkersSite: config.legacy?.site !== undefined,
186+
assets: config.assets,
175187
legacyAssetPaths: config.legacy?.site?.bucket
176188
? {
177189
baseDirectory: config.legacy?.site?.bucket,

packages/wrangler/src/dev/create-worker-preview.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,9 @@ import { logger } from "../logger";
77
import { ParseError, parseJSON } from "../parse";
88
import { getAccessToken } from "../user/access";
99
import { isAbortError } from "../utils/isAbortError";
10-
import type {
11-
CfWorkerContext,
12-
CfWorkerInit,
13-
} from "../deployment-bundle/worker";
10+
import type { CfWorkerContext } from "../deployment-bundle/worker";
1411
import type { ApiCredentials } from "../user";
12+
import type { CfWorkerInitWithName } from "./remote";
1513
import type { HeadersInit } from "undici";
1614

1715
/**
@@ -227,18 +225,17 @@ export async function createPreviewSession(
227225
*/
228226
async function createPreviewToken(
229227
account: CfAccount,
230-
worker: CfWorkerInit,
228+
worker: CfWorkerInitWithName,
231229
ctx: CfWorkerContext,
232230
session: CfPreviewSession,
233231
abortSignal: AbortSignal
234232
): Promise<CfPreviewToken> {
235233
const { value, host, inspectorUrl, prewarmUrl } = session;
236234
const { accountId } = account;
237-
const scriptId = worker.name || (ctx.zone ? session.id : host.split(".")[0]);
238235
const url =
239236
ctx.env && !ctx.legacyEnv
240-
? `/accounts/${accountId}/workers/services/${scriptId}/environments/${ctx.env}/edge-preview`
241-
: `/accounts/${accountId}/workers/scripts/${scriptId}/edge-preview`;
237+
? `/accounts/${accountId}/workers/services/${worker.name}/environments/${ctx.env}/edge-preview`
238+
: `/accounts/${accountId}/workers/scripts/${worker.name}/edge-preview`;
242239

243240
const mode: CfPreviewMode = ctx.zone
244241
? {
@@ -303,7 +300,7 @@ async function createPreviewToken(
303300
* const {value, host} = await createWorker(init, acct);
304301
*/
305302
export async function createWorkerPreview(
306-
init: CfWorkerInit,
303+
init: CfWorkerInitWithName,
307304
account: CfAccount,
308305
ctx: CfWorkerContext,
309306
session: CfPreviewSession,

packages/wrangler/src/dev/remote.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import assert from "node:assert";
22
import path from "node:path";
3+
import { syncAssets } from "../assets";
34
import { printBundleSize } from "../deployment-bundle/bundle-reporter";
45
import { getBundleType } from "../deployment-bundle/bundle-type";
56
import { withSourceURLs } from "../deployment-bundle/source-url";
@@ -10,6 +11,7 @@ import { syncLegacyAssets } from "../sites";
1011
import { requireApiToken } from "../user";
1112
import { isAbortError } from "../utils/isAbortError";
1213
import { getZoneIdForPreview } from "../zones";
14+
import type { AssetsOptions } from "../assets";
1315
import type { Route } from "../config/environment";
1416
import type {
1517
CfModule,
@@ -79,14 +81,18 @@ export function handlePreviewSessionCreationError(
7981
}
8082
}
8183

84+
export type CfWorkerInitWithName = Required<Pick<CfWorkerInit, "name">> &
85+
CfWorkerInit;
86+
8287
export async function createRemoteWorkerInit(props: {
8388
bundle: EsbuildBundle;
8489
modules: CfModule[];
8590
accountId: string;
86-
name: string | undefined;
91+
name: string;
8792
legacyEnv: boolean | undefined;
8893
env: string | undefined;
8994
isWorkersSite: boolean;
95+
assets: AssetsOptions | undefined;
9096
legacyAssetPaths: LegacyAssetPaths | undefined;
9197
format: CfScriptFormat;
9298
bindings: CfWorkerInit["bindings"];
@@ -130,7 +136,11 @@ export async function createRemoteWorkerInit(props: {
130136
});
131137
}
132138

133-
const init: CfWorkerInit = {
139+
const assetsJwt = props.assets
140+
? await syncAssets(props.accountId, props.assets.directory, props.name)
141+
: undefined;
142+
143+
const init: CfWorkerInitWithName = {
134144
name: props.name,
135145
main: {
136146
name: path.basename(props.bundle.path),
@@ -161,10 +171,17 @@ export async function createRemoteWorkerInit(props: {
161171
keepSecrets: true,
162172
logpush: false,
163173
sourceMaps: undefined,
174+
assets:
175+
props.assets && assetsJwt
176+
? {
177+
jwt: assetsJwt,
178+
routingConfig: props.assets.routingConfig,
179+
assetConfig: props.assets.assetConfig,
180+
}
181+
: undefined,
164182
placement: undefined, // no placement in dev
165183
tail_consumers: undefined, // no tail consumers in dev - TODO revisit?
166184
limits: undefined, // no limits in preview - not supported yet but can be added
167-
assets: undefined, // no remote mode for assets
168185
observability: undefined, // no observability in dev
169186
};
170187

0 commit comments

Comments
 (0)