From 22145c1dbd8d2dbbb87b2b0e7c999efb10184712 Mon Sep 17 00:00:00 2001 From: Andy Jessop Date: Mon, 3 Feb 2025 15:19:30 +0100 Subject: [PATCH] docs: add better console messaging for remote development --- .changeset/light-taxis-move.md | 5 + .../wrangler/e2e/dev-with-resources.test.ts | 2 + packages/wrangler/e2e/pages-dev.test.ts | 8 +- packages/wrangler/src/__tests__/dev.test.ts | 44 +++++- packages/wrangler/src/utils/print-bindings.ts | 142 +++++++++++++----- 5 files changed, 154 insertions(+), 47 deletions(-) create mode 100644 .changeset/light-taxis-move.md diff --git a/.changeset/light-taxis-move.md b/.changeset/light-taxis-move.md new file mode 100644 index 000000000000..52125d956c5f --- /dev/null +++ b/.changeset/light-taxis-move.md @@ -0,0 +1,5 @@ +--- +"wrangler": patch +--- + +docs: clarifies that local resources are "simulated locally" or "connected to remote resource", and adds console messages to help explain local dev diff --git a/packages/wrangler/e2e/dev-with-resources.test.ts b/packages/wrangler/e2e/dev-with-resources.test.ts index ede124475acd..57b49cdc76d0 100644 --- a/packages/wrangler/e2e/dev-with-resources.test.ts +++ b/packages/wrangler/e2e/dev-with-resources.test.ts @@ -572,6 +572,8 @@ describe.sequential.each(RUNTIMES)("Bindings: $flags", ({ runtime, flags }) => { const worker = helper.runLongLived( `wrangler dev ${flags} --port ${port} --inspector-port ${inspectorPort} --experimental-vectorize-bind-to-prod` ); + const matches = await worker.readUntil(/connected to remote resource/); + expect(matches.length).toEqual(1); const { url } = await worker.waitForReady(); const res = await fetch(url); diff --git a/packages/wrangler/e2e/pages-dev.test.ts b/packages/wrangler/e2e/pages-dev.test.ts index 6bbca0a1864e..f4d88522ff4f 100644 --- a/packages/wrangler/e2e/pages-dev.test.ts +++ b/packages/wrangler/e2e/pages-dev.test.ts @@ -121,13 +121,13 @@ describe.sequential.each([{ cmd: "wrangler pages dev" }])( expect(normalizeOutput(worker.currentOutput)).toContain( dedent`Your worker has access to the following bindings: - Durable Objects: - - TEST_DO: TestDurableObject (defined in a [not connected]) (local) + - TEST_DO: TestDurableObject (defined in a [not connected]) - KV Namespaces: - - TEST_KV: TEST_KV (local) + - TEST_KV: TEST_KV [simulated locally] - D1 Databases: - - TEST_D1: local-TEST_D1 (TEST_D1) (local) + - TEST_D1: local-TEST_D1 (TEST_D1) [simulated locally] - R2 Buckets: - - TEST_R2: TEST_R2 (local) + - TEST_R2: TEST_R2 [simulated locally] - Services: - TEST_SERVICE: test-worker [not connected] ` diff --git a/packages/wrangler/src/__tests__/dev.test.ts b/packages/wrangler/src/__tests__/dev.test.ts index 5cbce0f5ecb1..6df012d194cf 100644 --- a/packages/wrangler/src/__tests__/dev.test.ts +++ b/packages/wrangler/src/__tests__/dev.test.ts @@ -1206,12 +1206,17 @@ describe.sequential("wrangler dev", () => { process.platform === "win32" ? "127.0.0.1" : "localhost" ); expect(std.out).toMatchInlineSnapshot(` - "Your worker has access to the following bindings: + "Your Worker and resources are simulated locally via Miniflare. For more information, see: https://developers.cloudflare.com/workers/testing/local-development. + + Your worker has access to the following bindings: - Durable Objects: - - NAME_1: CLASS_1 (local) - - NAME_2: CLASS_2 (defined in SCRIPT_A [not connected]) (local) - - NAME_3: CLASS_3 (local) - - NAME_4: CLASS_4 (defined in SCRIPT_B [not connected]) (local) + - NAME_1: CLASS_1 [simulated locally] + - NAME_2: CLASS_2 (defined in SCRIPT_A [not connected]) + - NAME_3: CLASS_3 [simulated locally] + - NAME_4: CLASS_4 (defined in SCRIPT_B [not connected]) + + Use \\"wrangler dev --remote\\" to run both your Worker and all bindings remotely (https://developers.cloudflare.com/workers/testing/local-development/#develop-using-remote-resources-and-bindings). + " `); expect(std.warn).toMatchInlineSnapshot(` @@ -1297,6 +1302,8 @@ describe.sequential("wrangler dev", () => { }); expect(std.out).toMatchInlineSnapshot(` "Using vars defined in .dev.vars + Your Worker and resources are simulated locally via Miniflare. For more information, see: https://developers.cloudflare.com/workers/testing/local-development. + Your worker has access to the following bindings: - Vars: - VAR_1: \\"(hidden)\\" @@ -1306,6 +1313,9 @@ describe.sequential("wrangler dev", () => { - VAR_MULTI_LINE_2: \\"(hidden)\\" - EMPTY: \\"(hidden)\\" - UNQUOTED: \\"(hidden)\\" + + Use \\"wrangler dev --remote\\" to run both your Worker and all bindings remotely (https://developers.cloudflare.com/workers/testing/local-development/#develop-using-remote-resources-and-bindings). + " `); }); @@ -1331,9 +1341,14 @@ describe.sequential("wrangler dev", () => { expect(varBindings).toEqual({ CUSTOM_VAR: "custom" }); expect(std.out).toMatchInlineSnapshot(` "Using vars defined in .dev.vars.custom + Your Worker and resources are simulated locally via Miniflare. For more information, see: https://developers.cloudflare.com/workers/testing/local-development. + Your worker has access to the following bindings: - Vars: - CUSTOM_VAR: \\"(hidden)\\" + + Use \\"wrangler dev --remote\\" to run both your Worker and all bindings remotely (https://developers.cloudflare.com/workers/testing/local-development/#develop-using-remote-resources-and-bindings). + " `); }); @@ -1850,10 +1865,15 @@ describe.sequential("wrangler dev", () => { fs.writeFileSync("index.js", `export default {};`); await runWranglerUntilConfig("dev index.js"); expect(std.out).toMatchInlineSnapshot(` - "Your worker has access to the following bindings: + "Your Worker and resources are simulated locally via Miniflare. For more information, see: https://developers.cloudflare.com/workers/testing/local-development. + + Your worker has access to the following bindings: - Services: - WorkerA: A [not connected] - WorkerB: B [not connected] + + Use \\"wrangler dev --remote\\" to run both your Worker and all bindings remotely (https://developers.cloudflare.com/workers/testing/local-development/#develop-using-remote-resources-and-bindings). + " `); expect(std.warn).toMatchInlineSnapshot(`""`); @@ -1871,10 +1891,15 @@ describe.sequential("wrangler dev", () => { fs.writeFileSync("index.js", `export default {};`); await runWranglerUntilConfig("dev index.js"); expect(std.out).toMatchInlineSnapshot(` - "Your worker has access to the following bindings: + "Your Worker and resources are simulated locally via Miniflare. For more information, see: https://developers.cloudflare.com/workers/testing/local-development. + + Your worker has access to the following bindings: - Services: - WorkerA: A [not connected] - WorkerB: B [not connected] + + Use \\"wrangler dev --remote\\" to run both your Worker and all bindings remotely (https://developers.cloudflare.com/workers/testing/local-development/#develop-using-remote-resources-and-bindings). + " `); expect(std.warn).toMatchInlineSnapshot(`""`); @@ -1898,11 +1923,16 @@ describe.sequential("wrangler dev", () => { await runWranglerUntilConfig("dev index.js"); expect(std.out).toMatchInlineSnapshot(` "Using vars defined in .dev.vars + Your Worker and resources are simulated locally via Miniflare. For more information, see: https://developers.cloudflare.com/workers/testing/local-development. + Your worker has access to the following bindings: - Vars: - variable: 123 - overriden: \\"(hidden)\\" - SECRET: \\"(hidden)\\" + + Use \\"wrangler dev --remote\\" to run both your Worker and all bindings remotely (https://developers.cloudflare.com/workers/testing/local-development/#develop-using-remote-resources-and-bindings). + " `); }); diff --git a/packages/wrangler/src/utils/print-bindings.ts b/packages/wrangler/src/utils/print-bindings.ts index 16506f7eab55..6031fb59f4df 100644 --- a/packages/wrangler/src/utils/print-bindings.ts +++ b/packages/wrangler/src/utils/print-bindings.ts @@ -4,17 +4,6 @@ import { logger } from "../logger"; import type { CfWorkerInit } from "../deployment-bundle/worker"; import type { WorkerRegistry } from "../dev-registry"; -function addLocalSuffix( - id: string | symbol | undefined, - local: boolean = false -) { - if (!id || typeof id === "symbol") { - id = ""; - } - - return `${id}${local ? " (local)" : ""}`; -} - export const friendlyBindingNames: Record< keyof CfWorkerInit["bindings"], string @@ -58,6 +47,10 @@ export function printBindings( } = {} ) { let hasConnectionStatus = false; + const addSuffix = createAddSuffix({ + isProvisioning: context.provisioning, + isLocalDev: context.local, + }); const truncate = (item: string | Record) => { const s = typeof item === "string" ? item : JSON.stringify(item); const maxLength = 40; @@ -137,7 +130,11 @@ export function printBindings( return { key: name, - value: addLocalSuffix(value, context.local), + value: script_name + ? value + : addSuffix(value, { + isSimulatedLocally: true, + }), }; } ), @@ -155,7 +152,7 @@ export function printBindings( return { key: binding, - value, + value: script_name ? value : addSuffix(value), }; }), }); @@ -167,7 +164,9 @@ export function printBindings( entries: kv_namespaces.map(({ binding, id }) => { return { key: binding, - value: addLocalSuffix(id, context.local), + value: addSuffix(id, { + isSimulatedLocally: true, + }), }; }), }); @@ -180,10 +179,11 @@ export function printBindings( ({ name, destination_address, allowed_destination_addresses }) => { return { key: name, - value: + value: addSuffix( destination_address || - allowed_destination_addresses?.join(", ") || - "unrestricted", + allowed_destination_addresses?.join(", ") || + "unrestricted" + ), }; } ), @@ -196,7 +196,9 @@ export function printBindings( entries: queues.map(({ binding, queue_name }) => { return { key: binding, - value: addLocalSuffix(queue_name, context.local), + value: addSuffix(queue_name, { + isSimulatedLocally: true, + }), }; }), }); @@ -220,7 +222,9 @@ export function printBindings( } return { key: binding, - value: addLocalSuffix(databaseValue, context.local), + value: addSuffix(databaseValue, { + isSimulatedLocally: true, + }), }; } ), @@ -233,7 +237,7 @@ export function printBindings( entries: vectorize.map(({ binding, index_name }) => { return { key: binding, - value: index_name, + value: addSuffix(index_name), }; }), }); @@ -245,7 +249,9 @@ export function printBindings( entries: hyperdrive.map(({ binding, id }) => { return { key: binding, - value: addLocalSuffix(id, context.local), + value: addSuffix(id, { + isSimulatedLocally: true, + }), }; }), }); @@ -263,7 +269,9 @@ export function printBindings( return { key: binding, - value: addLocalSuffix(name, context.local), + value: addSuffix(name, { + isSimulatedLocally: true, + }), }; }), }); @@ -275,7 +283,7 @@ export function printBindings( entries: logfwdr.bindings.map((binding) => { return { key: binding.name, - value: binding.destination, + value: addSuffix(binding.destination), }; }), }); @@ -321,7 +329,7 @@ export function printBindings( entries: analytics_engine_datasets.map(({ binding, dataset }) => { return { key: binding, - value: dataset ?? binding, + value: addSuffix(dataset ?? binding), }; }), }); @@ -332,7 +340,7 @@ export function printBindings( name: friendlyBindingNames.text_blobs, entries: Object.entries(text_blobs).map(([key, value]) => ({ key, - value: truncate(value), + value: addSuffix(truncate(value)), })), }); } @@ -346,10 +354,13 @@ export function printBindings( if (ai !== undefined) { const entries: [{ key: string; value: string | boolean }] = [ - { key: "Name", value: ai.binding }, + { key: "Name", value: addSuffix(ai.binding) }, ]; if (ai.staging) { - entries.push({ key: "Staging", value: ai.staging }); + entries.push({ + key: "Staging", + value: addSuffix(ai.staging.toString()), + }); } output.push({ @@ -363,7 +374,7 @@ export function printBindings( name: friendlyBindingNames.pipelines, entries: pipelines.map(({ binding, pipeline }) => ({ key: binding, - value: pipeline, + value: addSuffix(pipeline), })), }); } @@ -371,7 +382,7 @@ export function printBindings( if (version_metadata !== undefined) { output.push({ name: friendlyBindingNames.version_metadata, - entries: [{ key: "Name", value: version_metadata.binding }], + entries: [{ key: "Name", value: addSuffix(version_metadata.binding) }], }); } @@ -380,7 +391,7 @@ export function printBindings( name: friendlyBindingNames.unsafe, entries: unsafe.bindings.map(({ name, type }) => ({ key: type, - value: name, + value: addSuffix(name), })), }); } @@ -410,7 +421,9 @@ export function printBindings( name: friendlyBindingNames.wasm_modules, entries: Object.entries(wasm_modules).map(([key, value]) => ({ key, - value: typeof value === "string" ? truncate(value) : "", + value: addSuffix( + typeof value === "string" ? truncate(value) : "" + ), })), }); } @@ -421,9 +434,11 @@ export function printBindings( entries: dispatch_namespaces.map(({ binding, namespace, outbound }) => { return { key: binding, - value: outbound - ? `${namespace} (outbound -> ${outbound.service})` - : namespace, + value: addSuffix( + outbound + ? `${namespace} (outbound -> ${outbound.service})` + : namespace + ), }; }), }); @@ -435,7 +450,7 @@ export function printBindings( entries: mtls_certificates.map(({ binding, certificate_id }) => { return { key: binding, - value: certificate_id, + value: addSuffix(certificate_id), }; }), }); @@ -446,7 +461,7 @@ export function printBindings( name: friendlyBindingNames.unsafe, entries: Object.entries(unsafe.metadata).map(([key, value]) => ({ key, - value: JSON.stringify(value), + value: addSuffix(JSON.stringify(value)), })), }); } @@ -455,6 +470,12 @@ export function printBindings( return; } + if (context.local) { + logger.log( + `Your Worker and resources are simulated locally via Miniflare. For more information, see: https://developers.cloudflare.com/workers/testing/local-development.\n` + ); + } + let title: string; if (context.provisioning) { title = "The following bindings need to be provisioned:"; @@ -485,4 +506,53 @@ export function printBindings( `\nService bindings & durable object bindings connect to other \`wrangler dev\` processes running locally, with their connection status indicated by ${chalk.green("[connected]")} or ${chalk.red("[not connected]")}. For more details, refer to https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/#local-development\n` ); } + + if (context.local) { + logger.log( + `\nUse "wrangler dev --remote" to run both your Worker and all bindings remotely (https://developers.cloudflare.com/workers/testing/local-development/#develop-using-remote-resources-and-bindings).\n` + ); + } +} + +function normalizeValue(value: string | symbol | undefined) { + if (!value || typeof value === "symbol") { + return ""; + } + + return value; +} + +/** + * Creates a function for adding a suffix to the value of a binding in the console. + * + * The suffix is only for local dev so it can be used to determine whether a binding is + * simulated locally or connected to a remote resource. + * + * We don't show the suffix when provisioning because the bindings are not yet available in local dev. + */ +function createAddSuffix({ + isProvisioning = false, + isLocalDev = false, +}: { + isProvisioning?: boolean; + isLocalDev?: boolean; +}) { + return function addSuffix( + value: string | symbol | undefined, + { + isSimulatedLocally = false, + }: { + isSimulatedLocally?: boolean; + } = {} + ) { + const normalizedValue = normalizeValue(value); + + if (isProvisioning || !isLocalDev) { + return normalizedValue; + } + + return isSimulatedLocally + ? `${normalizedValue} [simulated locally]` + : `${normalizedValue} [connected to remote resource]`; + }; }