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
@@ -1,41 +1,276 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import * as TOML from "@iarna/toml";
4
+ import { writeAuthConfigFile } from "../user";
4
5
  import { mockAccountId, mockApiToken } from "./helpers/mock-account-id";
5
6
  import {
6
7
  createFetchResult,
7
8
  setMockRawResponse,
8
9
  setMockResponse,
9
10
  unsetAllMocks,
11
+ unsetMockFetchKVGetValues,
10
12
  } from "./helpers/mock-cfetch";
11
- import { mockConsoleMethods } from "./helpers/mock-console";
13
+ import { mockConsoleMethods, normalizeSlashes } from "./helpers/mock-console";
14
+ import { useMockIsTTY } from "./helpers/mock-istty";
12
15
  import { mockKeyListRequest } from "./helpers/mock-kv";
16
+ import { mockOAuthFlow } from "./helpers/mock-oauth-flow";
13
17
  import { runInTempDir } from "./helpers/run-in-tmp";
14
18
  import { runWrangler } from "./helpers/run-wrangler";
19
+ import { writeWorkerSource } from "./helpers/write-worker-source";
15
20
  import writeWranglerToml from "./helpers/write-wrangler-toml";
21
+ import type { Config } from "../config";
16
22
  import type { WorkerMetadata } from "../create-worker-upload-form";
17
23
  import type { KVNamespaceInfo } from "../kv";
24
+ import type { CfWorkerInit } from "../worker";
18
25
  import type { FormData, File } from "undici";
19
26
 
20
27
  describe("publish", () => {
28
+ mockAccountId();
29
+ mockApiToken();
30
+ runInTempDir({ homedir: "./home" });
31
+ const { setIsTTY } = useMockIsTTY();
32
+ const std = mockConsoleMethods();
33
+ const {
34
+ mockOAuthServerCallback,
35
+ mockGrantAccessToken,
36
+ mockGrantAuthorization,
37
+ mockGetMemberships,
38
+ } = mockOAuthFlow();
39
+
21
40
  beforeEach(() => {
22
41
  // @ts-expect-error we're using a very simple setTimeout mock here
23
42
  jest.spyOn(global, "setTimeout").mockImplementation((fn, _period) => {
24
- fn();
43
+ setImmediate(fn);
25
44
  });
45
+ setIsTTY(true);
26
46
  });
27
- mockAccountId();
28
- mockApiToken();
29
- runInTempDir();
30
- const std = mockConsoleMethods();
31
47
 
32
48
  afterEach(() => {
33
49
  unsetAllMocks();
50
+ unsetMockFetchKVGetValues();
51
+ });
52
+
53
+ describe("authentication", () => {
54
+ mockApiToken({ apiToken: null });
55
+ beforeEach(() => {
56
+ // @ts-expect-error disable the mock we'd setup earlier
57
+ // or else our server won't bother listening for oauth requests
58
+ // and will timeout and fail
59
+ global.setTimeout.mockRestore();
60
+ });
61
+
62
+ it("drops a user into the login flow if they're unauthenticated", async () => {
63
+ writeWranglerToml();
64
+ writeWorkerSource();
65
+ mockSubDomainRequest();
66
+ mockUploadWorkerRequest();
67
+
68
+ mockOAuthServerCallback();
69
+ const accessTokenRequest = mockGrantAccessToken({ respondWith: "ok" });
70
+ mockGrantAuthorization({ respondWith: "success" });
71
+
72
+ await expect(runWrangler("publish index.js")).resolves.toBeUndefined();
73
+
74
+ expect(accessTokenRequest.actual.url).toEqual(
75
+ accessTokenRequest.expected.url
76
+ );
77
+
78
+ expect(std.out).toMatchInlineSnapshot(`
79
+ "Attempting to login via OAuth...
80
+ Successfully logged in.
81
+ Uploaded test-name (TIMINGS)
82
+ Published test-name (TIMINGS)
83
+ test-name.test-sub-domain.workers.dev"
84
+ `);
85
+ expect(std.warn).toMatchInlineSnapshot(`""`);
86
+ expect(std.err).toMatchInlineSnapshot(`""`);
87
+ });
88
+
89
+ it("warns a user when they're authenticated with an API token in wrangler config file", async () => {
90
+ writeWranglerToml();
91
+ writeWorkerSource();
92
+ mockSubDomainRequest();
93
+ mockUploadWorkerRequest();
94
+ writeAuthConfigFile({
95
+ api_token: "some-api-token",
96
+ });
97
+
98
+ const accessTokenRequest = mockGrantAccessToken({ respondWith: "ok" });
99
+ mockGrantAuthorization({ respondWith: "success" });
100
+
101
+ await expect(runWrangler("publish index.js")).resolves.toBeUndefined();
102
+
103
+ expect(accessTokenRequest.actual.url).toEqual(
104
+ accessTokenRequest.expected.url
105
+ );
106
+
107
+ expect(std.out).toMatchInlineSnapshot(`
108
+ "Attempting to login via OAuth...
109
+ Successfully logged in.
110
+ Uploaded test-name (TIMINGS)
111
+ Published test-name (TIMINGS)
112
+ test-name.test-sub-domain.workers.dev"
113
+ `);
114
+ expect(std.warn).toMatchInlineSnapshot(`
115
+ "▲ [WARNING] It looks like you have used Wrangler 1's \`config\` command to login with an API token.
116
+
117
+ This is no longer supported in the current version of Wrangler.
118
+ If you wish to authenticate via an API token then please set the \`CLOUDFLARE_API_TOKEN\`
119
+ environment variable.
120
+
121
+ "
122
+ `);
123
+ expect(std.err).toMatchInlineSnapshot(`""`);
124
+ });
125
+
126
+ describe("non-TTY", () => {
127
+ const ENV_COPY = process.env;
128
+
129
+ afterEach(() => {
130
+ process.env = ENV_COPY;
131
+ });
132
+
133
+ it("should not throw an error in non-TTY if 'CLOUDFLARE_API_TOKEN' & 'account_id' are in scope", async () => {
134
+ process.env = {
135
+ CLOUDFLARE_API_TOKEN: "123456789",
136
+ };
137
+ setIsTTY(false);
138
+ writeWranglerToml({
139
+ account_id: "some-account-id",
140
+ });
141
+ writeWorkerSource();
142
+ mockSubDomainRequest();
143
+ mockUploadWorkerRequest();
144
+ mockOAuthServerCallback();
145
+
146
+ await runWrangler("publish index.js");
147
+
148
+ expect(std.out).toMatchInlineSnapshot(`
149
+ "Uploaded test-name (TIMINGS)
150
+ Published test-name (TIMINGS)
151
+ test-name.test-sub-domain.workers.dev"
152
+ `);
153
+ expect(std.err).toMatchInlineSnapshot(`""`);
154
+ });
155
+
156
+ it("should not throw an error if 'CLOUDFLARE_ACCOUNT_ID' & 'CLOUDFLARE_API_TOKEN' are in scope", async () => {
157
+ process.env = {
158
+ CLOUDFLARE_API_TOKEN: "hunter2",
159
+ CLOUDFLARE_ACCOUNT_ID: "some-account-id",
160
+ };
161
+ setIsTTY(false);
162
+ writeWranglerToml();
163
+ writeWorkerSource();
164
+ mockSubDomainRequest();
165
+ mockUploadWorkerRequest();
166
+ mockOAuthServerCallback();
167
+ mockGetMemberships({
168
+ success: true,
169
+ result: [],
170
+ });
171
+
172
+ await runWrangler("publish index.js");
173
+
174
+ expect(std.out).toMatchInlineSnapshot(`
175
+ "Uploaded test-name (TIMINGS)
176
+ Published test-name (TIMINGS)
177
+ test-name.test-sub-domain.workers.dev"
178
+ `);
179
+ expect(std.err).toMatchInlineSnapshot(`""`);
180
+ });
181
+
182
+ it("should throw an error in non-TTY & there is more than one account associated with API token", async () => {
183
+ setIsTTY(false);
184
+ process.env = {
185
+ CLOUDFLARE_API_TOKEN: "hunter2",
186
+ CLOUDFLARE_ACCOUNT_ID: undefined,
187
+ };
188
+ writeWranglerToml({
189
+ account_id: undefined,
190
+ });
191
+ writeWorkerSource();
192
+ mockSubDomainRequest();
193
+ mockUploadWorkerRequest();
194
+ mockOAuthServerCallback();
195
+ mockGetMemberships({
196
+ success: true,
197
+ result: [
198
+ { id: "IG-88", account: { id: "1701", name: "enterprise" } },
199
+ { id: "R2-D2", account: { id: "nx01", name: "enterprise-nx" } },
200
+ ],
201
+ });
202
+
203
+ await expect(runWrangler("publish index.js")).rejects
204
+ .toMatchInlineSnapshot(`
205
+ [Error: More than one account available but unable to select one in non-interactive mode.
206
+ Please set the appropriate \`account_id\` in your \`wrangler.toml\` file.
207
+ Available accounts are ("<name>" - "<id>"):
208
+ "enterprise" - "1701")
209
+ "enterprise-nx" - "nx01")]
210
+ `);
211
+ });
212
+
213
+ it("should throw error in non-TTY if 'CLOUDFLARE_API_TOKEN' is missing", async () => {
214
+ setIsTTY(false);
215
+ writeWranglerToml({
216
+ account_id: undefined,
217
+ });
218
+ process.env = {
219
+ CLOUDFLARE_API_TOKEN: undefined,
220
+ CLOUDFLARE_ACCOUNT_ID: "badwolf",
221
+ };
222
+ writeWorkerSource();
223
+ mockSubDomainRequest();
224
+ mockUploadWorkerRequest();
225
+ mockOAuthServerCallback();
226
+ mockGetMemberships({
227
+ success: true,
228
+ result: [
229
+ { id: "IG-88", account: { id: "1701", name: "enterprise" } },
230
+ { id: "R2-D2", account: { id: "nx01", name: "enterprise-nx" } },
231
+ ],
232
+ });
233
+
234
+ await expect(runWrangler("publish index.js")).rejects.toThrowError();
235
+
236
+ expect(std.err).toMatchInlineSnapshot(`
237
+ "X [ERROR] In a non-interactive environment, it's necessary to set a CLOUDFLARE_API_TOKEN environment variable for wrangler to work. Please go to https://developers.cloudflare.com/api/tokens/create/ for instructions on how to create an api token, and assign its value to CLOUDFLARE_API_TOKEN.
238
+
239
+ "
240
+ `);
241
+ });
242
+ it("should throw error with no account ID provided and no members retrieved", async () => {
243
+ setIsTTY(false);
244
+ writeWranglerToml({
245
+ account_id: undefined,
246
+ });
247
+ process.env = {
248
+ CLOUDFLARE_API_TOKEN: "picard",
249
+ CLOUDFLARE_ACCOUNT_ID: undefined,
250
+ };
251
+ writeWorkerSource();
252
+ mockSubDomainRequest();
253
+ mockUploadWorkerRequest();
254
+ mockOAuthServerCallback();
255
+ mockGetMemberships({
256
+ success: false,
257
+ result: [],
258
+ });
259
+
260
+ await expect(runWrangler("publish index.js")).rejects.toThrowError();
261
+
262
+ expect(std.err).toMatchInlineSnapshot(`
263
+ "X [ERROR] Failed to automatically retrieve account IDs for the logged in user. In a non-interactive environment, it is mandatory to specify an account ID, either by assigning its value to CLOUDFLARE_ACCOUNT_ID, or as \`account_id\` in your \`wrangler.toml\` file.
264
+
265
+ "
266
+ `);
267
+ });
268
+ });
34
269
  });
35
270
 
36
271
  describe("environments", () => {
37
272
  it("should use legacy environments by default", async () => {
38
- writeWranglerToml();
273
+ writeWranglerToml({ env: { "some-env": {} } });
39
274
  writeWorkerSource();
40
275
  mockSubDomainRequest();
41
276
  mockUploadWorkerRequest({
@@ -70,8 +305,8 @@ describe("publish", () => {
70
305
  expect(std.warn).toMatchInlineSnapshot(`""`);
71
306
  });
72
307
 
73
- it("appends the environment name when provided", async () => {
74
- writeWranglerToml();
308
+ it("appends the environment name when provided, and there is associated config", async () => {
309
+ writeWranglerToml({ env: { "some-env": {} } });
75
310
  writeWorkerSource();
76
311
  mockSubDomainRequest();
77
312
  mockUploadWorkerRequest({
@@ -87,6 +322,74 @@ describe("publish", () => {
87
322
  expect(std.err).toMatchInlineSnapshot(`""`);
88
323
  expect(std.warn).toMatchInlineSnapshot(`""`);
89
324
  });
325
+
326
+ it("appends the environment name when provided (with a warning), if there are no configured environments", async () => {
327
+ writeWranglerToml({});
328
+ writeWorkerSource();
329
+ mockSubDomainRequest();
330
+ mockUploadWorkerRequest({
331
+ env: "some-env",
332
+ legacyEnv: true,
333
+ });
334
+ await runWrangler("publish index.js --env some-env --legacy-env true");
335
+ expect(std.out).toMatchInlineSnapshot(`
336
+ "Uploaded test-name-some-env (TIMINGS)
337
+ Published test-name-some-env (TIMINGS)
338
+ test-name-some-env.test-sub-domain.workers.dev"
339
+ `);
340
+ expect(std.err).toMatchInlineSnapshot(`""`);
341
+ expect(std.warn).toMatchInlineSnapshot(`
342
+ "▲ [WARNING] Processing wrangler.toml configuration:
343
+
344
+ - No environment found in configuration with name \\"some-env\\".
345
+ Before using \`--env=some-env\` there should be an equivalent environment section in the
346
+ configuration.
347
+
348
+ Consider adding an environment configuration section to the wrangler.toml file:
349
+ \`\`\`
350
+ [env.some-env]
351
+ \`\`\`
352
+
353
+
354
+ "
355
+ `);
356
+ });
357
+
358
+ it("should throw an error when an environment name when provided, which doesn't match those in the config", async () => {
359
+ writeWranglerToml({ env: { "other-env": {} } });
360
+ writeWorkerSource();
361
+ mockSubDomainRequest();
362
+ await expect(
363
+ runWrangler("publish index.js --env some-env --legacy-env true")
364
+ ).rejects.toThrowErrorMatchingInlineSnapshot(`
365
+ "Processing wrangler.toml configuration:
366
+ - No environment found in configuration with name \\"some-env\\".
367
+ Before using \`--env=some-env\` there should be an equivalent environment section in the configuration.
368
+ The available configured environment names are: [\\"other-env\\"]
369
+
370
+ Consider adding an environment configuration section to the wrangler.toml file:
371
+ \`\`\`
372
+ [env.some-env]
373
+ \`\`\`
374
+ "
375
+ `);
376
+ });
377
+
378
+ it("should throw an error w/ helpful message when using --env --name", async () => {
379
+ writeWranglerToml({ env: { "some-env": {} } });
380
+ writeWorkerSource();
381
+ mockSubDomainRequest();
382
+ await runWrangler(
383
+ "publish index.js --name voyager --env some-env --legacy-env true"
384
+ ).catch((err) =>
385
+ expect(err).toMatchInlineSnapshot(`
386
+ [Error: In legacy environment mode you cannot use --name and --env together. If you want to specify a Worker name for a specific environment you can add the following to your wrangler.toml config:
387
+ [env.some-env]
388
+ name = "voyager"
389
+ ]
390
+ `)
391
+ );
392
+ });
90
393
  });
91
394
 
92
395
  describe("services", () => {
@@ -97,18 +400,25 @@ describe("publish", () => {
97
400
  mockUploadWorkerRequest({
98
401
  legacyEnv: false,
99
402
  });
100
- await runWrangler("publish index.js");
403
+ await runWrangler("publish index.js --legacy-env false");
101
404
  expect(std.out).toMatchInlineSnapshot(`
102
405
  "Uploaded test-name (TIMINGS)
103
406
  Published test-name (TIMINGS)
104
407
  test-name.test-sub-domain.workers.dev"
105
408
  `);
106
409
  expect(std.err).toMatchInlineSnapshot(`""`);
107
- expect(std.warn).toMatchInlineSnapshot(`""`);
410
+ expect(std.warn).toMatchInlineSnapshot(`
411
+ "▲ [WARNING] Processing wrangler.toml configuration:
412
+
413
+ - Experimental: Service environments are in beta, and their behaviour is guaranteed to change in
414
+ the future. DO NOT USE IN PRODUCTION.
415
+
416
+ "
417
+ `);
108
418
  });
109
419
 
110
420
  it("publishes as an environment when provided", async () => {
111
- writeWranglerToml();
421
+ writeWranglerToml({ env: { "some-env": {} } });
112
422
  writeWorkerSource();
113
423
  mockSubDomainRequest();
114
424
  mockUploadWorkerRequest({
@@ -122,7 +432,14 @@ describe("publish", () => {
122
432
  some-env.test-name.test-sub-domain.workers.dev"
123
433
  `);
124
434
  expect(std.err).toMatchInlineSnapshot(`""`);
125
- expect(std.warn).toMatchInlineSnapshot(`""`);
435
+ expect(std.warn).toMatchInlineSnapshot(`
436
+ "▲ [WARNING] Processing wrangler.toml configuration:
437
+
438
+ - Experimental: Service environments are in beta, and their behaviour is guaranteed to change in
439
+ the future. DO NOT USE IN PRODUCTION.
440
+
441
+ "
442
+ `);
126
443
  });
127
444
  });
128
445
  });
@@ -171,6 +488,116 @@ describe("publish", () => {
171
488
  await runWrangler("publish ./index");
172
489
  });
173
490
 
491
+ it("should publish to a route with a pattern/{zone_id|zone_name} combo", async () => {
492
+ writeWranglerToml({
493
+ routes: [
494
+ "some-example.com/some-route/*",
495
+ { pattern: "*a-boring-website.com", zone_id: "54sdf7fsda" },
496
+ {
497
+ pattern: "*another-boring-website.com",
498
+ zone_name: "some-zone.com",
499
+ },
500
+ { pattern: "example.com/some-route/*", zone_id: "JGHFHG654gjcj" },
501
+ "more-examples.com/*",
502
+ ],
503
+ });
504
+ writeWorkerSource();
505
+ mockUpdateWorkerRequest({ enabled: false });
506
+ mockUploadWorkerRequest({ expectedType: "esm" });
507
+ mockPublishRoutesRequest({
508
+ routes: [
509
+ "some-example.com/some-route/*",
510
+ { pattern: "*a-boring-website.com", zone_id: "54sdf7fsda" },
511
+ {
512
+ pattern: "*another-boring-website.com",
513
+ zone_name: "some-zone.com",
514
+ },
515
+ { pattern: "example.com/some-route/*", zone_id: "JGHFHG654gjcj" },
516
+ "more-examples.com/*",
517
+ ],
518
+ });
519
+ await runWrangler("publish ./index");
520
+ expect(std).toMatchInlineSnapshot(`
521
+ Object {
522
+ "debug": "",
523
+ "err": "",
524
+ "out": "Uploaded test-name (TIMINGS)
525
+ Published test-name (TIMINGS)
526
+ some-example.com/some-route/*
527
+ *a-boring-website.com (zone id: 54sdf7fsda)
528
+ *another-boring-website.com (zone name: some-zone.com)
529
+ example.com/some-route/* (zone id: JGHFHG654gjcj)
530
+ more-examples.com/*",
531
+ "warn": "",
532
+ }
533
+ `);
534
+ });
535
+
536
+ it("should publish to a route with a pattern/{zone_id|zone_name} combo (service environments)", async () => {
537
+ writeWranglerToml({
538
+ env: {
539
+ staging: {
540
+ routes: [
541
+ "some-example.com/some-route/*",
542
+ { pattern: "*a-boring-website.com", zone_id: "54sdf7fsda" },
543
+ {
544
+ pattern: "*another-boring-website.com",
545
+ zone_name: "some-zone.com",
546
+ },
547
+ { pattern: "example.com/some-route/*", zone_id: "JGHFHG654gjcj" },
548
+ "more-examples.com/*",
549
+ ],
550
+ },
551
+ },
552
+ });
553
+ mockSubDomainRequest();
554
+ writeWorkerSource();
555
+ mockUpdateWorkerRequest({
556
+ enabled: false,
557
+ env: "staging",
558
+ legacyEnv: false,
559
+ });
560
+ mockUploadWorkerRequest({
561
+ expectedType: "esm",
562
+ env: "staging",
563
+ legacyEnv: false,
564
+ });
565
+ mockPublishRoutesRequest({
566
+ routes: [
567
+ "some-example.com/some-route/*",
568
+ { pattern: "*a-boring-website.com", zone_id: "54sdf7fsda" },
569
+ {
570
+ pattern: "*another-boring-website.com",
571
+ zone_name: "some-zone.com",
572
+ },
573
+ { pattern: "example.com/some-route/*", zone_id: "JGHFHG654gjcj" },
574
+ "more-examples.com/*",
575
+ ],
576
+ env: "staging",
577
+ legacyEnv: false,
578
+ });
579
+ await runWrangler("publish ./index --legacy-env false --env staging");
580
+ expect(std).toMatchInlineSnapshot(`
581
+ Object {
582
+ "debug": "",
583
+ "err": "",
584
+ "out": "Uploaded test-name (staging) (TIMINGS)
585
+ Published test-name (staging) (TIMINGS)
586
+ some-example.com/some-route/*
587
+ *a-boring-website.com (zone id: 54sdf7fsda)
588
+ *another-boring-website.com (zone name: some-zone.com)
589
+ example.com/some-route/* (zone id: JGHFHG654gjcj)
590
+ more-examples.com/*",
591
+ "warn": "▲ [WARNING] Processing wrangler.toml configuration:
592
+
593
+ - Experimental: Service environments are in beta, and their behaviour is guaranteed to change in
594
+ the future. DO NOT USE IN PRODUCTION.
595
+
596
+ ",
597
+ }
598
+ `);
599
+ });
600
+
174
601
  it("should publish to legacy environment specific routes", async () => {
175
602
  writeWranglerToml({
176
603
  routes: ["example.com/some-route/*"],
@@ -302,13 +729,16 @@ describe("publish", () => {
302
729
  `);
303
730
  expect(std.err).toMatchInlineSnapshot(`""`);
304
731
  expect(std.warn).toMatchInlineSnapshot(`
305
- "Processing wrangler.toml configuration:
306
- - DEPRECATION: \\"build.upload.main\\":
307
- Delete the \`build.upload.main\` and \`build.upload.dir\` fields.
308
- Then add the top level \`main\` field to your configuration file:
309
- \`\`\`
310
- main = \\"dist/index.js\\"
311
- \`\`\`"
732
+ "▲ [WARNING] Processing wrangler.toml configuration:
733
+
734
+ - Deprecation: \\"build.upload.main\\":
735
+ Delete the \`build.upload.main\` and \`build.upload.dir\` fields.
736
+ Then add the top level \`main\` field to your configuration file:
737
+ \`\`\`
738
+ main = \\"dist/index.js\\"
739
+ \`\`\`
740
+
741
+ "
312
742
  `);
313
743
  });
314
744
 
@@ -334,15 +764,19 @@ describe("publish", () => {
334
764
  `);
335
765
  expect(std.err).toMatchInlineSnapshot(`""`);
336
766
  expect(std.warn).toMatchInlineSnapshot(`
337
- "Processing ../wrangler.toml configuration:
338
- - DEPRECATION: \\"build.upload.main\\":
339
- Delete the \`build.upload.main\` and \`build.upload.dir\` fields.
340
- Then add the top level \`main\` field to your configuration file:
341
- \`\`\`
342
- main = \\"foo/index.js\\"
343
- \`\`\`
344
- - DEPRECATION: \\"build.upload.dir\\":
345
- Use the top level \\"main\\" field or a command-line argument to specify the entry-point for the Worker."
767
+ "▲ [WARNING] Processing ../wrangler.toml configuration:
768
+
769
+ - Deprecation: \\"build.upload.main\\":
770
+ Delete the \`build.upload.main\` and \`build.upload.dir\` fields.
771
+ Then add the top level \`main\` field to your configuration file:
772
+ \`\`\`
773
+ main = \\"foo/index.js\\"
774
+ \`\`\`
775
+ - Deprecation: \\"build.upload.dir\\":
776
+ Use the top level \\"main\\" field or a command-line argument to specify the entry-point for the
777
+ Worker.
778
+
779
+ "
346
780
  `);
347
781
  });
348
782
 
@@ -478,17 +912,32 @@ export default{
478
912
  - \\"site.bucket\\" is a required field."
479
913
  `);
480
914
 
481
- expect(std.out).toMatchInlineSnapshot(`""`);
915
+ expect(std.out).toMatchInlineSnapshot(`
916
+ "
917
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
918
+ `);
482
919
  expect(std.err).toMatchInlineSnapshot(`
483
- "Processing wrangler.toml configuration:
484
- - \\"site.bucket\\" is a required field.
920
+ "X [ERROR] Processing wrangler.toml configuration:
485
921
 
486
- %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
922
+ - \\"site.bucket\\" is a required field.
923
+
924
+ "
925
+ `);
926
+ expect(normalizeSlashes(std.warn)).toMatchInlineSnapshot(`
927
+ "▲ [WARNING] Processing wrangler.toml configuration:
928
+
929
+ - Because you've defined a [site] configuration, we're defaulting to \\"workers-site\\" for the
930
+ deprecated \`site.entry-point\`field.
931
+ Add the top level \`main\` field to your configuration file:
932
+ \`\`\`
933
+ main = \\"workers-site/index.js\\"
934
+ \`\`\`
935
+
936
+ "
487
937
  `);
488
- expect(std.warn).toMatchInlineSnapshot(`""`);
489
938
  });
490
939
 
491
- it("should error if there is a `site.entry-point` configuration", async () => {
940
+ it("should warn if there is a `site.entry-point` configuration", async () => {
492
941
  const assets = [
493
942
  { filePath: "assets/file-1.txt", content: "Content of file-1" },
494
943
  { filePath: "assets/file-2.txt", content: "Content of file-2" },
@@ -511,52 +960,35 @@ export default{
511
960
  mockListKVNamespacesRequest(kvNamespace);
512
961
  mockKeyListRequest(kvNamespace.id, []);
513
962
  mockUploadAssetsToKVRequest(kvNamespace.id, assets);
514
-
515
- await expect(runWrangler("publish ./index.js")).rejects
516
- .toThrowErrorMatchingInlineSnapshot(`
517
- "Processing wrangler.toml configuration:
518
- - DEPRECATION: \\"site.entry-point\\":
519
- The \`site.entry-point\` config field is no longer used.
520
- The entry-point should be specified via the command line or the \`main\` config field."
521
- `);
522
-
523
- expect(std.out).toMatchInlineSnapshot(`""`);
524
- expect(std.err).toMatchInlineSnapshot(`
525
- "Processing wrangler.toml configuration:
526
- - DEPRECATION: \\"site.entry-point\\":
527
- The \`site.entry-point\` config field is no longer used.
528
- The entry-point should be specified via the command line or the \`main\` config field.
529
-
530
- %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
531
- `);
532
- expect(std.warn).toMatchInlineSnapshot(`
533
- "Processing wrangler.toml configuration:
534
- - Unexpected fields found in site field: \\"entry-point\\""
535
- `);
536
- });
537
-
538
- it("should error if there is no entry-point specified", async () => {
539
- writeWranglerToml();
540
- writeWorkerSource();
541
- mockUploadWorkerRequest();
542
- mockSubDomainRequest();
543
- await expect(
544
- runWrangler("publish")
545
- ).rejects.toThrowErrorMatchingInlineSnapshot(
546
- `"Missing entry-point: The entry-point should be specified via the command line (e.g. \`wrangler publish path/to/script\`) or the \`main\` config field."`
547
- );
548
-
549
- expect(std.out).toMatchInlineSnapshot(`""`);
550
- expect(std.err).toMatchInlineSnapshot(`
551
- "Missing entry-point: The entry-point should be specified via the command line (e.g. \`wrangler publish path/to/script\`) or the \`main\` config field.
552
-
553
- %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
963
+ await runWrangler("publish ./index.js");
964
+
965
+ expect(std).toMatchInlineSnapshot(`
966
+ Object {
967
+ "debug": "",
968
+ "err": "",
969
+ "out": "Reading assets/file-1.txt...
970
+ Uploading as assets/file-1.2ca234f380.txt...
971
+ Reading assets/file-2.txt...
972
+ Uploading as assets/file-2.5938485188.txt...
973
+ ↗️ Done syncing assets
974
+ Uploaded test-name (TIMINGS)
975
+ Published test-name (TIMINGS)
976
+ test-name.test-sub-domain.workers.dev",
977
+ "warn": "▲ [WARNING] Processing wrangler.toml configuration:
978
+
979
+ - Deprecation: \\"site.entry-point\\":
980
+ Delete the \`site.entry-point\` field, then add the top level \`main\` field to your configuration
981
+ file:
982
+ \`\`\`
983
+ main = \\"index.js\\"
984
+ \`\`\`
985
+
986
+ ",
987
+ }
554
988
  `);
555
989
  });
556
- });
557
990
 
558
- describe("asset upload", () => {
559
- it("should upload all the files in the directory specified by `config.site.bucket`", async () => {
991
+ it("should resolve site.entry-point relative to wrangler.toml", async () => {
560
992
  const assets = [
561
993
  { filePath: "assets/file-1.txt", content: "Content of file-1" },
562
994
  { filePath: "assets/file-2.txt", content: "Content of file-2" },
@@ -565,51 +997,147 @@ export default{
565
997
  title: "__test-name-workers_sites_assets",
566
998
  id: "__test-name-workers_sites_assets-id",
567
999
  };
1000
+ fs.mkdirSync("my-site");
1001
+ process.chdir("my-site");
568
1002
  writeWranglerToml({
569
- main: "./index.js",
570
1003
  site: {
571
1004
  bucket: "assets",
1005
+ "entry-point": "my-entry",
572
1006
  },
573
1007
  });
574
- writeWorkerSource();
1008
+ fs.mkdirSync("my-entry");
1009
+ fs.writeFileSync("my-entry/index.js", "export default {}");
575
1010
  writeAssets(assets);
1011
+ process.chdir("..");
576
1012
  mockUploadWorkerRequest();
577
1013
  mockSubDomainRequest();
578
1014
  mockListKVNamespacesRequest(kvNamespace);
579
1015
  mockKeyListRequest(kvNamespace.id, []);
580
1016
  mockUploadAssetsToKVRequest(kvNamespace.id, assets);
581
- await runWrangler("publish");
1017
+ await runWrangler("publish --config ./my-site/wrangler.toml");
582
1018
 
583
1019
  expect(std.out).toMatchInlineSnapshot(`
584
- "reading assets/file-1.txt...
585
- uploading as assets/file-1.2ca234f380.txt...
586
- reading assets/file-2.txt...
587
- uploading as assets/file-2.5938485188.txt...
1020
+ "Reading assets/file-1.txt...
1021
+ Uploading as assets/file-1.2ca234f380.txt...
1022
+ Reading assets/file-2.txt...
1023
+ Uploading as assets/file-2.5938485188.txt...
1024
+ ↗️ Done syncing assets
588
1025
  Uploaded test-name (TIMINGS)
589
1026
  Published test-name (TIMINGS)
590
1027
  test-name.test-sub-domain.workers.dev"
591
1028
  `);
592
1029
  expect(std.err).toMatchInlineSnapshot(`""`);
1030
+ expect(normalizeSlashes(std.warn)).toMatchInlineSnapshot(`
1031
+ "▲ [WARNING] Processing my-site/wrangler.toml configuration:
1032
+
1033
+ - Deprecation: \\"site.entry-point\\":
1034
+ Delete the \`site.entry-point\` field, then add the top level \`main\` field to your configuration
1035
+ file:
1036
+ \`\`\`
1037
+ main = \\"my-entry/index.js\\"
1038
+ \`\`\`
1039
+
1040
+ "
1041
+ `);
593
1042
  });
594
1043
 
595
- it("when using a service worker type, it should add an asset manifest as a text_blob, and bind to a namespace", async () => {
596
- const assets = [
597
- { filePath: "assets/file-1.txt", content: "Content of file-1" },
598
- { filePath: "assets/file-2.txt", content: "Content of file-2" },
599
- ];
600
- const kvNamespace = {
601
- title: "__test-name-workers_sites_assets",
602
- id: "__test-name-workers_sites_assets-id",
603
- };
1044
+ it("should error if both main and site.entry-point are specified", async () => {
604
1045
  writeWranglerToml({
605
- main: "./index.js",
1046
+ main: "some-entry",
606
1047
  site: {
607
- bucket: "assets",
1048
+ bucket: "some-bucket",
1049
+ "entry-point": "./index.js",
608
1050
  },
609
1051
  });
610
- writeWorkerSource({ type: "sw" });
611
- writeAssets(assets);
612
- mockUploadWorkerRequest({
1052
+
1053
+ await expect(runWrangler("publish")).rejects
1054
+ .toThrowErrorMatchingInlineSnapshot(`
1055
+ "Processing wrangler.toml configuration:
1056
+ - Don't define both the \`main\` and \`site.entry-point\` fields in your configuration.
1057
+ They serve the same purpose: to point to the entry-point of your worker.
1058
+ Delete the deprecated \`site.entry-point\` field from your config."
1059
+ `);
1060
+ });
1061
+
1062
+ it("should error if there is no entry-point specified", async () => {
1063
+ writeWranglerToml();
1064
+ writeWorkerSource();
1065
+ mockUploadWorkerRequest();
1066
+ mockSubDomainRequest();
1067
+ await expect(
1068
+ runWrangler("publish")
1069
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
1070
+ `"Missing entry-point: The entry-point should be specified via the command line (e.g. \`wrangler publish path/to/script\`) or the \`main\` config field."`
1071
+ );
1072
+
1073
+ expect(std.out).toMatchInlineSnapshot(`
1074
+ "
1075
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
1076
+ `);
1077
+ expect(std.err).toMatchInlineSnapshot(`
1078
+ "X [ERROR] Missing entry-point: The entry-point should be specified via the command line (e.g. \`wrangler publish path/to/script\`) or the \`main\` config field.
1079
+
1080
+ "
1081
+ `);
1082
+ });
1083
+ });
1084
+
1085
+ describe("asset upload", () => {
1086
+ it("should upload all the files in the directory specified by `config.site.bucket`", async () => {
1087
+ const assets = [
1088
+ { filePath: "assets/file-1.txt", content: "Content of file-1" },
1089
+ { filePath: "assets/file-2.txt", content: "Content of file-2" },
1090
+ ];
1091
+ const kvNamespace = {
1092
+ title: "__test-name-workers_sites_assets",
1093
+ id: "__test-name-workers_sites_assets-id",
1094
+ };
1095
+ writeWranglerToml({
1096
+ main: "./index.js",
1097
+ site: {
1098
+ bucket: "assets",
1099
+ },
1100
+ });
1101
+ writeWorkerSource();
1102
+ writeAssets(assets);
1103
+ mockUploadWorkerRequest();
1104
+ mockSubDomainRequest();
1105
+ mockListKVNamespacesRequest(kvNamespace);
1106
+ mockKeyListRequest(kvNamespace.id, []);
1107
+ mockUploadAssetsToKVRequest(kvNamespace.id, assets);
1108
+ await runWrangler("publish");
1109
+
1110
+ expect(std.out).toMatchInlineSnapshot(`
1111
+ "Reading assets/file-1.txt...
1112
+ Uploading as assets/file-1.2ca234f380.txt...
1113
+ Reading assets/file-2.txt...
1114
+ Uploading as assets/file-2.5938485188.txt...
1115
+ ↗️ Done syncing assets
1116
+ Uploaded test-name (TIMINGS)
1117
+ Published test-name (TIMINGS)
1118
+ test-name.test-sub-domain.workers.dev"
1119
+ `);
1120
+ expect(std.err).toMatchInlineSnapshot(`""`);
1121
+ });
1122
+
1123
+ it("when using a service-worker type, it should add an asset manifest as a text_blob, and bind to a namespace", async () => {
1124
+ const assets = [
1125
+ { filePath: "assets/file-1.txt", content: "Content of file-1" },
1126
+ { filePath: "assets/file-2.txt", content: "Content of file-2" },
1127
+ ];
1128
+ const kvNamespace = {
1129
+ title: "__test-name-workers_sites_assets",
1130
+ id: "__test-name-workers_sites_assets-id",
1131
+ };
1132
+ writeWranglerToml({
1133
+ main: "./index.js",
1134
+ site: {
1135
+ bucket: "assets",
1136
+ },
1137
+ });
1138
+ writeWorkerSource({ type: "sw" });
1139
+ writeAssets(assets);
1140
+ mockUploadWorkerRequest({
613
1141
  expectedType: "sw",
614
1142
  expectedModules: {
615
1143
  __STATIC_CONTENT_MANIFEST:
@@ -636,10 +1164,11 @@ export default{
636
1164
  await runWrangler("publish");
637
1165
 
638
1166
  expect(std.out).toMatchInlineSnapshot(`
639
- "reading assets/file-1.txt...
640
- uploading as assets/file-1.2ca234f380.txt...
641
- reading assets/file-2.txt...
642
- uploading as assets/file-2.5938485188.txt...
1167
+ "Reading assets/file-1.txt...
1168
+ Uploading as assets/file-1.2ca234f380.txt...
1169
+ Reading assets/file-2.txt...
1170
+ Uploading as assets/file-2.5938485188.txt...
1171
+ ↗️ Done syncing assets
643
1172
  Uploaded test-name (TIMINGS)
644
1173
  Published test-name (TIMINGS)
645
1174
  test-name.test-sub-domain.workers.dev"
@@ -685,10 +1214,11 @@ export default{
685
1214
  await runWrangler("publish");
686
1215
 
687
1216
  expect(std.out).toMatchInlineSnapshot(`
688
- "reading assets/file-1.txt...
689
- uploading as assets/file-1.2ca234f380.txt...
690
- reading assets/file-2.txt...
691
- uploading as assets/file-2.5938485188.txt...
1217
+ "Reading assets/file-1.txt...
1218
+ Uploading as assets/file-1.2ca234f380.txt...
1219
+ Reading assets/file-2.txt...
1220
+ Uploading as assets/file-2.5938485188.txt...
1221
+ ↗️ Done syncing assets
692
1222
  Uploaded test-name (TIMINGS)
693
1223
  Published test-name (TIMINGS)
694
1224
  test-name.test-sub-domain.workers.dev"
@@ -711,6 +1241,7 @@ export default{
711
1241
  site: {
712
1242
  bucket: "assets",
713
1243
  },
1244
+ env: { "some-env": {} },
714
1245
  });
715
1246
  writeWorkerSource();
716
1247
  writeAssets(assets);
@@ -731,10 +1262,11 @@ export default{
731
1262
  await runWrangler("publish --env some-env --legacy-env false");
732
1263
 
733
1264
  expect(std.out).toMatchInlineSnapshot(`
734
- "reading assets/file-1.txt...
735
- uploading as assets/file-1.2ca234f380.txt...
736
- reading assets/file-2.txt...
737
- uploading as assets/file-2.5938485188.txt...
1265
+ "Reading assets/file-1.txt...
1266
+ Uploading as assets/file-1.2ca234f380.txt...
1267
+ Reading assets/file-2.txt...
1268
+ Uploading as assets/file-2.5938485188.txt...
1269
+ ↗️ Done syncing assets
738
1270
  Uploaded test-name (some-env) (TIMINGS)
739
1271
  Published test-name (some-env) (TIMINGS)
740
1272
  some-env.test-name.test-sub-domain.workers.dev"
@@ -757,6 +1289,7 @@ export default{
757
1289
  site: {
758
1290
  bucket: "assets",
759
1291
  },
1292
+ env: { "some-env": {} },
760
1293
  });
761
1294
  writeWorkerSource();
762
1295
  writeAssets(assets);
@@ -778,10 +1311,11 @@ export default{
778
1311
  await runWrangler("publish --env some-env --legacy-env true");
779
1312
 
780
1313
  expect(std.out).toMatchInlineSnapshot(`
781
- "reading assets/file-1.txt...
782
- uploading as assets/file-1.2ca234f380.txt...
783
- reading assets/file-2.txt...
784
- uploading as assets/file-2.5938485188.txt...
1314
+ "Reading assets/file-1.txt...
1315
+ Uploading as assets/file-1.2ca234f380.txt...
1316
+ Reading assets/file-2.txt...
1317
+ Uploading as assets/file-2.5938485188.txt...
1318
+ ↗️ Done syncing assets
785
1319
  Uploaded test-name-some-env (TIMINGS)
786
1320
  Published test-name-some-env (TIMINGS)
787
1321
  test-name-some-env.test-sub-domain.workers.dev"
@@ -810,7 +1344,9 @@ export default{
810
1344
  mockSubDomainRequest();
811
1345
  mockListKVNamespacesRequest(kvNamespace);
812
1346
  // Put file-1 in the KV namespace
813
- mockKeyListRequest(kvNamespace.id, ["assets/file-1.2ca234f380.txt"]);
1347
+ mockKeyListRequest(kvNamespace.id, [
1348
+ { name: "assets/file-1.2ca234f380.txt" },
1349
+ ]);
814
1350
  // Check we do not upload file-1
815
1351
  mockUploadAssetsToKVRequest(
816
1352
  kvNamespace.id,
@@ -819,10 +1355,11 @@ export default{
819
1355
  await runWrangler("publish");
820
1356
 
821
1357
  expect(std.out).toMatchInlineSnapshot(`
822
- "reading assets/file-1.txt...
823
- skipping - already uploaded
824
- reading assets/file-2.txt...
825
- uploading as assets/file-2.5938485188.txt...
1358
+ "Reading assets/file-1.txt...
1359
+ Skipping - already uploaded.
1360
+ Reading assets/file-2.txt...
1361
+ Uploading as assets/file-2.5938485188.txt...
1362
+ ↗️ Done syncing assets
826
1363
  Uploaded test-name (TIMINGS)
827
1364
  Published test-name (TIMINGS)
828
1365
  test-name.test-sub-domain.workers.dev"
@@ -859,8 +1396,9 @@ export default{
859
1396
  await runWrangler("publish --site-include file-1.txt");
860
1397
 
861
1398
  expect(std.out).toMatchInlineSnapshot(`
862
- "reading assets/file-1.txt...
863
- uploading as assets/file-1.2ca234f380.txt...
1399
+ "Reading assets/file-1.txt...
1400
+ Uploading as assets/file-1.2ca234f380.txt...
1401
+ ↗️ Done syncing assets
864
1402
  Uploaded test-name (TIMINGS)
865
1403
  Published test-name (TIMINGS)
866
1404
  test-name.test-sub-domain.workers.dev"
@@ -897,8 +1435,9 @@ export default{
897
1435
  await runWrangler("publish --site-exclude file-2.txt");
898
1436
 
899
1437
  expect(std.out).toMatchInlineSnapshot(`
900
- "reading assets/file-1.txt...
901
- uploading as assets/file-1.2ca234f380.txt...
1438
+ "Reading assets/file-1.txt...
1439
+ Uploading as assets/file-1.2ca234f380.txt...
1440
+ ↗️ Done syncing assets
902
1441
  Uploaded test-name (TIMINGS)
903
1442
  Published test-name (TIMINGS)
904
1443
  test-name.test-sub-domain.workers.dev"
@@ -936,8 +1475,9 @@ export default{
936
1475
  await runWrangler("publish");
937
1476
 
938
1477
  expect(std.out).toMatchInlineSnapshot(`
939
- "reading assets/file-1.txt...
940
- uploading as assets/file-1.2ca234f380.txt...
1478
+ "Reading assets/file-1.txt...
1479
+ Uploading as assets/file-1.2ca234f380.txt...
1480
+ ↗️ Done syncing assets
941
1481
  Uploaded test-name (TIMINGS)
942
1482
  Published test-name (TIMINGS)
943
1483
  test-name.test-sub-domain.workers.dev"
@@ -975,8 +1515,9 @@ export default{
975
1515
  await runWrangler("publish");
976
1516
 
977
1517
  expect(std.out).toMatchInlineSnapshot(`
978
- "reading assets/file-1.txt...
979
- uploading as assets/file-1.2ca234f380.txt...
1518
+ "Reading assets/file-1.txt...
1519
+ Uploading as assets/file-1.2ca234f380.txt...
1520
+ ↗️ Done syncing assets
980
1521
  Uploaded test-name (TIMINGS)
981
1522
  Published test-name (TIMINGS)
982
1523
  test-name.test-sub-domain.workers.dev"
@@ -1014,8 +1555,9 @@ export default{
1014
1555
  await runWrangler("publish --site-include file-1.txt");
1015
1556
 
1016
1557
  expect(std.out).toMatchInlineSnapshot(`
1017
- "reading assets/file-1.txt...
1018
- uploading as assets/file-1.2ca234f380.txt...
1558
+ "Reading assets/file-1.txt...
1559
+ Uploading as assets/file-1.2ca234f380.txt...
1560
+ ↗️ Done syncing assets
1019
1561
  Uploaded test-name (TIMINGS)
1020
1562
  Published test-name (TIMINGS)
1021
1563
  test-name.test-sub-domain.workers.dev"
@@ -1053,8 +1595,9 @@ export default{
1053
1595
  await runWrangler("publish --site-exclude file-2.txt");
1054
1596
 
1055
1597
  expect(std.out).toMatchInlineSnapshot(`
1056
- "reading assets/file-1.txt...
1057
- uploading as assets/file-1.2ca234f380.txt...
1598
+ "Reading assets/file-1.txt...
1599
+ Uploading as assets/file-1.2ca234f380.txt...
1600
+ ↗️ Done syncing assets
1058
1601
  Uploaded test-name (TIMINGS)
1059
1602
  Published test-name (TIMINGS)
1060
1603
  test-name.test-sub-domain.workers.dev"
@@ -1094,8 +1637,9 @@ export default{
1094
1637
  await runWrangler("publish");
1095
1638
 
1096
1639
  expect(std.out).toMatchInlineSnapshot(`
1097
- "reading assets/directory-1/file-1.txt...
1098
- uploading as assets/directory-1/file-1.2ca234f380.txt...
1640
+ "Reading assets/directory-1/file-1.txt...
1641
+ Uploading as assets/directory-1/file-1.2ca234f380.txt...
1642
+ ↗️ Done syncing assets
1099
1643
  Uploaded test-name (TIMINGS)
1100
1644
  Published test-name (TIMINGS)
1101
1645
  test-name.test-sub-domain.workers.dev"
@@ -1139,8 +1683,9 @@ export default{
1139
1683
  await runWrangler("publish");
1140
1684
 
1141
1685
  expect(std.out).toMatchInlineSnapshot(`
1142
- "reading assets/.well-known/file-2.txt...
1143
- uploading as assets/.well-known/file-2.5938485188.txt...
1686
+ "Reading assets/.well-known/file-2.txt...
1687
+ Uploading as assets/.well-known/file-2.5938485188.txt...
1688
+ ↗️ Done syncing assets
1144
1689
  Uploaded test-name (TIMINGS)
1145
1690
  Published test-name (TIMINGS)
1146
1691
  test-name.test-sub-domain.workers.dev"
@@ -1185,13 +1730,15 @@ export default{
1185
1730
  );
1186
1731
 
1187
1732
  expect(std.out).toMatchInlineSnapshot(`
1188
- "reading assets/large-file.txt...
1189
- uploading as assets/large-file.0ea0637a45.txt..."
1733
+ "Reading assets/large-file.txt...
1734
+ Uploading as assets/large-file.0ea0637a45.txt...
1735
+
1736
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
1190
1737
  `);
1191
1738
  expect(std.err).toMatchInlineSnapshot(`
1192
- "File assets/too-large-file.txt is too big, it should be under 25 MiB. See https://developers.cloudflare.com/workers/platform/limits#kv-limits
1739
+ "X [ERROR] File assets/too-large-file.txt is too big, it should be under 25 MiB. See https://developers.cloudflare.com/workers/platform/limits#kv-limits
1193
1740
 
1194
- %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
1741
+ "
1195
1742
  `);
1196
1743
  });
1197
1744
 
@@ -1223,14 +1770,72 @@ export default{
1223
1770
  `"The asset path key \\"assets/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/file.3da0d0cd12.txt\\" exceeds the maximum key size limit of 512. See https://developers.cloudflare.com/workers/platform/limits#kv-limits\\","`
1224
1771
  );
1225
1772
 
1226
- expect(std.out).toMatchInlineSnapshot(
1227
- `"reading assets/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/file.txt..."`
1228
- );
1773
+ expect(std.out).toMatchInlineSnapshot(`
1774
+ "Reading assets/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/file.txt...
1775
+
1776
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
1777
+ `);
1229
1778
  expect(std.err).toMatchInlineSnapshot(`
1230
- "The asset path key \\"assets/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/file.3da0d0cd12.txt\\" exceeds the maximum key size limit of 512. See https://developers.cloudflare.com/workers/platform/limits#kv-limits\\",
1779
+ "X [ERROR] The asset path key \\"assets/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/folder/file.3da0d0cd12.txt\\" exceeds the maximum key size limit of 512. See https://developers.cloudflare.com/workers/platform/limits#kv-limits\\",
1780
+
1781
+ "
1782
+ `);
1783
+ });
1784
+
1785
+ it("should delete uploaded assets that aren't included anymore", async () => {
1786
+ const assets = [
1787
+ { filePath: "assets/file-1.txt", content: "Content of file-1" },
1788
+ { filePath: "assets/file-2.txt", content: "Content of file-2" },
1789
+ ];
1790
+ const kvNamespace = {
1791
+ title: "__test-name-workers_sites_assets",
1792
+ id: "__test-name-workers_sites_assets-id",
1793
+ };
1794
+ writeWranglerToml({
1795
+ main: "./index.js",
1796
+ site: {
1797
+ bucket: "assets",
1798
+ },
1799
+ });
1800
+ writeWorkerSource();
1801
+ writeAssets(assets);
1802
+ mockUploadWorkerRequest();
1803
+ mockSubDomainRequest();
1804
+ mockListKVNamespacesRequest(kvNamespace);
1805
+ mockKeyListRequest(kvNamespace.id, [
1806
+ // Put file-1 in the KV namespace
1807
+ { name: "assets/file-1.2ca234f380.txt" },
1808
+ // As well as a couple from a previous upload
1809
+ { name: "assets/file-3.somehash.txt" },
1810
+ { name: "assets/file-4.anotherhash.txt" },
1811
+ ]);
1812
+
1813
+ // we upload only file-1.txt
1814
+ mockUploadAssetsToKVRequest(kvNamespace.id, [
1815
+ ...assets.filter((a) => a.filePath !== "assets/file-1.txt"),
1816
+ ]);
1817
+
1818
+ // and mark file-3 and file-4 for deletion
1819
+ mockDeleteUnusedAssetsRequest(kvNamespace.id, [
1820
+ "assets/file-3.somehash.txt",
1821
+ "assets/file-4.anotherhash.txt",
1822
+ ]);
1823
+
1824
+ await runWrangler("publish");
1231
1825
 
1232
- %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
1826
+ expect(std.out).toMatchInlineSnapshot(`
1827
+ "Reading assets/file-1.txt...
1828
+ Skipping - already uploaded.
1829
+ Reading assets/file-2.txt...
1830
+ Uploading as assets/file-2.5938485188.txt...
1831
+ Deleting assets/file-3.somehash.txt from the asset store...
1832
+ Deleting assets/file-4.anotherhash.txt from the asset store...
1833
+ ↗️ Done syncing assets
1834
+ Uploaded test-name (TIMINGS)
1835
+ Published test-name (TIMINGS)
1836
+ test-name.test-sub-domain.workers.dev"
1233
1837
  `);
1838
+ expect(std.err).toMatchInlineSnapshot(`""`);
1234
1839
  });
1235
1840
  });
1236
1841
 
@@ -1256,7 +1861,26 @@ export default{
1256
1861
  workers_dev: true,
1257
1862
  });
1258
1863
  writeWorkerSource();
1259
- mockUploadWorkerRequest();
1864
+ mockUploadWorkerRequest({ available_on_subdomain: false });
1865
+ mockSubDomainRequest();
1866
+ mockUpdateWorkerRequest({ enabled: true });
1867
+
1868
+ await runWrangler("publish ./index");
1869
+
1870
+ expect(std.out).toMatchInlineSnapshot(`
1871
+ "Uploaded test-name (TIMINGS)
1872
+ Published test-name (TIMINGS)
1873
+ test-name.test-sub-domain.workers.dev"
1874
+ `);
1875
+ expect(std.err).toMatchInlineSnapshot(`""`);
1876
+ });
1877
+
1878
+ it("should not try to enable the workers.dev domain if it has been enabled before", async () => {
1879
+ writeWranglerToml({
1880
+ workers_dev: true,
1881
+ });
1882
+ writeWorkerSource();
1883
+ mockUploadWorkerRequest({ available_on_subdomain: true });
1260
1884
  mockSubDomainRequest();
1261
1885
 
1262
1886
  await runWrangler("publish ./index");
@@ -1286,6 +1910,25 @@ export default{
1286
1910
  expect(std.err).toMatchInlineSnapshot(`""`);
1287
1911
  });
1288
1912
 
1913
+ it("should not try to disable the workers.dev domain if it is not already available", async () => {
1914
+ writeWranglerToml({
1915
+ workers_dev: false,
1916
+ });
1917
+ writeWorkerSource();
1918
+ mockSubDomainRequest("test-sub-domain", false);
1919
+ mockUploadWorkerRequest({ available_on_subdomain: false });
1920
+
1921
+ // note the lack of a mock for the subdomain disable request
1922
+
1923
+ await runWrangler("publish ./index");
1924
+
1925
+ expect(std.out).toMatchInlineSnapshot(`
1926
+ "Uploaded test-name (TIMINGS)
1927
+ No publish targets for test-name (TIMINGS)"
1928
+ `);
1929
+ expect(std.err).toMatchInlineSnapshot(`""`);
1930
+ });
1931
+
1289
1932
  it("should disable the workers.dev domain if workers_dev is undefined but overwritten to `false` in environment", async () => {
1290
1933
  writeWranglerToml({
1291
1934
  env: {
@@ -1522,233 +2165,277 @@ export default{
1522
2165
  https://dash.cloudflare.com/some-account-id/workers/onboarding"
1523
2166
  `);
1524
2167
  });
1525
- });
1526
2168
 
1527
- describe("custom builds", () => {
1528
- beforeEach(() => {
1529
- // @ts-expect-error disable the mock we'd setup earlier
1530
- // or else custom builds will timeout immediately
1531
- global.setTimeout.mockRestore();
1532
- });
1533
- it("should run a custom build before publishing", async () => {
2169
+ it("should not deploy to workers.dev if there are any routes defined", async () => {
1534
2170
  writeWranglerToml({
1535
- build: {
1536
- command: `node -e "console.log('custom build'); require('fs').writeFileSync('index.js', 'export default { fetch(){ return new Response(123) } }')"`,
1537
- },
1538
- });
1539
-
1540
- mockUploadWorkerRequest({
1541
- expectedEntry: "return new Response(123)",
2171
+ routes: ["http://example.com/*"],
1542
2172
  });
2173
+ writeWorkerSource();
1543
2174
  mockSubDomainRequest();
1544
-
2175
+ mockUploadWorkerRequest();
2176
+ mockUpdateWorkerRequest({
2177
+ enabled: false,
2178
+ });
2179
+ mockPublishRoutesRequest({ routes: ["http://example.com/*"] });
1545
2180
  await runWrangler("publish index.js");
2181
+
1546
2182
  expect(std.out).toMatchInlineSnapshot(`
1547
- "running: node -e \\"console.log('custom build'); require('fs').writeFileSync('index.js', 'export default { fetch(){ return new Response(123) } }')\\"
1548
- Uploaded test-name (TIMINGS)
2183
+ "Uploaded test-name (TIMINGS)
1549
2184
  Published test-name (TIMINGS)
1550
- test-name.test-sub-domain.workers.dev"
2185
+ http://example.com/*"
1551
2186
  `);
1552
2187
  expect(std.err).toMatchInlineSnapshot(`""`);
1553
2188
  expect(std.warn).toMatchInlineSnapshot(`""`);
1554
2189
  });
1555
2190
 
1556
- if (process.platform !== "win32") {
1557
- it("should run a custom build of multiple steps combined by && before publishing", async () => {
1558
- writeWranglerToml({
1559
- build: {
1560
- command: `echo "custom build" && echo "export default { fetch(){ return new Response(123) } }" > index.js`,
2191
+ it("should not deploy to workers.dev if there are any routes defined (environments)", async () => {
2192
+ writeWranglerToml({
2193
+ routes: ["http://example.com/*"],
2194
+ env: {
2195
+ production: {
2196
+ routes: ["http://production.example.com/*"],
1561
2197
  },
1562
- });
1563
-
1564
- mockUploadWorkerRequest({
1565
- expectedEntry: "return new Response(123)",
1566
- });
1567
- mockSubDomainRequest();
1568
-
1569
- await runWrangler("publish index.js");
1570
- expect(std.out).toMatchInlineSnapshot(`
1571
- "running: echo \\"custom build\\" && echo \\"export default { fetch(){ return new Response(123) } }\\" > index.js
1572
- Uploaded test-name (TIMINGS)
1573
- Published test-name (TIMINGS)
1574
- test-name.test-sub-domain.workers.dev"
1575
- `);
1576
- expect(std.err).toMatchInlineSnapshot(`""`);
1577
- expect(std.warn).toMatchInlineSnapshot(`""`);
2198
+ },
1578
2199
  });
1579
- }
2200
+ writeWorkerSource();
2201
+ mockSubDomainRequest();
2202
+ mockUploadWorkerRequest({ env: "production", legacyEnv: true });
2203
+ mockUpdateWorkerRequest({
2204
+ enabled: false,
2205
+ env: "production",
2206
+ legacyEnv: true,
2207
+ });
2208
+ mockPublishRoutesRequest({
2209
+ routes: ["http://production.example.com/*"],
2210
+ env: "production",
2211
+ legacyEnv: true,
2212
+ });
2213
+ await runWrangler("publish index.js --env production");
1580
2214
 
1581
- it("should throw an error if the entry doesn't exist after the build finishes", async () => {
2215
+ expect(std.out).toMatchInlineSnapshot(`
2216
+ "Uploaded test-name-production (TIMINGS)
2217
+ Published test-name-production (TIMINGS)
2218
+ http://production.example.com/*"
2219
+ `);
2220
+ expect(std.err).toMatchInlineSnapshot(`""`);
2221
+ expect(std.warn).toMatchInlineSnapshot(`""`);
2222
+ });
2223
+
2224
+ it("should not deploy to workers.dev if there are any routes defined (only in environments)", async () => {
1582
2225
  writeWranglerToml({
1583
- main: "index.js",
1584
- build: {
1585
- command: `node -e "console.log('custom build');"`,
2226
+ env: {
2227
+ production: {
2228
+ routes: ["http://production.example.com/*"],
2229
+ },
1586
2230
  },
1587
2231
  });
2232
+ writeWorkerSource();
2233
+ mockSubDomainRequest();
2234
+ mockUploadWorkerRequest({ env: "production", legacyEnv: true });
2235
+ mockUpdateWorkerRequest({
2236
+ enabled: false,
2237
+ env: "production",
2238
+ legacyEnv: true,
2239
+ });
2240
+ mockPublishRoutesRequest({
2241
+ routes: ["http://production.example.com/*"],
2242
+ env: "production",
2243
+ legacyEnv: true,
2244
+ });
2245
+ await runWrangler("publish index.js --env production");
1588
2246
 
1589
- await expect(
1590
- runWrangler("publish index.js")
1591
- ).rejects.toThrowErrorMatchingInlineSnapshot(
1592
- `"Could not resolve \\"index.js\\" after running custom build: node -e \\"console.log('custom build');\\""`
1593
- );
1594
- expect(std.out).toMatchInlineSnapshot(
1595
- `"running: node -e \\"console.log('custom build');\\""`
1596
- );
1597
- expect(std.err).toMatchInlineSnapshot(`
1598
- "Could not resolve \\"index.js\\" after running custom build: node -e \\"console.log('custom build');\\"
1599
-
1600
- %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
2247
+ expect(std.out).toMatchInlineSnapshot(`
2248
+ "Uploaded test-name-production (TIMINGS)
2249
+ Published test-name-production (TIMINGS)
2250
+ http://production.example.com/*"
1601
2251
  `);
2252
+ expect(std.err).toMatchInlineSnapshot(`""`);
1602
2253
  expect(std.warn).toMatchInlineSnapshot(`""`);
1603
2254
  });
1604
- });
1605
2255
 
1606
- describe("[wasm_modules]", () => {
1607
- it("should be able to define wasm modules for service-worker format workers", async () => {
2256
+ it("can deploy to both workers.dev and routes if both defined ", async () => {
1608
2257
  writeWranglerToml({
1609
- wasm_modules: {
1610
- TESTWASMNAME: "./path/to/test.wasm",
1611
- },
1612
- });
1613
- writeWorkerSource({ type: "sw" });
1614
- fs.mkdirSync("./path/to", { recursive: true });
1615
- fs.writeFileSync("./path/to/test.wasm", "SOME WASM CONTENT");
1616
- mockUploadWorkerRequest({
1617
- expectedType: "sw",
1618
- expectedModules: { TESTWASMNAME: "SOME WASM CONTENT" },
1619
- expectedBindings: [
1620
- { name: "TESTWASMNAME", part: "TESTWASMNAME", type: "wasm_module" },
1621
- ],
2258
+ workers_dev: true,
2259
+ routes: ["http://example.com/*"],
1622
2260
  });
2261
+ writeWorkerSource();
1623
2262
  mockSubDomainRequest();
2263
+ mockUploadWorkerRequest();
2264
+ mockUpdateWorkerRequest({
2265
+ enabled: false,
2266
+ });
2267
+ mockPublishRoutesRequest({
2268
+ routes: ["http://example.com/*"],
2269
+ });
1624
2270
  await runWrangler("publish index.js");
2271
+
1625
2272
  expect(std.out).toMatchInlineSnapshot(`
1626
2273
  "Uploaded test-name (TIMINGS)
1627
2274
  Published test-name (TIMINGS)
1628
- test-name.test-sub-domain.workers.dev"
2275
+ test-name.test-sub-domain.workers.dev
2276
+ http://example.com/*"
1629
2277
  `);
1630
2278
  expect(std.err).toMatchInlineSnapshot(`""`);
1631
2279
  expect(std.warn).toMatchInlineSnapshot(`""`);
1632
2280
  });
1633
2281
 
1634
- it("should error when defining wasm modules for modules format workers", async () => {
2282
+ it("can deploy to both workers.dev and routes if both defined (environments: 1)", async () => {
1635
2283
  writeWranglerToml({
1636
- wasm_modules: {
1637
- TESTWASMNAME: "./path/to/test.wasm",
1638
- },
1639
- });
1640
- writeWorkerSource({ type: "esm" });
1641
- fs.mkdirSync("./path/to", { recursive: true });
1642
- fs.writeFileSync("./path/to/test.wasm", "SOME WASM CONTENT");
1643
-
1644
- await expect(
1645
- runWrangler("publish index.js")
1646
- ).rejects.toThrowErrorMatchingInlineSnapshot(
1647
- `"You cannot configure [wasm_modules] with an ES module worker. Instead, import the .wasm module directly in your code"`
1648
- );
1649
- expect(std.out).toMatchInlineSnapshot(`""`);
1650
- expect(std.err).toMatchInlineSnapshot(`
1651
- "You cannot configure [wasm_modules] with an ES module worker. Instead, import the .wasm module directly in your code
2284
+ workers_dev: true,
2285
+ env: {
2286
+ production: {
2287
+ routes: ["http://production.example.com/*"],
2288
+ },
2289
+ },
2290
+ });
2291
+ writeWorkerSource();
2292
+ mockSubDomainRequest();
2293
+ mockUploadWorkerRequest({ env: "production", legacyEnv: true });
2294
+ mockUpdateWorkerRequest({
2295
+ enabled: false,
2296
+ env: "production",
2297
+ legacyEnv: true,
2298
+ });
2299
+ mockPublishRoutesRequest({
2300
+ routes: ["http://production.example.com/*"],
2301
+ env: "production",
2302
+ legacyEnv: true,
2303
+ });
2304
+ await runWrangler("publish index.js --env production");
1652
2305
 
1653
- %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
2306
+ expect(std.out).toMatchInlineSnapshot(`
2307
+ "Uploaded test-name-production (TIMINGS)
2308
+ Published test-name-production (TIMINGS)
2309
+ test-name-production.test-sub-domain.workers.dev
2310
+ http://production.example.com/*"
1654
2311
  `);
2312
+ expect(std.err).toMatchInlineSnapshot(`""`);
1655
2313
  expect(std.warn).toMatchInlineSnapshot(`""`);
1656
2314
  });
1657
2315
 
1658
- it("should resolve wasm modules relative to the wrangler.toml file", async () => {
1659
- fs.mkdirSync("./path/to/and/the/path/to/", { recursive: true });
1660
- fs.writeFileSync(
1661
- "./path/to/wrangler.toml",
1662
- TOML.stringify({
1663
- compatibility_date: "2022-01-12",
1664
- name: "test-name",
1665
- wasm_modules: {
1666
- TESTWASMNAME: "./and/the/path/to/test.wasm",
2316
+ it("can deploy to both workers.dev and routes if both defined (environments: 2)", async () => {
2317
+ writeWranglerToml({
2318
+ env: {
2319
+ production: {
2320
+ workers_dev: true,
2321
+ routes: ["http://production.example.com/*"],
1667
2322
  },
1668
- }),
2323
+ },
2324
+ });
2325
+ writeWorkerSource();
2326
+ mockSubDomainRequest();
2327
+ mockUploadWorkerRequest({ env: "production", legacyEnv: true });
2328
+ mockUpdateWorkerRequest({
2329
+ enabled: false,
2330
+ env: "production",
2331
+ legacyEnv: true,
2332
+ });
2333
+ mockPublishRoutesRequest({
2334
+ routes: ["http://production.example.com/*"],
2335
+ env: "production",
2336
+ legacyEnv: true,
2337
+ });
2338
+ await runWrangler("publish index.js --env production");
1669
2339
 
1670
- "utf-8"
1671
- );
2340
+ expect(std.out).toMatchInlineSnapshot(`
2341
+ "Uploaded test-name-production (TIMINGS)
2342
+ Published test-name-production (TIMINGS)
2343
+ test-name-production.test-sub-domain.workers.dev
2344
+ http://production.example.com/*"
2345
+ `);
2346
+ expect(std.err).toMatchInlineSnapshot(`""`);
2347
+ expect(std.warn).toMatchInlineSnapshot(`""`);
2348
+ });
1672
2349
 
1673
- writeWorkerSource({ type: "sw" });
1674
- fs.writeFileSync(
1675
- "./path/to/and/the/path/to/test.wasm",
1676
- "SOME WASM CONTENT"
1677
- );
1678
- mockUploadWorkerRequest({
1679
- expectedType: "sw",
1680
- expectedModules: { TESTWASMNAME: "SOME WASM CONTENT" },
1681
- expectedBindings: [
1682
- { name: "TESTWASMNAME", part: "TESTWASMNAME", type: "wasm_module" },
1683
- ],
1684
- expectedCompatibilityDate: "2022-01-12",
2350
+ it("will deploy only to routes when workers_dev is false (environments 1) ", async () => {
2351
+ writeWranglerToml({
2352
+ workers_dev: false,
2353
+ env: {
2354
+ production: {
2355
+ routes: ["http://production.example.com/*"],
2356
+ },
2357
+ },
1685
2358
  });
2359
+ writeWorkerSource();
1686
2360
  mockSubDomainRequest();
1687
- await runWrangler("publish index.js --config ./path/to/wrangler.toml");
2361
+ mockUploadWorkerRequest({ env: "production", legacyEnv: true });
2362
+ mockUpdateWorkerRequest({
2363
+ enabled: false,
2364
+ env: "production",
2365
+ legacyEnv: true,
2366
+ });
2367
+ mockPublishRoutesRequest({
2368
+ routes: ["http://production.example.com/*"],
2369
+ env: "production",
2370
+ legacyEnv: true,
2371
+ });
2372
+ await runWrangler("publish index.js --env production");
2373
+
1688
2374
  expect(std.out).toMatchInlineSnapshot(`
1689
- "Uploaded test-name (TIMINGS)
1690
- Published test-name (TIMINGS)
1691
- test-name.test-sub-domain.workers.dev"
2375
+ "Uploaded test-name-production (TIMINGS)
2376
+ Published test-name-production (TIMINGS)
2377
+ http://production.example.com/*"
1692
2378
  `);
1693
2379
  expect(std.err).toMatchInlineSnapshot(`""`);
1694
2380
  expect(std.warn).toMatchInlineSnapshot(`""`);
1695
2381
  });
1696
2382
 
1697
- it("should be able to import .wasm modules from service-worker format workers", async () => {
1698
- writeWranglerToml();
1699
- fs.writeFileSync("./index.js", "import TESTWASMNAME from './test.wasm';");
1700
- fs.writeFileSync("./test.wasm", "SOME WASM CONTENT");
1701
- mockUploadWorkerRequest({
1702
- expectedType: "sw",
1703
- expectedModules: {
1704
- __94b240d0d692281e6467aa42043986e5c7eea034_test_wasm:
1705
- "SOME WASM CONTENT",
1706
- },
1707
- expectedBindings: [
1708
- {
1709
- name: "__94b240d0d692281e6467aa42043986e5c7eea034_test_wasm",
1710
- part: "__94b240d0d692281e6467aa42043986e5c7eea034_test_wasm",
1711
- type: "wasm_module",
2383
+ it("will deploy only to routes when workers_dev is false (environments 2) ", async () => {
2384
+ writeWranglerToml({
2385
+ env: {
2386
+ production: {
2387
+ workers_dev: false,
2388
+ routes: ["http://production.example.com/*"],
1712
2389
  },
1713
- ],
2390
+ },
1714
2391
  });
2392
+ writeWorkerSource();
1715
2393
  mockSubDomainRequest();
1716
- await runWrangler("publish index.js");
2394
+ mockUploadWorkerRequest({ env: "production", legacyEnv: true });
2395
+ mockUpdateWorkerRequest({
2396
+ enabled: false,
2397
+ env: "production",
2398
+ legacyEnv: true,
2399
+ });
2400
+ mockPublishRoutesRequest({
2401
+ routes: ["http://production.example.com/*"],
2402
+ env: "production",
2403
+ legacyEnv: true,
2404
+ });
2405
+ await runWrangler("publish index.js --env production");
2406
+
1717
2407
  expect(std.out).toMatchInlineSnapshot(`
1718
- "Uploaded test-name (TIMINGS)
1719
- Published test-name (TIMINGS)
1720
- test-name.test-sub-domain.workers.dev"
2408
+ "Uploaded test-name-production (TIMINGS)
2409
+ Published test-name-production (TIMINGS)
2410
+ http://production.example.com/*"
1721
2411
  `);
1722
2412
  expect(std.err).toMatchInlineSnapshot(`""`);
1723
2413
  expect(std.warn).toMatchInlineSnapshot(`""`);
1724
2414
  });
1725
2415
  });
1726
2416
 
1727
- describe("[text_blobs]", () => {
1728
- it("should be able to define text blobs for service-worker format workers", async () => {
2417
+ describe("custom builds", () => {
2418
+ beforeEach(() => {
2419
+ // @ts-expect-error disable the mock we'd setup earlier
2420
+ // or else custom builds will timeout immediately
2421
+ global.setTimeout.mockRestore();
2422
+ });
2423
+ it("should run a custom build before publishing", async () => {
1729
2424
  writeWranglerToml({
1730
- text_blobs: {
1731
- TESTTEXTBLOBNAME: "./path/to/text.file",
2425
+ build: {
2426
+ command: `node -e "console.log('custom build'); require('fs').writeFileSync('index.js', 'export default { fetch(){ return new Response(123) } }')"`,
1732
2427
  },
1733
2428
  });
1734
- writeWorkerSource({ type: "sw" });
1735
- fs.mkdirSync("./path/to", { recursive: true });
1736
- fs.writeFileSync("./path/to/text.file", "SOME TEXT CONTENT");
2429
+
1737
2430
  mockUploadWorkerRequest({
1738
- expectedType: "sw",
1739
- expectedModules: { TESTTEXTBLOBNAME: "SOME TEXT CONTENT" },
1740
- expectedBindings: [
1741
- {
1742
- name: "TESTTEXTBLOBNAME",
1743
- part: "TESTTEXTBLOBNAME",
1744
- type: "text_blob",
1745
- },
1746
- ],
2431
+ expectedEntry: "return new Response(123)",
1747
2432
  });
1748
2433
  mockSubDomainRequest();
2434
+
1749
2435
  await runWrangler("publish index.js");
1750
2436
  expect(std.out).toMatchInlineSnapshot(`
1751
- "Uploaded test-name (TIMINGS)
2437
+ "Running custom build: node -e \\"console.log('custom build'); require('fs').writeFileSync('index.js', 'export default { fetch(){ return new Response(123) } }')\\"
2438
+ Uploaded test-name (TIMINGS)
1752
2439
  Published test-name (TIMINGS)
1753
2440
  test-name.test-sub-domain.workers.dev"
1754
2441
  `);
@@ -1756,97 +2443,170 @@ export default{
1756
2443
  expect(std.warn).toMatchInlineSnapshot(`""`);
1757
2444
  });
1758
2445
 
1759
- it("should error when defining text blobs for modules format workers", async () => {
2446
+ if (process.platform !== "win32") {
2447
+ it("should run a custom build of multiple steps combined by && before publishing", async () => {
2448
+ writeWranglerToml({
2449
+ build: {
2450
+ command: `echo "custom build" && echo "export default { fetch(){ return new Response(123) } }" > index.js`,
2451
+ },
2452
+ });
2453
+
2454
+ mockUploadWorkerRequest({
2455
+ expectedEntry: "return new Response(123)",
2456
+ });
2457
+ mockSubDomainRequest();
2458
+
2459
+ await runWrangler("publish index.js");
2460
+ expect(std.out).toMatchInlineSnapshot(`
2461
+ "Running custom build: echo \\"custom build\\" && echo \\"export default { fetch(){ return new Response(123) } }\\" > index.js
2462
+ Uploaded test-name (TIMINGS)
2463
+ Published test-name (TIMINGS)
2464
+ test-name.test-sub-domain.workers.dev"
2465
+ `);
2466
+ expect(std.err).toMatchInlineSnapshot(`""`);
2467
+ expect(std.warn).toMatchInlineSnapshot(`""`);
2468
+ });
2469
+ }
2470
+
2471
+ it("should throw an error if the entry doesn't exist after the build finishes", async () => {
1760
2472
  writeWranglerToml({
1761
- text_blobs: {
1762
- TESTTEXTBLOBNAME: "./path/to/text.file",
2473
+ main: "index.js",
2474
+ build: {
2475
+ command: `node -e "console.log('custom build');"`,
1763
2476
  },
1764
2477
  });
1765
- writeWorkerSource({ type: "esm" });
1766
- fs.mkdirSync("./path/to", { recursive: true });
1767
- fs.writeFileSync("./path/to/text.file", "SOME TEXT CONTENT");
1768
2478
 
1769
2479
  await expect(
1770
2480
  runWrangler("publish index.js")
1771
2481
  ).rejects.toThrowErrorMatchingInlineSnapshot(
1772
- `"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"`
2482
+ `"Could not resolve \\"index.js\\" after running custom build: node -e \\"console.log('custom build');\\""`
1773
2483
  );
1774
- expect(std.out).toMatchInlineSnapshot(`""`);
2484
+ expect(std.out).toMatchInlineSnapshot(`
2485
+ "Running custom build: node -e \\"console.log('custom build');\\"
2486
+
2487
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
2488
+ `);
1775
2489
  expect(std.err).toMatchInlineSnapshot(`
1776
- "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
2490
+ "X [ERROR] Could not resolve \\"index.js\\" after running custom build: node -e \\"console.log('custom build');\\"
1777
2491
 
1778
- %s If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
2492
+ "
1779
2493
  `);
1780
2494
  expect(std.warn).toMatchInlineSnapshot(`""`);
1781
2495
  });
1782
2496
 
1783
- it("should resolve text blobs relative to the wrangler.toml file", async () => {
1784
- fs.mkdirSync("./path/to/and/the/path/to/", { recursive: true });
2497
+ it("should minify the script when `--minify` is true (sw)", async () => {
2498
+ writeWranglerToml({
2499
+ main: "./index.js",
2500
+ });
1785
2501
  fs.writeFileSync(
1786
- "./path/to/wrangler.toml",
1787
- TOML.stringify({
1788
- compatibility_date: "2022-01-12",
1789
- name: "test-name",
1790
- text_blobs: {
1791
- TESTTEXTBLOBNAME: "./and/the/path/to/text.file",
1792
- },
1793
- }),
1794
-
1795
- "utf-8"
2502
+ "./index.js",
2503
+ `export
2504
+ default {
2505
+ fetch() {
2506
+ return new Response( "hello Cpt Picard" )
2507
+ }
2508
+ }
2509
+ `
1796
2510
  );
1797
2511
 
1798
- writeWorkerSource({ type: "sw" });
1799
- fs.writeFileSync(
1800
- "./path/to/and/the/path/to/text.file",
1801
- "SOME TEXT CONTENT"
1802
- );
1803
2512
  mockUploadWorkerRequest({
1804
- expectedType: "sw",
1805
- expectedModules: { TESTTEXTBLOBNAME: "SOME TEXT CONTENT" },
1806
- expectedBindings: [
1807
- {
1808
- name: "TESTTEXTBLOBNAME",
1809
- part: "TESTTEXTBLOBNAME",
1810
- type: "text_blob",
1811
- },
1812
- ],
1813
- expectedCompatibilityDate: "2022-01-12",
2513
+ expectedEntry: 'fetch(){return new Response("hello Cpt Picard")',
1814
2514
  });
2515
+
1815
2516
  mockSubDomainRequest();
1816
- await runWrangler("publish index.js --config ./path/to/wrangler.toml");
2517
+ await runWrangler("publish index.js --minify");
1817
2518
  expect(std.out).toMatchInlineSnapshot(`
1818
2519
  "Uploaded test-name (TIMINGS)
1819
2520
  Published test-name (TIMINGS)
1820
2521
  test-name.test-sub-domain.workers.dev"
1821
2522
  `);
1822
2523
  expect(std.err).toMatchInlineSnapshot(`""`);
1823
- expect(std.warn).toMatchInlineSnapshot(`""`);
1824
2524
  });
1825
- });
1826
2525
 
1827
- describe("vars bindings", () => {
1828
- it("should support json bindings", async () => {
2526
+ it("should minify the script when `minify` in config is true (esm)", async () => {
1829
2527
  writeWranglerToml({
1830
- vars: {
1831
- text: "plain ol' string",
1832
- count: 1,
1833
- complex: { enabled: true, id: 123 },
2528
+ main: "./index.js",
2529
+ legacy_env: false,
2530
+ env: {
2531
+ testEnv: {
2532
+ minify: true,
2533
+ },
1834
2534
  },
1835
2535
  });
1836
- writeWorkerSource();
1837
- mockSubDomainRequest();
2536
+ fs.writeFileSync(
2537
+ "./index.js",
2538
+ `export
2539
+ default {
2540
+ fetch() {
2541
+ return new Response( "hello Cpt Picard" )
2542
+ }
2543
+ }
2544
+ `
2545
+ );
2546
+
1838
2547
  mockUploadWorkerRequest({
1839
- expectedBindings: [
1840
- { name: "text", type: "plain_text", text: "plain ol' string" },
1841
- { name: "count", type: "json", json: 1 },
1842
- {
1843
- name: "complex",
1844
- type: "json",
1845
- json: { enabled: true, id: 123 },
1846
- },
1847
- ],
2548
+ env: "testEnv",
2549
+ expectedType: "esm",
2550
+ legacyEnv: false,
2551
+ expectedEntry: `fetch(){return new Response("hello Cpt Picard")`,
2552
+ });
2553
+
2554
+ mockSubDomainRequest();
2555
+ await runWrangler("publish -e testEnv index.js");
2556
+ expect(std.out).toMatchInlineSnapshot(`
2557
+ "Uploaded test-name (testEnv) (TIMINGS)
2558
+ Published test-name (testEnv) (TIMINGS)
2559
+ testEnv.test-name.test-sub-domain.workers.dev"
2560
+ `);
2561
+ expect(std.err).toMatchInlineSnapshot(`""`);
2562
+ });
2563
+ });
2564
+
2565
+ describe("durable object migrations", () => {
2566
+ it("should warn when you try to publish durable objects without migrations", async () => {
2567
+ writeWranglerToml({
2568
+ durable_objects: {
2569
+ bindings: [{ name: "SOMENAME", class_name: "SomeClass" }],
2570
+ },
1848
2571
  });
2572
+ fs.writeFileSync(
2573
+ "index.js",
2574
+ `export class SomeClass{}; export default {};`
2575
+ );
2576
+ mockSubDomainRequest();
2577
+ mockUploadWorkerRequest();
2578
+ await runWrangler("publish index.js");
2579
+ expect(std.out).toMatchInlineSnapshot(`
2580
+ "Uploaded test-name (TIMINGS)
2581
+ Published test-name (TIMINGS)
2582
+ test-name.test-sub-domain.workers.dev"
2583
+ `);
2584
+ expect(std.err).toMatchInlineSnapshot(`""`);
2585
+ expect(std.warn).toMatchInlineSnapshot(`
2586
+ "▲ [WARNING] In wrangler.toml, you have configured [durable_objects] exported by this Worker (SomeClass), 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.
2587
+
2588
+ "
2589
+ `);
2590
+ });
1849
2591
 
2592
+ it("does not warn if all the durable object bindings are to external classes", async () => {
2593
+ writeWranglerToml({
2594
+ durable_objects: {
2595
+ bindings: [
2596
+ {
2597
+ name: "SOMENAME",
2598
+ class_name: "SomeClass",
2599
+ script_name: "some-script",
2600
+ },
2601
+ ],
2602
+ },
2603
+ });
2604
+ fs.writeFileSync(
2605
+ "index.js",
2606
+ `export class SomeClass{}; export default {};`
2607
+ );
2608
+ mockSubDomainRequest();
2609
+ mockUploadWorkerRequest();
1850
2610
  await runWrangler("publish index.js");
1851
2611
  expect(std.out).toMatchInlineSnapshot(`
1852
2612
  "Uploaded test-name (TIMINGS)
@@ -1856,19 +2616,34 @@ export default{
1856
2616
  expect(std.err).toMatchInlineSnapshot(`""`);
1857
2617
  expect(std.warn).toMatchInlineSnapshot(`""`);
1858
2618
  });
1859
- });
1860
2619
 
1861
- describe("r2 bucket bindings", () => {
1862
- it("should support r2 bucket bindings", async () => {
2620
+ it("should publish all migrations on first publish", async () => {
1863
2621
  writeWranglerToml({
1864
- r2_buckets: [{ binding: "FOO", bucket_name: "foo-bucket" }],
2622
+ durable_objects: {
2623
+ bindings: [
2624
+ { name: "SOMENAME", class_name: "SomeClass" },
2625
+ { name: "SOMEOTHERNAME", class_name: "SomeOtherClass" },
2626
+ ],
2627
+ },
2628
+ migrations: [
2629
+ { tag: "v1", new_classes: ["SomeClass"] },
2630
+ { tag: "v2", new_classes: ["SomeOtherClass"] },
2631
+ ],
1865
2632
  });
1866
- writeWorkerSource();
2633
+ fs.writeFileSync(
2634
+ "index.js",
2635
+ `export class SomeClass{}; export class SomeOtherClass{}; export default {};`
2636
+ );
1867
2637
  mockSubDomainRequest();
2638
+ mockLegacyScriptData({ scripts: [] }); // no previously uploaded scripts at all
1868
2639
  mockUploadWorkerRequest({
1869
- expectedBindings: [
1870
- { bucket_name: "foo-bucket", name: "FOO", type: "r2_bucket" },
1871
- ],
2640
+ expectedMigrations: {
2641
+ new_tag: "v2",
2642
+ steps: [
2643
+ { new_classes: ["SomeClass"] },
2644
+ { new_classes: ["SomeOtherClass"] },
2645
+ ],
2646
+ },
1872
2647
  });
1873
2648
 
1874
2649
  await runWrangler("publish index.js");
@@ -1880,84 +2655,1437 @@ export default{
1880
2655
  expect(std.err).toMatchInlineSnapshot(`""`);
1881
2656
  expect(std.warn).toMatchInlineSnapshot(`""`);
1882
2657
  });
1883
- });
1884
2658
 
1885
- describe("unsafe bindings", () => {
1886
- it("should warn if using unsafe bindings", async () => {
1887
- writeWranglerToml({
1888
- unsafe: {
1889
- bindings: [
2659
+ it("should upload migrations past a previously uploaded tag", async () => {
2660
+ writeWranglerToml({
2661
+ durable_objects: {
2662
+ bindings: [
2663
+ { name: "SOMENAME", class_name: "SomeClass" },
2664
+ { name: "SOMEOTHERNAME", class_name: "SomeOtherClass" },
2665
+ ],
2666
+ },
2667
+ migrations: [
2668
+ { tag: "v1", new_classes: ["SomeClass"] },
2669
+ { tag: "v2", new_classes: ["SomeOtherClass"] },
2670
+ ],
2671
+ });
2672
+ fs.writeFileSync(
2673
+ "index.js",
2674
+ `export class SomeClass{}; export class SomeOtherClass{}; export default {};`
2675
+ );
2676
+ mockSubDomainRequest();
2677
+ mockLegacyScriptData({
2678
+ scripts: [{ id: "test-name", migration_tag: "v1" }],
2679
+ });
2680
+ mockUploadWorkerRequest({
2681
+ expectedMigrations: {
2682
+ old_tag: "v1",
2683
+ new_tag: "v2",
2684
+ steps: [
2685
+ {
2686
+ new_classes: ["SomeOtherClass"],
2687
+ },
2688
+ ],
2689
+ },
2690
+ });
2691
+
2692
+ await runWrangler("publish index.js");
2693
+ expect(std).toMatchInlineSnapshot(`
2694
+ Object {
2695
+ "debug": "",
2696
+ "err": "",
2697
+ "out": "Uploaded test-name (TIMINGS)
2698
+ Published test-name (TIMINGS)
2699
+ test-name.test-sub-domain.workers.dev",
2700
+ "warn": "",
2701
+ }
2702
+ `);
2703
+ });
2704
+
2705
+ it("should not send migrations if they've all already been sent", async () => {
2706
+ writeWranglerToml({
2707
+ durable_objects: {
2708
+ bindings: [
2709
+ { name: "SOMENAME", class_name: "SomeClass" },
2710
+ { name: "SOMEOTHERNAME", class_name: "SomeOtherClass" },
2711
+ ],
2712
+ },
2713
+ migrations: [
2714
+ { tag: "v1", new_classes: ["SomeClass"] },
2715
+ { tag: "v2", new_classes: ["SomeOtherClass"] },
2716
+ { tag: "v3", new_classes: ["YetAnotherClass"] },
2717
+ ],
2718
+ });
2719
+ fs.writeFileSync(
2720
+ "index.js",
2721
+ `export class SomeClass{}; export class SomeOtherClass{}; export class YetAnotherClass{}; export default {};`
2722
+ );
2723
+ mockSubDomainRequest();
2724
+ mockLegacyScriptData({
2725
+ scripts: [{ id: "test-name", migration_tag: "v3" }],
2726
+ });
2727
+ mockUploadWorkerRequest({
2728
+ expectedMigrations: undefined,
2729
+ });
2730
+
2731
+ await runWrangler("publish index.js");
2732
+ expect(std).toMatchInlineSnapshot(`
2733
+ Object {
2734
+ "debug": "",
2735
+ "err": "",
2736
+ "out": "Uploaded test-name (TIMINGS)
2737
+ Published test-name (TIMINGS)
2738
+ test-name.test-sub-domain.workers.dev",
2739
+ "warn": "",
2740
+ }
2741
+ `);
2742
+ });
2743
+
2744
+ describe("service environments", () => {
2745
+ it("should publish all migrations on first publish", async () => {
2746
+ writeWranglerToml({
2747
+ durable_objects: {
2748
+ bindings: [
2749
+ { name: "SOMENAME", class_name: "SomeClass" },
2750
+ { name: "SOMEOTHERNAME", class_name: "SomeOtherClass" },
2751
+ ],
2752
+ },
2753
+ migrations: [
2754
+ { tag: "v1", new_classes: ["SomeClass"] },
2755
+ { tag: "v2", new_classes: ["SomeOtherClass"] },
2756
+ ],
2757
+ });
2758
+ fs.writeFileSync(
2759
+ "index.js",
2760
+ `export class SomeClass{}; export class SomeOtherClass{}; export default {};`
2761
+ );
2762
+ mockSubDomainRequest();
2763
+ mockServiceScriptData({}); // no scripts at all
2764
+ mockUploadWorkerRequest({
2765
+ legacyEnv: false,
2766
+ expectedMigrations: {
2767
+ new_tag: "v2",
2768
+ steps: [
2769
+ { new_classes: ["SomeClass"] },
2770
+ { new_classes: ["SomeOtherClass"] },
2771
+ ],
2772
+ },
2773
+ });
2774
+
2775
+ await runWrangler("publish index.js --legacy-env false");
2776
+ expect(std.out).toMatchInlineSnapshot(`
2777
+ "Uploaded test-name (TIMINGS)
2778
+ Published test-name (TIMINGS)
2779
+ test-name.test-sub-domain.workers.dev"
2780
+ `);
2781
+ expect(std.err).toMatchInlineSnapshot(`""`);
2782
+ expect(std.warn).toMatchInlineSnapshot(`
2783
+ "▲ [WARNING] Processing wrangler.toml configuration:
2784
+
2785
+ - Experimental: Service environments are in beta, and their behaviour is guaranteed to change in
2786
+ the future. DO NOT USE IN PRODUCTION.
2787
+
2788
+ "
2789
+ `);
2790
+ });
2791
+
2792
+ it("should publish all migrations on first publish (--env)", async () => {
2793
+ writeWranglerToml({
2794
+ durable_objects: {
2795
+ bindings: [
2796
+ { name: "SOMENAME", class_name: "SomeClass" },
2797
+ { name: "SOMEOTHERNAME", class_name: "SomeOtherClass" },
2798
+ ],
2799
+ },
2800
+ env: {
2801
+ xyz: {
2802
+ durable_objects: {
2803
+ bindings: [
2804
+ { name: "SOMENAME", class_name: "SomeClass" },
2805
+ { name: "SOMEOTHERNAME", class_name: "SomeOtherClass" },
2806
+ ],
2807
+ },
2808
+ },
2809
+ },
2810
+ migrations: [
2811
+ { tag: "v1", new_classes: ["SomeClass"] },
2812
+ { tag: "v2", new_classes: ["SomeOtherClass"] },
2813
+ ],
2814
+ });
2815
+ fs.writeFileSync(
2816
+ "index.js",
2817
+ `export class SomeClass{}; export class SomeOtherClass{}; export default {};`
2818
+ );
2819
+ mockSubDomainRequest();
2820
+ mockServiceScriptData({ env: "xyz" }); // no scripts at all
2821
+ mockUploadWorkerRequest({
2822
+ legacyEnv: false,
2823
+ env: "xyz",
2824
+ expectedMigrations: {
2825
+ new_tag: "v2",
2826
+ steps: [
2827
+ { new_classes: ["SomeClass"] },
2828
+ { new_classes: ["SomeOtherClass"] },
2829
+ ],
2830
+ },
2831
+ });
2832
+
2833
+ await runWrangler("publish index.js --legacy-env false --env xyz");
2834
+ expect(std.out).toMatchInlineSnapshot(`
2835
+ "Uploaded test-name (xyz) (TIMINGS)
2836
+ Published test-name (xyz) (TIMINGS)
2837
+ xyz.test-name.test-sub-domain.workers.dev"
2838
+ `);
2839
+ expect(std.err).toMatchInlineSnapshot(`""`);
2840
+ expect(std.warn).toMatchInlineSnapshot(`
2841
+ "▲ [WARNING] Processing wrangler.toml configuration:
2842
+
2843
+ - Experimental: Service environments are in beta, and their behaviour is guaranteed to change in
2844
+ the future. DO NOT USE IN PRODUCTION.
2845
+
2846
+ "
2847
+ `);
2848
+ });
2849
+
2850
+ it("should use a script's current migration tag when publishing migrations", async () => {
2851
+ writeWranglerToml({
2852
+ durable_objects: {
2853
+ bindings: [
2854
+ { name: "SOMENAME", class_name: "SomeClass" },
2855
+ { name: "SOMEOTHERNAME", class_name: "SomeOtherClass" },
2856
+ ],
2857
+ },
2858
+ migrations: [
2859
+ { tag: "v1", new_classes: ["SomeClass"] },
2860
+ { tag: "v2", new_classes: ["SomeOtherClass"] },
2861
+ ],
2862
+ });
2863
+ fs.writeFileSync(
2864
+ "index.js",
2865
+ `export class SomeClass{}; export class SomeOtherClass{}; export default {};`
2866
+ );
2867
+ mockSubDomainRequest();
2868
+ mockServiceScriptData({
2869
+ script: { id: "test-name", migration_tag: "v1" },
2870
+ });
2871
+ mockUploadWorkerRequest({
2872
+ legacyEnv: false,
2873
+ expectedMigrations: {
2874
+ old_tag: "v1",
2875
+ new_tag: "v2",
2876
+ steps: [
2877
+ {
2878
+ new_classes: ["SomeOtherClass"],
2879
+ },
2880
+ ],
2881
+ },
2882
+ });
2883
+
2884
+ await runWrangler("publish index.js --legacy-env false");
2885
+ expect(std).toMatchInlineSnapshot(`
2886
+ Object {
2887
+ "debug": "",
2888
+ "err": "",
2889
+ "out": "Uploaded test-name (TIMINGS)
2890
+ Published test-name (TIMINGS)
2891
+ test-name.test-sub-domain.workers.dev",
2892
+ "warn": "▲ [WARNING] Processing wrangler.toml configuration:
2893
+
2894
+ - Experimental: Service environments are in beta, and their behaviour is guaranteed to change in
2895
+ the future. DO NOT USE IN PRODUCTION.
2896
+
2897
+ ",
2898
+ }
2899
+ `);
2900
+ });
2901
+
2902
+ it("should use an environment's current migration tag when publishing migrations", async () => {
2903
+ writeWranglerToml({
2904
+ durable_objects: {
2905
+ bindings: [
2906
+ { name: "SOMENAME", class_name: "SomeClass" },
2907
+ { name: "SOMEOTHERNAME", class_name: "SomeOtherClass" },
2908
+ ],
2909
+ },
2910
+ env: {
2911
+ xyz: {
2912
+ durable_objects: {
2913
+ bindings: [
2914
+ { name: "SOMENAME", class_name: "SomeClass" },
2915
+ { name: "SOMEOTHERNAME", class_name: "SomeOtherClass" },
2916
+ ],
2917
+ },
2918
+ },
2919
+ },
2920
+ migrations: [
2921
+ { tag: "v1", new_classes: ["SomeClass"] },
2922
+ { tag: "v2", new_classes: ["SomeOtherClass"] },
2923
+ ],
2924
+ });
2925
+ fs.writeFileSync(
2926
+ "index.js",
2927
+ `export class SomeClass{}; export class SomeOtherClass{}; export default {};`
2928
+ );
2929
+ mockSubDomainRequest();
2930
+ mockServiceScriptData({
2931
+ script: { id: "test-name", migration_tag: "v1" },
2932
+ env: "xyz",
2933
+ });
2934
+ mockUploadWorkerRequest({
2935
+ legacyEnv: false,
2936
+ env: "xyz",
2937
+ expectedMigrations: {
2938
+ old_tag: "v1",
2939
+ new_tag: "v2",
2940
+ steps: [
2941
+ {
2942
+ new_classes: ["SomeOtherClass"],
2943
+ },
2944
+ ],
2945
+ },
2946
+ });
2947
+
2948
+ await runWrangler("publish index.js --legacy-env false --env xyz");
2949
+ expect(std).toMatchInlineSnapshot(`
2950
+ Object {
2951
+ "debug": "",
2952
+ "err": "",
2953
+ "out": "Uploaded test-name (xyz) (TIMINGS)
2954
+ Published test-name (xyz) (TIMINGS)
2955
+ xyz.test-name.test-sub-domain.workers.dev",
2956
+ "warn": "▲ [WARNING] Processing wrangler.toml configuration:
2957
+
2958
+ - Experimental: Service environments are in beta, and their behaviour is guaranteed to change in
2959
+ the future. DO NOT USE IN PRODUCTION.
2960
+
2961
+ ",
2962
+ }
2963
+ `);
2964
+ });
2965
+ });
2966
+ });
2967
+
2968
+ describe("bindings", () => {
2969
+ it("should allow bindings with different names", async () => {
2970
+ writeWranglerToml({
2971
+ migrations: [
2972
+ {
2973
+ tag: "v1",
2974
+ new_classes: ["SomeDurableObject", "AnotherDurableObject"],
2975
+ },
2976
+ ],
2977
+ durable_objects: {
2978
+ bindings: [
2979
+ {
2980
+ name: "DURABLE_OBJECT_ONE",
2981
+ class_name: "SomeDurableObject",
2982
+ script_name: "some-durable-object-worker",
2983
+ },
2984
+ {
2985
+ name: "DURABLE_OBJECT_TWO",
2986
+ class_name: "AnotherDurableObject",
2987
+ script_name: "another-durable-object-worker",
2988
+ },
2989
+ ],
2990
+ },
2991
+ kv_namespaces: [
2992
+ { binding: "KV_NAMESPACE_ONE", id: "kv-ns-one-id" },
2993
+ { binding: "KV_NAMESPACE_TWO", id: "kv-ns-two-id" },
2994
+ ],
2995
+ r2_buckets: [
2996
+ { binding: "R2_BUCKET_ONE", bucket_name: "r2-bucket-one-name" },
2997
+ { binding: "R2_BUCKET_TWO", bucket_name: "r2-bucket-two-name" },
2998
+ ],
2999
+ text_blobs: {
3000
+ TEXT_BLOB_ONE: "./my-entire-app-depends-on-this.cfg",
3001
+ TEXT_BLOB_TWO: "./the-entirety-of-human-knowledge.txt",
3002
+ },
3003
+ unsafe: {
3004
+ bindings: [
3005
+ {
3006
+ name: "UNSAFE_BINDING_ONE",
3007
+ type: "some unsafe thing",
3008
+ data: { some: { unsafe: "thing" } },
3009
+ },
3010
+ {
3011
+ name: "UNSAFE_BINDING_TWO",
3012
+ type: "another unsafe thing",
3013
+ data: 1337,
3014
+ },
3015
+ ],
3016
+ },
3017
+ vars: {
3018
+ ENV_VAR_ONE: 123,
3019
+ ENV_VAR_TWO: "Hello, I'm an environment variable",
3020
+ },
3021
+ wasm_modules: {
3022
+ WASM_MODULE_ONE: "./some_wasm.wasm",
3023
+ WASM_MODULE_TWO: "./more_wasm.wasm",
3024
+ },
3025
+ data_blobs: {
3026
+ DATA_BLOB_ONE: "./some-data-blob.bin",
3027
+ DATA_BLOB_TWO: "./more-data-blob.bin",
3028
+ },
3029
+ });
3030
+
3031
+ writeWorkerSource({ type: "sw" });
3032
+ fs.writeFileSync("./my-entire-app-depends-on-this.cfg", "config = value");
3033
+ fs.writeFileSync(
3034
+ "./the-entirety-of-human-knowledge.txt",
3035
+ "Everything's bigger in Texas"
3036
+ );
3037
+ fs.writeFileSync("./some_wasm.wasm", "some wasm");
3038
+ fs.writeFileSync("./more_wasm.wasm", "more wasm");
3039
+
3040
+ fs.writeFileSync("./some-data-blob.bin", "some data");
3041
+ fs.writeFileSync("./more-data-blob.bin", "more data");
3042
+
3043
+ mockUploadWorkerRequest({
3044
+ expectedType: "sw",
3045
+ expectedBindings: [
3046
+ {
3047
+ name: "KV_NAMESPACE_ONE",
3048
+ namespace_id: "kv-ns-one-id",
3049
+ type: "kv_namespace",
3050
+ },
3051
+ {
3052
+ name: "KV_NAMESPACE_TWO",
3053
+ namespace_id: "kv-ns-two-id",
3054
+ type: "kv_namespace",
3055
+ },
3056
+ {
3057
+ class_name: "SomeDurableObject",
3058
+ name: "DURABLE_OBJECT_ONE",
3059
+ script_name: "some-durable-object-worker",
3060
+ type: "durable_object_namespace",
3061
+ },
3062
+ {
3063
+ class_name: "AnotherDurableObject",
3064
+ name: "DURABLE_OBJECT_TWO",
3065
+ script_name: "another-durable-object-worker",
3066
+ type: "durable_object_namespace",
3067
+ },
3068
+ {
3069
+ bucket_name: "r2-bucket-one-name",
3070
+ name: "R2_BUCKET_ONE",
3071
+ type: "r2_bucket",
3072
+ },
3073
+ {
3074
+ bucket_name: "r2-bucket-two-name",
3075
+ name: "R2_BUCKET_TWO",
3076
+ type: "r2_bucket",
3077
+ },
3078
+ { json: 123, name: "ENV_VAR_ONE", type: "json" },
3079
+ {
3080
+ name: "ENV_VAR_TWO",
3081
+ text: "Hello, I'm an environment variable",
3082
+ type: "plain_text",
3083
+ },
3084
+ {
3085
+ name: "WASM_MODULE_ONE",
3086
+ part: "WASM_MODULE_ONE",
3087
+ type: "wasm_module",
3088
+ },
3089
+ {
3090
+ name: "WASM_MODULE_TWO",
3091
+ part: "WASM_MODULE_TWO",
3092
+ type: "wasm_module",
3093
+ },
3094
+ { name: "TEXT_BLOB_ONE", part: "TEXT_BLOB_ONE", type: "text_blob" },
3095
+ { name: "TEXT_BLOB_TWO", part: "TEXT_BLOB_TWO", type: "text_blob" },
3096
+ { name: "DATA_BLOB_ONE", part: "DATA_BLOB_ONE", type: "data_blob" },
3097
+ { name: "DATA_BLOB_TWO", part: "DATA_BLOB_TWO", type: "data_blob" },
3098
+ {
3099
+ data: { some: { unsafe: "thing" } },
3100
+ name: "UNSAFE_BINDING_ONE",
3101
+ type: "some unsafe thing",
3102
+ },
3103
+ {
3104
+ data: 1337,
3105
+ name: "UNSAFE_BINDING_TWO",
3106
+ type: "another unsafe thing",
3107
+ },
3108
+ ],
3109
+ });
3110
+ mockSubDomainRequest();
3111
+ mockLegacyScriptData({ scripts: [] });
3112
+
3113
+ await expect(runWrangler("publish index.js")).resolves.toBeUndefined();
3114
+ expect(std.out).toMatchInlineSnapshot(`
3115
+ "Uploaded test-name (TIMINGS)
3116
+ Published test-name (TIMINGS)
3117
+ test-name.test-sub-domain.workers.dev"
3118
+ `);
3119
+ expect(std.err).toMatchInlineSnapshot(`""`);
3120
+ expect(std.warn).toMatchInlineSnapshot(`
3121
+ "▲ [WARNING] Processing wrangler.toml configuration:
3122
+
3123
+ - \\"unsafe\\" fields are experimental and may change or break at any time.
3124
+
3125
+ "
3126
+ `);
3127
+ });
3128
+
3129
+ it("should error when bindings of different types have the same name", async () => {
3130
+ writeWranglerToml({
3131
+ durable_objects: {
3132
+ bindings: [
3133
+ {
3134
+ name: "CONFLICTING_NAME_ONE",
3135
+ class_name: "SomeDurableObject",
3136
+ script_name: "some-durable-object-worker",
3137
+ },
3138
+ {
3139
+ name: "CONFLICTING_NAME_TWO",
3140
+ class_name: "AnotherDurableObject",
3141
+ script_name: "another-durable-object-worker",
3142
+ },
3143
+ ],
3144
+ },
3145
+ kv_namespaces: [
3146
+ { binding: "CONFLICTING_NAME_ONE", id: "kv-ns-one-id" },
3147
+ { binding: "CONFLICTING_NAME_TWO", id: "kv-ns-two-id" },
3148
+ ],
3149
+ r2_buckets: [
3150
+ {
3151
+ binding: "CONFLICTING_NAME_ONE",
3152
+ bucket_name: "r2-bucket-one-name",
3153
+ },
3154
+ {
3155
+ binding: "CONFLICTING_NAME_THREE",
3156
+ bucket_name: "r2-bucket-two-name",
3157
+ },
3158
+ ],
3159
+ text_blobs: {
3160
+ CONFLICTING_NAME_THREE: "./my-entire-app-depends-on-this.cfg",
3161
+ CONFLICTING_NAME_FOUR: "./the-entirety-of-human-knowledge.txt",
3162
+ },
3163
+ unsafe: {
3164
+ bindings: [
3165
+ {
3166
+ name: "CONFLICTING_NAME_THREE",
3167
+ type: "some unsafe thing",
3168
+ data: { some: { unsafe: "thing" } },
3169
+ },
3170
+ {
3171
+ name: "CONFLICTING_NAME_FOUR",
3172
+ type: "another unsafe thing",
3173
+ data: 1337,
3174
+ },
3175
+ ],
3176
+ },
3177
+ vars: {
3178
+ ENV_VAR_ONE: 123,
3179
+ CONFLICTING_NAME_THREE: "Hello, I'm an environment variable",
3180
+ },
3181
+ wasm_modules: {
3182
+ WASM_MODULE_ONE: "./some_wasm.wasm",
3183
+ CONFLICTING_NAME_THREE: "./more_wasm.wasm",
3184
+ },
3185
+ data_blobs: {
3186
+ DATA_BLOB_ONE: "./some_data.bin",
3187
+ CONFLICTING_NAME_THREE: "./more_data.bin",
3188
+ },
3189
+ });
3190
+
3191
+ writeWorkerSource({ type: "sw" });
3192
+ fs.writeFileSync("./my-entire-app-depends-on-this.cfg", "config = value");
3193
+ fs.writeFileSync(
3194
+ "./the-entirety-of-human-knowledge.txt",
3195
+ "Everything's bigger in Texas"
3196
+ );
3197
+ fs.writeFileSync("./some_wasm.wasm", "some wasm");
3198
+ fs.writeFileSync("./more_wasm.wasm", "more wasm");
3199
+
3200
+ await expect(runWrangler("publish index.js")).rejects
3201
+ .toMatchInlineSnapshot(`
3202
+ [Error: Processing wrangler.toml configuration:
3203
+ - CONFLICTING_NAME_ONE assigned to Durable Object, KV Namespace, and R2 Bucket bindings.
3204
+ - CONFLICTING_NAME_TWO assigned to Durable Object and KV Namespace bindings.
3205
+ - CONFLICTING_NAME_THREE assigned to R2 Bucket, Text Blob, Unsafe, Environment Variable, WASM Module, and Data Blob bindings.
3206
+ - CONFLICTING_NAME_FOUR assigned to Text Blob and Unsafe bindings.
3207
+ - Bindings must have unique names, so that they can all be referenced in the worker.
3208
+ Please change your bindings to have unique names.]
3209
+ `);
3210
+ expect(std.out).toMatchInlineSnapshot(`
3211
+ "
3212
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
3213
+ `);
3214
+ expect(std.err).toMatchInlineSnapshot(`
3215
+ "X [ERROR] Processing wrangler.toml configuration:
3216
+
3217
+ - CONFLICTING_NAME_ONE assigned to Durable Object, KV Namespace, and R2 Bucket bindings.
3218
+ - CONFLICTING_NAME_TWO assigned to Durable Object and KV Namespace bindings.
3219
+ - CONFLICTING_NAME_THREE assigned to R2 Bucket, Text Blob, Unsafe, Environment Variable, WASM
3220
+ Module, and Data Blob bindings.
3221
+ - CONFLICTING_NAME_FOUR assigned to Text Blob and Unsafe bindings.
3222
+ - Bindings must have unique names, so that they can all be referenced in the worker.
3223
+ Please change your bindings to have unique names.
3224
+
3225
+ "
3226
+ `);
3227
+ expect(std.warn).toMatchInlineSnapshot(`
3228
+ "▲ [WARNING] Processing wrangler.toml configuration:
3229
+
3230
+ - \\"unsafe\\" fields are experimental and may change or break at any time.
3231
+
3232
+ "
3233
+ `);
3234
+ });
3235
+
3236
+ it("should error when bindings of the same type have the same name", async () => {
3237
+ writeWranglerToml({
3238
+ durable_objects: {
3239
+ bindings: [
3240
+ {
3241
+ name: "CONFLICTING_DURABLE_OBJECT_NAME",
3242
+ class_name: "SomeDurableObject",
3243
+ script_name: "some-durable-object-worker",
3244
+ },
3245
+ {
3246
+ name: "CONFLICTING_DURABLE_OBJECT_NAME",
3247
+ class_name: "AnotherDurableObject",
3248
+ script_name: "another-durable-object-worker",
3249
+ },
3250
+ ],
3251
+ },
3252
+ kv_namespaces: [
3253
+ { binding: "CONFLICTING_KV_NAMESPACE_NAME", id: "kv-ns-one-id" },
3254
+ { binding: "CONFLICTING_KV_NAMESPACE_NAME", id: "kv-ns-two-id" },
3255
+ ],
3256
+ r2_buckets: [
3257
+ {
3258
+ binding: "CONFLICTING_R2_BUCKET_NAME",
3259
+ bucket_name: "r2-bucket-one-name",
3260
+ },
3261
+ {
3262
+ binding: "CONFLICTING_R2_BUCKET_NAME",
3263
+ bucket_name: "r2-bucket-two-name",
3264
+ },
3265
+ ],
3266
+ unsafe: {
3267
+ bindings: [
3268
+ {
3269
+ name: "CONFLICTING_UNSAFE_NAME",
3270
+ type: "some unsafe thing",
3271
+ data: { some: { unsafe: "thing" } },
3272
+ },
3273
+ {
3274
+ name: "CONFLICTING_UNSAFE_NAME",
3275
+ type: "another unsafe thing",
3276
+ data: 1337,
3277
+ },
3278
+ ],
3279
+ },
3280
+ // text_blobs, vars, wasm_modules and data_blobs are fine because they're object literals,
3281
+ // and by definition cannot have two keys of the same name
3282
+ //
3283
+ // text_blobs: {
3284
+ // CONFLICTING_TEXT_BLOB_NAME: "./my-entire-app-depends-on-this.cfg",
3285
+ // CONFLICTING_TEXT_BLOB_NAME: "./the-entirety-of-human-knowledge.txt",
3286
+ // },
3287
+ // vars: {
3288
+ // CONFLICTING_VARS_NAME: 123,
3289
+ // CONFLICTING_VARS_NAME: "Hello, I'm an environment variable",
3290
+ // },
3291
+ // wasm_modules: {
3292
+ // CONFLICTING_WASM_MODULE_NAME: "./some_wasm.wasm",
3293
+ // CONFLICTING_WASM_MODULE_NAME: "./more_wasm.wasm",
3294
+ // },
3295
+ });
3296
+
3297
+ writeWorkerSource({ type: "sw" });
3298
+
3299
+ await expect(runWrangler("publish index.js")).rejects
3300
+ .toMatchInlineSnapshot(`
3301
+ [Error: Processing wrangler.toml configuration:
3302
+ - CONFLICTING_DURABLE_OBJECT_NAME assigned to multiple Durable Object bindings.
3303
+ - CONFLICTING_KV_NAMESPACE_NAME assigned to multiple KV Namespace bindings.
3304
+ - CONFLICTING_R2_BUCKET_NAME assigned to multiple R2 Bucket bindings.
3305
+ - CONFLICTING_UNSAFE_NAME assigned to multiple Unsafe bindings.
3306
+ - Bindings must have unique names, so that they can all be referenced in the worker.
3307
+ Please change your bindings to have unique names.]
3308
+ `);
3309
+ expect(std.out).toMatchInlineSnapshot(`
3310
+ "
3311
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
3312
+ `);
3313
+ expect(std.err).toMatchInlineSnapshot(`
3314
+ "X [ERROR] Processing wrangler.toml configuration:
3315
+
3316
+ - CONFLICTING_DURABLE_OBJECT_NAME assigned to multiple Durable Object bindings.
3317
+ - CONFLICTING_KV_NAMESPACE_NAME assigned to multiple KV Namespace bindings.
3318
+ - CONFLICTING_R2_BUCKET_NAME assigned to multiple R2 Bucket bindings.
3319
+ - CONFLICTING_UNSAFE_NAME assigned to multiple Unsafe bindings.
3320
+ - Bindings must have unique names, so that they can all be referenced in the worker.
3321
+ Please change your bindings to have unique names.
3322
+
3323
+ "
3324
+ `);
3325
+ expect(std.warn).toMatchInlineSnapshot(`
3326
+ "▲ [WARNING] Processing wrangler.toml configuration:
3327
+
3328
+ - \\"unsafe\\" fields are experimental and may change or break at any time.
3329
+
3330
+ "
3331
+ `);
3332
+ });
3333
+
3334
+ it("should error correctly when bindings of the same and different types use the same name", async () => {
3335
+ writeWranglerToml({
3336
+ durable_objects: {
3337
+ bindings: [
3338
+ {
3339
+ name: "CONFLICTING_DURABLE_OBJECT_NAME",
3340
+ class_name: "SomeDurableObject",
3341
+ script_name: "some-durable-object-worker",
3342
+ },
3343
+ {
3344
+ name: "CONFLICTING_DURABLE_OBJECT_NAME",
3345
+ class_name: "AnotherDurableObject",
3346
+ script_name: "another-durable-object-worker",
3347
+ },
3348
+ ],
3349
+ },
3350
+ kv_namespaces: [
3351
+ {
3352
+ binding: "CONFLICTING_KV_NAMESPACE_NAME",
3353
+ id: "kv-ns-one-id",
3354
+ },
3355
+ {
3356
+ binding: "CONFLICTING_KV_NAMESPACE_NAME",
3357
+ id: "kv-ns-two-id",
3358
+ },
3359
+ { binding: "CONFLICTING_NAME_ONE", id: "kv-ns-three-id" },
3360
+ { binding: "CONFLICTING_NAME_TWO", id: "kv-ns-four-id" },
3361
+ ],
3362
+ r2_buckets: [
3363
+ {
3364
+ binding: "CONFLICTING_R2_BUCKET_NAME",
3365
+ bucket_name: "r2-bucket-one-name",
3366
+ },
3367
+ {
3368
+ binding: "CONFLICTING_R2_BUCKET_NAME",
3369
+ bucket_name: "r2-bucket-two-name",
3370
+ },
3371
+ {
3372
+ binding: "CONFLICTING_NAME_THREE",
3373
+ bucket_name: "r2-bucket-three-name",
3374
+ },
3375
+ {
3376
+ binding: "CONFLICTING_NAME_FOUR",
3377
+ bucket_name: "r2-bucket-four-name",
3378
+ },
3379
+ ],
3380
+ text_blobs: {
3381
+ CONFLICTING_NAME_THREE: "./my-entire-app-depends-on-this.cfg",
3382
+ CONFLICTING_NAME_FOUR: "./the-entirety-of-human-knowledge.txt",
3383
+ },
3384
+ unsafe: {
3385
+ bindings: [
3386
+ {
3387
+ name: "CONFLICTING_UNSAFE_NAME",
3388
+ type: "some unsafe thing",
3389
+ data: { some: { unsafe: "thing" } },
3390
+ },
3391
+ {
3392
+ name: "CONFLICTING_UNSAFE_NAME",
3393
+ type: "another unsafe thing",
3394
+ data: 1337,
3395
+ },
3396
+ {
3397
+ name: "CONFLICTING_NAME_THREE",
3398
+ type: "yet another unsafe thing",
3399
+ data: "how is a string unsafe?",
3400
+ },
3401
+ {
3402
+ name: "CONFLICTING_NAME_FOUR",
3403
+ type: "a fourth unsafe thing",
3404
+ data: null,
3405
+ },
3406
+ ],
3407
+ },
3408
+ vars: {
3409
+ ENV_VAR_ONE: 123,
3410
+ CONFLICTING_NAME_THREE: "Hello, I'm an environment variable",
3411
+ },
3412
+ wasm_modules: {
3413
+ WASM_MODULE_ONE: "./some_wasm.wasm",
3414
+ CONFLICTING_NAME_THREE: "./more_wasm.wasm",
3415
+ },
3416
+ data_blobs: {
3417
+ DATA_BLOB_ONE: "./some_data.bin",
3418
+ CONFLICTING_NAME_THREE: "./more_data.bin",
3419
+ },
3420
+ });
3421
+
3422
+ writeWorkerSource({ type: "sw" });
3423
+ fs.writeFileSync("./my-entire-app-depends-on-this.cfg", "config = value");
3424
+ fs.writeFileSync(
3425
+ "./the-entirety-of-human-knowledge.txt",
3426
+ "Everything's bigger in Texas"
3427
+ );
3428
+ fs.writeFileSync("./some_wasm.wasm", "some wasm");
3429
+ fs.writeFileSync("./more_wasm.wasm", "more wasm");
3430
+
3431
+ await expect(runWrangler("publish index.js")).rejects
3432
+ .toMatchInlineSnapshot(`
3433
+ [Error: Processing wrangler.toml configuration:
3434
+ - CONFLICTING_DURABLE_OBJECT_NAME assigned to multiple Durable Object bindings.
3435
+ - CONFLICTING_KV_NAMESPACE_NAME assigned to multiple KV Namespace bindings.
3436
+ - CONFLICTING_R2_BUCKET_NAME assigned to multiple R2 Bucket bindings.
3437
+ - CONFLICTING_NAME_THREE assigned to R2 Bucket, Text Blob, Unsafe, Environment Variable, WASM Module, and Data Blob bindings.
3438
+ - CONFLICTING_NAME_FOUR assigned to R2 Bucket, Text Blob, and Unsafe bindings.
3439
+ - CONFLICTING_UNSAFE_NAME assigned to multiple Unsafe bindings.
3440
+ - Bindings must have unique names, so that they can all be referenced in the worker.
3441
+ Please change your bindings to have unique names.]
3442
+ `);
3443
+ expect(std.out).toMatchInlineSnapshot(`
3444
+ "
3445
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
3446
+ `);
3447
+ expect(std.err).toMatchInlineSnapshot(`
3448
+ "X [ERROR] Processing wrangler.toml configuration:
3449
+
3450
+ - CONFLICTING_DURABLE_OBJECT_NAME assigned to multiple Durable Object bindings.
3451
+ - CONFLICTING_KV_NAMESPACE_NAME assigned to multiple KV Namespace bindings.
3452
+ - CONFLICTING_R2_BUCKET_NAME assigned to multiple R2 Bucket bindings.
3453
+ - CONFLICTING_NAME_THREE assigned to R2 Bucket, Text Blob, Unsafe, Environment Variable, WASM
3454
+ Module, and Data Blob bindings.
3455
+ - CONFLICTING_NAME_FOUR assigned to R2 Bucket, Text Blob, and Unsafe bindings.
3456
+ - CONFLICTING_UNSAFE_NAME assigned to multiple Unsafe bindings.
3457
+ - Bindings must have unique names, so that they can all be referenced in the worker.
3458
+ Please change your bindings to have unique names.
3459
+
3460
+ "
3461
+ `);
3462
+ expect(std.warn).toMatchInlineSnapshot(`
3463
+ "▲ [WARNING] Processing wrangler.toml configuration:
3464
+
3465
+ - \\"unsafe\\" fields are experimental and may change or break at any time.
3466
+
3467
+ "
3468
+ `);
3469
+ });
3470
+
3471
+ describe("[wasm_modules]", () => {
3472
+ it("should be able to define wasm modules for service-worker format workers", async () => {
3473
+ writeWranglerToml({
3474
+ wasm_modules: {
3475
+ TESTWASMNAME: "./path/to/test.wasm",
3476
+ },
3477
+ });
3478
+ writeWorkerSource({ type: "sw" });
3479
+ fs.mkdirSync("./path/to", { recursive: true });
3480
+ fs.writeFileSync("./path/to/test.wasm", "SOME WASM CONTENT");
3481
+ mockUploadWorkerRequest({
3482
+ expectedType: "sw",
3483
+ expectedModules: { TESTWASMNAME: "SOME WASM CONTENT" },
3484
+ expectedBindings: [
3485
+ { name: "TESTWASMNAME", part: "TESTWASMNAME", type: "wasm_module" },
3486
+ ],
3487
+ });
3488
+ mockSubDomainRequest();
3489
+ await runWrangler("publish index.js");
3490
+ expect(std.out).toMatchInlineSnapshot(`
3491
+ "Uploaded test-name (TIMINGS)
3492
+ Published test-name (TIMINGS)
3493
+ test-name.test-sub-domain.workers.dev"
3494
+ `);
3495
+ expect(std.err).toMatchInlineSnapshot(`""`);
3496
+ expect(std.warn).toMatchInlineSnapshot(`""`);
3497
+ });
3498
+
3499
+ it("should error when defining wasm modules for modules format workers", async () => {
3500
+ writeWranglerToml({
3501
+ wasm_modules: {
3502
+ TESTWASMNAME: "./path/to/test.wasm",
3503
+ },
3504
+ });
3505
+ writeWorkerSource({ type: "esm" });
3506
+ fs.mkdirSync("./path/to", { recursive: true });
3507
+ fs.writeFileSync("./path/to/test.wasm", "SOME WASM CONTENT");
3508
+
3509
+ await expect(
3510
+ runWrangler("publish index.js")
3511
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
3512
+ `"You cannot configure [wasm_modules] with an ES module worker. Instead, import the .wasm module directly in your code"`
3513
+ );
3514
+ expect(std.out).toMatchInlineSnapshot(`
3515
+ "
3516
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
3517
+ `);
3518
+ expect(std.err).toMatchInlineSnapshot(`
3519
+ "X [ERROR] You cannot configure [wasm_modules] with an ES module worker. Instead, import the .wasm module directly in your code
3520
+
3521
+ "
3522
+ `);
3523
+ expect(std.warn).toMatchInlineSnapshot(`""`);
3524
+ });
3525
+
3526
+ it("should resolve wasm modules relative to the wrangler.toml file", async () => {
3527
+ fs.mkdirSync("./path/to/and/the/path/to/", { recursive: true });
3528
+ fs.writeFileSync(
3529
+ "./path/to/wrangler.toml",
3530
+ TOML.stringify({
3531
+ compatibility_date: "2022-01-12",
3532
+ name: "test-name",
3533
+ wasm_modules: {
3534
+ TESTWASMNAME: "./and/the/path/to/test.wasm",
3535
+ },
3536
+ }),
3537
+
3538
+ "utf-8"
3539
+ );
3540
+
3541
+ writeWorkerSource({ type: "sw" });
3542
+ fs.writeFileSync(
3543
+ "./path/to/and/the/path/to/test.wasm",
3544
+ "SOME WASM CONTENT"
3545
+ );
3546
+ mockUploadWorkerRequest({
3547
+ expectedType: "sw",
3548
+ expectedModules: { TESTWASMNAME: "SOME WASM CONTENT" },
3549
+ expectedBindings: [
3550
+ { name: "TESTWASMNAME", part: "TESTWASMNAME", type: "wasm_module" },
3551
+ ],
3552
+ expectedCompatibilityDate: "2022-01-12",
3553
+ });
3554
+ mockSubDomainRequest();
3555
+ await runWrangler("publish index.js --config ./path/to/wrangler.toml");
3556
+ expect(std.out).toMatchInlineSnapshot(`
3557
+ "Uploaded test-name (TIMINGS)
3558
+ Published test-name (TIMINGS)
3559
+ test-name.test-sub-domain.workers.dev"
3560
+ `);
3561
+ expect(std.err).toMatchInlineSnapshot(`""`);
3562
+ expect(std.warn).toMatchInlineSnapshot(`""`);
3563
+ });
3564
+
3565
+ it("should be able to import .wasm modules from service-worker format workers", async () => {
3566
+ writeWranglerToml();
3567
+ fs.writeFileSync(
3568
+ "./index.js",
3569
+ "import TESTWASMNAME from './test.wasm';"
3570
+ );
3571
+ fs.writeFileSync("./test.wasm", "SOME WASM CONTENT");
3572
+ mockUploadWorkerRequest({
3573
+ expectedType: "sw",
3574
+ expectedModules: {
3575
+ __94b240d0d692281e6467aa42043986e5c7eea034_test_wasm:
3576
+ "SOME WASM CONTENT",
3577
+ },
3578
+ expectedBindings: [
3579
+ {
3580
+ name: "__94b240d0d692281e6467aa42043986e5c7eea034_test_wasm",
3581
+ part: "__94b240d0d692281e6467aa42043986e5c7eea034_test_wasm",
3582
+ type: "wasm_module",
3583
+ },
3584
+ ],
3585
+ });
3586
+ mockSubDomainRequest();
3587
+ await runWrangler("publish index.js");
3588
+ expect(std.out).toMatchInlineSnapshot(`
3589
+ "Uploaded test-name (TIMINGS)
3590
+ Published test-name (TIMINGS)
3591
+ test-name.test-sub-domain.workers.dev"
3592
+ `);
3593
+ expect(std.err).toMatchInlineSnapshot(`""`);
3594
+ expect(std.warn).toMatchInlineSnapshot(`""`);
3595
+ });
3596
+ });
3597
+
3598
+ describe("[text_blobs]", () => {
3599
+ it("should be able to define text blobs for service-worker format workers", async () => {
3600
+ writeWranglerToml({
3601
+ text_blobs: {
3602
+ TESTTEXTBLOBNAME: "./path/to/text.file",
3603
+ },
3604
+ });
3605
+ writeWorkerSource({ type: "sw" });
3606
+ fs.mkdirSync("./path/to", { recursive: true });
3607
+ fs.writeFileSync("./path/to/text.file", "SOME TEXT CONTENT");
3608
+ mockUploadWorkerRequest({
3609
+ expectedType: "sw",
3610
+ expectedModules: { TESTTEXTBLOBNAME: "SOME TEXT CONTENT" },
3611
+ expectedBindings: [
3612
+ {
3613
+ name: "TESTTEXTBLOBNAME",
3614
+ part: "TESTTEXTBLOBNAME",
3615
+ type: "text_blob",
3616
+ },
3617
+ ],
3618
+ });
3619
+ mockSubDomainRequest();
3620
+ await runWrangler("publish index.js");
3621
+ expect(std.out).toMatchInlineSnapshot(`
3622
+ "Uploaded test-name (TIMINGS)
3623
+ Published test-name (TIMINGS)
3624
+ test-name.test-sub-domain.workers.dev"
3625
+ `);
3626
+ expect(std.err).toMatchInlineSnapshot(`""`);
3627
+ expect(std.warn).toMatchInlineSnapshot(`""`);
3628
+ });
3629
+
3630
+ it("should error when defining text blobs for modules format workers", async () => {
3631
+ writeWranglerToml({
3632
+ text_blobs: {
3633
+ TESTTEXTBLOBNAME: "./path/to/text.file",
3634
+ },
3635
+ });
3636
+ writeWorkerSource({ type: "esm" });
3637
+ fs.mkdirSync("./path/to", { recursive: true });
3638
+ fs.writeFileSync("./path/to/text.file", "SOME TEXT CONTENT");
3639
+
3640
+ await expect(
3641
+ runWrangler("publish index.js")
3642
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
3643
+ `"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"`
3644
+ );
3645
+ expect(std.out).toMatchInlineSnapshot(`
3646
+ "
3647
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
3648
+ `);
3649
+ expect(std.err).toMatchInlineSnapshot(`
3650
+ "X [ERROR] 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
3651
+
3652
+ "
3653
+ `);
3654
+ expect(std.warn).toMatchInlineSnapshot(`""`);
3655
+ });
3656
+
3657
+ it("should resolve text blobs relative to the wrangler.toml file", async () => {
3658
+ fs.mkdirSync("./path/to/and/the/path/to/", { recursive: true });
3659
+ fs.writeFileSync(
3660
+ "./path/to/wrangler.toml",
3661
+ TOML.stringify({
3662
+ compatibility_date: "2022-01-12",
3663
+ name: "test-name",
3664
+ text_blobs: {
3665
+ TESTTEXTBLOBNAME: "./and/the/path/to/text.file",
3666
+ },
3667
+ }),
3668
+
3669
+ "utf-8"
3670
+ );
3671
+
3672
+ writeWorkerSource({ type: "sw" });
3673
+ fs.writeFileSync(
3674
+ "./path/to/and/the/path/to/text.file",
3675
+ "SOME TEXT CONTENT"
3676
+ );
3677
+ mockUploadWorkerRequest({
3678
+ expectedType: "sw",
3679
+ expectedModules: { TESTTEXTBLOBNAME: "SOME TEXT CONTENT" },
3680
+ expectedBindings: [
3681
+ {
3682
+ name: "TESTTEXTBLOBNAME",
3683
+ part: "TESTTEXTBLOBNAME",
3684
+ type: "text_blob",
3685
+ },
3686
+ ],
3687
+ expectedCompatibilityDate: "2022-01-12",
3688
+ });
3689
+ mockSubDomainRequest();
3690
+ await runWrangler("publish index.js --config ./path/to/wrangler.toml");
3691
+ expect(std.out).toMatchInlineSnapshot(`
3692
+ "Uploaded test-name (TIMINGS)
3693
+ Published test-name (TIMINGS)
3694
+ test-name.test-sub-domain.workers.dev"
3695
+ `);
3696
+ expect(std.err).toMatchInlineSnapshot(`""`);
3697
+ expect(std.warn).toMatchInlineSnapshot(`""`);
3698
+ });
3699
+ });
3700
+
3701
+ describe("[data_blobs]", () => {
3702
+ it("should be able to define data blobs for service-worker format workers", async () => {
3703
+ writeWranglerToml({
3704
+ data_blobs: {
3705
+ TESTDATABLOBNAME: "./path/to/data.bin",
3706
+ },
3707
+ });
3708
+ writeWorkerSource({ type: "sw" });
3709
+ fs.mkdirSync("./path/to", { recursive: true });
3710
+ fs.writeFileSync("./path/to/data.bin", "SOME DATA CONTENT");
3711
+ mockUploadWorkerRequest({
3712
+ expectedType: "sw",
3713
+ expectedModules: { TESTDATABLOBNAME: "SOME DATA CONTENT" },
3714
+ expectedBindings: [
3715
+ {
3716
+ name: "TESTDATABLOBNAME",
3717
+ part: "TESTDATABLOBNAME",
3718
+ type: "data_blob",
3719
+ },
3720
+ ],
3721
+ });
3722
+ mockSubDomainRequest();
3723
+ await runWrangler("publish index.js");
3724
+ expect(std.out).toMatchInlineSnapshot(`
3725
+ "Uploaded test-name (TIMINGS)
3726
+ Published test-name (TIMINGS)
3727
+ test-name.test-sub-domain.workers.dev"
3728
+ `);
3729
+ expect(std.err).toMatchInlineSnapshot(`""`);
3730
+ expect(std.warn).toMatchInlineSnapshot(`""`);
3731
+ });
3732
+
3733
+ it("should error when defining data blobs for modules format workers", async () => {
3734
+ writeWranglerToml({
3735
+ data_blobs: {
3736
+ TESTDATABLOBNAME: "./path/to/data.bin",
3737
+ },
3738
+ });
3739
+ writeWorkerSource({ type: "esm" });
3740
+ fs.mkdirSync("./path/to", { recursive: true });
3741
+ fs.writeFileSync("./path/to/data.bin", "SOME DATA CONTENT");
3742
+
3743
+ await expect(
3744
+ runWrangler("publish index.js")
3745
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
3746
+ `"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"`
3747
+ );
3748
+ expect(std.out).toMatchInlineSnapshot(`
3749
+ "
3750
+ If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
3751
+ `);
3752
+ expect(std.err).toMatchInlineSnapshot(`
3753
+ "X [ERROR] 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
3754
+
3755
+ "
3756
+ `);
3757
+ expect(std.warn).toMatchInlineSnapshot(`""`);
3758
+ });
3759
+
3760
+ it("should resolve data blobs relative to the wrangler.toml file", async () => {
3761
+ fs.mkdirSync("./path/to/and/the/path/to/", { recursive: true });
3762
+ fs.writeFileSync(
3763
+ "./path/to/wrangler.toml",
3764
+ TOML.stringify({
3765
+ compatibility_date: "2022-01-12",
3766
+ name: "test-name",
3767
+ data_blobs: {
3768
+ TESTDATABLOBNAME: "./and/the/path/to/data.bin",
3769
+ },
3770
+ }),
3771
+
3772
+ "utf-8"
3773
+ );
3774
+
3775
+ writeWorkerSource({ type: "sw" });
3776
+ fs.writeFileSync(
3777
+ "./path/to/and/the/path/to/data.bin",
3778
+ "SOME DATA CONTENT"
3779
+ );
3780
+ mockUploadWorkerRequest({
3781
+ expectedType: "sw",
3782
+ expectedModules: { TESTDATABLOBNAME: "SOME DATA CONTENT" },
3783
+ expectedBindings: [
3784
+ {
3785
+ name: "TESTDATABLOBNAME",
3786
+ part: "TESTDATABLOBNAME",
3787
+ type: "data_blob",
3788
+ },
3789
+ ],
3790
+ expectedCompatibilityDate: "2022-01-12",
3791
+ });
3792
+ mockSubDomainRequest();
3793
+ await runWrangler("publish index.js --config ./path/to/wrangler.toml");
3794
+ expect(std.out).toMatchInlineSnapshot(`
3795
+ "Uploaded test-name (TIMINGS)
3796
+ Published test-name (TIMINGS)
3797
+ test-name.test-sub-domain.workers.dev"
3798
+ `);
3799
+ expect(std.err).toMatchInlineSnapshot(`""`);
3800
+ expect(std.warn).toMatchInlineSnapshot(`""`);
3801
+ });
3802
+ });
3803
+
3804
+ describe("[vars]", () => {
3805
+ it("should support json bindings", async () => {
3806
+ writeWranglerToml({
3807
+ vars: {
3808
+ text: "plain ol' string",
3809
+ count: 1,
3810
+ complex: { enabled: true, id: 123 },
3811
+ },
3812
+ });
3813
+ writeWorkerSource();
3814
+ mockSubDomainRequest();
3815
+ mockUploadWorkerRequest({
3816
+ expectedBindings: [
3817
+ { name: "text", type: "plain_text", text: "plain ol' string" },
3818
+ { name: "count", type: "json", json: 1 },
3819
+ {
3820
+ name: "complex",
3821
+ type: "json",
3822
+ json: { enabled: true, id: 123 },
3823
+ },
3824
+ ],
3825
+ });
3826
+
3827
+ await runWrangler("publish index.js");
3828
+ expect(std.out).toMatchInlineSnapshot(`
3829
+ "Uploaded test-name (TIMINGS)
3830
+ Published test-name (TIMINGS)
3831
+ test-name.test-sub-domain.workers.dev"
3832
+ `);
3833
+ expect(std.err).toMatchInlineSnapshot(`""`);
3834
+ expect(std.warn).toMatchInlineSnapshot(`""`);
3835
+ });
3836
+ });
3837
+
3838
+ describe("[r2_buckets]", () => {
3839
+ it("should support r2 bucket bindings", async () => {
3840
+ writeWranglerToml({
3841
+ r2_buckets: [{ binding: "FOO", bucket_name: "foo-bucket" }],
3842
+ });
3843
+ writeWorkerSource();
3844
+ mockSubDomainRequest();
3845
+ mockUploadWorkerRequest({
3846
+ expectedBindings: [
3847
+ { bucket_name: "foo-bucket", name: "FOO", type: "r2_bucket" },
3848
+ ],
3849
+ });
3850
+
3851
+ await runWrangler("publish index.js");
3852
+ expect(std.out).toMatchInlineSnapshot(`
3853
+ "Uploaded test-name (TIMINGS)
3854
+ Published test-name (TIMINGS)
3855
+ test-name.test-sub-domain.workers.dev"
3856
+ `);
3857
+ expect(std.err).toMatchInlineSnapshot(`""`);
3858
+ expect(std.warn).toMatchInlineSnapshot(`""`);
3859
+ });
3860
+ });
3861
+
3862
+ describe("[durable_objects]", () => {
3863
+ it("should support durable object bindings", async () => {
3864
+ writeWranglerToml({
3865
+ durable_objects: {
3866
+ bindings: [
3867
+ {
3868
+ name: "EXAMPLE_DO_BINDING",
3869
+ class_name: "ExampleDurableObject",
3870
+ },
3871
+ ],
3872
+ },
3873
+ migrations: [{ tag: "v1", new_classes: ["ExampleDurableObject"] }],
3874
+ });
3875
+ fs.writeFileSync(
3876
+ "index.js",
3877
+ `export class ExampleDurableObject {}; export default{};`
3878
+ );
3879
+ mockSubDomainRequest();
3880
+ mockLegacyScriptData({
3881
+ scripts: [{ id: "test-name", migration_tag: "v1" }],
3882
+ });
3883
+ mockUploadWorkerRequest({
3884
+ expectedBindings: [
3885
+ {
3886
+ class_name: "ExampleDurableObject",
3887
+ name: "EXAMPLE_DO_BINDING",
3888
+ type: "durable_object_namespace",
3889
+ },
3890
+ ],
3891
+ });
3892
+
3893
+ await runWrangler("publish index.js");
3894
+ expect(std.out).toMatchInlineSnapshot(`
3895
+ "Uploaded test-name (TIMINGS)
3896
+ Published test-name (TIMINGS)
3897
+ test-name.test-sub-domain.workers.dev"
3898
+ `);
3899
+ expect(std.err).toMatchInlineSnapshot(`""`);
3900
+ expect(std.warn).toMatchInlineSnapshot(`""`);
3901
+ });
3902
+
3903
+ it("should support service-workers binding to external durable objects", async () => {
3904
+ writeWranglerToml({
3905
+ durable_objects: {
3906
+ bindings: [
3907
+ {
3908
+ name: "EXAMPLE_DO_BINDING",
3909
+ class_name: "ExampleDurableObject",
3910
+ script_name: "example-do-binding-worker",
3911
+ },
3912
+ ],
3913
+ },
3914
+ });
3915
+ writeWorkerSource({ type: "sw" });
3916
+ mockSubDomainRequest();
3917
+ mockUploadWorkerRequest({
3918
+ expectedType: "sw",
3919
+ expectedBindings: [
3920
+ {
3921
+ name: "EXAMPLE_DO_BINDING",
3922
+ class_name: "ExampleDurableObject",
3923
+ script_name: "example-do-binding-worker",
3924
+ type: "durable_object_namespace",
3925
+ },
3926
+ ],
3927
+ });
3928
+
3929
+ await runWrangler("publish index.js");
3930
+ expect(std.out).toMatchInlineSnapshot(`
3931
+ "Uploaded test-name (TIMINGS)
3932
+ Published test-name (TIMINGS)
3933
+ test-name.test-sub-domain.workers.dev"
3934
+ `);
3935
+ expect(std.err).toMatchInlineSnapshot(`""`);
3936
+ expect(std.warn).toMatchInlineSnapshot(`""`);
3937
+ });
3938
+
3939
+ it("should support module workers implementing durable objects", async () => {
3940
+ writeWranglerToml({
3941
+ durable_objects: {
3942
+ bindings: [
3943
+ {
3944
+ name: "EXAMPLE_DO_BINDING",
3945
+ class_name: "ExampleDurableObject",
3946
+ },
3947
+ ],
3948
+ },
3949
+ migrations: [{ tag: "v1", new_classes: ["ExampleDurableObject"] }],
3950
+ });
3951
+ fs.writeFileSync(
3952
+ "index.js",
3953
+ `export class ExampleDurableObject {}; export default{};`
3954
+ );
3955
+ mockSubDomainRequest();
3956
+ mockLegacyScriptData({
3957
+ scripts: [{ id: "test-name", migration_tag: "v1" }],
3958
+ });
3959
+ mockUploadWorkerRequest({
3960
+ expectedType: "esm",
3961
+ expectedBindings: [
3962
+ {
3963
+ name: "EXAMPLE_DO_BINDING",
3964
+ class_name: "ExampleDurableObject",
3965
+ type: "durable_object_namespace",
3966
+ },
3967
+ ],
3968
+ });
3969
+
3970
+ await runWrangler("publish index.js");
3971
+ expect(std.out).toMatchInlineSnapshot(`
3972
+ "Uploaded test-name (TIMINGS)
3973
+ Published test-name (TIMINGS)
3974
+ test-name.test-sub-domain.workers.dev"
3975
+ `);
3976
+ expect(std.err).toMatchInlineSnapshot(`""`);
3977
+ expect(std.warn).toMatchInlineSnapshot(`""`);
3978
+ });
3979
+
3980
+ it("should error when detecting a service-worker worker implementing durable objects", async () => {
3981
+ writeWranglerToml({
3982
+ durable_objects: {
3983
+ bindings: [
3984
+ {
3985
+ name: "EXAMPLE_DO_BINDING",
3986
+ class_name: "ExampleDurableObject",
3987
+ },
3988
+ ],
3989
+ },
3990
+ });
3991
+ writeWorkerSource({ type: "sw" });
3992
+ mockSubDomainRequest();
3993
+
3994
+ await expect(runWrangler("publish index.js")).rejects
3995
+ .toThrowErrorMatchingInlineSnapshot(`
3996
+ "You seem to be trying to use Durable Objects in a Worker written as a service-worker.
3997
+ You can use Durable Objects defined in other Workers by specifying a \`script_name\` in your wrangler.toml, where \`script_name\` is the name of the Worker that implements that Durable Object. For example:
3998
+ { name = EXAMPLE_DO_BINDING, class_name = ExampleDurableObject } ==> { name = EXAMPLE_DO_BINDING, class_name = ExampleDurableObject, script_name = example-do-binding-worker }
3999
+ Alternatively, migrate your worker to ES Module syntax to implement a Durable Object in this Worker:
4000
+ https://developers.cloudflare.com/workers/learning/migrating-to-module-workers/"
4001
+ `);
4002
+ });
4003
+ });
4004
+
4005
+ describe("[unsafe]", () => {
4006
+ it("should warn if using unsafe bindings", async () => {
4007
+ writeWranglerToml({
4008
+ unsafe: {
4009
+ bindings: [
4010
+ {
4011
+ name: "my-binding",
4012
+ type: "binding-type",
4013
+ param: "binding-param",
4014
+ },
4015
+ ],
4016
+ },
4017
+ });
4018
+ writeWorkerSource();
4019
+ mockSubDomainRequest();
4020
+ mockUploadWorkerRequest({
4021
+ expectedBindings: [
1890
4022
  {
1891
4023
  name: "my-binding",
1892
4024
  type: "binding-type",
1893
4025
  param: "binding-param",
1894
4026
  },
1895
4027
  ],
1896
- },
4028
+ });
4029
+
4030
+ await runWrangler("publish index.js");
4031
+ expect(std.out).toMatchInlineSnapshot(`
4032
+ "Uploaded test-name (TIMINGS)
4033
+ Published test-name (TIMINGS)
4034
+ test-name.test-sub-domain.workers.dev"
4035
+ `);
4036
+ expect(std.err).toMatchInlineSnapshot(`""`);
4037
+ expect(std.warn).toMatchInlineSnapshot(`
4038
+ "▲ [WARNING] Processing wrangler.toml configuration:
4039
+
4040
+ - \\"unsafe\\" fields are experimental and may change or break at any time.
4041
+
4042
+ "
4043
+ `);
1897
4044
  });
1898
- writeWorkerSource();
1899
- mockSubDomainRequest();
1900
- mockUploadWorkerRequest({
1901
- expectedBindings: [
1902
- {
1903
- name: "my-binding",
1904
- type: "binding-type",
1905
- param: "binding-param",
4045
+ it("should warn if using unsafe bindings already handled by wrangler", async () => {
4046
+ writeWranglerToml({
4047
+ unsafe: {
4048
+ bindings: [
4049
+ {
4050
+ name: "my-binding",
4051
+ type: "plain_text",
4052
+ text: "text",
4053
+ },
4054
+ ],
1906
4055
  },
1907
- ],
1908
- });
1909
-
1910
- await runWrangler("publish index.js");
1911
- expect(std.out).toMatchInlineSnapshot(`
1912
- "Uploaded test-name (TIMINGS)
1913
- Published test-name (TIMINGS)
1914
- test-name.test-sub-domain.workers.dev"
1915
- `);
1916
- expect(std.err).toMatchInlineSnapshot(`""`);
1917
- expect(std.warn).toMatchInlineSnapshot(`
1918
- "Processing wrangler.toml configuration:
1919
- - \\"unsafe\\" fields are experimental and may change or break at any time."
1920
- `);
1921
- });
1922
- it("should warn if using unsafe bindings already handled by wrangler", async () => {
1923
- writeWranglerToml({
1924
- unsafe: {
1925
- bindings: [
4056
+ });
4057
+ writeWorkerSource();
4058
+ mockSubDomainRequest();
4059
+ mockUploadWorkerRequest({
4060
+ expectedBindings: [
1926
4061
  {
1927
4062
  name: "my-binding",
1928
4063
  type: "plain_text",
1929
4064
  text: "text",
1930
4065
  },
1931
4066
  ],
1932
- },
1933
- });
1934
- writeWorkerSource();
1935
- mockSubDomainRequest();
1936
- mockUploadWorkerRequest({
1937
- expectedBindings: [
1938
- {
1939
- name: "my-binding",
1940
- type: "plain_text",
1941
- text: "text",
1942
- },
1943
- ],
1944
- });
4067
+ });
1945
4068
 
1946
- await runWrangler("publish index.js");
1947
- expect(std.out).toMatchInlineSnapshot(`
1948
- "Uploaded test-name (TIMINGS)
1949
- Published test-name (TIMINGS)
1950
- test-name.test-sub-domain.workers.dev"
1951
- `);
1952
- expect(std.err).toMatchInlineSnapshot(`""`);
1953
- expect(std.warn).toMatchInlineSnapshot(`
1954
- "Processing wrangler.toml configuration:
1955
- - \\"unsafe\\" fields are experimental and may change or break at any time.
1956
- - \\"unsafe.bindings[0]\\": {\\"name\\":\\"my-binding\\",\\"type\\":\\"plain_text\\",\\"text\\":\\"text\\"}
1957
- - The binding type \\"plain_text\\" is directly supported by wrangler.
1958
- Consider migrating this unsafe binding to a format for 'plain_text' bindings that is supported by wrangler for optimal support.
1959
- For more details, see https://developers.cloudflare.com/workers/cli-wrangler/configuration"
1960
- `);
4069
+ await runWrangler("publish index.js");
4070
+ expect(std.out).toMatchInlineSnapshot(`
4071
+ "Uploaded test-name (TIMINGS)
4072
+ Published test-name (TIMINGS)
4073
+ test-name.test-sub-domain.workers.dev"
4074
+ `);
4075
+ expect(std.err).toMatchInlineSnapshot(`""`);
4076
+ expect(std.warn).toMatchInlineSnapshot(`
4077
+ "▲ [WARNING] Processing wrangler.toml configuration:
4078
+
4079
+ - \\"unsafe\\" fields are experimental and may change or break at any time.
4080
+ - \\"unsafe.bindings[0]\\": {\\"name\\":\\"my-binding\\",\\"type\\":\\"plain_text\\",\\"text\\":\\"text\\"}
4081
+ - The binding type \\"plain_text\\" is directly supported by wrangler.
4082
+ Consider migrating this unsafe binding to a format for 'plain_text' bindings that is
4083
+ supported by wrangler for optimal support.
4084
+ For more details, see https://developers.cloudflare.com/workers/cli-wrangler/configuration
4085
+
4086
+ "
4087
+ `);
4088
+ });
1961
4089
  });
1962
4090
  });
1963
4091
 
@@ -2051,14 +4179,19 @@ export default{
2051
4179
  `);
2052
4180
  expect(std.err).toMatchInlineSnapshot(`""`);
2053
4181
  expect(std.warn).toMatchInlineSnapshot(`
2054
- "Processing wrangler.toml configuration:
2055
- - DEPRECATION: The \`build.upload.rules\` config field is no longer used, the rules should be specified via the \`rules\` config field. Delete the \`build.upload\` field from the configuration file, and add this:
2056
- \`\`\`
2057
- [[rules]]
2058
- type = \\"Text\\"
2059
- globs = [ \\"**/*.file\\" ]
2060
- fallthrough = true
2061
- \`\`\`"
4182
+ "▲ [WARNING] Processing wrangler.toml configuration:
4183
+
4184
+ - Deprecation: The \`build.upload.rules\` config field is no longer used, the rules should be
4185
+ specified via the \`rules\` config field. Delete the \`build.upload\` field from the configuration
4186
+ file, and add this:
4187
+ \`\`\`
4188
+ [[rules]]
4189
+ type = \\"Text\\"
4190
+ globs = [ \\"**/*.file\\" ]
4191
+ fallthrough = true
4192
+ \`\`\`
4193
+
4194
+ "
2062
4195
  `);
2063
4196
  });
2064
4197
 
@@ -2150,10 +4283,48 @@ export default{
2150
4283
  );
2151
4284
  // and the warnings because fallthrough was not explicitly set
2152
4285
  expect(std.warn).toMatchInlineSnapshot(`
2153
- "The module rule at position 1 ({\\"type\\":\\"Text\\",\\"globs\\":[\\"**/*.other\\"]}) has the same type as a previous rule (at position 0, {\\"type\\":\\"Text\\",\\"globs\\":[\\"**/*.file\\"]}). This rule will be ignored. To the previous rule, add \`fallthrough = true\` to allow this one to also be used, or \`fallthrough = false\` to silence this warning.
2154
- The default module rule {\\"type\\":\\"Text\\",\\"globs\\":[\\"**/*.txt\\",\\"**/*.html\\"]} has the same type as a previous rule (at position 0, {\\"type\\":\\"Text\\",\\"globs\\":[\\"**/*.file\\"]}). This rule will be ignored. To the previous rule, add \`fallthrough = true\` to allow the default one to also be used, or \`fallthrough = false\` to silence this warning."
4286
+ "▲ [WARNING] The module rule at position 1 ({\\"type\\":\\"Text\\",\\"globs\\":[\\"**/*.other\\"]}) has the same type as a previous rule (at position 0, {\\"type\\":\\"Text\\",\\"globs\\":[\\"**/*.file\\"]}). This rule will be ignored. To the previous rule, add \`fallthrough = true\` to allow this one to also be used, or \`fallthrough = false\` to silence this warning.
4287
+
4288
+
4289
+ ▲ [WARNING] The default module rule {\\"type\\":\\"Text\\",\\"globs\\":[\\"**/*.txt\\",\\"**/*.html\\"]} has the same type as a previous rule (at position 0, {\\"type\\":\\"Text\\",\\"globs\\":[\\"**/*.file\\"]}). This rule will be ignored. To the previous rule, add \`fallthrough = true\` to allow the default one to also be used, or \`fallthrough = false\` to silence this warning.
4290
+
4291
+ "
2155
4292
  `);
2156
4293
  });
4294
+
4295
+ describe("inject process.env.NODE_ENV", () => {
4296
+ let actualProcessEnvNodeEnv: string | undefined;
4297
+ beforeEach(() => {
4298
+ actualProcessEnvNodeEnv = process.env.NODE_ENV;
4299
+ process.env.NODE_ENV = "some-node-env";
4300
+ });
4301
+ afterEach(() => {
4302
+ process.env.NODE_ENV = actualProcessEnvNodeEnv;
4303
+ });
4304
+ it("should replace `process.env.NODE_ENV` in scripts", async () => {
4305
+ writeWranglerToml();
4306
+ fs.writeFileSync(
4307
+ "./index.js",
4308
+ `export default {
4309
+ fetch(){
4310
+ return new Response(process.env.NODE_ENV);
4311
+ }
4312
+ }`
4313
+ );
4314
+ mockSubDomainRequest();
4315
+ mockUploadWorkerRequest({
4316
+ expectedEntry: `return new Response("some-node-env");`,
4317
+ });
4318
+ await runWrangler("publish index.js");
4319
+ expect(std.out).toMatchInlineSnapshot(`
4320
+ "Uploaded test-name (TIMINGS)
4321
+ Published test-name (TIMINGS)
4322
+ test-name.test-sub-domain.workers.dev"
4323
+ `);
4324
+ expect(std.err).toMatchInlineSnapshot(`""`);
4325
+ expect(std.warn).toMatchInlineSnapshot(`""`);
4326
+ });
4327
+ });
2157
4328
  });
2158
4329
 
2159
4330
  describe("legacy module specifiers", () => {
@@ -2180,68 +4351,214 @@ export default{
2180
4351
  test-name.test-sub-domain.workers.dev"
2181
4352
  `);
2182
4353
  expect(std.err).toMatchInlineSnapshot(`""`);
2183
- expect(std.warn).toMatchInlineSnapshot(
2184
- `"Deprecation warning: detected a legacy module import in \\"./index.js\\". This will stop working in the future. Replace references to \\"text.file\\" with \\"./text.file\\";"`
4354
+ expect(std.warn).toMatchInlineSnapshot(`
4355
+ "▲ [WARNING] Deprecation: detected a legacy module import in \\"./index.js\\". This will stop working in the future. Replace references to \\"text.file\\" with \\"./text.file\\";
4356
+
4357
+ "
4358
+ `);
4359
+ });
4360
+
4361
+ it("should work with legacy module specifiers, with a deprecation warning (2)", async () => {
4362
+ writeWranglerToml();
4363
+ fs.writeFileSync(
4364
+ "./index.js",
4365
+ `import WASM from 'index.wasm'; export default {};`
4366
+ );
4367
+ fs.writeFileSync("./index.wasm", "SOME WASM CONTENT");
4368
+ mockSubDomainRequest();
4369
+ mockUploadWorkerRequest({
4370
+ expectedModules: {
4371
+ "./94b240d0d692281e6467aa42043986e5c7eea034-index.wasm":
4372
+ "SOME WASM CONTENT",
4373
+ },
4374
+ });
4375
+ await runWrangler("publish index.js");
4376
+ expect(std.out).toMatchInlineSnapshot(`
4377
+ "Uploaded test-name (TIMINGS)
4378
+ Published test-name (TIMINGS)
4379
+ test-name.test-sub-domain.workers.dev"
4380
+ `);
4381
+ expect(std.err).toMatchInlineSnapshot(`""`);
4382
+ expect(std.warn).toMatchInlineSnapshot(`
4383
+ "▲ [WARNING] Deprecation: detected a legacy module import in \\"./index.js\\". This will stop working in the future. Replace references to \\"index.wasm\\" with \\"./index.wasm\\";
4384
+
4385
+ "
4386
+ `);
4387
+ });
4388
+
4389
+ it("should not match regular module specifiers when there aren't any possible legacy module matches", async () => {
4390
+ // see https://github.com/cloudflare/wrangler2/issues/655 for bug details
4391
+
4392
+ fs.writeFileSync(
4393
+ "./index.js",
4394
+ `import inner from './inner/index.js'; export default {};`
4395
+ );
4396
+ fs.mkdirSync("./inner", { recursive: true });
4397
+ fs.writeFileSync("./inner/index.js", `export default 123`);
4398
+ mockSubDomainRequest();
4399
+ mockUploadWorkerRequest();
4400
+
4401
+ await runWrangler(
4402
+ "publish index.js --compatibility-date 2022-03-17 --name test-name"
4403
+ );
4404
+ expect(std.out).toMatchInlineSnapshot(`
4405
+ "Uploaded test-name (TIMINGS)
4406
+ Published test-name (TIMINGS)
4407
+ test-name.test-sub-domain.workers.dev"
4408
+ `);
4409
+ expect(std.err).toMatchInlineSnapshot(`""`);
4410
+ expect(std.warn).toMatchInlineSnapshot(`""`);
4411
+ });
4412
+ });
4413
+
4414
+ describe("tsconfig", () => {
4415
+ it("should use compilerOptions.paths to resolve modules", async () => {
4416
+ writeWranglerToml({
4417
+ main: "index.ts",
4418
+ });
4419
+ fs.writeFileSync(
4420
+ "index.ts",
4421
+ `import { foo } from '~lib/foo'; export default { fetch() { return new Response(foo)} }`
4422
+ );
4423
+ fs.mkdirSync("lib", { recursive: true });
4424
+ fs.writeFileSync("lib/foo.ts", `export const foo = 123;`);
4425
+ fs.writeFileSync(
4426
+ "tsconfig.json",
4427
+ JSON.stringify({
4428
+ compilerOptions: {
4429
+ baseUrl: ".",
4430
+ paths: {
4431
+ "~lib/*": ["lib/*"],
4432
+ },
4433
+ },
4434
+ })
4435
+ );
4436
+ mockSubDomainRequest();
4437
+ mockUploadWorkerRequest({
4438
+ expectedEntry: "var foo = 123;", // make sure it imported the module correctly
4439
+ });
4440
+ await runWrangler("publish index.ts");
4441
+ expect(std).toMatchInlineSnapshot(`
4442
+ Object {
4443
+ "debug": "",
4444
+ "err": "",
4445
+ "out": "Uploaded test-name (TIMINGS)
4446
+ Published test-name (TIMINGS)
4447
+ test-name.test-sub-domain.workers.dev",
4448
+ "warn": "",
4449
+ }
4450
+ `);
4451
+ });
4452
+
4453
+ it("should output to target es2020 even if tsconfig says otherwise", async () => {
4454
+ writeWranglerToml();
4455
+ writeWorkerSource();
4456
+ fs.writeFileSync(
4457
+ "tsconfig.json",
4458
+ JSON.stringify({
4459
+ compilerOptions: {
4460
+ target: "es5",
4461
+ module: "commonjs",
4462
+ },
4463
+ })
2185
4464
  );
4465
+ mockSubDomainRequest();
4466
+ mockUploadWorkerRequest({
4467
+ expectedEntry: "export {", // just check that the export is preserved
4468
+ });
4469
+ await runWrangler("publish index.js"); // this would throw if we tried to compile with es5
4470
+ expect(std).toMatchInlineSnapshot(`
4471
+ Object {
4472
+ "debug": "",
4473
+ "err": "",
4474
+ "out": "Uploaded test-name (TIMINGS)
4475
+ Published test-name (TIMINGS)
4476
+ test-name.test-sub-domain.workers.dev",
4477
+ "warn": "",
4478
+ }
4479
+ `);
2186
4480
  });
2187
4481
  });
2188
4482
 
2189
- it("should work with legacy module specifiers, with a deprecation warning (2)", async () => {
2190
- writeWranglerToml();
2191
- fs.writeFileSync(
2192
- "./index.js",
2193
- `import WASM from 'index.wasm'; export default {};`
2194
- );
2195
- fs.writeFileSync("./index.wasm", "SOME WASM CONTENT");
2196
- mockSubDomainRequest();
2197
- mockUploadWorkerRequest({
2198
- expectedModules: {
2199
- "./94b240d0d692281e6467aa42043986e5c7eea034-index.wasm":
2200
- "SOME WASM CONTENT",
2201
- },
4483
+ describe("--outdir", () => {
4484
+ it("should generate built assets at --outdir if specified", async () => {
4485
+ writeWranglerToml();
4486
+ writeWorkerSource();
4487
+ mockSubDomainRequest();
4488
+ mockUploadWorkerRequest();
4489
+ await runWrangler("publish index.js --outdir some-dir");
4490
+ expect(fs.existsSync("some-dir/index.js")).toBe(true);
4491
+ expect(fs.existsSync("some-dir/index.js.map")).toBe(true);
4492
+ expect(std).toMatchInlineSnapshot(`
4493
+ Object {
4494
+ "debug": "",
4495
+ "err": "",
4496
+ "out": "Uploaded test-name (TIMINGS)
4497
+ Published test-name (TIMINGS)
4498
+ test-name.test-sub-domain.workers.dev",
4499
+ "warn": "",
4500
+ }
4501
+ `);
2202
4502
  });
2203
- await runWrangler("publish index.js");
2204
- expect(std.out).toMatchInlineSnapshot(`
2205
- "Uploaded test-name (TIMINGS)
2206
- Published test-name (TIMINGS)
2207
- test-name.test-sub-domain.workers.dev"
2208
- `);
2209
- expect(std.err).toMatchInlineSnapshot(`""`);
2210
- expect(std.warn).toMatchInlineSnapshot(
2211
- `"Deprecation warning: detected a legacy module import in \\"./index.js\\". This will stop working in the future. Replace references to \\"index.wasm\\" with \\"./index.wasm\\";"`
2212
- );
2213
4503
  });
2214
- });
2215
4504
 
2216
- /** Write a mock Worker script to disk. */
2217
- function writeWorkerSource({
2218
- basePath = ".",
2219
- format = "js",
2220
- type = "esm",
2221
- }: {
2222
- basePath?: string;
2223
- format?: "js" | "ts" | "jsx" | "tsx" | "mjs";
2224
- type?: "esm" | "sw";
2225
- } = {}) {
2226
- if (basePath !== ".") {
2227
- fs.mkdirSync(basePath, { recursive: true });
2228
- }
2229
- fs.writeFileSync(
2230
- `${basePath}/index.${format}`,
2231
- type === "esm"
2232
- ? `import { foo } from "./another";
2233
- export default {
2234
- async fetch(request) {
2235
- return new Response('Hello' + foo);
2236
- },
2237
- };`
2238
- : `import { foo } from "./another";
2239
- addEventListener('fetch', event => {
2240
- event.respondWith(new Response('Hello' + foo));
2241
- })`
2242
- );
2243
- fs.writeFileSync(`${basePath}/another.${format}`, `export const foo = 100;`);
2244
- }
4505
+ describe("--dry-run", () => {
4506
+ it("should not publish the worker if --dry-run is specified", async () => {
4507
+ writeWranglerToml();
4508
+ writeWorkerSource();
4509
+ await runWrangler("publish index.js --dry-run");
4510
+ expect(std).toMatchInlineSnapshot(`
4511
+ Object {
4512
+ "debug": "",
4513
+ "err": "",
4514
+ "out": "--dry-run: exiting now.",
4515
+ "warn": "",
4516
+ }
4517
+ `);
4518
+ });
4519
+ });
4520
+
4521
+ describe("--node-compat", () => {
4522
+ it("should warn when using node compatibility mode", async () => {
4523
+ writeWranglerToml();
4524
+ writeWorkerSource();
4525
+ await runWrangler("publish index.js --node-compat --dry-run");
4526
+ expect(std).toMatchInlineSnapshot(`
4527
+ Object {
4528
+ "debug": "",
4529
+ "err": "",
4530
+ "out": "--dry-run: exiting now.",
4531
+ "warn": "▲ [WARNING] 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.
4532
+
4533
+ ",
4534
+ }
4535
+ `);
4536
+ });
4537
+
4538
+ it("should polyfill node builtins when enabled", async () => {
4539
+ writeWranglerToml();
4540
+ fs.writeFileSync(
4541
+ "index.js",
4542
+ `
4543
+ import path from 'path';
4544
+ console.log(path.join("some/path/to", "a/file.txt"));
4545
+ export default {}
4546
+ `
4547
+ );
4548
+ await runWrangler("publish index.js --node-compat --dry-run"); // this would throw if node compatibility didn't exist
4549
+ expect(std).toMatchInlineSnapshot(`
4550
+ Object {
4551
+ "debug": "",
4552
+ "err": "",
4553
+ "out": "--dry-run: exiting now.",
4554
+ "warn": "▲ [WARNING] 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.
4555
+
4556
+ ",
4557
+ }
4558
+ `);
4559
+ });
4560
+ });
4561
+ });
2245
4562
 
2246
4563
  /** Write mock assets to the file system so they can be uploaded. */
2247
4564
  function writeAssets(assets: { filePath: string; content: string }[]) {
@@ -2252,27 +4569,32 @@ function writeAssets(assets: { filePath: string; content: string }[]) {
2252
4569
  }
2253
4570
 
2254
4571
  /** Create a mock handler for the request to upload a worker script. */
2255
- function mockUploadWorkerRequest({
2256
- available_on_subdomain = true,
2257
- expectedEntry,
2258
- expectedType = "esm",
2259
- expectedBindings,
2260
- expectedModules = {},
2261
- expectedCompatibilityDate,
2262
- expectedCompatibilityFlags,
2263
- env = undefined,
2264
- legacyEnv = false,
2265
- }: {
2266
- available_on_subdomain?: boolean;
2267
- expectedEntry?: string;
2268
- expectedType?: "esm" | "sw";
2269
- expectedBindings?: unknown;
2270
- expectedModules?: Record<string, string>;
2271
- expectedCompatibilityDate?: string;
2272
- expectedCompatibilityFlags?: string[];
2273
- env?: string;
2274
- legacyEnv?: boolean;
2275
- } = {}) {
4572
+ function mockUploadWorkerRequest(
4573
+ options: {
4574
+ available_on_subdomain?: boolean;
4575
+ expectedEntry?: string;
4576
+ expectedType?: "esm" | "sw";
4577
+ expectedBindings?: unknown;
4578
+ expectedModules?: Record<string, string>;
4579
+ expectedCompatibilityDate?: string;
4580
+ expectedCompatibilityFlags?: string[];
4581
+ expectedMigrations?: CfWorkerInit["migrations"];
4582
+ env?: string;
4583
+ legacyEnv?: boolean;
4584
+ } = {}
4585
+ ) {
4586
+ const {
4587
+ available_on_subdomain = true,
4588
+ expectedEntry,
4589
+ expectedType = "esm",
4590
+ expectedBindings,
4591
+ expectedModules = {},
4592
+ expectedCompatibilityDate,
4593
+ expectedCompatibilityFlags,
4594
+ env = undefined,
4595
+ legacyEnv = false,
4596
+ expectedMigrations,
4597
+ } = options;
2276
4598
  setMockResponse(
2277
4599
  env && !legacyEnv
2278
4600
  ? "/accounts/:accountId/workers/services/:scriptName/environments/:envName"
@@ -2286,7 +4608,7 @@ function mockUploadWorkerRequest({
2286
4608
  if (!legacyEnv) {
2287
4609
  expect(envName).toEqual(env);
2288
4610
  }
2289
- expect(queryParams.get("available_on_subdomain")).toEqual("true");
4611
+ expect(queryParams.get("include_subdomain_availability")).toEqual("true");
2290
4612
  const formBody = body as FormData;
2291
4613
  if (expectedEntry !== undefined) {
2292
4614
  expect(await (formBody.get("index.js") as File).text()).toMatch(
@@ -2301,17 +4623,20 @@ function mockUploadWorkerRequest({
2301
4623
  } else {
2302
4624
  expect(metadata.body_part).toEqual("index.js");
2303
4625
  }
2304
- if (expectedBindings !== undefined) {
4626
+ if ("expectedBindings" in options) {
2305
4627
  expect(metadata.bindings).toEqual(expectedBindings);
2306
4628
  }
2307
- if (expectedCompatibilityDate !== undefined) {
4629
+ if ("expectedCompatibilityDate" in options) {
2308
4630
  expect(metadata.compatibility_date).toEqual(expectedCompatibilityDate);
2309
4631
  }
2310
- if (expectedCompatibilityFlags !== undefined) {
4632
+ if ("expectedCompatibilityFlags" in options) {
2311
4633
  expect(metadata.compatibility_flags).toEqual(
2312
4634
  expectedCompatibilityFlags
2313
4635
  );
2314
4636
  }
4637
+ if ("expectedMigrations" in options) {
4638
+ expect(metadata.migrations).toEqual(expectedMigrations);
4639
+ }
2315
4640
  for (const [name, content] of Object.entries(expectedModules)) {
2316
4641
  expect(await (formBody.get(name) as File).text()).toEqual(content);
2317
4642
  }
@@ -2371,11 +4696,11 @@ function mockUpdateWorkerRequest({
2371
4696
  }
2372
4697
 
2373
4698
  function mockPublishRoutesRequest({
2374
- routes,
4699
+ routes = [],
2375
4700
  env = undefined,
2376
4701
  legacyEnv = false,
2377
4702
  }: {
2378
- routes: string[];
4703
+ routes: Config["routes"];
2379
4704
  env?: string | undefined;
2380
4705
  legacyEnv?: boolean | undefined;
2381
4706
  }) {
@@ -2395,7 +4720,9 @@ function mockPublishRoutesRequest({
2395
4720
  }
2396
4721
 
2397
4722
  expect(JSON.parse(body as string)).toEqual(
2398
- routes.map((pattern) => ({ pattern }))
4723
+ routes.map((route) =>
4724
+ typeof route !== "object" ? { pattern: route } : route
4725
+ )
2399
4726
  );
2400
4727
  return null;
2401
4728
  }
@@ -2417,7 +4744,12 @@ function mockListKVNamespacesRequest(...namespaces: KVNamespaceInfo[]) {
2417
4744
  /** Create a mock handler for the request that tries to do a bulk upload of assets to a KV namespace. */
2418
4745
  function mockUploadAssetsToKVRequest(
2419
4746
  expectedNamespaceId: string,
2420
- assets: { filePath: string; content: string }[]
4747
+ assets: {
4748
+ filePath: string;
4749
+ content: string;
4750
+ expiration?: number;
4751
+ expiration_ttl?: number;
4752
+ }[]
2421
4753
  ) {
2422
4754
  setMockResponse(
2423
4755
  "/accounts/:accountId/storage/kv/namespaces/:namespaceId/bulk",
@@ -2446,8 +4778,98 @@ function mockUploadAssetsToKVRequest(
2446
4778
  expect(Buffer.from(upload.value, "base64").toString()).toEqual(
2447
4779
  asset.content
2448
4780
  );
4781
+ expect(upload.expiration).toEqual(asset.expiration);
4782
+ expect(upload.expiration_ttl).toEqual(asset.expiration_ttl);
2449
4783
  }
2450
4784
  return null;
2451
4785
  }
2452
4786
  );
2453
4787
  }
4788
+
4789
+ /** Create a mock handler for thr request that does a bulk delete of unused assets */
4790
+ function mockDeleteUnusedAssetsRequest(
4791
+ expectedNamespaceId: string,
4792
+ assets: string[]
4793
+ ) {
4794
+ setMockResponse(
4795
+ "/accounts/:accountId/storage/kv/namespaces/:namespaceId/bulk",
4796
+ "DELETE",
4797
+ ([_url, accountId, namespaceId], { body }) => {
4798
+ expect(accountId).toEqual("some-account-id");
4799
+ expect(namespaceId).toEqual(expectedNamespaceId);
4800
+ const deletes = JSON.parse(body as string);
4801
+ expect(assets).toEqual(deletes);
4802
+ return null;
4803
+ }
4804
+ );
4805
+ }
4806
+
4807
+ type LegacyScriptInfo = { id: string; migration_tag?: string };
4808
+
4809
+ function mockLegacyScriptData(options: { scripts: LegacyScriptInfo[] }) {
4810
+ const { scripts } = options;
4811
+ setMockResponse(
4812
+ "/accounts/:accountId/workers/scripts",
4813
+ "GET",
4814
+ ([_url, accountId]) => {
4815
+ expect(accountId).toEqual("some-account-id");
4816
+ return scripts;
4817
+ }
4818
+ );
4819
+ }
4820
+
4821
+ type DurableScriptInfo = { id: string; migration_tag?: string };
4822
+
4823
+ function mockServiceScriptData(options: {
4824
+ script?: DurableScriptInfo;
4825
+ scriptName?: string;
4826
+ env?: string;
4827
+ }) {
4828
+ const { script } = options;
4829
+ if (options.env) {
4830
+ if (!script) {
4831
+ setMockRawResponse(
4832
+ "/accounts/:accountId/workers/services/:scriptName/environments/:envName",
4833
+ "GET",
4834
+ () => {
4835
+ return createFetchResult(null, false, [
4836
+ { code: 10092, message: "workers.api.error.environment_not_found" },
4837
+ ]);
4838
+ }
4839
+ );
4840
+ return;
4841
+ }
4842
+ setMockResponse(
4843
+ "/accounts/:accountId/workers/services/:scriptName/environments/:envName",
4844
+ "GET",
4845
+ ([_url, accountId, scriptName, envName]) => {
4846
+ expect(accountId).toEqual("some-account-id");
4847
+ expect(scriptName).toEqual(options.scriptName || "test-name");
4848
+ expect(envName).toEqual(options.env);
4849
+ return { script };
4850
+ }
4851
+ );
4852
+ } else {
4853
+ if (!script) {
4854
+ setMockRawResponse(
4855
+ "/accounts/:accountId/workers/services/:scriptName",
4856
+ "GET",
4857
+ () => {
4858
+ return createFetchResult(null, false, [
4859
+ { code: 10090, message: "workers.api.error.service_not_found" },
4860
+ ]);
4861
+ }
4862
+ );
4863
+ return;
4864
+ }
4865
+ setMockResponse(
4866
+ "/accounts/:accountId/workers/services/:scriptName",
4867
+ "GET",
4868
+ ([_url, accountId, scriptName]) => {
4869
+ expect(accountId).toEqual("some-account-id");
4870
+ expect(scriptName).toEqual(options.scriptName || "test-name");
4871
+ return { default_environment: { script } };
4872
+ }
4873
+ );
4874
+ }
4875
+ }