wrangler 0.0.0-e88a54e → 0.0.0-e95e5a0

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 (88) hide show
  1. package/README.md +25 -14
  2. package/bin/wrangler.js +13 -1
  3. package/miniflare-dist/index.mjs +4828 -21
  4. package/package.json +29 -24
  5. package/pages/functions/buildPlugin.ts +92 -0
  6. package/pages/functions/buildWorker.ts +80 -4
  7. package/pages/functions/filepath-routing.test.ts +11 -0
  8. package/pages/functions/filepath-routing.ts +8 -0
  9. package/pages/functions/routes.ts +6 -1
  10. package/pages/functions/template-plugin.ts +146 -0
  11. package/pages/functions/template-worker.ts +21 -8
  12. package/src/__tests__/config-cache.test.ts +36 -0
  13. package/src/__tests__/configuration.test.ts +530 -201
  14. package/src/__tests__/dev.test.tsx +284 -18
  15. package/src/__tests__/guess-worker-format.test.ts +28 -1
  16. package/src/__tests__/helpers/mock-cfetch.ts +5 -1
  17. package/src/__tests__/helpers/mock-console.ts +20 -3
  18. package/src/__tests__/helpers/mock-dialogs.ts +26 -1
  19. package/src/__tests__/helpers/mock-http-server.ts +46 -0
  20. package/src/__tests__/helpers/mock-istty.ts +27 -0
  21. package/src/__tests__/helpers/mock-oauth-flow.ts +255 -0
  22. package/src/__tests__/helpers/run-in-tmp.ts +7 -0
  23. package/src/__tests__/helpers/write-worker-source.ts +31 -0
  24. package/src/__tests__/https-options.test.ts +5 -2
  25. package/src/__tests__/index.test.ts +27 -769
  26. package/src/__tests__/init.test.ts +1516 -0
  27. package/src/__tests__/jest.setup.ts +4 -4
  28. package/src/__tests__/kv.test.ts +133 -61
  29. package/src/__tests__/logger.test.ts +115 -0
  30. package/src/__tests__/pages.test.ts +311 -26
  31. package/src/__tests__/publish.test.ts +2789 -618
  32. package/src/__tests__/r2.test.ts +26 -10
  33. package/src/__tests__/route.test.ts +4 -4
  34. package/src/__tests__/secret.test.ts +55 -34
  35. package/src/__tests__/tail.test.ts +146 -20
  36. package/src/__tests__/user.test.ts +131 -0
  37. package/src/__tests__/whoami.test.tsx +14 -9
  38. package/src/bundle.ts +30 -3
  39. package/src/cfetch/internal.ts +0 -2
  40. package/src/cli.ts +5 -11
  41. package/src/config/README.md +1 -1
  42. package/src/config/config.ts +20 -4
  43. package/src/config/environment.ts +32 -4
  44. package/src/config/index.ts +4 -3
  45. package/src/config/validation-helpers.ts +96 -7
  46. package/src/config/validation.ts +388 -54
  47. package/src/config-cache.ts +45 -0
  48. package/src/create-worker-preview.ts +22 -13
  49. package/src/create-worker-upload-form.ts +32 -9
  50. package/src/dev/dev-vars.ts +36 -0
  51. package/src/dev/dev.tsx +132 -78
  52. package/src/dev/local.tsx +101 -21
  53. package/src/dev/remote.tsx +23 -13
  54. package/src/dev/use-esbuild.ts +23 -5
  55. package/src/dialogs.tsx +16 -6
  56. package/src/entry.ts +83 -24
  57. package/src/environment-variables.ts +3 -1
  58. package/src/errors.ts +11 -0
  59. package/src/https-options.ts +3 -2
  60. package/src/index.tsx +689 -394
  61. package/src/inspect.ts +17 -11
  62. package/src/intl-polyfill.d.ts +139 -0
  63. package/src/logger.ts +66 -0
  64. package/src/miniflare-cli/index.ts +4 -0
  65. package/src/miniflare-cli/request-context.ts +40 -0
  66. package/src/module-collection.ts +7 -7
  67. package/src/open-in-browser.ts +5 -4
  68. package/src/package-manager.ts +9 -8
  69. package/src/pages.tsx +875 -56
  70. package/src/parse.ts +1 -1
  71. package/src/proxy.ts +28 -19
  72. package/src/publish.ts +263 -141
  73. package/src/sites.tsx +27 -14
  74. package/src/tail/index.ts +2 -2
  75. package/src/tail/printing.ts +13 -8
  76. package/src/update-check.ts +19 -0
  77. package/src/user.tsx +116 -41
  78. package/src/whoami.tsx +2 -1
  79. package/src/worker.ts +15 -6
  80. package/templates/gitignore +171 -0
  81. package/templates/static-asset-facade.js +1 -1
  82. package/wrangler-dist/cli.js +21162 -50227
  83. package/miniflare-dist/index.mjs.map +0 -7
  84. package/src/__tests__/dev2.test.tsx +0 -154
  85. package/src/__tests__/logout.test.ts +0 -52
  86. package/src/__tests__/sentry.test.ts +0 -146
  87. package/src/reporting.ts +0 -131
  88. package/wrangler-dist/cli.js.map +0 -7
package/src/parse.ts CHANGED
@@ -75,7 +75,7 @@ type TomlError = Error & {
75
75
  /**
76
76
  * A wrapper around `TOML.parse` that throws a `ParseError`.
77
77
  */
78
- export function parseTOML(input: string, file?: string): object {
78
+ export function parseTOML(input: string, file?: string): TOML.JsonMap | never {
79
79
  try {
80
80
  return TOML.parse(input);
81
81
  } catch (err) {
package/src/proxy.ts CHANGED
@@ -5,7 +5,7 @@ import WebSocket from "faye-websocket";
5
5
  import { useEffect, useRef, useState } from "react";
6
6
  import serveStatic from "serve-static";
7
7
  import { getHttpsOptions } from "./https-options";
8
- import { reportError } from "./reporting";
8
+ import { logger } from "./logger";
9
9
  import type { CfPreviewToken } from "./create-worker-preview";
10
10
  import type {
11
11
  IncomingHttpHeaders,
@@ -75,7 +75,7 @@ export function usePreviewServer({
75
75
  ip,
76
76
  }: {
77
77
  previewToken: CfPreviewToken | undefined;
78
- publicRoot: undefined | string;
78
+ publicRoot: string | undefined;
79
79
  localProtocol: "https" | "http";
80
80
  localPort: number;
81
81
  ip: string;
@@ -92,8 +92,7 @@ export function usePreviewServer({
92
92
  createProxyServer(localProtocol)
93
93
  .then((proxy) => setProxyServer(proxy))
94
94
  .catch(async (err) => {
95
- console.error("Failed to create proxy server.", err);
96
- await reportError(err);
95
+ logger.error("Failed to create proxy server:", err);
97
96
  });
98
97
  }
99
98
  }, [proxyServer, localProtocol]);
@@ -211,6 +210,10 @@ export function usePreviewServer({
211
210
  const request = message.pipe(remote.request(headers));
212
211
  request.on("response", (responseHeaders) => {
213
212
  const status = responseHeaders[":status"] ?? 500;
213
+
214
+ // log all requests to terminal
215
+ logger.log(new Date().toLocaleTimeString(), method, url, status);
216
+
214
217
  rewriteRemoteHostToLocalHostInHeaders(
215
218
  responseHeaders,
216
219
  previewToken.host,
@@ -289,21 +292,29 @@ export function usePreviewServer({
289
292
  // Start/stop the server whenever the
290
293
  // containing component is mounted/unmounted.
291
294
  useEffect(() => {
295
+ const abortController = new AbortController();
292
296
  if (proxyServer === undefined) {
293
297
  return;
294
298
  }
295
299
 
296
- waitForPortToBeAvailable(port, { retryPeriod: 200, timeout: 2000 })
300
+ waitForPortToBeAvailable(port, {
301
+ retryPeriod: 200,
302
+ timeout: 2000,
303
+ abortSignal: abortController.signal,
304
+ })
297
305
  .then(() => {
298
306
  proxyServer.listen(port, ip);
299
- console.log(`⬣ Listening at ${localProtocol}://${ip}:${port}`);
307
+ logger.log(`⬣ Listening at ${localProtocol}://${ip}:${port}`);
300
308
  })
301
309
  .catch((err) => {
302
- console.error(`⬣ Failed to start server: ${err}`);
310
+ if ((err as { code: string }).code !== "ABORT_ERR") {
311
+ logger.error(`Failed to start server: ${err}`);
312
+ }
303
313
  });
304
314
 
305
315
  return () => {
306
316
  proxyServer.close();
317
+ abortController.abort();
307
318
  };
308
319
  }, [port, ip, proxyServer, localProtocol]);
309
320
  }
@@ -342,18 +353,9 @@ async function createProxyServer(
342
353
  : createHttpServer();
343
354
 
344
355
  return server
345
- .on("request", function (req, res) {
346
- // log all requests
347
- console.log(
348
- new Date().toLocaleTimeString(),
349
- req.method,
350
- req.url,
351
- res.statusCode
352
- );
353
- })
354
356
  .on("upgrade", (req) => {
355
357
  // log all websocket connections
356
- console.log(
358
+ logger.log(
357
359
  new Date().toLocaleTimeString(),
358
360
  req.method,
359
361
  req.url,
@@ -363,7 +365,7 @@ async function createProxyServer(
363
365
  })
364
366
  .on("error", (err) => {
365
367
  // log all connection errors
366
- console.error(new Date().toLocaleTimeString(), err);
368
+ logger.error(new Date().toLocaleTimeString(), err);
367
369
  });
368
370
  }
369
371
 
@@ -398,9 +400,16 @@ function createStreamHandler(
398
400
  */
399
401
  export async function waitForPortToBeAvailable(
400
402
  port: number,
401
- options: { retryPeriod: number; timeout: number }
403
+ options: { retryPeriod: number; timeout: number; abortSignal: AbortSignal }
402
404
  ): Promise<void> {
403
405
  return new Promise((resolve, reject) => {
406
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
407
+ (options.abortSignal as any).addEventListener("abort", () => {
408
+ const abortError = new Error("waitForPortToBeAvailable() aborted");
409
+ (abortError as Error & { code: string }).code = "ABORT_ERR";
410
+ doReject(abortError);
411
+ });
412
+
404
413
  const timeout = setTimeout(() => {
405
414
  doReject(new Error(`Timed out waiting for port ${port}`));
406
415
  }, options.timeout);
package/src/publish.ts CHANGED
@@ -1,11 +1,12 @@
1
1
  import assert from "node:assert";
2
- import { readFileSync } from "node:fs";
2
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
3
  import path from "node:path";
4
4
  import { URLSearchParams } from "node:url";
5
5
  import tmp from "tmp-promise";
6
6
  import { bundleWorker } from "./bundle";
7
7
  import { fetchResult } from "./cfetch";
8
8
  import { createWorkerUploadForm } from "./create-worker-upload-form";
9
+ import { logger } from "./logger";
9
10
  import { syncAssets } from "./sites";
10
11
  import type { Config } from "./config";
11
12
  import type { Entry } from "./entry";
@@ -22,12 +23,17 @@ type Props = {
22
23
  compatibilityDate: string | undefined;
23
24
  compatibilityFlags: string[] | undefined;
24
25
  assetPaths: AssetPaths | undefined;
25
- triggers: (string | number)[] | undefined;
26
- routes: (string | number)[] | undefined;
26
+ triggers: string[] | undefined;
27
+ routes: string[] | undefined;
27
28
  legacyEnv: boolean | undefined;
28
- jsxFactory: undefined | string;
29
- jsxFragment: undefined | string;
29
+ jsxFactory: string | undefined;
30
+ jsxFragment: string | undefined;
31
+ tsconfig: string | undefined;
30
32
  experimentalPublic: boolean;
33
+ minify: boolean | undefined;
34
+ nodeCompat: boolean | undefined;
35
+ outDir: string | undefined;
36
+ dryRun: boolean | undefined;
31
37
  };
32
38
 
33
39
  function sleep(ms: number) {
@@ -40,18 +46,28 @@ export default async function publish(props: Props): Promise<void> {
40
46
 
41
47
  assert(
42
48
  props.compatibilityDate || config.compatibility_date,
43
- "A compatibility_date is required when publishing. Add one to your wrangler.toml file, or pass it in your terminal as --compatibility_date. See https://developers.cloudflare.com/workers/platform/compatibility-dates for more information."
49
+ "A compatibility_date is required when publishing. Add one to your wrangler.toml file, or pass it in your terminal as --compatibility-date. See https://developers.cloudflare.com/workers/platform/compatibility-dates for more information."
44
50
  );
45
51
 
46
52
  const triggers = props.triggers || config.triggers?.crons;
47
53
  const routes =
48
54
  props.routes ?? config.routes ?? (config.route ? [config.route] : []) ?? [];
49
55
 
50
- const deployToWorkersDev = config.workers_dev;
56
+ // deployToWorkersDev defaults to true only if there aren't any routes defined
57
+ const deployToWorkersDev = config.workers_dev ?? routes.length === 0;
51
58
 
52
59
  const jsxFactory = props.jsxFactory || config.jsx_factory;
53
60
  const jsxFragment = props.jsxFragment || config.jsx_fragment;
54
61
 
62
+ const minify = props.minify ?? config.minify;
63
+
64
+ const nodeCompat = props.nodeCompat ?? config.node_compat;
65
+ if (nodeCompat) {
66
+ logger.warn(
67
+ "Enabling node.js compatibility mode for builtins and globals. This is experimental and has serious tradeoffs. Please see https://github.com/ionic-team/rollup-plugin-node-polyfills/ for more details."
68
+ );
69
+ }
70
+
55
71
  const scriptName = props.name;
56
72
  assert(
57
73
  scriptName,
@@ -63,61 +79,138 @@ export default async function publish(props: Props): Promise<void> {
63
79
  "A [site] definition requires a `bucket` field with a path to the site's public directory."
64
80
  );
65
81
 
66
- const destination = await tmp.dir({ unsafeCleanup: true });
67
- try {
68
- const envName = props.env ?? "production";
82
+ if (props.outDir) {
83
+ // we're using a custom output directory,
84
+ // so let's first ensure it exists
85
+ mkdirSync(props.outDir, { recursive: true });
86
+ // add a README
87
+ const readmePath = path.join(props.outDir, "README.md");
88
+ writeFileSync(
89
+ readmePath,
90
+ `This folder contains the built output assets for the worker "${scriptName}" generated at ${new Date().toISOString()}.`
91
+ );
92
+ }
69
93
 
70
- const { format } = props.entry;
94
+ const destination = props.outDir ?? (await tmp.dir({ unsafeCleanup: true }));
95
+ const envName = props.env ?? "production";
71
96
 
72
- if (props.experimentalPublic && format === "service-worker") {
73
- // TODO: check config too
74
- throw new Error(
75
- "You cannot publish in the service worker format with a public directory."
76
- );
77
- }
97
+ const start = Date.now();
98
+ const notProd = !props.legacyEnv && props.env;
99
+ const workerName = notProd ? `${scriptName} (${envName})` : scriptName;
100
+ const workerUrl = notProd
101
+ ? `/accounts/${accountId}/workers/services/${scriptName}/environments/${envName}`
102
+ : `/accounts/${accountId}/workers/scripts/${scriptName}`;
78
103
 
79
- if (config.wasm_modules && format === "modules") {
80
- throw new Error(
81
- "You cannot configure [wasm_modules] with an ES module worker. Instead, import the .wasm module directly in your code"
82
- );
83
- }
104
+ let available_on_subdomain; // we'll set this later
84
105
 
85
- if (config.text_blobs && format === "modules") {
86
- throw new Error(
87
- "You cannot configure [text_blobs] with an ES module worker. Instead, import the file directly in your code, and optionally configure `[build.upload.rules]` in your wrangler.toml"
88
- );
89
- }
106
+ const { format } = props.entry;
107
+
108
+ if (props.experimentalPublic && format === "service-worker") {
109
+ throw new Error(
110
+ "You cannot publish in the service-worker format with a public directory."
111
+ );
112
+ }
113
+
114
+ if (config.wasm_modules && format === "modules") {
115
+ throw new Error(
116
+ "You cannot configure [wasm_modules] with an ES module worker. Instead, import the .wasm module directly in your code"
117
+ );
118
+ }
119
+
120
+ if (config.text_blobs && format === "modules") {
121
+ throw new Error(
122
+ "You cannot configure [text_blobs] with an ES module worker. Instead, import the file directly in your code, and optionally configure `[rules]` in your wrangler.toml"
123
+ );
124
+ }
90
125
 
126
+ if (config.data_blobs && format === "modules") {
127
+ throw new Error(
128
+ "You cannot configure [data_blobs] with an ES module worker. Instead, import the file directly in your code, and optionally configure `[rules]` in your wrangler.toml"
129
+ );
130
+ }
131
+ try {
91
132
  const { modules, resolvedEntryPointPath, bundleType } = await bundleWorker(
92
133
  props.entry,
93
- destination.path,
134
+ typeof destination === "string" ? destination : destination.path,
94
135
  {
95
136
  serveAssetsFromWorker: props.experimentalPublic,
96
137
  jsxFactory,
97
138
  jsxFragment,
98
139
  rules: props.rules,
140
+ tsconfig: props.tsconfig ?? config.tsconfig,
141
+ minify,
142
+ nodeCompat,
99
143
  }
100
144
  );
101
145
 
146
+ // Some validation of durable objects + migrations
147
+ if (config.durable_objects.bindings.length > 0) {
148
+ // intrinsic [durable_objects] implies [migrations]
149
+ const exportedDurableObjects = config.durable_objects.bindings.filter(
150
+ (binding) => !binding.script_name
151
+ );
152
+ if (exportedDurableObjects.length > 0 && config.migrations.length === 0) {
153
+ logger.warn(
154
+ `In wrangler.toml, you have configured [durable_objects] exported by this Worker (${exportedDurableObjects.map(
155
+ (durable) => durable.class_name
156
+ )}), but no [migrations] for them. This may not work as expected until you add a [migrations] section to your wrangler.toml. Refer to https://developers.cloudflare.com/workers/learning/using-durable-objects/#durable-object-migrations-in-wranglertoml for more details.`
157
+ );
158
+ }
159
+ }
160
+
102
161
  const content = readFileSync(resolvedEntryPointPath, {
103
162
  encoding: "utf-8",
104
163
  });
105
164
 
106
165
  // if config.migrations
107
- // get current migration tag
108
166
  let migrations;
109
167
  if (config.migrations.length > 0) {
110
- const scripts = await fetchResult<
111
- { id: string; migration_tag: string }[]
112
- >(`/accounts/${accountId}/workers/scripts`);
113
- const script = scripts.find(({ id }) => id === scriptName);
168
+ // get current migration tag
169
+ type ScriptData = { id: string; migration_tag?: string };
170
+ let script: ScriptData | undefined;
171
+ if (!props.legacyEnv) {
172
+ try {
173
+ if (props.env) {
174
+ const scriptData = await fetchResult<{
175
+ script: ScriptData;
176
+ }>(
177
+ `/accounts/${accountId}/workers/services/${scriptName}/environments/${props.env}`
178
+ );
179
+ script = scriptData.script;
180
+ } else {
181
+ const scriptData = await fetchResult<{
182
+ default_environment: {
183
+ script: ScriptData;
184
+ };
185
+ }>(`/accounts/${accountId}/workers/services/${scriptName}`);
186
+ script = scriptData.default_environment.script;
187
+ }
188
+ } catch (err) {
189
+ if (
190
+ ![
191
+ 10090, // corresponds to workers.api.error.service_not_found, so the script wasn't previously published at all
192
+ 10092, // workers.api.error.environment_not_found, so the script wasn't published to this environment yet
193
+ ].includes((err as { code: number }).code)
194
+ ) {
195
+ throw err;
196
+ }
197
+ // else it's a 404, no script found, and we can proceed
198
+ }
199
+ } else {
200
+ const scripts = await fetchResult<ScriptData[]>(
201
+ `/accounts/${accountId}/workers/scripts`
202
+ );
203
+ script = scripts.find(({ id }) => id === scriptName);
204
+ }
205
+
114
206
  if (script?.migration_tag) {
115
207
  // was already published once
208
+ const scriptMigrationTag = script.migration_tag;
116
209
  const foundIndex = config.migrations.findIndex(
117
- (migration) => migration.tag === script.migration_tag
210
+ (migration) => migration.tag === scriptMigrationTag
118
211
  );
119
212
  if (foundIndex === -1) {
120
- console.warn(
213
+ logger.warn(
121
214
  `The published script ${scriptName} has a migration tag "${script.migration_tag}, which was not found in wrangler.toml. You may have already deleted it. Applying all available migrations to the script...`
122
215
  );
123
216
  migrations = {
@@ -126,15 +219,21 @@ export default async function publish(props: Props): Promise<void> {
126
219
  steps: config.migrations.map(({ tag: _tag, ...rest }) => rest),
127
220
  };
128
221
  } else {
129
- migrations = {
130
- old_tag: script.migration_tag,
131
- new_tag: config.migrations[config.migrations.length - 1].tag,
132
- steps: config.migrations
133
- .slice(foundIndex + 1)
134
- .map(({ tag: _tag, ...rest }) => rest),
135
- };
222
+ if (foundIndex !== config.migrations.length - 1) {
223
+ // there are new migrations to send up
224
+ migrations = {
225
+ old_tag: script.migration_tag,
226
+ new_tag: config.migrations[config.migrations.length - 1].tag,
227
+ steps: config.migrations
228
+ .slice(foundIndex + 1)
229
+ .map(({ tag: _tag, ...rest }) => rest),
230
+ };
231
+ }
232
+ // else, we're up to date, no migrations to send
136
233
  }
137
234
  } else {
235
+ // first time publishing durable objects to this script,
236
+ // so we send all the migrations
138
237
  migrations = {
139
238
  new_tag: config.migrations[config.migrations.length - 1].tag,
140
239
  steps: config.migrations.map(({ tag: _tag, ...rest }) => rest),
@@ -150,7 +249,8 @@ export default async function publish(props: Props): Promise<void> {
150
249
  // concept of service environments for kv namespaces yet).
151
250
  scriptName + (!props.legacyEnv && props.env ? `-${props.env}` : ""),
152
251
  props.assetPaths,
153
- false
252
+ false,
253
+ props.dryRun
154
254
  );
155
255
 
156
256
  const bindings: CfWorkerInit["bindings"] = {
@@ -168,6 +268,7 @@ export default async function publish(props: Props): Promise<void> {
168
268
  __STATIC_CONTENT_MANIFEST: "__STATIC_CONTENT_MANIFEST",
169
269
  }),
170
270
  },
271
+ data_blobs: config.data_blobs,
171
272
  durable_objects: config.durable_objects,
172
273
  r2_buckets: config.r2_buckets,
173
274
  unsafe: config.unsafe?.bindings,
@@ -197,57 +298,68 @@ export default async function publish(props: Props): Promise<void> {
197
298
  usage_model: config.usage_model,
198
299
  };
199
300
 
200
- const start = Date.now();
201
- const notProd = !props.legacyEnv && props.env;
202
- const workerName = notProd ? `${scriptName} (${envName})` : scriptName;
203
- const workerUrl = notProd
204
- ? `/accounts/${accountId}/workers/services/${scriptName}/environments/${envName}`
205
- : `/accounts/${accountId}/workers/scripts/${scriptName}`;
301
+ if (!props.dryRun) {
302
+ // Upload the script so it has time to propagate.
303
+ // We can also now tell whether available_on_subdomain is set
304
+ available_on_subdomain = (
305
+ await fetchResult<{ available_on_subdomain: boolean }>(
306
+ workerUrl,
307
+ {
308
+ method: "PUT",
309
+ body: createWorkerUploadForm(worker),
310
+ },
311
+ new URLSearchParams({ include_subdomain_availability: "true" })
312
+ )
313
+ ).available_on_subdomain;
314
+ }
315
+ } finally {
316
+ if (typeof destination !== "string") {
317
+ // this means we're using a temp dir,
318
+ // so let's clean up before we proceed
319
+ await destination.cleanup();
320
+ }
321
+ }
206
322
 
207
- // Upload the script so it has time to propagate.
208
- const { available_on_subdomain } = await fetchResult(
209
- workerUrl,
210
- {
211
- method: "PUT",
212
- body: createWorkerUploadForm(worker),
213
- },
214
- new URLSearchParams({ available_on_subdomain: "true" })
215
- );
323
+ if (props.dryRun) {
324
+ logger.log(`--dry-run: exiting now.`);
325
+ return;
326
+ }
216
327
 
217
- const uploadMs = Date.now() - start;
218
- const deployments: Promise<string[]>[] = [];
219
-
220
- if (deployToWorkersDev) {
221
- // Deploy to a subdomain of `workers.dev`
222
- const userSubdomain = await getSubdomain(accountId);
223
- const scriptURL =
224
- props.legacyEnv || !props.env
225
- ? `${scriptName}.${userSubdomain}.workers.dev`
226
- : `${envName}.${scriptName}.${userSubdomain}.workers.dev`;
227
- if (!available_on_subdomain) {
228
- // Enable the `workers.dev` subdomain.
229
- deployments.push(
230
- fetchResult(`${workerUrl}/subdomain`, {
231
- method: "POST",
232
- body: JSON.stringify({ enabled: true }),
233
- headers: {
234
- "Content-Type": "application/json",
235
- },
328
+ const uploadMs = Date.now() - start;
329
+ const deployments: Promise<string[]>[] = [];
330
+
331
+ if (deployToWorkersDev) {
332
+ // Deploy to a subdomain of `workers.dev`
333
+ const userSubdomain = await getSubdomain(accountId);
334
+ const scriptURL =
335
+ props.legacyEnv || !props.env
336
+ ? `${scriptName}.${userSubdomain}.workers.dev`
337
+ : `${envName}.${scriptName}.${userSubdomain}.workers.dev`;
338
+ if (!available_on_subdomain) {
339
+ // Enable the `workers.dev` subdomain.
340
+ deployments.push(
341
+ fetchResult(`${workerUrl}/subdomain`, {
342
+ method: "POST",
343
+ body: JSON.stringify({ enabled: true }),
344
+ headers: {
345
+ "Content-Type": "application/json",
346
+ },
347
+ })
348
+ .then(() => [scriptURL])
349
+ // Add a delay when the subdomain is first created.
350
+ // This is to prevent an issue where a negative cache-hit
351
+ // causes the subdomain to be unavailable for 30 seconds.
352
+ // This is a temporary measure until we fix this on the edge.
353
+ .then(async (url) => {
354
+ await sleep(3000);
355
+ return url;
236
356
  })
237
- .then(() => [scriptURL])
238
- // Add a delay when the subdomain is first created.
239
- // This is to prevent an issue where a negative cache-hit
240
- // causes the subdomain to be unavailable for 30 seconds.
241
- // This is a temporary measure until we fix this on the edge.
242
- .then(async (url) => {
243
- await sleep(3000);
244
- return url;
245
- })
246
- );
247
- } else {
248
- deployments.push(Promise.resolve([scriptURL]));
249
- }
357
+ );
250
358
  } else {
359
+ deployments.push(Promise.resolve([scriptURL]));
360
+ }
361
+ } else {
362
+ if (available_on_subdomain) {
251
363
  // Disable the workers.dev deployment
252
364
  await fetchResult(`${workerUrl}/subdomain`, {
253
365
  method: "POST",
@@ -257,63 +369,73 @@ export default async function publish(props: Props): Promise<void> {
257
369
  },
258
370
  });
259
371
  }
372
+ }
260
373
 
261
- console.log("Uploaded", workerName, formatTime(uploadMs));
374
+ logger.log("Uploaded", workerName, formatTime(uploadMs));
262
375
 
263
- // Update routing table for the script.
264
- if (routes.length > 0) {
265
- deployments.push(
266
- fetchResult(`${workerUrl}/routes`, {
267
- // TODO: PATCH will not delete previous routes on this script,
268
- // whereas PUT will. We need to decide on the default behaviour
269
- // and how to configure it.
270
- method: "PUT",
271
- body: JSON.stringify(routes.map((pattern) => ({ pattern }))),
272
- headers: {
273
- "Content-Type": "application/json",
274
- },
275
- }).then(() => {
276
- if (routes.length > 10) {
277
- return routes
278
- .slice(0, 9)
279
- .map(String)
280
- .concat([`...and ${routes.length - 10} more routes`]);
281
- }
282
- return routes.map(String);
283
- })
284
- );
285
- }
376
+ // Update routing table for the script.
377
+ if (routes.length > 0) {
378
+ deployments.push(
379
+ fetchResult(`${workerUrl}/routes`, {
380
+ // Note: PUT will delete previous routes on this script.
381
+ method: "PUT",
382
+ body: JSON.stringify(
383
+ routes.map((route) =>
384
+ typeof route !== "object" ? { pattern: route } : route
385
+ )
386
+ ),
387
+ headers: {
388
+ "Content-Type": "application/json",
389
+ },
390
+ }).then(() => {
391
+ if (routes.length > 10) {
392
+ return routes
393
+ .slice(0, 9)
394
+ .map((route) =>
395
+ typeof route === "string"
396
+ ? route
397
+ : "zone_id" in route
398
+ ? `${route.pattern} (zone id: ${route.zone_id})`
399
+ : `${route.pattern} (zone name: ${route.zone_name})`
400
+ )
401
+ .concat([`...and ${routes.length - 10} more routes`]);
402
+ }
403
+ return routes.map((route) =>
404
+ typeof route === "string"
405
+ ? route
406
+ : "zone_id" in route
407
+ ? `${route.pattern} (zone id: ${route.zone_id})`
408
+ : `${route.pattern} (zone name: ${route.zone_name})`
409
+ );
410
+ })
411
+ );
412
+ }
286
413
 
287
- // Configure any schedules for the script.
288
- // TODO: rename this to `schedules`?
289
- if (triggers && triggers.length) {
290
- deployments.push(
291
- fetchResult(`${workerUrl}/schedules`, {
292
- // TODO: Unlike routes, this endpoint does not support PATCH.
293
- // So technically, this will override any previous schedules.
294
- // We should change the endpoint to support PATCH.
295
- method: "PUT",
296
- body: JSON.stringify(triggers.map((cron) => ({ cron }))),
297
- headers: {
298
- "Content-Type": "application/json",
299
- },
300
- }).then(() => triggers.map(String))
301
- );
302
- }
414
+ // Configure any schedules for the script.
415
+ // TODO: rename this to `schedules`?
416
+ if (triggers && triggers.length) {
417
+ deployments.push(
418
+ fetchResult(`${workerUrl}/schedules`, {
419
+ // Note: PUT will override previous schedules on this script.
420
+ method: "PUT",
421
+ body: JSON.stringify(triggers.map((cron) => ({ cron }))),
422
+ headers: {
423
+ "Content-Type": "application/json",
424
+ },
425
+ }).then(() => triggers.map((trigger) => `schedule: ${trigger}`))
426
+ );
427
+ }
303
428
 
304
- const targets = await Promise.all(deployments);
305
- const deployMs = Date.now() - start - uploadMs;
429
+ const targets = await Promise.all(deployments);
430
+ const deployMs = Date.now() - start - uploadMs;
306
431
 
307
- if (deployments.length > 0) {
308
- console.log("Published", workerName, formatTime(deployMs));
309
- for (const target of targets.flat()) {
310
- console.log(" ", target);
311
- }
312
- } else {
313
- console.log("No publish targets for", workerName, formatTime(deployMs));
432
+ if (deployments.length > 0) {
433
+ logger.log("Published", workerName, formatTime(deployMs));
434
+ for (const target of targets.flat()) {
435
+ logger.log(" ", target);
314
436
  }
315
- } finally {
316
- await destination.cleanup();
437
+ } else {
438
+ logger.log("No publish targets for", workerName, formatTime(deployMs));
317
439
  }
318
440
  }
319
441