Skip to content

Commit a25f060

Browse files
feat(miniflare): Implement Assets RPC Proxy Worker behaviour (#8376)
* feat(miniflare): Implement RPC Proxy Worker behaviour This commit adds the appropriate behaviour for the `RpcProxyWorker` used by Miniflare in Workers+Assets pipelines. * Update fixtures/workers-with-assets-and-service-bindings/workerA/src/workerD.util.ts Co-authored-by: Somhairle MacLeòid <[email protected]> * feedback fixes --------- Co-authored-by: Somhairle MacLeòid <[email protected]>
1 parent aadb49c commit a25f060

File tree

12 files changed

+246
-53
lines changed

12 files changed

+246
-53
lines changed

.changeset/real-jobs-own.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"miniflare": patch
3+
"wrangler": patch
4+
---
5+
6+
feat: Make local dev RPC behaviour on par with production for Workers with assets

fixtures/workers-with-assets-and-service-bindings/tests/index.test.ts

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import { runWranglerDev } from "../../shared/src/run-wrangler-long-lived";
66

77
const devCmds = [{ args: [] }, { args: ["--x-assets-rpc"] }];
88

9+
const failsIf = (condition: boolean) => {
10+
return condition ? it.fails : it;
11+
};
12+
913
describe.each(devCmds)(
1014
"[wrangler dev $args][Workers + Assets] Service bindings to Worker with assets",
1115
({ args }) => {
@@ -54,8 +58,9 @@ describe.each(devCmds)(
5458
describe("Workers running in separate wrangler dev sessions", () => {
5559
describe("Service binding to default export", () => {
5660
// this currently incorrectly returns the User Worker response
57-
// instead of the Asset Worker response
58-
it.fails(
61+
// instead of the Asset Worker response, unless `--x-assets-rpc`
62+
// is provided
63+
failsIf(!args.length)(
5964
"should return Asset Worker response for routes that serve static content",
6065
async ({ expect }) => {
6166
let response = await fetch(`http://${ipWorkerA}:${portWorkerA}`);
@@ -126,9 +131,10 @@ describe.each(devCmds)(
126131

127132
describe("Service binding to default entrypoint", () => {
128133
// this currently incorrectly returns the User Worker response
129-
// instead of the Asset Worker response
130-
it.fails(
131-
"should return Asset Worker response for fetch requestsfor routes that serve static content",
134+
// instead of the Asset Worker response, unless `--x-assets-rpc`
135+
// is provided
136+
failsIf(!args.length)(
137+
"should return Asset Worker response for fetch requests for routes that serve static content",
132138
async ({ expect }) => {
133139
let response = await fetch(`http://${ipWorkerA}:${portWorkerA}`);
134140
let text = await response.text();
@@ -241,6 +247,20 @@ describe.each(devCmds)(
241247
'env.NAMED_ENTRYPOINT.busyBee("🐝") response: Hello busy 🐝s from worker-d busyBee(bee)'
242248
);
243249
});
250+
251+
it("should support promise pipelining", async ({ expect }) => {
252+
// fetch URL is irrelevant here. workerA will internally call
253+
// the appropriate fns on the service binding instead
254+
let response = await fetch(`http://${ipWorkerA}:${portWorkerA}`);
255+
let text = await response.text();
256+
expect(response.status).toBe(200);
257+
expect(text).toContain(
258+
`env.NAMED_ENTRYPOINT.foo("🐙").bar.buzz() response: You made it! 🐙`
259+
);
260+
expect(text).toContain(
261+
`env.NAMED_ENTRYPOINT.newBeeCounter().value response: 2`
262+
);
263+
});
244264
});
245265
});
246266
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { defineProject, mergeConfig } from "vitest/config";
2+
import configShared from "../../vitest.shared";
3+
4+
export default mergeConfig(
5+
configShared,
6+
defineProject({
7+
test: {},
8+
})
9+
);

fixtures/workers-with-assets-and-service-bindings/workerA/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ export default {
2424
`env.NAMED_ENTRYPOINT.fetch() response: ${workerDResponses.fetchResponse}\n` +
2525
`env.NAMED_ENTRYPOINT.bee() response: ${workerDResponses.beeResult}\n` +
2626
`env.NAMED_ENTRYPOINT.busyBee("🐝") response: ${workerDResponses.busyBeeResult}\n` +
27+
`env.NAMED_ENTRYPOINT.foo("🐙").bar.buzz() response: ${workerDResponses.buzzResult}\n` +
28+
`env.NAMED_ENTRYPOINT.newBeeCounter().value response: ${workerDResponses.beeCountResult}\n` +
2729
`env.NAMED_ENTRYPOINT.scheduled() response: ${workerDResponses.scheduledResponse}\n\n`
2830
);
2931
},

fixtures/workers-with-assets-and-service-bindings/workerA/src/workerD.util.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,26 @@ export async function getWorkerDResponses(request: Request, env) {
99
// test named functions with parameters
1010
const busyBeeResult = await env.NAMED_ENTRYPOINT.busyBee("🐝");
1111

12+
// test nested functions + promise pipelining
13+
const foo = env.NAMED_ENTRYPOINT.foo("🐙");
14+
const buzzResult = await foo.bar.buzz();
15+
16+
// test RpcTarget + promise pipelining
17+
const beeCounter = env.NAMED_ENTRYPOINT.newBeeCounter();
18+
beeCounter.increment(1); // returns 1
19+
beeCounter.increment(2); // returns 3
20+
beeCounter.increment(-1); // returns 2
21+
const beeCountResult = await beeCounter.value; // returns 2
22+
1223
// tests Cron Triggers
1324
// Cron Triggers can only be defined on default exports, class-based or otherwise
1425

1526
return {
1627
fetchResponse,
1728
beeResult,
1829
busyBeeResult,
30+
buzzResult,
31+
beeCountResult,
1932
scheduledResponse:
2033
"Not supported. Cron Triggers can only be defined on default exports.",
2134
};

fixtures/workers-with-assets-and-service-bindings/workerB-with-default-export/src/index.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,17 @@ export default {
2222
return `Hello busy ${bee}s from worker-b busyBee(bee)`;
2323
},
2424

25+
/*
26+
* Nested functions
27+
*/
28+
async foo(emoji: string) {
29+
return {
30+
bar: {
31+
buzz: () => `You made it! ${emoji}`,
32+
},
33+
};
34+
},
35+
2536
/*
2637
* Cron Triggers
2738
*

fixtures/workers-with-assets-and-service-bindings/workerC-with-default-entrypoint/src/index.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { WorkerEntrypoint } from "cloudflare:workers";
1+
import { RpcTarget, WorkerEntrypoint } from "cloudflare:workers";
22

33
export default class extends WorkerEntrypoint {
44
/*
@@ -27,6 +27,28 @@ export default class extends WorkerEntrypoint {
2727
return `Hello busy ${bee}s from worker-c busyBee(bee)`;
2828
}
2929

30+
/*
31+
* Nested functions
32+
*
33+
* see https://developers.cloudflare.com/workers/runtime-apis/rpc/#promise-pipelining
34+
*/
35+
async foo(emoji: string) {
36+
return {
37+
bar: {
38+
buzz: () => `You made it! ${emoji}`,
39+
},
40+
};
41+
}
42+
43+
/*
44+
* Class instances
45+
*
46+
* see https://developers.cloudflare.com/workers/runtime-apis/rpc/#class-instances
47+
*/
48+
async newBeeCounter() {
49+
return new BeeCounter();
50+
}
51+
3052
/*
3153
* Cron Triggers
3254
*
@@ -39,3 +61,16 @@ export default class extends WorkerEntrypoint {
3961
console.log("Hello from worker-c scheduled()");
4062
}
4163
}
64+
65+
class BeeCounter extends RpcTarget {
66+
#value = 0;
67+
68+
increment(amount) {
69+
this.#value += amount;
70+
return this.#value;
71+
}
72+
73+
get value() {
74+
return this.#value;
75+
}
76+
}

fixtures/workers-with-assets-and-service-bindings/workerD-with-named-entrypoint/src/index.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { WorkerEntrypoint } from "cloudflare:workers";
1+
import { RpcTarget, WorkerEntrypoint } from "cloudflare:workers";
22

33
export class EntrypointD extends WorkerEntrypoint {
44
/*
@@ -22,6 +22,28 @@ export class EntrypointD extends WorkerEntrypoint {
2222
return `Hello busy ${bee}s from worker-d busyBee(bee)`;
2323
}
2424

25+
/*
26+
* Nested functions
27+
*
28+
* see https://developers.cloudflare.com/workers/runtime-apis/rpc/#promise-pipelining
29+
*/
30+
async foo(emoji: string) {
31+
return {
32+
bar: {
33+
buzz: () => `You made it! ${emoji}`,
34+
},
35+
};
36+
}
37+
38+
/*
39+
* Class instances
40+
*
41+
* see https://developers.cloudflare.com/workers/runtime-apis/rpc/#class-instances
42+
*/
43+
async newBeeCounter() {
44+
return new BeeCounter();
45+
}
46+
2547
/*
2648
* Cron Triggers/ Queues / etc can only be defined on a default
2749
* exports, class or non-class based
@@ -33,3 +55,16 @@ export default class extends WorkerEntrypoint {
3355
return new Response("Hello from worker-d default entrypoint fetch()");
3456
}
3557
}
58+
59+
class BeeCounter extends RpcTarget {
60+
#value = 0;
61+
62+
increment(amount) {
63+
this.#value += amount;
64+
return this.#value;
65+
}
66+
67+
get value() {
68+
return this.#value;
69+
}
70+
}

packages/miniflare/src/index.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1343,13 +1343,24 @@ export class Miniflare {
13431343
directSocket.host,
13441344
directSocket.port
13451345
);
1346+
// check if Worker with assets with default export
1347+
// (class or non-class based)
1348+
const service =
1349+
this.#sharedOpts.core.unsafeEnableAssetsRpc &&
1350+
workerOpts.assets.assets &&
1351+
entrypoint === "default"
1352+
? {
1353+
name: `${RPC_PROXY_SERVICE_NAME}:${workerOpts.core.name}`,
1354+
}
1355+
: {
1356+
name: getUserServiceName(workerName),
1357+
entrypoint: entrypoint === "default" ? undefined : entrypoint,
1358+
};
1359+
13461360
sockets.push({
13471361
name,
13481362
address,
1349-
service: {
1350-
name: getUserServiceName(workerName),
1351-
entrypoint: entrypoint === "default" ? undefined : entrypoint,
1352-
},
1363+
service,
13531364
http: {
13541365
style: directSocket.proxy ? HttpOptions_Style.PROXY : undefined,
13551366
cfBlobHeader: CoreHeaders.CF_BLOB,

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,12 @@ export const ASSETS_PLUGIN: Plugin<typeof AssetsOptionsSchema> = {
252252
name: `${ROUTER_SERVICE_NAME}:${id}`,
253253
},
254254
},
255+
{
256+
name: "USER_WORKER",
257+
service: {
258+
name: getUserServiceName(id),
259+
},
260+
},
255261
],
256262
},
257263
};

0 commit comments

Comments
 (0)