wrangler 2.0.29 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -177,6 +177,7 @@ async function runEsbuild({
177
177
  services: undefined,
178
178
  workerDefinitions: undefined,
179
179
  firstPartyWorkerDevFacade: undefined,
180
+ targetConsumer: "dev", // We are starting a dev server
180
181
  });
181
182
 
182
183
  return {
@@ -237,11 +238,14 @@ export async function startLocalServer({
237
238
  if (!bundle || !format) return;
238
239
 
239
240
  // port for the worker
240
- await waitForPortToBeAvailable(port, {
241
- retryPeriod: 200,
242
- timeout: 2000,
243
- abortSignal: abortController.signal,
244
- });
241
+
242
+ if (port !== 0) {
243
+ await waitForPortToBeAvailable(port, {
244
+ retryPeriod: 200,
245
+ timeout: 2000,
246
+ abortSignal: abortController.signal,
247
+ });
248
+ }
245
249
 
246
250
  if (bindings.services && bindings.services.length > 0) {
247
251
  throw new Error(
@@ -327,7 +331,7 @@ export async function startLocalServer({
327
331
  await registerWorker(workerName, {
328
332
  protocol: localProtocol,
329
333
  mode: "local",
330
- port,
334
+ port: message.mfPort,
331
335
  host: ip,
332
336
  durableObjects: internalDurableObjects.map((binding) => ({
333
337
  name: binding.name,
@@ -341,7 +345,7 @@ export async function startLocalServer({
341
345
  : {}),
342
346
  });
343
347
  }
344
- onReady?.();
348
+ onReady?.(ip, message.mfPort);
345
349
  }
346
350
  });
347
351
 
@@ -36,6 +36,7 @@ export function useEsbuild({
36
36
  services,
37
37
  durableObjects,
38
38
  firstPartyWorkerDevFacade,
39
+ targetConsumer,
39
40
  }: {
40
41
  entry: Entry;
41
42
  destination: string | undefined;
@@ -53,6 +54,7 @@ export function useEsbuild({
53
54
  workerDefinitions: WorkerRegistry;
54
55
  durableObjects: Config["durable_objects"];
55
56
  firstPartyWorkerDevFacade: boolean | undefined;
57
+ targetConsumer: "dev" | "publish";
56
58
  }): EsbuildBundle | undefined {
57
59
  const [bundle, setBundle] = useState<EsbuildBundle>();
58
60
  const { exit } = useApp();
@@ -116,6 +118,7 @@ export function useEsbuild({
116
118
  workerDefinitions,
117
119
  services,
118
120
  firstPartyWorkerDevFacade,
121
+ targetConsumer,
119
122
  });
120
123
 
121
124
  // Capture the `stop()` method to use as the `useEffect()` destructor.
@@ -173,6 +176,7 @@ export function useEsbuild({
173
176
  durableObjects,
174
177
  workerDefinitions,
175
178
  firstPartyWorkerDevFacade,
179
+ targetConsumer,
176
180
  ]);
177
181
  return bundle;
178
182
  }
package/src/dev.tsx CHANGED
@@ -3,7 +3,6 @@ import { watch } from "chokidar";
3
3
  import getPort from "get-port";
4
4
  import { render } from "ink";
5
5
  import React from "react";
6
- import { fetch } from "undici";
7
6
  import { findWranglerToml, printBindings, readConfig } from "./config";
8
7
  import Dev from "./dev/dev";
9
8
  import { getVarsForDev } from "./dev/dev-vars";
@@ -30,7 +29,6 @@ import type { Config } from "./config";
30
29
  import type { Route } from "./config/environment";
31
30
  import type { EnablePagesAssetsServiceBindingOptions } from "./miniflare-cli";
32
31
  import type { CfWorkerInit } from "./worker";
33
- import type { RequestInit } from "undici";
34
32
  import type { Argv, ArgumentsCamelCase } from "yargs";
35
33
 
36
34
  interface DevArgs {
@@ -69,7 +67,7 @@ interface DevArgs {
69
67
  "node-compat"?: boolean;
70
68
  "experimental-enable-local-persistence"?: boolean;
71
69
  "live-reload"?: boolean;
72
- onReady?: () => void;
70
+ onReady?: (ip: string, port: number) => void;
73
71
  logLevel?: "none" | "error" | "log" | "warn" | "debug";
74
72
  logPrefix?: string;
75
73
  showInteractiveDevSession?: boolean;
@@ -430,12 +428,6 @@ export async function startDev(args: StartDevOptions) {
430
428
  devReactElement.unmount();
431
429
  await watcher?.close();
432
430
  },
433
- fetch: async (init?: RequestInit) => {
434
- const port = args.port || config.dev.port || (await getLocalPort());
435
- const address = args.ip || config.dev.ip || "localhost";
436
-
437
- return await fetch(`http://${address}:${port}/`, init);
438
- },
439
431
  };
440
432
  } finally {
441
433
  await watcher?.close();
@@ -506,7 +498,11 @@ export async function startApiDev(args: StartDevOptions) {
506
498
  accountId: configParam.account_id || getAccountFromCache()?.id,
507
499
  assetPaths: assetPaths,
508
500
  assetsConfig: configParam.assets,
509
- port: args.port || configParam.dev.port || (await getLocalPort()),
501
+ //port can be 0, which means to use a random port
502
+ port:
503
+ args.port === 0
504
+ ? args.port
505
+ : args.port || configParam.dev.port || (await getLocalPort()),
510
506
  ip: args.ip || configParam.dev.ip,
511
507
  inspectorPort:
512
508
  args["inspector-port"] ||
@@ -544,12 +540,6 @@ export async function startApiDev(args: StartDevOptions) {
544
540
  stop: async () => {
545
541
  await devServer.stop();
546
542
  },
547
- fetch: async (init?: RequestInit) => {
548
- const port = args.port || config.dev.port || (await getLocalPort());
549
- const address = args.ip || config.dev.ip || "localhost";
550
-
551
- return await fetch(`http://${address}:${port}/`, init);
552
- },
553
543
  };
554
544
  }
555
545
  /**
package/src/dialogs.tsx CHANGED
@@ -133,3 +133,15 @@ export function select(
133
133
  );
134
134
  });
135
135
  }
136
+
137
+ export async function fromDashMessagePrompt(
138
+ deploySource: string
139
+ ): Promise<boolean | void> {
140
+ if (deploySource === "dash") {
141
+ logger.warn(
142
+ `You are about to publish a Workers Service that was last published via the Cloudflare Dashboard.
143
+ Edits that have been made via the dashboard will be overridden by your local code and config.`
144
+ );
145
+ return await confirm("Would you like to continue?");
146
+ }
147
+ }
package/src/index.tsx CHANGED
@@ -99,6 +99,9 @@ const proxy =
99
99
 
100
100
  if (proxy) {
101
101
  setGlobalDispatcher(new ProxyAgent(proxy));
102
+ logger.log(
103
+ `Proxy environment variables detected. We'll use your proxy for fetch requests.`
104
+ );
102
105
  }
103
106
 
104
107
  export function getRules(config: Config): Config["rules"] {
@@ -741,15 +744,13 @@ function createCLIParser(argv: string[]) {
741
744
  clientIp: args.ip,
742
745
  };
743
746
 
744
- const filters = translateCLICommandToFilterMessage(
745
- cliFilters,
746
- args.debug
747
- );
747
+ const filters = translateCLICommandToFilterMessage(cliFilters);
748
748
 
749
749
  const { tail, expiration, deleteTail } = await createTail(
750
750
  accountId,
751
751
  scriptName,
752
752
  filters,
753
+ args.debug,
753
754
  !isLegacyEnv(config) ? args.env : undefined
754
755
  );
755
756
 
package/src/init.ts CHANGED
@@ -5,6 +5,7 @@ import TOML from "@iarna/toml";
5
5
  import { findUp } from "find-up";
6
6
  import { version as wranglerVersion } from "../package.json";
7
7
 
8
+ import { fetchResult } from "./cfetch";
8
9
  import { fetchDashboardScript } from "./cfetch/internal";
9
10
  import { readConfig } from "./config";
10
11
  import { confirm, select } from "./dialogs";
@@ -15,6 +16,10 @@ import { parsePackageJSON, parseTOML, readFileSync } from "./parse";
15
16
  import { getBasePath } from "./paths";
16
17
  import { requireAuth } from "./user";
17
18
  import { CommandLineArgsError, printWranglerBanner } from "./index";
19
+ import type { RawConfig } from "./config";
20
+
21
+ import type { Route, SimpleRoute } from "./config/environment";
22
+ import type { WorkerMetadata } from "./create-worker-upload-form";
18
23
  import type { ConfigPath } from "./index";
19
24
  import type { Argv, ArgumentsCamelCase } from "yargs";
20
25
 
@@ -56,6 +61,53 @@ interface InitArgs {
56
61
  yes?: boolean;
57
62
  }
58
63
 
64
+ export type ServiceMetadataRes = {
65
+ id: string;
66
+ default_environment: {
67
+ environment: string;
68
+ created_on: string;
69
+ modified_on: string;
70
+ script: {
71
+ id: string;
72
+ tag: string;
73
+ etag: string;
74
+ handlers: string[];
75
+ modified_on: string;
76
+ created_on: string;
77
+ migration_tag: string;
78
+ usage_model: "bundled" | "unbound";
79
+ compatibility_date: string;
80
+ last_deployed_from?: "wrangler" | "dash" | "api";
81
+ };
82
+ };
83
+ created_on: string;
84
+ modified_on: string;
85
+ usage_model: "bundled" | "unbound";
86
+ environments: [
87
+ {
88
+ environment: string;
89
+ created_on: string;
90
+ modified_on: string;
91
+ }
92
+ ];
93
+ };
94
+
95
+ export type RawSimpleRoute = { pattern: string };
96
+ export type RawRoutes = (RawSimpleRoute | Exclude<Route, SimpleRoute>) & {
97
+ id: string;
98
+ };
99
+ export type RoutesRes = RawRoutes[];
100
+
101
+ export type CronTriggersRes = {
102
+ schedules: [
103
+ {
104
+ cron: string;
105
+ created_on: Date;
106
+ modified_on: Date;
107
+ }
108
+ ];
109
+ };
110
+
59
111
  export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
60
112
  await printWranglerBanner();
61
113
  if (args.type) {
@@ -457,17 +509,23 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
457
509
  path.join(creationDirectory, "./src/index.ts")
458
510
  );
459
511
  if (fromDashScriptName) {
512
+ logger.warn(
513
+ `After running "wrangler init --from-dash", modifying your worker via the Cloudflare dashboard is discouraged.
514
+ Edits made via the Dashboard will not be synchronized locally and will be overridden by your local code and config when you publish.`
515
+ );
460
516
  const config = readConfig(args.config as ConfigPath, args);
461
517
  const accountId = await requireAuth(config);
462
518
  await mkdir(path.join(creationDirectory, "./src"), {
463
519
  recursive: true,
464
520
  });
465
-
521
+ const serviceMetaData = await fetchResult<ServiceMetadataRes>(
522
+ `/accounts/${accountId}/workers/services/${fromDashScriptName}`
523
+ );
524
+ const defaultEnvironment =
525
+ serviceMetaData.default_environment.environment;
526
+ // I want the default environment, assuming it's the most up to date code.
466
527
  const dashScript = await fetchDashboardScript(
467
- `/accounts/${accountId}/workers/scripts/${fromDashScriptName}`,
468
- {
469
- method: "GET",
470
- }
528
+ `/accounts/${accountId}/workers/services/${fromDashScriptName}/environments/${defaultEnvironment}/content`
471
529
  );
472
530
 
473
531
  await writeFile(
@@ -480,7 +538,11 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
480
538
  justCreatedWranglerToml,
481
539
  pathToPackageJson,
482
540
  "src/index.ts",
483
- {}
541
+
542
+ (await getWorkerConfig(accountId, fromDashScriptName, {
543
+ defaultEnvironment,
544
+ environments: serviceMetaData.environments,
545
+ })) as TOML.JsonMap
484
546
  );
485
547
  } else {
486
548
  const newWorkerType = yesFlag
@@ -523,17 +585,25 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
523
585
  );
524
586
 
525
587
  if (fromDashScriptName) {
588
+ logger.warn(
589
+ `After running "wrangler init --from-dash", modifying your worker via the Cloudflare dashboard is discouraged.
590
+ Edits made via the Dashboard will not be synchronized locally and will be overridden by your local code and config when you publish.`
591
+ );
526
592
  const config = readConfig(args.config as ConfigPath, args);
527
593
  const accountId = await requireAuth(config);
528
594
  await mkdir(path.join(creationDirectory, "./src"), {
529
595
  recursive: true,
530
596
  });
531
597
 
598
+ const serviceMetaData = await fetchResult<ServiceMetadataRes>(
599
+ `/accounts/${accountId}/workers/services/${fromDashScriptName}`
600
+ );
601
+ const defaultEnvironment =
602
+ serviceMetaData.default_environment.environment;
603
+
604
+ // I want the default environment, assuming it's the most up to date code.
532
605
  const dashScript = await fetchDashboardScript(
533
- `/accounts/${accountId}/workers/scripts/${fromDashScriptName}`,
534
- {
535
- method: "GET",
536
- }
606
+ `/accounts/${accountId}/workers/services/${fromDashScriptName}/environments/${defaultEnvironment}/content`
537
607
  );
538
608
 
539
609
  await writeFile(
@@ -546,7 +616,11 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
546
616
  justCreatedWranglerToml,
547
617
  pathToPackageJson,
548
618
  "src/index.ts",
549
- {}
619
+ //? Should we have Environment argument for `wrangler init --from-dash` - Jacob
620
+ (await getWorkerConfig(accountId, fromDashScriptName, {
621
+ defaultEnvironment,
622
+ environments: serviceMetaData.environments,
623
+ })) as TOML.JsonMap
550
624
  );
551
625
  } else {
552
626
  const newWorkerType = yesFlag
@@ -620,3 +694,204 @@ async function findPath(
620
694
  });
621
695
  }
622
696
  }
697
+
698
+ async function getWorkerConfig(
699
+ accountId: string,
700
+ fromDashScriptName: string,
701
+ {
702
+ defaultEnvironment,
703
+ environments,
704
+ }: {
705
+ defaultEnvironment: string;
706
+ environments: ServiceMetadataRes["environments"];
707
+ }
708
+ ): Promise<RawConfig> {
709
+ const [bindings, routes, serviceEnvMetadata, cronTriggers] =
710
+ await Promise.all([
711
+ fetchResult<WorkerMetadata["bindings"]>(
712
+ `/accounts/${accountId}/workers/services/${fromDashScriptName}/environments/${defaultEnvironment}/bindings`
713
+ ),
714
+ fetchResult<RoutesRes>(
715
+ `/accounts/${accountId}/workers/services/${fromDashScriptName}/environments/${defaultEnvironment}/routes`
716
+ ),
717
+ fetchResult<ServiceMetadataRes["default_environment"]>(
718
+ `/accounts/${accountId}/workers/services/${fromDashScriptName}/environments/${defaultEnvironment}`
719
+ ),
720
+ fetchResult<CronTriggersRes>(
721
+ `/accounts/${accountId}/workers/scripts/${fromDashScriptName}/schedules`
722
+ ),
723
+ ]).catch((e) => {
724
+ throw new Error(
725
+ `Error Occurred ${e}: Unable to fetch bindings, routes, or services metadata from the dashboard. Please try again later.`
726
+ );
727
+ });
728
+
729
+ const mappedBindings = bindings
730
+ .filter((binding) => (binding.type as string) !== "secret_text")
731
+ // Combine the same types into {[type]: [binding]}
732
+ .reduce((configObj, binding) => {
733
+ // Some types have different names in wrangler.toml
734
+ // I want the type safety of the binding being destructured after the case narrowing the union but type is unused
735
+
736
+ switch (binding.type) {
737
+ case "plain_text":
738
+ {
739
+ configObj.vars = {
740
+ ...(configObj.vars ?? {}),
741
+ name: binding.name,
742
+ text: binding.text,
743
+ };
744
+ }
745
+ break;
746
+ case "json":
747
+ {
748
+ configObj.vars = {
749
+ ...(configObj.vars ?? {}),
750
+ name: binding.name,
751
+ json: binding.json,
752
+ };
753
+ }
754
+ break;
755
+ case "kv_namespace":
756
+ {
757
+ configObj.kv_namespaces = [
758
+ ...(configObj.kv_namespaces ?? []),
759
+ { id: binding.namespace_id, binding: binding.name },
760
+ ];
761
+ }
762
+ break;
763
+ case "durable_object_namespace":
764
+ {
765
+ configObj.durable_objects = {
766
+ bindings: [
767
+ ...(configObj.durable_objects?.bindings ?? []),
768
+ {
769
+ name: binding.name,
770
+ class_name: binding.class_name,
771
+ script_name: binding.script_name,
772
+ environment: binding.environment,
773
+ },
774
+ ],
775
+ };
776
+ }
777
+ break;
778
+ case "r2_bucket":
779
+ {
780
+ configObj.r2_buckets = [
781
+ ...(configObj.r2_buckets ?? []),
782
+ { binding: binding.name, bucket_name: binding.bucket_name },
783
+ ];
784
+ }
785
+ break;
786
+ case "service":
787
+ {
788
+ configObj.services = [
789
+ ...(configObj.services ?? []),
790
+ {
791
+ binding: binding.name,
792
+ service: binding.service,
793
+ environment: binding.environment,
794
+ },
795
+ ];
796
+ }
797
+ break;
798
+ case "namespace":
799
+ {
800
+ configObj.dispatch_namespaces = [
801
+ ...(configObj.dispatch_namespaces ?? []),
802
+ { binding: binding.name, namespace: binding.namespace },
803
+ ];
804
+ }
805
+ break;
806
+ case "logfwdr":
807
+ {
808
+ configObj.logfwdr = {
809
+ // TODO: Messaging about adding schema file path
810
+ schema: "",
811
+ bindings: [
812
+ ...(configObj.logfwdr?.bindings ?? []),
813
+ { name: binding.name, destination: binding.destination },
814
+ ],
815
+ };
816
+ }
817
+ break;
818
+ case "wasm_module":
819
+ {
820
+ configObj.wasm_modules = {
821
+ ...(configObj.wasm_modules ?? {}),
822
+ [binding.name]: binding.part,
823
+ };
824
+ }
825
+ break;
826
+ case "text_blob":
827
+ {
828
+ configObj.text_blobs = {
829
+ ...(configObj.text_blobs ?? {}),
830
+ [binding.name]: binding.part,
831
+ };
832
+ }
833
+ break;
834
+ case "data_blob":
835
+ {
836
+ configObj.data_blobs = {
837
+ ...(configObj.data_blobs ?? {}),
838
+ [binding.name]: binding.part,
839
+ };
840
+ }
841
+ break;
842
+ default: {
843
+ // If we don't know what the type is, its an unsafe binding
844
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
845
+ if (!(binding as any)?.type) break;
846
+ configObj.unsafe = {
847
+ bindings: [...(configObj.unsafe?.bindings ?? []), binding],
848
+ };
849
+ }
850
+ }
851
+
852
+ return configObj;
853
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
854
+ }, {} as RawConfig);
855
+
856
+ const durableObjectClassNames = bindings
857
+ .filter((binding) => binding.type === "durable_object_namespace")
858
+ .map(
859
+ (durableObject) => (durableObject as { class_name: string }).class_name
860
+ );
861
+
862
+ const routeOrRoutes = routes.map((rawRoute) => {
863
+ const { id: _id, ...route } = rawRoute;
864
+ if (Object.keys(route).length === 1) {
865
+ return route.pattern;
866
+ } else {
867
+ return route as Route;
868
+ }
869
+ });
870
+ const routeOrRoutesToConfig =
871
+ routeOrRoutes.length > 1
872
+ ? { routes: routeOrRoutes }
873
+ : { route: routeOrRoutes[0] };
874
+
875
+ return {
876
+ compatibility_date: serviceEnvMetadata.script.compatibility_date,
877
+ ...routeOrRoutesToConfig,
878
+ usage_model: serviceEnvMetadata.script.usage_model,
879
+ migrations: [
880
+ {
881
+ tag: serviceEnvMetadata.script.migration_tag,
882
+ new_classes: durableObjectClassNames,
883
+ },
884
+ ],
885
+ triggers: {
886
+ crons: cronTriggers.schedules.map((scheduled) => scheduled.cron),
887
+ },
888
+ env: environments
889
+ .filter((env) => env.environment !== "production")
890
+ // `env` can have multiple Environments, with different configs.
891
+ .reduce((envObj, { environment }) => {
892
+ return { ...envObj, [environment]: {} };
893
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
894
+ }, {} as RawConfig["env"]),
895
+ ...mappedBindings,
896
+ };
897
+ }