Skip to content

Commit b9bb4c9

Browse files
fix: add warning about wrangler dev with remote Durable Objects
Durable Objects that are being bound by `script_name` will not be isolated from the live data during development with `wrangler dev`. This change simply warns the developer about this, so that they can back out before accidentally changing live data. Fixes cloudflare#319
1 parent 62a89c6 commit b9bb4c9

File tree

3 files changed

+86
-14
lines changed

3 files changed

+86
-14
lines changed

.changeset/four-bags-admire.md

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
"wrangler": patch
3+
---
4+
5+
fix: add warning about `wrangler dev` with remote Durable Objects
6+
7+
Durable Objects that are being bound by `script_name` will not be isolated from the
8+
live data during development with `wrangler dev`.
9+
This change simply warns the developer about this, so that they can back out before
10+
accidentally changing live data.
11+
12+
Fixes #319

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

+37-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { runInTempDir } from "./helpers/run-in-tmp";
99
import { runWrangler } from "./helpers/run-wrangler";
1010
import writeWranglerToml from "./helpers/write-wrangler-toml";
1111

12-
describe("Dev component", () => {
12+
describe("wrangler dev", () => {
1313
mockAccountId();
1414
mockApiToken();
1515
runInTempDir();
@@ -480,6 +480,42 @@ describe("Dev component", () => {
480480
expect(std.err).toMatchInlineSnapshot(`""`);
481481
});
482482
});
483+
484+
describe("durable_objects", () => {
485+
it("should warn if there is one or more remote Durable Object", async () => {
486+
writeWranglerToml({
487+
main: "index.js",
488+
durable_objects: {
489+
bindings: [
490+
{ name: "NAME_1", class_name: "CLASS_1" },
491+
{
492+
name: "NAME_2",
493+
class_name: "CLASS_2",
494+
script_name: "SCRIPT_A",
495+
},
496+
{ name: "NAME_3", class_name: "CLASS_3" },
497+
{
498+
name: "NAME_4",
499+
class_name: "CLASS_4",
500+
script_name: "SCRIPT_B",
501+
},
502+
],
503+
},
504+
});
505+
fs.writeFileSync("index.js", `export default {};`);
506+
await runWrangler("dev");
507+
expect((Dev as jest.Mock).mock.calls[0][0].ip).toEqual("localhost");
508+
expect(std.out).toMatchInlineSnapshot(`""`);
509+
expect(std.warn).toMatchInlineSnapshot(`
510+
"WARNING: You have Durable Object bindings, which are not defined locally in the worker being developed.
511+
Be aware that changes to the data stored in these Durable Objects will be permanent and affect the live instances.
512+
Remote Durable Objects that are affected:
513+
- {\\"name\\":\\"NAME_2\\",\\"class_name\\":\\"CLASS_2\\",\\"script_name\\":\\"SCRIPT_A\\"}
514+
- {\\"name\\":\\"NAME_4\\",\\"class_name\\":\\"CLASS_4\\",\\"script_name\\":\\"SCRIPT_B\\"}"
515+
`);
516+
expect(std.err).toMatchInlineSnapshot(`""`);
517+
});
518+
});
483519
});
484520

485521
function mockGetZones(domain: string, zones: { id: string }[] = []) {

packages/wrangler/src/entry.ts

+37-13
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export type Entry = { file: string; directory: string; format: CfScriptFormat };
2020
export async function getEntry(
2121
args: { script?: string; format?: CfScriptFormat | undefined },
2222
config: Config,
23-
command: string
23+
command: "dev" | "publish"
2424
): Promise<Entry> {
2525
let file: string;
2626
let directory = process.cwd();
@@ -49,12 +49,24 @@ export async function getEntry(
4949
args.format ?? config.build?.upload?.format
5050
);
5151

52-
if (format === "service-worker" && hasDurableObjectImplementations(config)) {
52+
const { localBindings, remoteBindings } =
53+
partitionDurableObjectBindings(config);
54+
55+
if (command === "dev" && remoteBindings.length > 0) {
56+
console.warn(
57+
"WARNING: You have Durable Object bindings, which are not defined locally in the worker being developed.\n" +
58+
"Be aware that changes to the data stored in these Durable Objects will be permanent and affect the live instances.\n" +
59+
"Remote Durable Objects that are affected:\n" +
60+
remoteBindings.map((b) => `- ${JSON.stringify(b)}`).join("\n")
61+
);
62+
}
63+
64+
if (format === "service-worker" && localBindings.length > 0) {
5365
const errorMessage =
5466
"You seem to be trying to use Durable Objects in a Worker written with Service Worker syntax.";
5567
const addScriptName =
5668
"You can use Durable Objects defined in other Workers by specifying a `script_name` in your wrangler.toml, where `script_name` is the name of the Worker that implements that Durable Object. For example:";
57-
const addScriptNameExamples = generateAddScriptNameExamples(config);
69+
const addScriptNameExamples = generateAddScriptNameExamples(localBindings);
5870
const migrateText =
5971
"Alternatively, migrate your worker to ES Module syntax to implement a Durable Object in this Worker:";
6072
const migrateUrl =
@@ -174,29 +186,41 @@ export function fileExists(filePath: string): boolean {
174186
return false;
175187
}
176188

189+
type DurableObjectBindings = Config["durable_objects"]["bindings"];
190+
177191
/**
178-
* Returns true if the given config contains Durable Object bindings that are implemented
179-
* in this worker instead of being implemented elsewhere, and bound via a `script_name`
180-
* property in wrangler.toml
192+
* Groups the durable object bindings into two lists:
193+
* those that are defined locally and those that refer to a durable object defined in another script.
181194
*/
182-
function hasDurableObjectImplementations(config: Config): boolean {
183-
return config.durable_objects.bindings.some(
184-
(binding) => binding.script_name === undefined
185-
);
195+
function partitionDurableObjectBindings(config: Config): {
196+
localBindings: DurableObjectBindings;
197+
remoteBindings: DurableObjectBindings;
198+
} {
199+
const localBindings: DurableObjectBindings = [];
200+
const remoteBindings: DurableObjectBindings = [];
201+
for (const binding of config.durable_objects.bindings) {
202+
if (binding.script_name === undefined) {
203+
localBindings.push(binding);
204+
} else {
205+
remoteBindings.push(binding);
206+
}
207+
}
208+
return { localBindings, remoteBindings };
186209
}
187210

188211
/**
189212
* Generates some help text based on the Durable Object bindings in a given
190213
* config indicating how the user can add a `script_name` field to bind an
191214
* externally defined Durable Object.
192215
*/
193-
function generateAddScriptNameExamples(config: Config): string {
216+
function generateAddScriptNameExamples(
217+
localBindings: DurableObjectBindings
218+
): string {
194219
function exampleScriptName(binding_name: string): string {
195220
return `${binding_name.toLowerCase().replaceAll("_", "-")}-worker`;
196221
}
197222

198-
return config.durable_objects.bindings
199-
.filter((binding) => binding.script_name === undefined)
223+
return localBindings
200224
.map(({ name, class_name }) => {
201225
const script_name = exampleScriptName(name);
202226
const currentBinding = `{ name = ${name}, class_name = ${class_name} }`;

0 commit comments

Comments
 (0)