wrangler 0.0.0-e178d6fb → 0.0.0-e1c2f5b9

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.
Files changed (239) hide show
  1. package/README.md +16 -2
  2. package/bin/wrangler.js +31 -11
  3. package/miniflare-dist/index.mjs +1340 -330
  4. package/package.json +40 -13
  5. package/src/__tests__/access.test.ts +25 -0
  6. package/src/__tests__/api-dev.test.ts +238 -0
  7. package/src/__tests__/api-devregistry.test.js +64 -0
  8. package/src/__tests__/api.test.ts +80 -0
  9. package/src/__tests__/configuration.test.ts +683 -470
  10. package/src/__tests__/d1.test.ts +123 -0
  11. package/src/__tests__/delete.test.ts +263 -0
  12. package/src/__tests__/deployments.test.ts +105 -0
  13. package/src/__tests__/dev.test.tsx +453 -70
  14. package/src/__tests__/dialogs.test.tsx +40 -0
  15. package/src/__tests__/generate.test.ts +235 -0
  16. package/src/__tests__/get-host-from-url.test.ts +16 -0
  17. package/src/__tests__/helpers/mock-auth-domain.ts +20 -0
  18. package/src/__tests__/helpers/mock-cfetch.ts +92 -26
  19. package/src/__tests__/helpers/mock-console.ts +11 -2
  20. package/src/__tests__/helpers/mock-dialogs.ts +1 -1
  21. package/src/__tests__/helpers/mock-get-zone-from-host.ts +8 -0
  22. package/src/__tests__/helpers/mock-known-routes.ts +7 -0
  23. package/src/__tests__/helpers/mock-kv.ts +1 -1
  24. package/src/__tests__/helpers/mock-oauth-flow.ts +74 -53
  25. package/src/__tests__/helpers/mock-process.ts +8 -13
  26. package/src/__tests__/helpers/msw/handlers/access.ts +13 -0
  27. package/src/__tests__/helpers/msw/handlers/deployments.ts +59 -0
  28. package/src/__tests__/helpers/msw/handlers/namespaces.ts +104 -0
  29. package/src/__tests__/helpers/msw/handlers/oauth.ts +31 -0
  30. package/src/__tests__/helpers/msw/handlers/r2.ts +80 -0
  31. package/src/__tests__/helpers/msw/handlers/script.ts +75 -0
  32. package/src/__tests__/helpers/msw/handlers/user.ts +63 -0
  33. package/src/__tests__/helpers/msw/handlers/zones.ts +22 -0
  34. package/src/__tests__/helpers/msw/index.ts +21 -0
  35. package/src/__tests__/helpers/worker-scripts/child-wrangler.toml +1 -0
  36. package/src/__tests__/helpers/worker-scripts/hello-world-worker.js +5 -0
  37. package/src/__tests__/helpers/worker-scripts/hello-world-wrangler.toml +1 -0
  38. package/src/__tests__/helpers/worker-scripts/parent-worker.js +8 -0
  39. package/src/__tests__/helpers/worker-scripts/parent-wrangler.toml +5 -0
  40. package/src/__tests__/index.test.ts +103 -97
  41. package/src/__tests__/init.test.ts +1484 -382
  42. package/src/__tests__/jest.setup.ts +88 -5
  43. package/src/__tests__/kv.test.ts +655 -540
  44. package/src/__tests__/logout.test.ts +47 -0
  45. package/src/__tests__/metrics.test.ts +95 -45
  46. package/src/__tests__/middleware.scheduled.test.ts +145 -0
  47. package/src/__tests__/middleware.test.ts +750 -0
  48. package/src/__tests__/pages-deployment-tail.test.ts +884 -0
  49. package/src/__tests__/pages.test.ts +1301 -119
  50. package/src/__tests__/paths.test.ts +39 -0
  51. package/src/__tests__/publish.test.ts +2266 -1180
  52. package/src/__tests__/pubsub.test.ts +211 -88
  53. package/src/__tests__/queues.test.ts +522 -0
  54. package/src/__tests__/r2.test.ts +363 -203
  55. package/src/__tests__/secret.test.ts +261 -59
  56. package/src/__tests__/tail.test.ts +352 -151
  57. package/src/__tests__/test-old-node-version.js +3 -3
  58. package/src/__tests__/tsconfig-sanity.ts +12 -0
  59. package/src/__tests__/tsconfig.json +8 -0
  60. package/src/__tests__/tsconfig.tsbuildinfo +1 -0
  61. package/src/__tests__/type-generation.test.ts +233 -0
  62. package/src/__tests__/user.test.ts +59 -73
  63. package/src/__tests__/utils-collectKeyValues.test.ts +47 -0
  64. package/src/__tests__/validate-dev-props.test.ts +56 -0
  65. package/src/__tests__/whoami.test.tsx +114 -66
  66. package/src/__tests__/worker-namespace.test.ts +154 -155
  67. package/src/api/dev.ts +170 -38
  68. package/src/api/index.ts +1 -0
  69. package/src/bundle-reporter.tsx +41 -2
  70. package/src/bundle.ts +668 -81
  71. package/src/cfetch/index.ts +40 -3
  72. package/src/cfetch/internal.ts +144 -32
  73. package/src/cli.ts +4 -3
  74. package/src/config/config.ts +27 -3
  75. package/src/config/environment.ts +131 -7
  76. package/src/config/index.ts +128 -8
  77. package/src/config/validation-helpers.ts +19 -6
  78. package/src/config/validation.ts +411 -14
  79. package/src/config-cache.ts +3 -2
  80. package/src/create-worker-preview.ts +31 -6
  81. package/src/create-worker-upload-form.ts +84 -22
  82. package/src/d1/backups.tsx +223 -0
  83. package/src/d1/constants.ts +2 -0
  84. package/src/d1/create.tsx +63 -0
  85. package/src/d1/delete.tsx +56 -0
  86. package/src/d1/execute.tsx +352 -0
  87. package/src/d1/formatTimeAgo.ts +14 -0
  88. package/src/d1/index.ts +101 -0
  89. package/src/d1/list.tsx +48 -0
  90. package/src/d1/migrations/apply.tsx +196 -0
  91. package/src/d1/migrations/create.tsx +77 -0
  92. package/src/d1/migrations/helpers.ts +147 -0
  93. package/src/d1/migrations/index.tsx +3 -0
  94. package/src/d1/migrations/list.tsx +79 -0
  95. package/src/d1/migrations/options.ts +18 -0
  96. package/src/d1/options.ts +22 -0
  97. package/src/d1/types.tsx +24 -0
  98. package/src/d1/utils.ts +48 -0
  99. package/src/delete.ts +108 -0
  100. package/src/deployments.ts +83 -0
  101. package/src/deprecated/index.ts +145 -0
  102. package/src/dev/dev-vars.ts +11 -8
  103. package/src/dev/dev.tsx +220 -58
  104. package/src/dev/get-local-persistence-path.tsx +31 -0
  105. package/src/dev/local.tsx +740 -181
  106. package/src/dev/remote.tsx +340 -123
  107. package/src/dev/start-server.ts +543 -0
  108. package/src/dev/use-esbuild.ts +61 -7
  109. package/src/dev/validate-dev-props.ts +31 -0
  110. package/src/dev-registry.tsx +201 -0
  111. package/src/dev.tsx +614 -258
  112. package/src/dialogs.tsx +41 -8
  113. package/src/{worker-namespace.ts → dispatch-namespace.ts} +23 -22
  114. package/src/docs/index.ts +97 -0
  115. package/src/entry.ts +3 -2
  116. package/src/environment-variables/factory.ts +88 -0
  117. package/src/environment-variables/misc-variables.ts +30 -0
  118. package/src/generate/index.ts +300 -0
  119. package/src/git-client.ts +100 -7
  120. package/src/index.tsx +249 -1560
  121. package/src/init.ts +626 -150
  122. package/src/inspect.ts +137 -32
  123. package/src/{metrics/is-ci.ts → is-ci.ts} +0 -0
  124. package/src/is-interactive.ts +4 -0
  125. package/src/jest.d.ts +4 -0
  126. package/src/{kv.ts → kv/helpers.ts} +3 -3
  127. package/src/kv/index.ts +596 -0
  128. package/src/logger.ts +27 -8
  129. package/src/metrics/index.ts +1 -0
  130. package/src/metrics/metrics-config.ts +4 -4
  131. package/src/metrics/metrics-dispatcher.ts +1 -0
  132. package/src/metrics/metrics-usage-headers.ts +24 -0
  133. package/src/metrics/send-event.ts +13 -8
  134. package/src/miniflare-cli/assets.ts +172 -465
  135. package/src/miniflare-cli/index.ts +149 -24
  136. package/src/miniflare-cli/tsconfig.json +9 -0
  137. package/src/miniflare-cli/tsconfig.tsbuildinfo +1 -0
  138. package/src/miniflare-cli/types.ts +11 -0
  139. package/src/module-collection.ts +12 -3
  140. package/src/pages/build.tsx +108 -42
  141. package/src/pages/constants.ts +11 -0
  142. package/src/pages/deployment-tails.tsx +284 -0
  143. package/src/pages/deployments.tsx +9 -31
  144. package/src/pages/dev.tsx +417 -108
  145. package/src/pages/errors.ts +66 -0
  146. package/src/pages/functions/buildPlugin.ts +86 -84
  147. package/src/pages/functions/buildWorker.ts +121 -106
  148. package/src/pages/functions/routes-consolidation.test.ts +250 -0
  149. package/src/pages/functions/routes-consolidation.ts +73 -0
  150. package/src/pages/functions/routes-transformation.test.ts +282 -0
  151. package/src/pages/functions/routes-transformation.ts +115 -0
  152. package/src/pages/functions/routes-validation.test.ts +403 -0
  153. package/src/pages/functions/routes-validation.ts +202 -0
  154. package/src/pages/functions/tsconfig.json +8 -0
  155. package/src/pages/functions/tsconfig.tsbuildinfo +1 -0
  156. package/src/pages/functions.tsx +82 -0
  157. package/src/pages/hash.tsx +13 -0
  158. package/src/pages/index.tsx +73 -55
  159. package/src/pages/projects.tsx +9 -3
  160. package/src/pages/prompt-select-project.tsx +31 -0
  161. package/src/pages/publish.tsx +183 -60
  162. package/src/pages/types.ts +1 -0
  163. package/src/pages/upload.tsx +107 -56
  164. package/src/pages/utils.ts +13 -2
  165. package/src/paths.ts +49 -4
  166. package/src/proxy.ts +375 -157
  167. package/src/publish/index.ts +270 -0
  168. package/src/{publish.ts → publish/publish.ts} +431 -188
  169. package/src/pubsub/pubsub-commands.tsx +4 -3
  170. package/src/queues/cli/commands/consumer/add.ts +71 -0
  171. package/src/queues/cli/commands/consumer/index.ts +22 -0
  172. package/src/queues/cli/commands/consumer/remove.ts +38 -0
  173. package/src/queues/cli/commands/create.ts +25 -0
  174. package/src/queues/cli/commands/delete.ts +26 -0
  175. package/src/queues/cli/commands/index.ts +36 -0
  176. package/src/queues/cli/commands/list.ts +25 -0
  177. package/src/queues/client.ts +135 -0
  178. package/src/queues/utils.ts +18 -0
  179. package/src/r2/constants.ts +4 -0
  180. package/src/r2/helpers.ts +131 -0
  181. package/src/r2/index.ts +289 -0
  182. package/src/routes.ts +140 -0
  183. package/src/secret/index.ts +379 -0
  184. package/src/sites.tsx +3 -3
  185. package/src/tail/createTail.ts +379 -0
  186. package/src/tail/filters.ts +4 -6
  187. package/src/tail/index.ts +199 -284
  188. package/src/tail/printing.ts +43 -3
  189. package/src/tsconfig-sanity.ts +16 -0
  190. package/src/type-generation.ts +181 -0
  191. package/src/user/access.ts +68 -0
  192. package/src/user/auth-variables.ts +113 -0
  193. package/src/user/choose-account.tsx +1 -1
  194. package/src/user/index.ts +0 -1
  195. package/src/user/user.tsx +116 -49
  196. package/src/utils/collectKeyValues.ts +14 -0
  197. package/src/whoami.tsx +80 -2
  198. package/src/worker.ts +69 -9
  199. package/src/yargs-types.ts +24 -0
  200. package/src/zones.ts +124 -9
  201. package/templates/__tests__/pages-dev-util.test.ts +128 -0
  202. package/templates/__tests__/tsconfig-sanity.ts +12 -0
  203. package/templates/__tests__/tsconfig.json +8 -0
  204. package/templates/__tests__/tsconfig.tsbuildinfo +1 -0
  205. package/templates/d1-beta-facade.js +205 -0
  206. package/templates/facade.d.ts +14 -0
  207. package/templates/first-party-worker-module-facade.ts +19 -0
  208. package/templates/format-dev-errors.ts +33 -0
  209. package/templates/init-tests/test-jest-new-worker.js +23 -0
  210. package/templates/init-tests/test-vitest-new-worker.js +24 -0
  211. package/templates/init-tests/test-vitest-new-worker.ts +25 -0
  212. package/templates/middleware/common.ts +62 -0
  213. package/templates/middleware/loader-modules.ts +82 -0
  214. package/templates/middleware/loader-sw.ts +233 -0
  215. package/templates/middleware/middleware-miniflare3-json-error.ts +20 -0
  216. package/templates/middleware/middleware-pretty-error.ts +40 -0
  217. package/templates/middleware/middleware-scheduled.ts +15 -0
  218. package/templates/new-worker.js +1 -1
  219. package/templates/pages-dev-pipeline.ts +31 -0
  220. package/templates/pages-dev-util.ts +52 -0
  221. package/templates/pages-shim.ts +4 -1
  222. package/templates/pages-template-plugin.ts +21 -5
  223. package/templates/pages-template-worker.ts +37 -9
  224. package/templates/{static-asset-facade.js → serve-static-assets.ts} +23 -7
  225. package/templates/service-bindings-module-facade.js +54 -0
  226. package/templates/service-bindings-sw-facade.js +42 -0
  227. package/templates/tsconfig-sanity.ts +11 -0
  228. package/templates/tsconfig.init.json +106 -0
  229. package/templates/tsconfig.json +5 -102
  230. package/templates/tsconfig.tsbuildinfo +1 -0
  231. package/wrangler-dist/cli.d.ts +621 -42
  232. package/wrangler-dist/cli.js +144441 -102606
  233. package/src/environment-variables.ts +0 -35
  234. package/src/faye-websocket.d.ts +0 -6
  235. package/src/generate.ts +0 -33
  236. package/src/miniflare-cli/enum-keys.ts +0 -17
  237. package/src/preview.ts +0 -31
  238. package/src/r2.ts +0 -50
  239. package/src/user/env-vars.ts +0 -46
package/src/init.ts CHANGED
@@ -5,16 +5,31 @@ 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";
9
+ import { fetchDashboardScript } from "./cfetch/internal";
10
+ import { readConfig } from "./config";
8
11
  import { confirm, select } from "./dialogs";
9
- import { initializeGit, isGitInstalled, isInsideGitRepo } from "./git-client";
12
+ import { initializeGit, getGitVersioon, isInsideGitRepo } from "./git-client";
10
13
  import { logger } from "./logger";
11
14
  import { getPackageManager } from "./package-manager";
12
15
  import { parsePackageJSON, parseTOML, readFileSync } from "./parse";
16
+ import { getBasePath } from "./paths";
17
+ import { requireAuth } from "./user";
13
18
  import { CommandLineArgsError, printWranglerBanner } from "./index";
14
19
 
15
- import type { Argv, ArgumentsCamelCase } from "yargs";
20
+ import type { RawConfig } from "./config";
21
+ import type { Route, SimpleRoute } from "./config/environment";
22
+ import type { WorkerMetadata } from "./create-worker-upload-form";
23
+ import type { ConfigPath } from "./index";
24
+ import type { PackageManager } from "./package-manager";
25
+ import type { PackageJSON } from "./parse";
26
+ import type {
27
+ CommonYargsOptions,
28
+ YargsOptionsToInterface,
29
+ } from "./yargs-types";
30
+ import type { Argv } from "yargs";
16
31
 
17
- export async function initOptions(yargs: Argv) {
32
+ export function initOptions(yargs: Argv<CommonYargsOptions>) {
18
33
  return yargs
19
34
  .positional("name", {
20
35
  describe: "The name of your worker",
@@ -36,17 +51,65 @@ export async function initOptions(yargs: Argv) {
36
51
  describe: 'Answer "yes" to any prompts for new projects',
37
52
  type: "boolean",
38
53
  alias: "y",
54
+ })
55
+ .option("from-dash", {
56
+ describe:
57
+ "The name of the Worker you wish to download from the Cloudflare dashboard for local development.",
58
+ type: "string",
59
+ requiresArg: true,
39
60
  });
40
61
  }
41
62
 
42
- interface InitArgs {
43
- name: string;
44
- type?: string;
45
- site?: boolean;
46
- yes?: boolean;
47
- }
63
+ type InitArgs = YargsOptionsToInterface<typeof initOptions>;
64
+
65
+ export type ServiceMetadataRes = {
66
+ id: string;
67
+ default_environment: {
68
+ environment: string;
69
+ created_on: string;
70
+ modified_on: string;
71
+ script: {
72
+ id: string;
73
+ tag: string;
74
+ etag: string;
75
+ handlers: string[];
76
+ modified_on: string;
77
+ created_on: string;
78
+ migration_tag: string;
79
+ usage_model: "bundled" | "unbound";
80
+ compatibility_date: string;
81
+ last_deployed_from?: "wrangler" | "dash" | "api";
82
+ };
83
+ };
84
+ created_on: string;
85
+ modified_on: string;
86
+ usage_model: "bundled" | "unbound";
87
+ environments: [
88
+ {
89
+ environment: string;
90
+ created_on: string;
91
+ modified_on: string;
92
+ }
93
+ ];
94
+ };
95
+
96
+ export type RawSimpleRoute = { pattern: string };
97
+ export type RawRoutes = (RawSimpleRoute | Exclude<Route, SimpleRoute>) & {
98
+ id: string;
99
+ };
100
+ export type RoutesRes = RawRoutes[];
101
+
102
+ export type CronTriggersRes = {
103
+ schedules: [
104
+ {
105
+ cron: string;
106
+ created_on: Date;
107
+ modified_on: Date;
108
+ }
109
+ ];
110
+ };
48
111
 
49
- export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
112
+ export async function initHandler(args: InitArgs) {
50
113
  await printWranglerBanner();
51
114
  if (args.type) {
52
115
  let message = "The --type option is no longer supported.";
@@ -61,7 +124,11 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
61
124
  const devDepsToInstall: string[] = [];
62
125
  const instructions: string[] = [];
63
126
  let shouldRunPackageManagerInstall = false;
64
- const creationDirectory = path.resolve(process.cwd(), args.name ?? "");
127
+ const fromDashScriptName = args.fromDash;
128
+ const creationDirectory = path.resolve(
129
+ process.cwd(),
130
+ (args.name ? args.name : fromDashScriptName) ?? ""
131
+ );
65
132
 
66
133
  if (args.site) {
67
134
  const gitDirectory =
@@ -90,6 +157,7 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
90
157
 
91
158
  // TODO: ask which directory to make the worker in (defaults to args.name)
92
159
  // TODO: if args.name isn't provided, ask what to name the worker
160
+ // Note: `--from-dash` will be a fallback creationDir/Worker name if none is provided.
93
161
 
94
162
  const wranglerTomlDestination = path.join(
95
163
  creationDirectory,
@@ -97,13 +165,37 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
97
165
  );
98
166
  let justCreatedWranglerToml = false;
99
167
 
168
+ let accountId = "";
169
+ let serviceMetadata: undefined | ServiceMetadataRes;
170
+
171
+ // If --from-dash, check that script actually exists
172
+ if (fromDashScriptName) {
173
+ const config = readConfig(args.config as ConfigPath, args);
174
+ accountId = await requireAuth(config);
175
+ try {
176
+ serviceMetadata = await fetchResult<ServiceMetadataRes>(
177
+ `/accounts/${accountId}/workers/services/${fromDashScriptName}`
178
+ );
179
+ } catch (err) {
180
+ if ((err as { code?: number }).code === 10090) {
181
+ throw new Error(
182
+ "wrangler couldn't find a Worker script with that name in your account.\nRun `wrangler whoami` to confirm you're logged into the correct account."
183
+ );
184
+ }
185
+ throw err;
186
+ }
187
+ }
188
+
100
189
  if (fs.existsSync(wranglerTomlDestination)) {
190
+ let shouldContinue = false;
101
191
  logger.warn(
102
192
  `${path.relative(process.cwd(), wranglerTomlDestination)} already exists!`
103
193
  );
104
- const shouldContinue = await confirm(
105
- "Do you want to continue initializing this project?"
106
- );
194
+ if (!fromDashScriptName) {
195
+ shouldContinue = await confirm(
196
+ "Do you want to continue initializing this project?"
197
+ );
198
+ }
107
199
  if (!shouldContinue) {
108
200
  return;
109
201
  }
@@ -136,7 +228,7 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
136
228
 
137
229
  const yesFlag = args.yes ?? false;
138
230
 
139
- if (!(await isInsideGitRepo(creationDirectory)) && (await isGitInstalled())) {
231
+ if (!(await isInsideGitRepo(creationDirectory)) && (await getGitVersioon())) {
140
232
  const shouldInitGit =
141
233
  yesFlag ||
142
234
  (await confirm("Would you like to use git to manage this Worker?"));
@@ -144,7 +236,7 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
144
236
  await initializeGit(creationDirectory);
145
237
  await writeFile(
146
238
  path.join(creationDirectory, ".gitignore"),
147
- readFileSync(path.join(__dirname, "../templates/gitignore"))
239
+ readFileSync(path.join(getBasePath(), "templates/gitignore"))
148
240
  );
149
241
  logger.log(
150
242
  args.name && args.name !== "."
@@ -164,6 +256,8 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
164
256
  "package.json"
165
257
  );
166
258
  let shouldCreatePackageJson = false;
259
+ let shouldCreateTests = false;
260
+ let newWorkerTestType: "jest" | "vitest" = "jest";
167
261
 
168
262
  if (!pathToPackageJson) {
169
263
  // If no package.json exists, ask to create one
@@ -236,7 +330,7 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
236
330
  isTypescriptProject = true;
237
331
  await writeFile(
238
332
  path.join(creationDirectory, "./tsconfig.json"),
239
- readFileSync(path.join(__dirname, "../templates/tsconfig.json"))
333
+ readFileSync(path.join(getBasePath(), "templates/tsconfig.init.json"))
240
334
  );
241
335
  devDepsToInstall.push("@cloudflare/workers-types");
242
336
  devDepsToInstall.push("typescript");
@@ -285,48 +379,26 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
285
379
  !packageJsonContent.scripts?.publish &&
286
380
  shouldCreatePackageJson;
287
381
 
288
- /*
289
- * Passes the array of accumulated devDeps to install through to
290
- * the package manager. Also generates a human-readable list
291
- * of packages it installed.
292
- * If there are no devDeps to install, optionally runs
293
- * the package manager's install command.
294
- */
295
- async function installPackages(
296
- shouldRunInstall: boolean,
297
- depsToInstall: string[]
298
- ) {
299
- //lets install the devDeps they asked for
300
- //and run their package manager's install command if needed
301
- if (depsToInstall.length > 0) {
302
- const formatter = new Intl.ListFormat("en", {
303
- style: "long",
304
- type: "conjunction",
305
- });
306
- await packageManager.addDevDeps(...depsToInstall);
307
- const versionlessPackages = depsToInstall.map((dep) =>
308
- dep === `wrangler@${wranglerVersion}` ? "wrangler" : dep
309
- );
310
-
311
- logger.log(
312
- `✨ Installed ${formatter.format(
313
- versionlessPackages
314
- )} into devDependencies`
315
- );
316
- } else {
317
- if (shouldRunInstall) {
318
- await packageManager.install();
319
- }
382
+ async function writePackageJsonScriptsAndUpdateWranglerToml({
383
+ isWritingScripts,
384
+ isAddingTests,
385
+ testRunner,
386
+ isCreatingWranglerToml,
387
+ packagePath,
388
+ scriptPath,
389
+ extraToml,
390
+ }: {
391
+ isWritingScripts: boolean;
392
+ isAddingTests?: boolean;
393
+ testRunner?: "jest" | "vitest";
394
+ isCreatingWranglerToml: boolean;
395
+ packagePath: string;
396
+ scriptPath: string;
397
+ extraToml: TOML.JsonMap;
398
+ }) {
399
+ if (isAddingTests && !testRunner) {
400
+ logger.error("testRunner is required if isAddingTests");
320
401
  }
321
- }
322
-
323
- async function writePackageJsonScriptsAndUpdateWranglerToml(
324
- isWritingScripts: boolean,
325
- isCreatingWranglerToml: boolean,
326
- packagePath: string,
327
- scriptPath: string,
328
- extraToml: TOML.JsonMap
329
- ) {
330
402
  if (isCreatingWranglerToml) {
331
403
  // rewrite wrangler.toml with main = "path/to/script" and any additional config specified in `extraToml`
332
404
  const parsedWranglerToml = parseTOML(
@@ -342,7 +414,8 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
342
414
  }
343
415
  const isNamedWorker =
344
416
  isCreatingWranglerToml && path.dirname(packagePath) !== process.cwd();
345
-
417
+ const isAddingTestScripts =
418
+ isAddingTests && !packageJsonContent.scripts?.test;
346
419
  if (isWritingScripts) {
347
420
  await writeFile(
348
421
  packagePath,
@@ -357,17 +430,21 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
357
430
  deploy: isCreatingWranglerToml
358
431
  ? `wrangler publish`
359
432
  : `wrangler publish ${scriptPath}`,
433
+ ...(isAddingTestScripts && { test: testRunner }),
360
434
  },
361
- },
435
+ } as PackageJSON,
362
436
  null,
363
437
  2
364
438
  ) + "\n"
365
439
  );
366
440
  instructions.push(
367
441
  `\nTo start developing your Worker, run \`${
368
- isNamedWorker ? `cd ${args.name} && ` : ""
442
+ isNamedWorker ? `cd ${args.name || fromDashScriptName} && ` : ""
369
443
  }npm start\``
370
444
  );
445
+ if (isAddingTestScripts) {
446
+ instructions.push(`To start testing your Worker, run \`npm test\``);
447
+ }
371
448
  instructions.push(
372
449
  `To publish your Worker to the Internet, run \`npm run deploy\``
373
450
  );
@@ -385,89 +462,107 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
385
462
  }
386
463
  }
387
464
 
388
- async function getNewWorkerType(newWorkerFilename: string) {
389
- return select(
390
- `Would you like to create a Worker at ${newWorkerFilename}?`,
391
- [
392
- {
393
- value: "none",
394
- label: "None",
395
- },
396
- {
397
- value: "fetch",
398
- label: "Fetch handler",
399
- },
400
- {
401
- value: "scheduled",
402
- label: "Scheduled handler",
403
- },
404
- ],
405
- 1
406
- ) as Promise<"none" | "fetch" | "scheduled">;
407
- }
408
-
409
- function getNewWorkerTemplate(
410
- lang: "js" | "ts",
411
- workerType: "fetch" | "scheduled"
412
- ) {
413
- const templates = {
414
- "js-fetch": "new-worker.js",
415
- "js-scheduled": "new-worker-scheduled.js",
416
- "ts-fetch": "new-worker.ts",
417
- "ts-scheduled": "new-worker-scheduled.ts",
418
- };
419
-
420
- return templates[`${lang}-${workerType}`];
421
- }
422
-
423
- function getNewWorkerToml(workerType: "fetch" | "scheduled"): TOML.JsonMap {
424
- if (workerType === "scheduled") {
425
- return {
426
- triggers: {
427
- crons: ["1 * * * *"],
428
- },
429
- };
430
- }
431
-
432
- return {};
433
- }
434
-
435
465
  if (isTypescriptProject) {
436
466
  if (!fs.existsSync(path.join(creationDirectory, "./src/index.ts"))) {
437
467
  const newWorkerFilename = path.relative(
438
468
  process.cwd(),
439
469
  path.join(creationDirectory, "./src/index.ts")
440
470
  );
441
-
442
- const newWorkerType = yesFlag
443
- ? "fetch"
444
- : await getNewWorkerType(newWorkerFilename);
445
-
446
- if (newWorkerType !== "none") {
447
- const template = getNewWorkerTemplate("ts", newWorkerType);
471
+ if (fromDashScriptName) {
472
+ logger.warn(
473
+ "After running `wrangler init --from-dash`, modifying your worker via the Cloudflare dashboard is discouraged.\nEdits made via the Dashboard will not be synchronized locally and will be overridden by your local code and config when you publish."
474
+ );
448
475
 
449
476
  await mkdir(path.join(creationDirectory, "./src"), {
450
477
  recursive: true,
451
478
  });
479
+
480
+ const defaultEnvironment =
481
+ serviceMetadata?.default_environment.environment;
482
+ // I want the default environment, assuming it's the most up to date code.
483
+ const dashScript = await fetchDashboardScript(
484
+ `/accounts/${accountId}/workers/services/${fromDashScriptName}/environments/${defaultEnvironment}/content`
485
+ );
486
+
452
487
  await writeFile(
453
488
  path.join(creationDirectory, "./src/index.ts"),
454
- readFileSync(path.join(__dirname, `../templates/${template}`))
489
+ dashScript
455
490
  );
456
491
 
457
- logger.log(
458
- `✨ Created ${path.relative(
459
- process.cwd(),
460
- path.join(creationDirectory, "./src/index.ts")
461
- )}`
462
- );
492
+ await writePackageJsonScriptsAndUpdateWranglerToml({
493
+ isWritingScripts: shouldWritePackageJsonScripts,
494
+ isCreatingWranglerToml: justCreatedWranglerToml,
495
+ packagePath: pathToPackageJson,
496
+ scriptPath: "src/index.ts",
497
+ extraToml: (await getWorkerConfig(accountId, fromDashScriptName, {
498
+ defaultEnvironment,
499
+ environments: serviceMetadata?.environments,
500
+ })) as TOML.JsonMap,
501
+ });
502
+ } else {
503
+ const newWorkerType = yesFlag
504
+ ? "fetch"
505
+ : await getNewWorkerType(newWorkerFilename);
463
506
 
464
- await writePackageJsonScriptsAndUpdateWranglerToml(
465
- shouldWritePackageJsonScripts,
466
- justCreatedWranglerToml,
467
- pathToPackageJson,
468
- "src/index.ts",
469
- getNewWorkerToml(newWorkerType)
470
- );
507
+ if (newWorkerType !== "none") {
508
+ const template = getNewWorkerTemplate("ts", newWorkerType);
509
+
510
+ await mkdir(path.join(creationDirectory, "./src"), {
511
+ recursive: true,
512
+ });
513
+
514
+ await writeFile(
515
+ path.join(creationDirectory, "./src/index.ts"),
516
+ readFileSync(path.join(getBasePath(), `templates/${template}`))
517
+ );
518
+
519
+ logger.log(
520
+ `✨ Created ${path.relative(
521
+ process.cwd(),
522
+ path.join(creationDirectory, "./src/index.ts")
523
+ )}`
524
+ );
525
+
526
+ shouldCreateTests =
527
+ yesFlag ||
528
+ (await confirm(
529
+ "Would you like us to write your first test with Vitest?"
530
+ ));
531
+
532
+ if (shouldCreateTests) {
533
+ if (yesFlag) {
534
+ logger.info("Your project will use Vitest to run your tests.");
535
+ }
536
+
537
+ newWorkerTestType = "vitest";
538
+ devDepsToInstall.push(newWorkerTestType);
539
+
540
+ await writeFile(
541
+ path.join(creationDirectory, "./src/index.test.ts"),
542
+ readFileSync(
543
+ path.join(
544
+ getBasePath(),
545
+ `templates/init-tests/test-${newWorkerTestType}-new-worker.ts`
546
+ )
547
+ )
548
+ );
549
+ logger.log(
550
+ `✨ Created ${path.relative(
551
+ process.cwd(),
552
+ path.join(creationDirectory, "./src/index.test.ts")
553
+ )}`
554
+ );
555
+ }
556
+
557
+ await writePackageJsonScriptsAndUpdateWranglerToml({
558
+ isWritingScripts: shouldWritePackageJsonScripts,
559
+ isCreatingWranglerToml: justCreatedWranglerToml,
560
+ packagePath: pathToPackageJson,
561
+ testRunner: newWorkerTestType,
562
+ scriptPath: "src/index.ts",
563
+ extraToml: getNewWorkerToml(newWorkerType),
564
+ });
565
+ }
471
566
  }
472
567
  }
473
568
  } else {
@@ -477,41 +572,106 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
477
572
  path.join(creationDirectory, "./src/index.js")
478
573
  );
479
574
 
480
- const newWorkerType = yesFlag
481
- ? "fetch"
482
- : await getNewWorkerType(newWorkerFilename);
483
-
484
- if (newWorkerType !== "none") {
485
- const template = getNewWorkerTemplate("js", newWorkerType);
575
+ if (fromDashScriptName) {
576
+ logger.warn(
577
+ "After running `wrangler init --from-dash`, modifying your worker via the Cloudflare dashboard is discouraged.\nEdits made via the Dashboard will not be synchronized locally and will be overridden by your local code and config when you publish."
578
+ );
486
579
 
487
580
  await mkdir(path.join(creationDirectory, "./src"), {
488
581
  recursive: true,
489
582
  });
583
+
584
+ const defaultEnvironment =
585
+ serviceMetadata?.default_environment.environment;
586
+
587
+ // I want the default environment, assuming it's the most up to date code.
588
+ const dashScript = await fetchDashboardScript(
589
+ `/accounts/${accountId}/workers/services/${fromDashScriptName}/environments/${defaultEnvironment}/content`
590
+ );
591
+
490
592
  await writeFile(
491
593
  path.join(creationDirectory, "./src/index.js"),
492
- readFileSync(path.join(__dirname, `../templates/${template}`))
594
+ dashScript
493
595
  );
494
596
 
495
- logger.log(
496
- `✨ Created ${path.relative(
497
- process.cwd(),
498
- path.join(creationDirectory, "./src/index.js")
499
- )}`
500
- );
597
+ await writePackageJsonScriptsAndUpdateWranglerToml({
598
+ isWritingScripts: shouldWritePackageJsonScripts,
599
+ isCreatingWranglerToml: justCreatedWranglerToml,
600
+ packagePath: pathToPackageJson,
601
+ scriptPath: "src/index.ts",
602
+ //? Should we have Environment argument for `wrangler init --from-dash` - Jacob
603
+ extraToml: (await getWorkerConfig(accountId, fromDashScriptName, {
604
+ defaultEnvironment,
605
+ environments: serviceMetadata?.environments,
606
+ })) as TOML.JsonMap,
607
+ });
608
+ } else {
609
+ const newWorkerType = yesFlag
610
+ ? "fetch"
611
+ : await getNewWorkerType(newWorkerFilename);
501
612
 
502
- await writePackageJsonScriptsAndUpdateWranglerToml(
503
- shouldWritePackageJsonScripts,
504
- justCreatedWranglerToml,
505
- pathToPackageJson,
506
- "src/index.js",
507
- getNewWorkerToml(newWorkerType)
508
- );
613
+ if (newWorkerType !== "none") {
614
+ const template = getNewWorkerTemplate("js", newWorkerType);
615
+
616
+ await mkdir(path.join(creationDirectory, "./src"), {
617
+ recursive: true,
618
+ });
619
+ await writeFile(
620
+ path.join(creationDirectory, "./src/index.js"),
621
+ readFileSync(path.join(getBasePath(), `templates/${template}`))
622
+ );
623
+
624
+ logger.log(
625
+ `✨ Created ${path.relative(
626
+ process.cwd(),
627
+ path.join(creationDirectory, "./src/index.js")
628
+ )}`
629
+ );
630
+
631
+ shouldCreateTests =
632
+ yesFlag ||
633
+ (await confirm("Would you like us to write your first test?"));
634
+
635
+ if (shouldCreateTests) {
636
+ newWorkerTestType = await getNewWorkerTestType(yesFlag);
637
+ devDepsToInstall.push(newWorkerTestType);
638
+ await writeFile(
639
+ path.join(creationDirectory, "./src/index.test.js"),
640
+ readFileSync(
641
+ path.join(
642
+ getBasePath(),
643
+ `templates/init-tests/test-${newWorkerTestType}-new-worker.js`
644
+ )
645
+ )
646
+ );
647
+ logger.log(
648
+ `✨ Created ${path.relative(
649
+ process.cwd(),
650
+ path.join(creationDirectory, "./src/index.test.js")
651
+ )}`
652
+ );
653
+ }
654
+
655
+ await writePackageJsonScriptsAndUpdateWranglerToml({
656
+ isWritingScripts: shouldWritePackageJsonScripts,
657
+ isAddingTests: shouldCreateTests,
658
+ testRunner: newWorkerTestType,
659
+ isCreatingWranglerToml: justCreatedWranglerToml,
660
+ packagePath: pathToPackageJson,
661
+ scriptPath: "src/index.js",
662
+ extraToml: getNewWorkerToml(newWorkerType),
663
+ });
664
+ }
509
665
  }
510
666
  }
511
667
  }
512
668
  // install packages as the final step of init
513
669
  try {
514
- await installPackages(shouldRunPackageManagerInstall, devDepsToInstall);
670
+ await installPackages(
671
+ shouldRunPackageManagerInstall,
672
+ devDepsToInstall,
673
+ packageManager
674
+ );
515
675
  } catch (e) {
516
676
  // fetching packages could fail due to loss of internet, etc
517
677
  // we should let folks know we failed to fetch, but their
@@ -526,6 +686,108 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
526
686
  instructions.forEach((instruction) => logger.log(instruction));
527
687
  }
528
688
 
689
+ /*
690
+ * Passes the array of accumulated devDeps to install through to
691
+ * the package manager. Also generates a human-readable list
692
+ * of packages it installed.
693
+ * If there are no devDeps to install, optionally runs
694
+ * the package manager's install command.
695
+ */
696
+ async function installPackages(
697
+ shouldRunInstall: boolean,
698
+ depsToInstall: string[],
699
+ packageManager: PackageManager
700
+ ) {
701
+ //lets install the devDeps they asked for
702
+ //and run their package manager's install command if needed
703
+ if (depsToInstall.length > 0) {
704
+ const formatter = new Intl.ListFormat("en", {
705
+ style: "long",
706
+ type: "conjunction",
707
+ });
708
+ await packageManager.addDevDeps(...depsToInstall);
709
+ const versionlessPackages = depsToInstall.map((dep) =>
710
+ dep === `wrangler@${wranglerVersion}` ? "wrangler" : dep
711
+ );
712
+
713
+ logger.log(
714
+ `✨ Installed ${formatter.format(
715
+ versionlessPackages
716
+ )} into devDependencies`
717
+ );
718
+ } else {
719
+ if (shouldRunInstall) {
720
+ await packageManager.install();
721
+ }
722
+ }
723
+ }
724
+
725
+ async function getNewWorkerType(newWorkerFilename: string) {
726
+ return select(
727
+ `Would you like to create a Worker at ${newWorkerFilename}?`,
728
+ [
729
+ {
730
+ value: "none",
731
+ label: "None",
732
+ },
733
+ {
734
+ value: "fetch",
735
+ label: "Fetch handler",
736
+ },
737
+ {
738
+ value: "scheduled",
739
+ label: "Scheduled handler",
740
+ },
741
+ ],
742
+ 1
743
+ ) as Promise<"none" | "fetch" | "scheduled">;
744
+ }
745
+
746
+ async function getNewWorkerTestType(yesFlag?: boolean) {
747
+ return yesFlag
748
+ ? "jest"
749
+ : (select(
750
+ `Which test runner would you like to use?`,
751
+ [
752
+ {
753
+ value: "vitest",
754
+ label: "Vitest",
755
+ },
756
+ {
757
+ value: "jest",
758
+ label: "Jest",
759
+ },
760
+ ],
761
+ 1
762
+ ) as Promise<"jest" | "vitest">);
763
+ }
764
+
765
+ function getNewWorkerTemplate(
766
+ lang: "js" | "ts",
767
+ workerType: "fetch" | "scheduled"
768
+ ) {
769
+ const templates = {
770
+ "js-fetch": "new-worker.js",
771
+ "js-scheduled": "new-worker-scheduled.js",
772
+ "ts-fetch": "new-worker.ts",
773
+ "ts-scheduled": "new-worker-scheduled.ts",
774
+ };
775
+
776
+ return templates[`${lang}-${workerType}`];
777
+ }
778
+
779
+ function getNewWorkerToml(workerType: "fetch" | "scheduled"): TOML.JsonMap {
780
+ if (workerType === "scheduled") {
781
+ return {
782
+ triggers: {
783
+ crons: ["1 * * * *"],
784
+ },
785
+ };
786
+ }
787
+
788
+ return {};
789
+ }
790
+
529
791
  /**
530
792
  * Find the path to the given `basename` file from the `cwd`.
531
793
  *
@@ -547,3 +809,217 @@ async function findPath(
547
809
  });
548
810
  }
549
811
  }
812
+
813
+ async function getWorkerConfig(
814
+ accountId: string,
815
+ fromDashScriptName: string,
816
+ {
817
+ defaultEnvironment,
818
+ environments,
819
+ }: {
820
+ defaultEnvironment: string | undefined;
821
+ environments: ServiceMetadataRes["environments"] | undefined;
822
+ }
823
+ ): Promise<RawConfig> {
824
+ const [bindings, routes, serviceEnvMetadata, cronTriggers] =
825
+ await Promise.all([
826
+ fetchResult<WorkerMetadata["bindings"]>(
827
+ `/accounts/${accountId}/workers/services/${fromDashScriptName}/environments/${defaultEnvironment}/bindings`
828
+ ),
829
+ fetchResult<RoutesRes>(
830
+ `/accounts/${accountId}/workers/services/${fromDashScriptName}/environments/${defaultEnvironment}/routes`
831
+ ),
832
+ fetchResult<ServiceMetadataRes["default_environment"]>(
833
+ `/accounts/${accountId}/workers/services/${fromDashScriptName}/environments/${defaultEnvironment}`
834
+ ),
835
+ fetchResult<CronTriggersRes>(
836
+ `/accounts/${accountId}/workers/scripts/${fromDashScriptName}/schedules`
837
+ ),
838
+ ]).catch((e) => {
839
+ throw new Error(
840
+ `Error Occurred ${e}: Unable to fetch bindings, routes, or services metadata from the dashboard. Please try again later.`
841
+ );
842
+ });
843
+
844
+ const mappedBindings = bindings
845
+ .filter((binding) => (binding.type as string) !== "secret_text")
846
+ // Combine the same types into {[type]: [binding]}
847
+ .reduce((configObj, binding) => {
848
+ // Some types have different names in wrangler.toml
849
+ // I want the type safety of the binding being destructured after the case narrowing the union but type is unused
850
+
851
+ switch (binding.type) {
852
+ case "plain_text":
853
+ {
854
+ configObj.vars = {
855
+ ...(configObj.vars ?? {}),
856
+ [binding.name]: binding.text,
857
+ };
858
+ }
859
+ break;
860
+ case "json":
861
+ {
862
+ configObj.vars = {
863
+ ...(configObj.vars ?? {}),
864
+ name: binding.name,
865
+ json: binding.json,
866
+ };
867
+ }
868
+ break;
869
+ case "kv_namespace":
870
+ {
871
+ configObj.kv_namespaces = [
872
+ ...(configObj.kv_namespaces ?? []),
873
+ { id: binding.namespace_id, binding: binding.name },
874
+ ];
875
+ }
876
+ break;
877
+ case "durable_object_namespace":
878
+ {
879
+ configObj.durable_objects = {
880
+ bindings: [
881
+ ...(configObj.durable_objects?.bindings ?? []),
882
+ {
883
+ name: binding.name,
884
+ class_name: binding.class_name,
885
+ script_name: binding.script_name,
886
+ environment: binding.environment,
887
+ },
888
+ ],
889
+ };
890
+ }
891
+ break;
892
+ case "r2_bucket":
893
+ {
894
+ configObj.r2_buckets = [
895
+ ...(configObj.r2_buckets ?? []),
896
+ { binding: binding.name, bucket_name: binding.bucket_name },
897
+ ];
898
+ }
899
+ break;
900
+ case "service":
901
+ {
902
+ configObj.services = [
903
+ ...(configObj.services ?? []),
904
+ {
905
+ binding: binding.name,
906
+ service: binding.service,
907
+ environment: binding.environment,
908
+ },
909
+ ];
910
+ }
911
+ break;
912
+ case "analytics_engine":
913
+ {
914
+ configObj.analytics_engine_datasets = [
915
+ ...(configObj.analytics_engine_datasets ?? []),
916
+ { binding: binding.name, dataset: binding.dataset },
917
+ ];
918
+ }
919
+ break;
920
+ case "dispatch_namespace":
921
+ {
922
+ configObj.dispatch_namespaces = [
923
+ ...(configObj.dispatch_namespaces ?? []),
924
+ { binding: binding.name, namespace: binding.namespace },
925
+ ];
926
+ }
927
+ break;
928
+ case "logfwdr":
929
+ {
930
+ configObj.logfwdr = {
931
+ // TODO: Messaging about adding schema file path
932
+ schema: "",
933
+ bindings: [
934
+ ...(configObj.logfwdr?.bindings ?? []),
935
+ { name: binding.name, destination: binding.destination },
936
+ ],
937
+ };
938
+ }
939
+ break;
940
+ case "wasm_module":
941
+ {
942
+ configObj.wasm_modules = {
943
+ ...(configObj.wasm_modules ?? {}),
944
+ [binding.name]: binding.part,
945
+ };
946
+ }
947
+ break;
948
+ case "text_blob":
949
+ {
950
+ configObj.text_blobs = {
951
+ ...(configObj.text_blobs ?? {}),
952
+ [binding.name]: binding.part,
953
+ };
954
+ }
955
+ break;
956
+ case "data_blob":
957
+ {
958
+ configObj.data_blobs = {
959
+ ...(configObj.data_blobs ?? {}),
960
+ [binding.name]: binding.part,
961
+ };
962
+ }
963
+ break;
964
+ default: {
965
+ // If we don't know what the type is, its an unsafe binding
966
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
967
+ if (!(binding as any)?.type) break;
968
+ configObj.unsafe = {
969
+ bindings: [...(configObj.unsafe?.bindings ?? []), binding],
970
+ };
971
+ }
972
+ }
973
+
974
+ return configObj;
975
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
976
+ }, {} as RawConfig);
977
+
978
+ const durableObjectClassNames = bindings
979
+ .filter((binding) => binding.type === "durable_object_namespace")
980
+ .map(
981
+ (durableObject) => (durableObject as { class_name: string }).class_name
982
+ );
983
+
984
+ const routeOrRoutes = routes.map((rawRoute) => {
985
+ const { id: _id, ...route } = rawRoute;
986
+ if (Object.keys(route).length === 1) {
987
+ return route.pattern;
988
+ } else {
989
+ return route as Route;
990
+ }
991
+ });
992
+ const routeOrRoutesToConfig =
993
+ routeOrRoutes.length > 1
994
+ ? { routes: routeOrRoutes }
995
+ : { route: routeOrRoutes[0] };
996
+
997
+ return {
998
+ compatibility_date:
999
+ serviceEnvMetadata.script.compatibility_date ??
1000
+ new Date().toISOString().substring(0, 10),
1001
+ ...routeOrRoutesToConfig,
1002
+ usage_model: serviceEnvMetadata.script.usage_model,
1003
+ ...(durableObjectClassNames.length
1004
+ ? {
1005
+ migrations: [
1006
+ {
1007
+ tag: serviceEnvMetadata.script.migration_tag,
1008
+ new_classes: durableObjectClassNames,
1009
+ },
1010
+ ],
1011
+ }
1012
+ : {}),
1013
+ triggers: {
1014
+ crons: cronTriggers.schedules.map((scheduled) => scheduled.cron),
1015
+ },
1016
+ env: environments
1017
+ ?.filter((env) => env.environment !== "production")
1018
+ // `env` can have multiple Environments, with different configs.
1019
+ .reduce((envObj, { environment }) => {
1020
+ return { ...envObj, [environment]: {} };
1021
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1022
+ }, {} as RawConfig["env"]),
1023
+ ...mappedBindings,
1024
+ };
1025
+ }