From c7104ce24e98ef52737716a95af0aa2ea3d17bf6 Mon Sep 17 00:00:00 2001 From: emily-shen <69125074+emily-shen@users.noreply.github.com> Date: Wed, 11 Dec 2024 13:34:52 +0000 Subject: [PATCH 1/5] add experimental_readRawConfig and tests --- .../src/__tests__/configuration.test.ts | 55 ++++++++++++++++++- .../wrangler/src/config/config-helpers.ts | 3 +- packages/wrangler/src/config/index.ts | 21 ++++--- packages/wrangler/src/index.ts | 6 +- 4 files changed, 70 insertions(+), 15 deletions(-) diff --git a/packages/wrangler/src/__tests__/configuration.test.ts b/packages/wrangler/src/__tests__/configuration.test.ts index f20b25fbb28e..0cf1b1a8c770 100644 --- a/packages/wrangler/src/__tests__/configuration.test.ts +++ b/packages/wrangler/src/__tests__/configuration.test.ts @@ -1,9 +1,11 @@ +import * as fs from "fs"; import path from "node:path"; -import { readConfig } from "../config"; +import { experimental_readRawConfig, readConfig } from "../config"; import { normalizeAndValidateConfig } from "../config/validation"; import { run } from "../experimental-flags"; import { normalizeString } from "./helpers/normalize"; import { runInTempDir } from "./helpers/run-in-tmp"; +import { writeWorkerSource } from "./helpers/write-worker-source"; import { writeWranglerConfig } from "./helpers/write-wrangler-config"; import type { ConfigFields, @@ -6037,6 +6039,57 @@ describe("normalizeAndValidateConfig()", () => { }); }); +describe("experimental_readRawConfig()", () => { + describe.each(["json", "jsonc", "toml"])( + `with %s config files`, + (configType) => { + runInTempDir(); + it(`should find a ${configType} config file given a specific path`, () => { + fs.mkdirSync("../folder", { recursive: true }); + writeWranglerConfig({}, `../folder/config.${configType}`); + + const result = experimental_readRawConfig({ + config: `../folder/config.${configType}`, + }); + expect(result.rawConfig).toEqual({ + compatibility_date: "2022-01-12", + name: "test-name", + }); + }); + + it("should find a config file given a specific script", () => { + fs.mkdirSync("./path/to", { recursive: true }); + writeWranglerConfig( + { name: "config-one" }, + `./path/wrangler.${configType}` + ); + + fs.mkdirSync("../folder", { recursive: true }); + writeWranglerConfig( + { name: "config-two" }, + `../folder/wrangler.${configType}` + ); + + let result = experimental_readRawConfig({ + script: "./path/to/index.js", + }); + expect(result.rawConfig).toEqual({ + compatibility_date: "2022-01-12", + name: "config-one", + }); + + result = experimental_readRawConfig({ + script: "../folder/index.js", + }); + expect(result.rawConfig).toEqual({ + compatibility_date: "2022-01-12", + name: "config-two", + }); + }); + } + ); +}); + function normalizePath(text: string): string { return text .replace("project\\wrangler.toml", "project/wrangler.toml") diff --git a/packages/wrangler/src/config/config-helpers.ts b/packages/wrangler/src/config/config-helpers.ts index 15e5247f6c6f..d7b4313ea47b 100644 --- a/packages/wrangler/src/config/config-helpers.ts +++ b/packages/wrangler/src/config/config-helpers.ts @@ -17,7 +17,7 @@ export function resolveWranglerConfigPath({ } const leafPath = script !== undefined ? path.dirname(script) : process.cwd(); - + console.dir(leafPath); return findWranglerConfig(leafPath); } @@ -28,6 +28,7 @@ export function resolveWranglerConfigPath({ export function findWranglerConfig( referencePath: string = process.cwd() ): string | undefined { + console.dir(referencePath); return ( findUpSync(`wrangler.json`, { cwd: referencePath }) ?? findUpSync(`wrangler.jsonc`, { cwd: referencePath }) ?? diff --git a/packages/wrangler/src/config/index.ts b/packages/wrangler/src/config/index.ts index 6b369bacbe94..cec4953a44f4 100644 --- a/packages/wrangler/src/config/index.ts +++ b/packages/wrangler/src/config/index.ts @@ -82,8 +82,7 @@ export function readConfig( args: ReadConfigCommandArgs, { hideWarnings = false }: { hideWarnings?: boolean } = {} ): Config { - const configPath = resolveWranglerConfigPath(args); - const rawConfig = readRawConfig(configPath); + const { rawConfig, configPath } = experimental_readRawConfig(args); const { config, diagnostics } = normalizeAndValidateConfig( rawConfig, @@ -105,11 +104,10 @@ export function readPagesConfig( args: ReadConfigCommandArgs, { hideWarnings = false }: { hideWarnings?: boolean } = {} ): Omit & { pages_build_output_dir: string } { - const configPath = resolveWranglerConfigPath(args); - let rawConfig: RawConfig; + let configPath: string | undefined; try { - rawConfig = readRawConfig(configPath); + ({ rawConfig, configPath } = experimental_readRawConfig(args)); } catch (e) { logger.error(e); throw new FatalError( @@ -158,14 +156,19 @@ export function readPagesConfig( }; } -export const readRawConfig = (configPath: string | undefined): RawConfig => { +export const experimental_readRawConfig = ( + args: ReadConfigCommandArgs +): { rawConfig: RawConfig; configPath: string | undefined } => { // Load the configuration from disk if available + const configPath = resolveWranglerConfigPath(args); + console.dir(configPath); + let rawConfig: RawConfig = {}; if (configPath?.endsWith("toml")) { - return parseTOML(readFileSync(configPath), configPath); + rawConfig = parseTOML(readFileSync(configPath), configPath); } else if (configPath?.endsWith("json") || configPath?.endsWith("jsonc")) { - return parseJSONC(readFileSync(configPath), configPath); + rawConfig = parseJSONC(readFileSync(configPath), configPath); } - return {}; + return { rawConfig, configPath }; }; function addLocalSuffix( diff --git a/packages/wrangler/src/index.ts b/packages/wrangler/src/index.ts index 1511d4da84b5..531820189ef4 100644 --- a/packages/wrangler/src/index.ts +++ b/packages/wrangler/src/index.ts @@ -10,11 +10,10 @@ import { ai } from "./ai"; import { cloudchamber } from "./cloudchamber"; import { configFileName, + experimental_readRawConfig, formatConfigSnippet, loadDotEnv, - readRawConfig, } from "./config"; -import { resolveWranglerConfigPath } from "./config/config-helpers"; import { demandSingleValue } from "./core"; import { CommandRegistry } from "./core/CommandRegistry"; import { createRegisterYargsCommand } from "./core/register-yargs-command"; @@ -1153,8 +1152,7 @@ export async function main(argv: string[]): Promise { // key to fetch) or flags try { - const configPath = resolveWranglerConfigPath(args); - const rawConfig = readRawConfig(configPath); + const { rawConfig, configPath } = experimental_readRawConfig(args); dispatcher = getMetricsDispatcher({ sendMetrics: rawConfig.send_metrics, configPath, From 922e8bee3c0153b96765ecfe71afd71e78c11a7a Mon Sep 17 00:00:00 2001 From: emily-shen <69125074+emily-shen@users.noreply.github.com> Date: Wed, 11 Dec 2024 15:51:13 +0000 Subject: [PATCH 2/5] move printBindings out of /config/index.ts --- .../wrangler/docs/how-to/add-a-binding.md | 2 +- .../src/__tests__/configuration.test.ts | 1 - .../api/startDevWorker/ConfigController.ts | 3 +- packages/wrangler/src/config/index.ts | 486 ----------------- packages/wrangler/src/deploy/deploy.ts | 3 +- .../src/deployment-bundle/bindings.ts | 2 +- packages/wrangler/src/utils/print-bindings.ts | 488 ++++++++++++++++++ packages/wrangler/src/versions/upload.ts | 3 +- 8 files changed, 496 insertions(+), 492 deletions(-) create mode 100644 packages/wrangler/src/utils/print-bindings.ts diff --git a/packages/wrangler/docs/how-to/add-a-binding.md b/packages/wrangler/docs/how-to/add-a-binding.md index 9cabb2d6d52b..669fd845a055 100644 --- a/packages/wrangler/docs/how-to/add-a-binding.md +++ b/packages/wrangler/docs/how-to/add-a-binding.md @@ -7,7 +7,7 @@ - `CfWorkerInit` in: `packages/wrangler/src/deployment-bundle/worker.ts` [ref](https://github.com/cloudflare/workers-sdk/blob/ce7db9d9cb4f5bcd5a326b86dde051cb54b999fb/packages/wrangler/src/deployment-bundle/worker.ts#L79C1-L85C2) - `WorkerMetadataBinding` in: `packages/wrangler/src/deployment-bundle/create-worker-upload-form.ts` [ref-1](https://github.com/cloudflare/workers-sdk/blob/ce7db9d9cb4f5bcd5a326b86dde051cb54b999fb/packages/wrangler/src/deployment-bundle/create-worker-upload-form.ts#L65) [ref-2](https://github.com/cloudflare/workers-sdk/blob/ce7db9d9cb4f5bcd5a326b86dde051cb54b999fb/packages/wrangler/src/deployment-bundle/create-worker-upload-form.ts#L219-L225) 1. Add type to DevEnv `Binding` union in: `packages/wrangler/src/api/startDevWorker/types.ts` [ref](https://github.com/cloudflare/workers-sdk/blob/ce7db9d9cb4f5bcd5a326b86dde051cb54b999fb/packages/wrangler/src/api/startDevWorker/types.ts#L246) -1. Add user-friendly output for `printBindings` in: `packages/wrangler/src/config/index.ts` [ref](https://github.com/cloudflare/workers-sdk/blob/ce7db9d9cb4f5bcd5a326b86dde051cb54b999fb/packages/wrangler/src/config/index.ts#L270-L280) +1. Add user-friendly output for `printBindings` in: `packages/wrangler/src/utils/print-bindings.ts` [ref](https://github.com/cloudflare/workers-sdk/blob/ce7db9d9cb4f5bcd5a326b86dde051cb54b999fb/packages/wrangler/src/utils/print-bindings.ts) 1. Add mapping functions to: - `createWorkerUploadForm` in: `packages/wrangler/src/deployment-bundle/create-worker-upload-form.ts` [ref](https://github.com/cloudflare/workers-sdk/blob/ce7db9d9cb4f5bcd5a326b86dde051cb54b999fb/packages/wrangler/src/deployment-bundle/create-worker-upload-form.ts#L219-L225) - `convertCfWorkerInitBindingstoBindings` in: `packages/wrangler/src/api/startDevWorker/utils.ts` [ref](https://github.com/cloudflare/workers-sdk/blob/ce7db9d9cb4f5bcd5a326b86dde051cb54b999fb/packages/wrangler/src/api/startDevWorker/utils.ts#L118-L123) diff --git a/packages/wrangler/src/__tests__/configuration.test.ts b/packages/wrangler/src/__tests__/configuration.test.ts index 0cf1b1a8c770..01d2b05185ef 100644 --- a/packages/wrangler/src/__tests__/configuration.test.ts +++ b/packages/wrangler/src/__tests__/configuration.test.ts @@ -5,7 +5,6 @@ import { normalizeAndValidateConfig } from "../config/validation"; import { run } from "../experimental-flags"; import { normalizeString } from "./helpers/normalize"; import { runInTempDir } from "./helpers/run-in-tmp"; -import { writeWorkerSource } from "./helpers/write-worker-source"; import { writeWranglerConfig } from "./helpers/write-wrangler-config"; import type { ConfigFields, diff --git a/packages/wrangler/src/api/startDevWorker/ConfigController.ts b/packages/wrangler/src/api/startDevWorker/ConfigController.ts index 2394f59b0450..bbd8d7ae192e 100644 --- a/packages/wrangler/src/api/startDevWorker/ConfigController.ts +++ b/packages/wrangler/src/api/startDevWorker/ConfigController.ts @@ -10,7 +10,7 @@ import { isLegacyEnv, } from "../.."; import { getAssetsOptions, validateAssetsArgsAndConfig } from "../../assets"; -import { printBindings, readConfig } from "../../config"; +import { readConfig } from "../../config"; import { getEntry } from "../../deployment-bundle/entry"; import { getBindings, @@ -24,6 +24,7 @@ import { UserError } from "../../errors"; import { logger } from "../../logger"; import { requireApiToken, requireAuth } from "../../user"; import { memoizeGetPort } from "../../utils/memoizeGetPort"; +import { printBindings } from "../../utils/print-bindings"; import { getZoneIdForPreview } from "../../zones"; import { Controller } from "./BaseController"; import { castErrorCause } from "./events"; diff --git a/packages/wrangler/src/config/index.ts b/packages/wrangler/src/config/index.ts index cec4953a44f4..380ffd419d1b 100644 --- a/packages/wrangler/src/config/index.ts +++ b/packages/wrangler/src/config/index.ts @@ -1,17 +1,13 @@ import fs from "node:fs"; import TOML from "@iarna/toml"; -import chalk from "chalk"; import dotenv from "dotenv"; import { FatalError, UserError } from "../errors"; -import { getFlag } from "../experimental-flags"; import { logger } from "../logger"; import { EXIT_CODE_INVALID_PAGES_CONFIG } from "../pages/errors"; import { parseJSONC, parseTOML, readFileSync } from "../parse"; import { resolveWranglerConfigPath } from "./config-helpers"; import { isPagesConfig, normalizeAndValidateConfig } from "./validation"; import { validatePagesConfig } from "./validation-pages"; -import type { CfWorkerInit } from "../deployment-bundle/worker"; -import type { WorkerRegistry } from "../dev-registry"; import type { CommonYargsOptions } from "../yargs-types"; import type { Config, OnlyCamelCase, RawConfig } from "./config"; import type { NormalizeAndValidateConfigArgs } from "./validation"; @@ -171,488 +167,6 @@ export const experimental_readRawConfig = ( return { rawConfig, configPath }; }; -function addLocalSuffix( - id: string | symbol | undefined, - local: boolean = false -) { - if (id === undefined || typeof id === "symbol") { - id = ""; - } - return `${id}${local ? " (local)" : ""}`; -} - -export const friendlyBindingNames: Record< - keyof CfWorkerInit["bindings"], - string -> = { - data_blobs: "Data Blobs", - durable_objects: "Durable Objects", - kv_namespaces: "KV Namespaces", - send_email: "Send Email", - queues: "Queues", - d1_databases: "D1 Databases", - vectorize: "Vectorize Indexes", - hyperdrive: "Hyperdrive Configs", - r2_buckets: "R2 Buckets", - logfwdr: "logfwdr", - services: "Services", - analytics_engine_datasets: "Analytics Engine Datasets", - text_blobs: "Text Blobs", - browser: "Browser", - ai: "AI", - version_metadata: "Worker Version Metadata", - unsafe: "Unsafe Metadata", - vars: "Vars", - wasm_modules: "Wasm Modules", - dispatch_namespaces: "Dispatch Namespaces", - mtls_certificates: "mTLS Certificates", - workflows: "Workflows", - pipelines: "Pipelines", - assets: "Assets", -} as const; - -/** - * Print all the bindings a worker using a given config would have access to - */ -export function printBindings( - bindings: Partial, - context: { - registry?: WorkerRegistry | null; - local?: boolean; - name?: string; - provisioning?: boolean; - } = {} -) { - let hasConnectionStatus = false; - const truncate = (item: string | Record) => { - const s = typeof item === "string" ? item : JSON.stringify(item); - const maxLength = 40; - if (s.length < maxLength) { - return s; - } - - return `${s.substring(0, maxLength - 3)}...`; - }; - - const output: { - name: string; - entries: { key: string; value: string | boolean }[]; - }[] = []; - - const { - data_blobs, - durable_objects, - workflows, - kv_namespaces, - send_email, - queues, - d1_databases, - vectorize, - hyperdrive, - r2_buckets, - logfwdr, - services, - analytics_engine_datasets, - text_blobs, - browser, - ai, - version_metadata, - unsafe, - vars, - wasm_modules, - dispatch_namespaces, - mtls_certificates, - pipelines, - } = bindings; - - if (data_blobs !== undefined && Object.keys(data_blobs).length > 0) { - output.push({ - name: friendlyBindingNames.data_blobs, - entries: Object.entries(data_blobs).map(([key, value]) => ({ - key, - value: typeof value === "string" ? truncate(value) : "", - })), - }); - } - - if (durable_objects !== undefined && durable_objects.bindings.length > 0) { - output.push({ - name: friendlyBindingNames.durable_objects, - entries: durable_objects.bindings.map( - ({ name, class_name, script_name }) => { - let value = class_name; - if (script_name) { - if (context.local && context.registry !== null) { - const registryDefinition = context.registry?.[script_name]; - - hasConnectionStatus = true; - if ( - registryDefinition && - registryDefinition.durableObjects.some( - (d) => d.className === class_name - ) - ) { - value += ` (defined in ${script_name} ${chalk.green("[connected]")})`; - } else { - value += ` (defined in ${script_name} ${chalk.red("[not connected]")})`; - } - } else { - value += ` (defined in ${script_name})`; - } - } - - return { - key: name, - value, - }; - } - ), - }); - } - - if (workflows !== undefined && workflows.length > 0) { - output.push({ - name: friendlyBindingNames.workflows, - entries: workflows.map(({ class_name, script_name, binding }) => { - let value = class_name; - if (script_name) { - value += ` (defined in ${script_name})`; - } - - return { - key: binding, - value, - }; - }), - }); - } - - if (kv_namespaces !== undefined && kv_namespaces.length > 0) { - output.push({ - name: friendlyBindingNames.kv_namespaces, - entries: kv_namespaces.map(({ binding, id }) => { - return { - key: binding, - value: addLocalSuffix(id, context.local), - }; - }), - }); - } - - if (send_email !== undefined && send_email.length > 0) { - output.push({ - name: friendlyBindingNames.send_email, - entries: send_email.map( - ({ name, destination_address, allowed_destination_addresses }) => { - return { - key: name, - value: - destination_address || - allowed_destination_addresses?.join(", ") || - "unrestricted", - }; - } - ), - }); - } - - if (queues !== undefined && queues.length > 0) { - output.push({ - name: friendlyBindingNames.queues, - entries: queues.map(({ binding, queue_name }) => { - return { - key: binding, - value: addLocalSuffix(queue_name, context.local), - }; - }), - }); - } - - if (d1_databases !== undefined && d1_databases.length > 0) { - output.push({ - name: friendlyBindingNames.d1_databases, - entries: d1_databases.map( - ({ binding, database_name, database_id, preview_database_id }) => { - const remoteDatabaseId = - typeof database_id === "string" ? database_id : null; - let databaseValue = - remoteDatabaseId && database_name - ? `${database_name} (${remoteDatabaseId})` - : remoteDatabaseId ?? database_name; - - //database_id is local when running `wrangler dev --local` - if (preview_database_id && database_id !== "local") { - databaseValue = `${databaseValue ? `${databaseValue}, ` : ""}Preview: (${preview_database_id})`; - } - return { - key: binding, - value: addLocalSuffix(databaseValue, context.local), - }; - } - ), - }); - } - - if (vectorize !== undefined && vectorize.length > 0) { - output.push({ - name: friendlyBindingNames.vectorize, - entries: vectorize.map(({ binding, index_name }) => { - return { - key: binding, - value: addLocalSuffix(index_name, context.local), - }; - }), - }); - } - - if (hyperdrive !== undefined && hyperdrive.length > 0) { - output.push({ - name: friendlyBindingNames.hyperdrive, - entries: hyperdrive.map(({ binding, id }) => { - return { - key: binding, - value: addLocalSuffix(id, context.local), - }; - }), - }); - } - - if (r2_buckets !== undefined && r2_buckets.length > 0) { - output.push({ - name: friendlyBindingNames.r2_buckets, - entries: r2_buckets.map(({ binding, bucket_name, jurisdiction }) => { - let name = typeof bucket_name === "string" ? bucket_name : ""; - - if (jurisdiction !== undefined) { - name += ` (${jurisdiction})`; - } - - return { - key: binding, - value: addLocalSuffix(name, context.local), - }; - }), - }); - } - - if (logfwdr !== undefined && logfwdr.bindings.length > 0) { - output.push({ - name: friendlyBindingNames.logfwdr, - entries: logfwdr.bindings.map((binding) => { - return { - key: binding.name, - value: binding.destination, - }; - }), - }); - } - - if (services !== undefined && services.length > 0) { - output.push({ - name: friendlyBindingNames.services, - entries: services.map(({ binding, service, entrypoint }) => { - let value = service; - if (entrypoint) { - value += `#${entrypoint}`; - } - - if (context.local && context.registry !== null) { - const registryDefinition = context.registry?.[service]; - hasConnectionStatus = true; - - if ( - registryDefinition && - (!entrypoint || - registryDefinition.entrypointAddresses?.[entrypoint]) - ) { - value = value + " " + chalk.green("[connected]"); - } else { - value = value + " " + chalk.red("[not connected]"); - } - } - return { - key: binding, - value, - }; - }), - }); - } - - if ( - analytics_engine_datasets !== undefined && - analytics_engine_datasets.length > 0 - ) { - output.push({ - name: friendlyBindingNames.analytics_engine_datasets, - entries: analytics_engine_datasets.map(({ binding, dataset }) => { - return { - key: binding, - value: dataset ?? binding, - }; - }), - }); - } - - if (text_blobs !== undefined && Object.keys(text_blobs).length > 0) { - output.push({ - name: friendlyBindingNames.text_blobs, - entries: Object.entries(text_blobs).map(([key, value]) => ({ - key, - value: truncate(value), - })), - }); - } - - if (browser !== undefined) { - output.push({ - name: friendlyBindingNames.browser, - entries: [{ key: "Name", value: browser.binding }], - }); - } - - if (ai !== undefined) { - const entries: [{ key: string; value: string | boolean }] = [ - { key: "Name", value: ai.binding }, - ]; - if (ai.staging) { - entries.push({ key: "Staging", value: ai.staging }); - } - - output.push({ - name: friendlyBindingNames.ai, - entries: entries, - }); - } - - if (pipelines?.length) { - output.push({ - name: friendlyBindingNames.pipelines, - entries: pipelines.map(({ binding, pipeline }) => ({ - key: binding, - value: pipeline, - })), - }); - } - - if (version_metadata !== undefined) { - output.push({ - name: friendlyBindingNames.version_metadata, - entries: [{ key: "Name", value: version_metadata.binding }], - }); - } - - if (unsafe?.bindings !== undefined && unsafe.bindings.length > 0) { - output.push({ - name: friendlyBindingNames.unsafe, - entries: unsafe.bindings.map(({ name, type }) => ({ - key: type, - value: name, - })), - }); - } - - if (vars !== undefined && Object.keys(vars).length > 0) { - output.push({ - name: friendlyBindingNames.vars, - entries: Object.entries(vars).map(([key, value]) => { - let parsedValue; - if (typeof value === "string") { - parsedValue = `"${truncate(value)}"`; - } else if (typeof value === "object") { - parsedValue = JSON.stringify(value, null, 1); - } else { - parsedValue = `${truncate(`${value}`)}`; - } - return { - key, - value: parsedValue, - }; - }), - }); - } - - if (wasm_modules !== undefined && Object.keys(wasm_modules).length > 0) { - output.push({ - name: friendlyBindingNames.wasm_modules, - entries: Object.entries(wasm_modules).map(([key, value]) => ({ - key, - value: typeof value === "string" ? truncate(value) : "", - })), - }); - } - - if (dispatch_namespaces !== undefined && dispatch_namespaces.length > 0) { - output.push({ - name: friendlyBindingNames.dispatch_namespaces, - entries: dispatch_namespaces.map(({ binding, namespace, outbound }) => { - return { - key: binding, - value: outbound - ? `${namespace} (outbound -> ${outbound.service})` - : namespace, - }; - }), - }); - } - - if (mtls_certificates !== undefined && mtls_certificates.length > 0) { - output.push({ - name: friendlyBindingNames.mtls_certificates, - entries: mtls_certificates.map(({ binding, certificate_id }) => { - return { - key: binding, - value: certificate_id, - }; - }), - }); - } - - if (unsafe?.metadata !== undefined) { - output.push({ - name: friendlyBindingNames.unsafe, - entries: Object.entries(unsafe.metadata).map(([key, value]) => ({ - key, - value: JSON.stringify(value), - })), - }); - } - - if (output.length === 0) { - return; - } - - let title: string; - if (context.provisioning) { - title = "The following bindings need to be provisioned:"; - } else if (context.name && getFlag("MULTIWORKER")) { - title = `${chalk.blue(context.name)} has access to the following bindings:`; - } else { - title = "Your worker has access to the following bindings:"; - } - - const message = [ - title, - ...output - .map((bindingGroup) => { - return [ - `- ${bindingGroup.name}:`, - bindingGroup.entries.map( - ({ key, value }) => ` - ${key}${value ? ":" : ""} ${value}` - ), - ]; - }) - .flat(2), - ].join("\n"); - - logger.log(message); - - if (hasConnectionStatus) { - logger.once.info( - `\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` - ); - } -} - export function withConfig( handler: ( args: OnlyCamelCase & { config: Config } diff --git a/packages/wrangler/src/deploy/deploy.ts b/packages/wrangler/src/deploy/deploy.ts index a5e3c88c25bd..80f35354e5b8 100644 --- a/packages/wrangler/src/deploy/deploy.ts +++ b/packages/wrangler/src/deploy/deploy.ts @@ -5,7 +5,7 @@ import { URLSearchParams } from "node:url"; import { cancel } from "@cloudflare/cli"; import { syncAssets } from "../assets"; import { fetchListResult, fetchResult } from "../cfetch"; -import { configFileName, formatConfigSnippet, printBindings } from "../config"; +import { configFileName, formatConfigSnippet } from "../config"; import { getBindings, provisionBindings } from "../deployment-bundle/bindings"; import { bundleWorker } from "../deployment-bundle/bundle"; import { @@ -47,6 +47,7 @@ import { maybeRetrieveFileSourceMap, } from "../sourcemap"; import triggersDeploy from "../triggers/deploy"; +import { printBindings } from "../utils/print-bindings"; import { retryOnError } from "../utils/retry"; import { createDeployment, diff --git a/packages/wrangler/src/deployment-bundle/bindings.ts b/packages/wrangler/src/deployment-bundle/bindings.ts index 1773c2041c8b..a9a90516564d 100644 --- a/packages/wrangler/src/deployment-bundle/bindings.ts +++ b/packages/wrangler/src/deployment-bundle/bindings.ts @@ -1,7 +1,6 @@ import chalk from "chalk"; import { isLegacyEnv } from ".."; import { fetchResult } from "../cfetch"; -import { printBindings } from "../config"; import { createD1Database } from "../d1/create"; import { listDatabases } from "../d1/list"; import { prompt, select } from "../dialogs"; @@ -9,6 +8,7 @@ import { FatalError, UserError } from "../errors"; import { createKVNamespace, listKVNamespaces } from "../kv/helpers"; import { logger } from "../logger"; import { createR2Bucket, listR2Buckets } from "../r2/helpers"; +import { printBindings } from "../utils/print-bindings"; import type { Config } from "../config"; import type { WorkerMetadataBinding } from "./create-worker-upload-form"; import type { diff --git a/packages/wrangler/src/utils/print-bindings.ts b/packages/wrangler/src/utils/print-bindings.ts new file mode 100644 index 000000000000..9d42c24e42aa --- /dev/null +++ b/packages/wrangler/src/utils/print-bindings.ts @@ -0,0 +1,488 @@ +import chalk from "chalk"; +import { getFlag } from "../experimental-flags"; +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 +> = { + data_blobs: "Data Blobs", + durable_objects: "Durable Objects", + kv_namespaces: "KV Namespaces", + send_email: "Send Email", + queues: "Queues", + d1_databases: "D1 Databases", + vectorize: "Vectorize Indexes", + hyperdrive: "Hyperdrive Configs", + r2_buckets: "R2 Buckets", + logfwdr: "logfwdr", + services: "Services", + analytics_engine_datasets: "Analytics Engine Datasets", + text_blobs: "Text Blobs", + browser: "Browser", + ai: "AI", + version_metadata: "Worker Version Metadata", + unsafe: "Unsafe Metadata", + vars: "Vars", + wasm_modules: "Wasm Modules", + dispatch_namespaces: "Dispatch Namespaces", + mtls_certificates: "mTLS Certificates", + workflows: "Workflows", + pipelines: "Pipelines", + assets: "Assets", +} as const; + +/** + * Print all the bindings a worker using a given config would have access to + */ +export function printBindings( + bindings: Partial, + context: { + registry?: WorkerRegistry | null; + local?: boolean; + name?: string; + provisioning?: boolean; + } = {} +) { + let hasConnectionStatus = false; + const truncate = (item: string | Record) => { + const s = typeof item === "string" ? item : JSON.stringify(item); + const maxLength = 40; + if (s.length < maxLength) { + return s; + } + + return `${s.substring(0, maxLength - 3)}...`; + }; + + const output: { + name: string; + entries: { key: string; value: string | boolean }[]; + }[] = []; + + const { + data_blobs, + durable_objects, + workflows, + kv_namespaces, + send_email, + queues, + d1_databases, + vectorize, + hyperdrive, + r2_buckets, + logfwdr, + services, + analytics_engine_datasets, + text_blobs, + browser, + ai, + version_metadata, + unsafe, + vars, + wasm_modules, + dispatch_namespaces, + mtls_certificates, + pipelines, + } = bindings; + + if (data_blobs !== undefined && Object.keys(data_blobs).length > 0) { + output.push({ + name: friendlyBindingNames.data_blobs, + entries: Object.entries(data_blobs).map(([key, value]) => ({ + key, + value: typeof value === "string" ? truncate(value) : "", + })), + }); + } + + if (durable_objects !== undefined && durable_objects.bindings.length > 0) { + output.push({ + name: friendlyBindingNames.durable_objects, + entries: durable_objects.bindings.map( + ({ name, class_name, script_name }) => { + let value = class_name; + if (script_name) { + if (context.local && context.registry !== null) { + const registryDefinition = context.registry?.[script_name]; + + hasConnectionStatus = true; + if ( + registryDefinition && + registryDefinition.durableObjects.some( + (d) => d.className === class_name + ) + ) { + value += ` (defined in ${script_name} ${chalk.green("[connected]")})`; + } else { + value += ` (defined in ${script_name} ${chalk.red("[not connected]")})`; + } + } else { + value += ` (defined in ${script_name})`; + } + } + + return { + key: name, + value, + }; + } + ), + }); + } + + if (workflows !== undefined && workflows.length > 0) { + output.push({ + name: friendlyBindingNames.workflows, + entries: workflows.map(({ class_name, script_name, binding }) => { + let value = class_name; + if (script_name) { + value += ` (defined in ${script_name})`; + } + + return { + key: binding, + value, + }; + }), + }); + } + + if (kv_namespaces !== undefined && kv_namespaces.length > 0) { + output.push({ + name: friendlyBindingNames.kv_namespaces, + entries: kv_namespaces.map(({ binding, id }) => { + return { + key: binding, + value: addLocalSuffix(id, context.local), + }; + }), + }); + } + + if (send_email !== undefined && send_email.length > 0) { + output.push({ + name: friendlyBindingNames.send_email, + entries: send_email.map( + ({ name, destination_address, allowed_destination_addresses }) => { + return { + key: name, + value: + destination_address || + allowed_destination_addresses?.join(", ") || + "unrestricted", + }; + } + ), + }); + } + + if (queues !== undefined && queues.length > 0) { + output.push({ + name: friendlyBindingNames.queues, + entries: queues.map(({ binding, queue_name }) => { + return { + key: binding, + value: addLocalSuffix(queue_name, context.local), + }; + }), + }); + } + + if (d1_databases !== undefined && d1_databases.length > 0) { + output.push({ + name: friendlyBindingNames.d1_databases, + entries: d1_databases.map( + ({ binding, database_name, database_id, preview_database_id }) => { + const remoteDatabaseId = + typeof database_id === "string" ? database_id : null; + let databaseValue = + remoteDatabaseId && database_name + ? `${database_name} (${remoteDatabaseId})` + : remoteDatabaseId ?? database_name; + + //database_id is local when running `wrangler dev --local` + if (preview_database_id && database_id !== "local") { + databaseValue = `${databaseValue ? `${databaseValue}, ` : ""}Preview: (${preview_database_id})`; + } + return { + key: binding, + value: addLocalSuffix(databaseValue, context.local), + }; + } + ), + }); + } + + if (vectorize !== undefined && vectorize.length > 0) { + output.push({ + name: friendlyBindingNames.vectorize, + entries: vectorize.map(({ binding, index_name }) => { + return { + key: binding, + value: addLocalSuffix(index_name, context.local), + }; + }), + }); + } + + if (hyperdrive !== undefined && hyperdrive.length > 0) { + output.push({ + name: friendlyBindingNames.hyperdrive, + entries: hyperdrive.map(({ binding, id }) => { + return { + key: binding, + value: addLocalSuffix(id, context.local), + }; + }), + }); + } + + if (r2_buckets !== undefined && r2_buckets.length > 0) { + output.push({ + name: friendlyBindingNames.r2_buckets, + entries: r2_buckets.map(({ binding, bucket_name, jurisdiction }) => { + let name = typeof bucket_name === "string" ? bucket_name : ""; + + if (jurisdiction !== undefined) { + name += ` (${jurisdiction})`; + } + + return { + key: binding, + value: addLocalSuffix(name, context.local), + }; + }), + }); + } + + if (logfwdr !== undefined && logfwdr.bindings.length > 0) { + output.push({ + name: friendlyBindingNames.logfwdr, + entries: logfwdr.bindings.map((binding) => { + return { + key: binding.name, + value: binding.destination, + }; + }), + }); + } + + if (services !== undefined && services.length > 0) { + output.push({ + name: friendlyBindingNames.services, + entries: services.map(({ binding, service, entrypoint }) => { + let value = service; + if (entrypoint) { + value += `#${entrypoint}`; + } + + if (context.local && context.registry !== null) { + const registryDefinition = context.registry?.[service]; + hasConnectionStatus = true; + + if ( + registryDefinition && + (!entrypoint || + registryDefinition.entrypointAddresses?.[entrypoint]) + ) { + value = value + " " + chalk.green("[connected]"); + } else { + value = value + " " + chalk.red("[not connected]"); + } + } + return { + key: binding, + value, + }; + }), + }); + } + + if ( + analytics_engine_datasets !== undefined && + analytics_engine_datasets.length > 0 + ) { + output.push({ + name: friendlyBindingNames.analytics_engine_datasets, + entries: analytics_engine_datasets.map(({ binding, dataset }) => { + return { + key: binding, + value: dataset ?? binding, + }; + }), + }); + } + + if (text_blobs !== undefined && Object.keys(text_blobs).length > 0) { + output.push({ + name: friendlyBindingNames.text_blobs, + entries: Object.entries(text_blobs).map(([key, value]) => ({ + key, + value: truncate(value), + })), + }); + } + + if (browser !== undefined) { + output.push({ + name: friendlyBindingNames.browser, + entries: [{ key: "Name", value: browser.binding }], + }); + } + + if (ai !== undefined) { + const entries: [{ key: string; value: string | boolean }] = [ + { key: "Name", value: ai.binding }, + ]; + if (ai.staging) { + entries.push({ key: "Staging", value: ai.staging }); + } + + output.push({ + name: friendlyBindingNames.ai, + entries: entries, + }); + } + + if (pipelines?.length) { + output.push({ + name: friendlyBindingNames.pipelines, + entries: pipelines.map(({ binding, pipeline }) => ({ + key: binding, + value: pipeline, + })), + }); + } + + if (version_metadata !== undefined) { + output.push({ + name: friendlyBindingNames.version_metadata, + entries: [{ key: "Name", value: version_metadata.binding }], + }); + } + + if (unsafe?.bindings !== undefined && unsafe.bindings.length > 0) { + output.push({ + name: friendlyBindingNames.unsafe, + entries: unsafe.bindings.map(({ name, type }) => ({ + key: type, + value: name, + })), + }); + } + + if (vars !== undefined && Object.keys(vars).length > 0) { + output.push({ + name: friendlyBindingNames.vars, + entries: Object.entries(vars).map(([key, value]) => { + let parsedValue; + if (typeof value === "string") { + parsedValue = `"${truncate(value)}"`; + } else if (typeof value === "object") { + parsedValue = JSON.stringify(value, null, 1); + } else { + parsedValue = `${truncate(`${value}`)}`; + } + return { + key, + value: parsedValue, + }; + }), + }); + } + + if (wasm_modules !== undefined && Object.keys(wasm_modules).length > 0) { + output.push({ + name: friendlyBindingNames.wasm_modules, + entries: Object.entries(wasm_modules).map(([key, value]) => ({ + key, + value: typeof value === "string" ? truncate(value) : "", + })), + }); + } + + if (dispatch_namespaces !== undefined && dispatch_namespaces.length > 0) { + output.push({ + name: friendlyBindingNames.dispatch_namespaces, + entries: dispatch_namespaces.map(({ binding, namespace, outbound }) => { + return { + key: binding, + value: outbound + ? `${namespace} (outbound -> ${outbound.service})` + : namespace, + }; + }), + }); + } + + if (mtls_certificates !== undefined && mtls_certificates.length > 0) { + output.push({ + name: friendlyBindingNames.mtls_certificates, + entries: mtls_certificates.map(({ binding, certificate_id }) => { + return { + key: binding, + value: certificate_id, + }; + }), + }); + } + + if (unsafe?.metadata !== undefined) { + output.push({ + name: friendlyBindingNames.unsafe, + entries: Object.entries(unsafe.metadata).map(([key, value]) => ({ + key, + value: JSON.stringify(value), + })), + }); + } + + if (output.length === 0) { + return; + } + + let title: string; + if (context.provisioning) { + title = "The following bindings need to be provisioned:"; + } else if (context.name && getFlag("MULTIWORKER")) { + title = `${chalk.blue(context.name)} has access to the following bindings:`; + } else { + title = "Your worker has access to the following bindings:"; + } + + const message = [ + title, + ...output + .map((bindingGroup) => { + return [ + `- ${bindingGroup.name}:`, + bindingGroup.entries.map( + ({ key, value }) => ` - ${key}${value ? ":" : ""} ${value}` + ), + ]; + }) + .flat(2), + ].join("\n"); + + logger.log(message); + + if (hasConnectionStatus) { + logger.once.info( + `\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` + ); + } +} diff --git a/packages/wrangler/src/versions/upload.ts b/packages/wrangler/src/versions/upload.ts index 36dce73addea..94f6d2e2c56f 100644 --- a/packages/wrangler/src/versions/upload.ts +++ b/packages/wrangler/src/versions/upload.ts @@ -8,7 +8,7 @@ import { validateAssetsArgsAndConfig, } from "../assets"; import { fetchResult } from "../cfetch"; -import { configFileName, formatConfigSnippet, printBindings } from "../config"; +import { configFileName, formatConfigSnippet } from "../config"; import { createCommand } from "../core/create-command"; import { getBindings } from "../deployment-bundle/bindings"; import { bundleWorker } from "../deployment-bundle/bundle"; @@ -50,6 +50,7 @@ import { } from "../sourcemap"; import { requireAuth } from "../user"; import { collectKeyValues } from "../utils/collectKeyValues"; +import { printBindings } from "../utils/print-bindings"; import { retryOnError } from "../utils/retry"; import type { AssetsOptions } from "../assets"; import type { Config } from "../config"; From 078650243b22aaa20a331c81d488e69b83fe6ad0 Mon Sep 17 00:00:00 2001 From: emily-shen <69125074+emily-shen@users.noreply.github.com> Date: Fri, 13 Dec 2024 12:40:43 +0000 Subject: [PATCH 3/5] fixups --- packages/wrangler/src/config/config-helpers.ts | 2 -- packages/wrangler/src/config/index.ts | 1 - packages/wrangler/src/config/validation.ts | 3 ++- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/wrangler/src/config/config-helpers.ts b/packages/wrangler/src/config/config-helpers.ts index d7b4313ea47b..a02b6a47bf26 100644 --- a/packages/wrangler/src/config/config-helpers.ts +++ b/packages/wrangler/src/config/config-helpers.ts @@ -17,7 +17,6 @@ export function resolveWranglerConfigPath({ } const leafPath = script !== undefined ? path.dirname(script) : process.cwd(); - console.dir(leafPath); return findWranglerConfig(leafPath); } @@ -28,7 +27,6 @@ export function resolveWranglerConfigPath({ export function findWranglerConfig( referencePath: string = process.cwd() ): string | undefined { - console.dir(referencePath); return ( findUpSync(`wrangler.json`, { cwd: referencePath }) ?? findUpSync(`wrangler.jsonc`, { cwd: referencePath }) ?? diff --git a/packages/wrangler/src/config/index.ts b/packages/wrangler/src/config/index.ts index 380ffd419d1b..b1b73f933ad6 100644 --- a/packages/wrangler/src/config/index.ts +++ b/packages/wrangler/src/config/index.ts @@ -157,7 +157,6 @@ export const experimental_readRawConfig = ( ): { rawConfig: RawConfig; configPath: string | undefined } => { // Load the configuration from disk if available const configPath = resolveWranglerConfigPath(args); - console.dir(configPath); let rawConfig: RawConfig = {}; if (configPath?.endsWith("toml")) { rawConfig = parseTOML(readFileSync(configPath), configPath); diff --git a/packages/wrangler/src/config/validation.ts b/packages/wrangler/src/config/validation.ts index b4f2f8343b1b..a36826ed1cc1 100644 --- a/packages/wrangler/src/config/validation.ts +++ b/packages/wrangler/src/config/validation.ts @@ -4,6 +4,7 @@ import TOML from "@iarna/toml"; import { dedent } from "ts-dedent"; import { UserError } from "../errors"; import { getFlag } from "../experimental-flags"; +import { friendlyBindingNames } from "../utils/print-bindings"; import { Diagnostics } from "./diagnostics"; import { all, @@ -32,7 +33,7 @@ import { validateRequiredProperty, validateTypedArray, } from "./validation-helpers"; -import { configFileName, formatConfigSnippet, friendlyBindingNames } from "."; +import { configFileName, formatConfigSnippet } from "."; import type { CreateApplicationRequest, UserDeploymentConfiguration, From 59e9d03ab87e765223ce22393c6a4dfc265707d0 Mon Sep 17 00:00:00 2001 From: emily-shen <69125074+emily-shen@users.noreply.github.com> Date: Tue, 17 Dec 2024 16:21:13 +0000 Subject: [PATCH 4/5] changeset --- .changeset/blue-laws-bathe.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/blue-laws-bathe.md diff --git a/.changeset/blue-laws-bathe.md b/.changeset/blue-laws-bathe.md new file mode 100644 index 000000000000..9b65d75318fc --- /dev/null +++ b/.changeset/blue-laws-bathe.md @@ -0,0 +1,7 @@ +--- +"wrangler": patch +--- + +feat: add experimental_readRawConfig() + +Adds a Wrangler API to find and read a config file From 8aecc4ea08ac72924d33d9de91bd331b96b07a46 Mon Sep 17 00:00:00 2001 From: emily-shen <69125074+emily-shen@users.noreply.github.com> Date: Tue, 17 Dec 2024 17:07:57 +0000 Subject: [PATCH 5/5] actually export --- packages/wrangler/src/cli.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/wrangler/src/cli.ts b/packages/wrangler/src/cli.ts index e9e0ae4b50bd..6da850958a54 100644 --- a/packages/wrangler/src/cli.ts +++ b/packages/wrangler/src/cli.ts @@ -58,3 +58,5 @@ const generateASSETSBinding: ( // eslint-disable-next-line @typescript-eslint/no-var-requires require("./miniflare-cli/assets").default; export { generateASSETSBinding as unstable_generateASSETSBinding }; + +export { experimental_readRawConfig } from "./config";