wrangler 0.0.0-e95bea2 → 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 (93) hide show
  1. package/README.md +25 -14
  2. package/bin/wrangler.js +13 -1
  3. package/miniflare-dist/index.mjs +4886 -0
  4. package/package.json +33 -25
  5. package/pages/functions/buildPlugin.ts +92 -0
  6. package/pages/functions/buildWorker.ts +80 -4
  7. package/pages/functions/filepath-routing.test.ts +136 -25
  8. package/pages/functions/filepath-routing.ts +14 -3
  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 +1179 -271
  14. package/src/__tests__/dev.test.tsx +382 -25
  15. package/src/__tests__/guess-worker-format.test.ts +28 -1
  16. package/src/__tests__/helpers/mock-account-id.ts +4 -0
  17. package/src/__tests__/helpers/mock-cfetch.ts +5 -1
  18. package/src/__tests__/helpers/mock-console.ts +20 -3
  19. package/src/__tests__/helpers/mock-dialogs.ts +26 -1
  20. package/src/__tests__/helpers/mock-http-server.ts +46 -0
  21. package/src/__tests__/helpers/mock-istty.ts +27 -0
  22. package/src/__tests__/helpers/mock-kv.ts +9 -16
  23. package/src/__tests__/helpers/mock-oauth-flow.ts +255 -0
  24. package/src/__tests__/helpers/run-in-tmp.ts +10 -0
  25. package/src/__tests__/helpers/write-worker-source.ts +31 -0
  26. package/src/__tests__/https-options.test.ts +5 -2
  27. package/src/__tests__/index.test.ts +27 -769
  28. package/src/__tests__/init.test.ts +1516 -0
  29. package/src/__tests__/jest.setup.ts +4 -4
  30. package/src/__tests__/kv.test.ts +172 -126
  31. package/src/__tests__/logger.test.ts +115 -0
  32. package/src/__tests__/pages.test.ts +311 -26
  33. package/src/__tests__/publish.test.ts +2946 -524
  34. package/src/__tests__/r2.test.ts +26 -10
  35. package/src/__tests__/route.test.ts +4 -4
  36. package/src/__tests__/secret.test.ts +55 -34
  37. package/src/__tests__/tail.test.ts +146 -20
  38. package/src/__tests__/user.test.ts +131 -0
  39. package/src/__tests__/whoami.test.tsx +26 -7
  40. package/src/bundle.ts +30 -3
  41. package/src/cfetch/internal.ts +9 -7
  42. package/src/cli.ts +5 -11
  43. package/src/config/README.md +7 -7
  44. package/src/config/config.ts +33 -78
  45. package/src/config/environment.ts +93 -4
  46. package/src/config/index.ts +15 -4
  47. package/src/config/validation-helpers.ts +202 -15
  48. package/src/config/validation.ts +530 -133
  49. package/src/config-cache.ts +45 -0
  50. package/src/create-worker-preview.ts +22 -13
  51. package/src/create-worker-upload-form.ts +32 -9
  52. package/src/dev/dev-vars.ts +36 -0
  53. package/src/dev/dev.tsx +136 -75
  54. package/src/dev/local.tsx +196 -125
  55. package/src/dev/remote.tsx +25 -13
  56. package/src/dev/use-esbuild.ts +23 -5
  57. package/src/dialogs.tsx +16 -6
  58. package/src/entry.ts +119 -11
  59. package/src/environment-variables.ts +3 -1
  60. package/src/errors.ts +11 -0
  61. package/src/https-options.ts +3 -2
  62. package/src/index.tsx +739 -400
  63. package/src/inspect.ts +17 -11
  64. package/src/intl-polyfill.d.ts +139 -0
  65. package/src/{kv/index.tsx → kv.ts} +8 -28
  66. package/src/logger.ts +66 -0
  67. package/src/miniflare-cli/README.md +30 -0
  68. package/src/miniflare-cli/enum-keys.ts +17 -0
  69. package/src/miniflare-cli/index.ts +41 -0
  70. package/src/miniflare-cli/request-context.ts +40 -0
  71. package/src/module-collection.ts +63 -58
  72. package/src/open-in-browser.ts +5 -4
  73. package/src/package-manager.ts +9 -8
  74. package/src/pages.tsx +883 -62
  75. package/src/parse.ts +1 -1
  76. package/src/proxy.ts +32 -21
  77. package/src/publish.ts +276 -161
  78. package/src/sites.tsx +88 -33
  79. package/src/tail/index.ts +2 -2
  80. package/src/tail/printing.ts +13 -8
  81. package/src/update-check.ts +19 -0
  82. package/src/user.tsx +231 -103
  83. package/src/whoami.tsx +2 -1
  84. package/src/worker.ts +15 -6
  85. package/templates/gitignore +171 -0
  86. package/templates/static-asset-facade.js +1 -1
  87. package/wrangler-dist/cli.js +22087 -49340
  88. package/src/__tests__/dev2.test.tsx +0 -152
  89. package/src/__tests__/helpers/mock-user.ts +0 -27
  90. package/src/__tests__/logout.test.ts +0 -50
  91. package/src/__tests__/sentry.test.ts +0 -146
  92. package/src/reporting.ts +0 -131
  93. package/wrangler-dist/cli.js.map +0 -7
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) {
@@ -38,25 +44,29 @@ export default async function publish(props: Props): Promise<void> {
38
44
  // TODO: warn if git/hg has uncommitted changes
39
45
  const { config, accountId } = props;
40
46
 
41
- // TODO: should we automatically fallback to top level config if there is no matching environment??
42
- const envRootObj = (props.env && config.env[props.env]) || config;
43
-
44
47
  assert(
45
- props.compatibilityDate || envRootObj.compatibility_date,
46
- "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."
48
+ props.compatibilityDate || config.compatibility_date,
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."
47
50
  );
48
51
 
49
- const triggers = props.triggers || envRootObj.triggers?.crons;
52
+ const triggers = props.triggers || config.triggers?.crons;
50
53
  const routes =
51
- props.routes ??
52
- envRootObj.routes ??
53
- (envRootObj.route ? [envRootObj.route] : []) ??
54
- [];
54
+ props.routes ?? config.routes ?? (config.route ? [config.route] : []) ?? [];
55
+
56
+ // deployToWorkersDev defaults to true only if there aren't any routes defined
57
+ const deployToWorkersDev = config.workers_dev ?? routes.length === 0;
58
+
59
+ const jsxFactory = props.jsxFactory || config.jsx_factory;
60
+ const jsxFragment = props.jsxFragment || config.jsx_fragment;
55
61
 
56
- const deployToWorkersDev = envRootObj.workers_dev;
62
+ const minify = props.minify ?? config.minify;
57
63
 
58
- const jsxFactory = props.jsxFactory || envRootObj.jsx_factory;
59
- const jsxFragment = props.jsxFragment || envRootObj.jsx_fragment;
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
+ }
60
70
 
61
71
  const scriptName = props.name;
62
72
  assert(
@@ -69,61 +79,138 @@ export default async function publish(props: Props): Promise<void> {
69
79
  "A [site] definition requires a `bucket` field with a path to the site's public directory."
70
80
  );
71
81
 
72
- const destination = await tmp.dir({ unsafeCleanup: true });
73
- try {
74
- 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
+ }
75
93
 
76
- const { format } = props.entry;
94
+ const destination = props.outDir ?? (await tmp.dir({ unsafeCleanup: true }));
95
+ const envName = props.env ?? "production";
77
96
 
78
- if (props.experimentalPublic && format === "service-worker") {
79
- // TODO: check config too
80
- throw new Error(
81
- "You cannot publish in the service worker format with a public directory."
82
- );
83
- }
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}`;
84
103
 
85
- if (config.wasm_modules && format === "modules") {
86
- throw new Error(
87
- "You cannot configure [wasm_modules] with an ES module worker. Instead, import the .wasm module directly in your code"
88
- );
89
- }
104
+ let available_on_subdomain; // we'll set this later
90
105
 
91
- if (config.text_blobs && format === "modules") {
92
- throw new Error(
93
- "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"
94
- );
95
- }
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
+ }
96
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 {
97
132
  const { modules, resolvedEntryPointPath, bundleType } = await bundleWorker(
98
133
  props.entry,
99
- destination.path,
134
+ typeof destination === "string" ? destination : destination.path,
100
135
  {
101
136
  serveAssetsFromWorker: props.experimentalPublic,
102
137
  jsxFactory,
103
138
  jsxFragment,
104
139
  rules: props.rules,
140
+ tsconfig: props.tsconfig ?? config.tsconfig,
141
+ minify,
142
+ nodeCompat,
105
143
  }
106
144
  );
107
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
+
108
161
  const content = readFileSync(resolvedEntryPointPath, {
109
162
  encoding: "utf-8",
110
163
  });
111
164
 
112
165
  // if config.migrations
113
- // get current migration tag
114
166
  let migrations;
115
167
  if (config.migrations.length > 0) {
116
- const scripts = await fetchResult<
117
- { id: string; migration_tag: string }[]
118
- >(`/accounts/${accountId}/workers/scripts`);
119
- 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
+
120
206
  if (script?.migration_tag) {
121
207
  // was already published once
208
+ const scriptMigrationTag = script.migration_tag;
122
209
  const foundIndex = config.migrations.findIndex(
123
- (migration) => migration.tag === script.migration_tag
210
+ (migration) => migration.tag === scriptMigrationTag
124
211
  );
125
212
  if (foundIndex === -1) {
126
- console.warn(
213
+ logger.warn(
127
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...`
128
215
  );
129
216
  migrations = {
@@ -132,15 +219,21 @@ export default async function publish(props: Props): Promise<void> {
132
219
  steps: config.migrations.map(({ tag: _tag, ...rest }) => rest),
133
220
  };
134
221
  } else {
135
- migrations = {
136
- old_tag: script.migration_tag,
137
- new_tag: config.migrations[config.migrations.length - 1].tag,
138
- steps: config.migrations
139
- .slice(foundIndex + 1)
140
- .map(({ tag: _tag, ...rest }) => rest),
141
- };
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
142
233
  }
143
234
  } else {
235
+ // first time publishing durable objects to this script,
236
+ // so we send all the migrations
144
237
  migrations = {
145
238
  new_tag: config.migrations[config.migrations.length - 1].tag,
146
239
  steps: config.migrations.map(({ tag: _tag, ...rest }) => rest),
@@ -156,16 +249,17 @@ export default async function publish(props: Props): Promise<void> {
156
249
  // concept of service environments for kv namespaces yet).
157
250
  scriptName + (!props.legacyEnv && props.env ? `-${props.env}` : ""),
158
251
  props.assetPaths,
159
- false
252
+ false,
253
+ props.dryRun
160
254
  );
161
255
 
162
256
  const bindings: CfWorkerInit["bindings"] = {
163
- kv_namespaces: (envRootObj.kv_namespaces || []).concat(
257
+ kv_namespaces: (config.kv_namespaces || []).concat(
164
258
  assets.namespace
165
259
  ? { binding: "__STATIC_CONTENT", id: assets.namespace }
166
260
  : []
167
261
  ),
168
- vars: envRootObj.vars,
262
+ vars: config.vars,
169
263
  wasm_modules: config.wasm_modules,
170
264
  text_blobs: {
171
265
  ...config.text_blobs,
@@ -174,9 +268,10 @@ export default async function publish(props: Props): Promise<void> {
174
268
  __STATIC_CONTENT_MANIFEST: "__STATIC_CONTENT_MANIFEST",
175
269
  }),
176
270
  },
177
- durable_objects: envRootObj.durable_objects,
178
- r2_buckets: envRootObj.r2_buckets,
179
- unsafe: envRootObj.unsafe?.bindings,
271
+ data_blobs: config.data_blobs,
272
+ durable_objects: config.durable_objects,
273
+ r2_buckets: config.r2_buckets,
274
+ unsafe: config.unsafe?.bindings,
180
275
  };
181
276
 
182
277
  if (assets.manifest) {
@@ -197,64 +292,74 @@ export default async function publish(props: Props): Promise<void> {
197
292
  bindings,
198
293
  migrations,
199
294
  modules,
200
- compatibility_date:
201
- props.compatibilityDate ?? envRootObj.compatibility_date,
295
+ compatibility_date: props.compatibilityDate ?? config.compatibility_date,
202
296
  compatibility_flags:
203
- props.compatibilityFlags ?? envRootObj.compatibility_flags,
204
- usage_model: envRootObj.usage_model,
297
+ props.compatibilityFlags ?? config.compatibility_flags,
298
+ usage_model: config.usage_model,
205
299
  };
206
300
 
207
- const start = Date.now();
208
- const notProd = !props.legacyEnv && props.env;
209
- const workerName = notProd ? `${scriptName} (${envName})` : scriptName;
210
- const workerUrl = notProd
211
- ? `/accounts/${accountId}/workers/services/${scriptName}/environments/${envName}`
212
- : `/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
+ }
213
322
 
214
- // Upload the script so it has time to propagate.
215
- const { available_on_subdomain } = await fetchResult(
216
- workerUrl,
217
- {
218
- method: "PUT",
219
- body: createWorkerUploadForm(worker),
220
- },
221
- new URLSearchParams({ available_on_subdomain: "true" })
222
- );
323
+ if (props.dryRun) {
324
+ logger.log(`--dry-run: exiting now.`);
325
+ return;
326
+ }
223
327
 
224
- const uploadMs = Date.now() - start;
225
- const deployments: Promise<string[]>[] = [];
226
-
227
- if (deployToWorkersDev) {
228
- // Deploy to a subdomain of `workers.dev`
229
- const userSubdomain = await getSubdomain(accountId);
230
- const scriptURL =
231
- props.legacyEnv || !props.env
232
- ? `${scriptName}.${userSubdomain}.workers.dev`
233
- : `${envName}.${scriptName}.${userSubdomain}.workers.dev`;
234
- if (!available_on_subdomain) {
235
- // Enable the `workers.dev` subdomain.
236
- deployments.push(
237
- fetchResult(`${workerUrl}/subdomain`, {
238
- method: "POST",
239
- body: JSON.stringify({ enabled: true }),
240
- headers: {
241
- "Content-Type": "application/json",
242
- },
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;
243
356
  })
244
- .then(() => [scriptURL])
245
- // Add a delay when the subdomain is first created.
246
- // This is to prevent an issue where a negative cache-hit
247
- // causes the subdomain to be unavailable for 30 seconds.
248
- // This is a temporary measure until we fix this on the edge.
249
- .then(async (url) => {
250
- await sleep(3000);
251
- return url;
252
- })
253
- );
254
- } else {
255
- deployments.push(Promise.resolve([scriptURL]));
256
- }
357
+ );
257
358
  } else {
359
+ deployments.push(Promise.resolve([scriptURL]));
360
+ }
361
+ } else {
362
+ if (available_on_subdomain) {
258
363
  // Disable the workers.dev deployment
259
364
  await fetchResult(`${workerUrl}/subdomain`, {
260
365
  method: "POST",
@@ -264,63 +369,73 @@ export default async function publish(props: Props): Promise<void> {
264
369
  },
265
370
  });
266
371
  }
372
+ }
267
373
 
268
- console.log("Uploaded", workerName, formatTime(uploadMs));
374
+ logger.log("Uploaded", workerName, formatTime(uploadMs));
269
375
 
270
- // Update routing table for the script.
271
- if (routes.length > 0) {
272
- deployments.push(
273
- fetchResult(`${workerUrl}/routes`, {
274
- // TODO: PATCH will not delete previous routes on this script,
275
- // whereas PUT will. We need to decide on the default behaviour
276
- // and how to configure it.
277
- method: "PUT",
278
- body: JSON.stringify(routes.map((pattern) => ({ pattern }))),
279
- headers: {
280
- "Content-Type": "application/json",
281
- },
282
- }).then(() => {
283
- if (routes.length > 10) {
284
- return routes
285
- .slice(0, 9)
286
- .map(String)
287
- .concat([`...and ${routes.length - 10} more routes`]);
288
- }
289
- return routes.map(String);
290
- })
291
- );
292
- }
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
+ }
293
413
 
294
- // Configure any schedules for the script.
295
- // TODO: rename this to `schedules`?
296
- if (triggers && triggers.length) {
297
- deployments.push(
298
- fetchResult(`${workerUrl}/schedules`, {
299
- // TODO: Unlike routes, this endpoint does not support PATCH.
300
- // So technically, this will override any previous schedules.
301
- // We should change the endpoint to support PATCH.
302
- method: "PUT",
303
- body: JSON.stringify(triggers.map((cron) => ({ cron }))),
304
- headers: {
305
- "Content-Type": "application/json",
306
- },
307
- }).then(() => triggers.map(String))
308
- );
309
- }
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
+ }
310
428
 
311
- const targets = await Promise.all(deployments);
312
- const deployMs = Date.now() - start - uploadMs;
429
+ const targets = await Promise.all(deployments);
430
+ const deployMs = Date.now() - start - uploadMs;
313
431
 
314
- if (deployments.length > 0) {
315
- console.log("Published", workerName, formatTime(deployMs));
316
- for (const target of targets.flat()) {
317
- console.log(" ", target);
318
- }
319
- } else {
320
- 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);
321
436
  }
322
- } finally {
323
- await destination.cleanup();
437
+ } else {
438
+ logger.log("No publish targets for", workerName, formatTime(deployMs));
324
439
  }
325
440
  }
326
441