wrangler 0.0.0-e33294b0 → 0.0.0-e351afcf

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 (127) hide show
  1. package/bin/wrangler.js +1 -1
  2. package/miniflare-dist/index.mjs +183 -92
  3. package/package.json +15 -10
  4. package/src/__tests__/api-devregistry.test.ts +121 -0
  5. package/src/__tests__/api.test.ts +27 -5
  6. package/src/__tests__/configuration.test.ts +294 -18
  7. package/src/__tests__/constellation.test.ts +371 -0
  8. package/src/__tests__/d1/execute.test.ts +3 -1
  9. package/src/__tests__/d1/migrate.test.ts +102 -0
  10. package/src/__tests__/delete.test.ts +6 -0
  11. package/src/__tests__/deployments.test.ts +335 -95
  12. package/src/__tests__/dev.test.tsx +87 -57
  13. package/src/__tests__/generate.test.ts +3 -4
  14. package/src/__tests__/guess-worker-format.test.ts +35 -0
  15. package/src/__tests__/helpers/mock-console.ts +6 -0
  16. package/src/__tests__/helpers/msw/handlers/deployments.ts +70 -3
  17. package/src/__tests__/helpers/msw/index.ts +4 -2
  18. package/src/__tests__/helpers/worker-scripts/parent-worker.js +4 -1
  19. package/src/__tests__/index.test.ts +14 -6
  20. package/src/__tests__/init.test.ts +136 -104
  21. package/src/__tests__/jest.setup.ts +4 -0
  22. package/src/__tests__/kv.test.ts +9 -9
  23. package/src/__tests__/logger.test.ts +68 -2
  24. package/src/__tests__/middleware.scheduled.test.ts +2 -2
  25. package/src/__tests__/middleware.test.ts +4 -3
  26. package/src/__tests__/mtls-certificates.test.ts +7 -3
  27. package/src/__tests__/pages/deployment-list.test.ts +2 -0
  28. package/src/__tests__/pages/functions-build.test.ts +159 -39
  29. package/src/__tests__/pages/project-list.test.ts +2 -0
  30. package/src/__tests__/pages/project-upload.test.ts +43 -24
  31. package/src/__tests__/pages/publish.test.ts +267 -148
  32. package/src/__tests__/publish.test.ts +1040 -339
  33. package/src/__tests__/pubsub.test.ts +3 -0
  34. package/src/__tests__/queues.test.ts +5 -2
  35. package/src/__tests__/test-old-node-version.js +3 -3
  36. package/src/__tests__/traverse-module-graph.test.ts +220 -0
  37. package/src/__tests__/tsconfig.tsbuildinfo +1 -1
  38. package/src/__tests__/type-generation.test.ts +1 -0
  39. package/src/__tests__/user.test.ts +2 -2
  40. package/src/__tests__/worker-namespace.test.ts +1 -0
  41. package/src/api/dev.ts +15 -22
  42. package/src/api/pages/create-worker-bundle-contents.ts +1 -0
  43. package/src/api/pages/publish.tsx +64 -61
  44. package/src/bundle.ts +51 -16
  45. package/src/config/environment.ts +36 -4
  46. package/src/config/index.ts +30 -2
  47. package/src/config/validation-helpers.ts +1 -1
  48. package/src/config/validation.ts +149 -5
  49. package/src/constellation/createProject.tsx +51 -0
  50. package/src/constellation/deleteProject.ts +51 -0
  51. package/src/constellation/deleteProjectModel.ts +68 -0
  52. package/src/constellation/index.ts +75 -0
  53. package/src/constellation/listCatalog.tsx +35 -0
  54. package/src/constellation/listModel.tsx +41 -0
  55. package/src/constellation/listProject.tsx +28 -0
  56. package/src/constellation/listRuntime.tsx +28 -0
  57. package/src/constellation/options.ts +17 -0
  58. package/src/constellation/types.ts +17 -0
  59. package/src/constellation/uploadModel.tsx +64 -0
  60. package/src/constellation/utils.ts +90 -0
  61. package/src/create-worker-preview.ts +19 -8
  62. package/src/create-worker-upload-form.ts +28 -3
  63. package/src/d1/backups.tsx +19 -15
  64. package/src/d1/create.tsx +45 -39
  65. package/src/d1/delete.ts +1 -1
  66. package/src/d1/execute.tsx +23 -20
  67. package/src/d1/list.tsx +14 -12
  68. package/src/d1/migrations/apply.tsx +36 -30
  69. package/src/d1/migrations/create.tsx +15 -12
  70. package/src/d1/migrations/helpers.ts +5 -8
  71. package/src/d1/migrations/list.tsx +10 -7
  72. package/src/deployments.ts +260 -8
  73. package/src/dev/dev.tsx +6 -4
  74. package/src/dev/local.tsx +65 -40
  75. package/src/dev/start-server.ts +57 -52
  76. package/src/dev/use-esbuild.ts +59 -52
  77. package/src/dev/validate-dev-props.ts +9 -0
  78. package/src/dev-registry.ts +2 -1
  79. package/src/dev.tsx +38 -18
  80. package/src/docs/helpers.ts +50 -0
  81. package/src/docs/index.ts +25 -68
  82. package/src/entry.ts +19 -9
  83. package/src/environment-variables/factory.ts +2 -1
  84. package/src/generate/index.ts +2 -2
  85. package/src/index.ts +87 -24
  86. package/src/init.ts +144 -134
  87. package/src/inspect.ts +16 -1
  88. package/src/kv/helpers.ts +7 -4
  89. package/src/logger.ts +23 -4
  90. package/src/metrics/send-event.ts +2 -1
  91. package/src/miniflare-cli/assets.ts +8 -7
  92. package/src/miniflare-cli/index.ts +3 -3
  93. package/src/miniflare-cli/tsconfig.tsbuildinfo +1 -1
  94. package/src/module-collection.ts +92 -26
  95. package/src/pages/build.ts +290 -100
  96. package/src/pages/buildFunctions.ts +20 -10
  97. package/src/pages/deployments.tsx +3 -5
  98. package/src/pages/dev.ts +44 -26
  99. package/src/pages/errors.ts +1 -0
  100. package/src/pages/functions/buildPlugin.ts +57 -34
  101. package/src/pages/functions/buildWorker.ts +91 -31
  102. package/src/pages/functions/tsconfig.tsbuildinfo +1 -1
  103. package/src/pages/projects.tsx +2 -5
  104. package/src/pages/publish.tsx +1 -10
  105. package/src/pages/upload.tsx +29 -9
  106. package/src/paths.ts +1 -1
  107. package/src/publish/index.ts +1 -1
  108. package/src/publish/publish.ts +29 -34
  109. package/src/queues/cli/commands/consumer/add.ts +6 -0
  110. package/src/queues/client.ts +1 -0
  111. package/src/r2/helpers.ts +1 -0
  112. package/src/secret/index.ts +2 -1
  113. package/src/sites.ts +164 -52
  114. package/src/traverse-module-graph.ts +53 -0
  115. package/src/type-generation.ts +1 -1
  116. package/src/user/user.ts +2 -1
  117. package/src/utils/render.ts +93 -0
  118. package/src/worker.ts +18 -1
  119. package/templates/d1-beta-facade.js +5 -2
  120. package/templates/gitignore +1 -0
  121. package/templates/middleware/middleware-miniflare3-json-error.ts +17 -5
  122. package/templates/pages-template-plugin.ts +7 -3
  123. package/templates/tsconfig.init.json +1 -2
  124. package/templates/tsconfig.tsbuildinfo +1 -1
  125. package/wrangler-dist/cli.d.ts +552 -16
  126. package/wrangler-dist/cli.js +32141 -24052
  127. package/src/__tests__/api-devregistry.test.js +0 -64
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wrangler",
3
- "version": "0.0.0-e33294b0",
3
+ "version": "0.0.0-e351afcf",
4
4
  "description": "Command-line interface for all things Cloudflare Workers",
5
5
  "keywords": [
6
6
  "wrangler",
@@ -63,9 +63,11 @@
63
63
  "emit-types": "tsc -p tsconfig.emit.json && node -r esbuild-register scripts/emit-types.ts",
64
64
  "prepublishOnly": "SOURCEMAPS=false npm run build",
65
65
  "start": "npm run bundle && cross-env NODE_OPTIONS=--enable-source-maps ./bin/wrangler.js",
66
- "test": "npm run assert-git-version && jest --silent=false --verbose=true",
66
+ "test": "npm run assert-git-version && jest",
67
67
  "test-watch": "npm run test -- --runInBand --testTimeout=50000 --watch",
68
- "test:ci": "npm run test -- --verbose=true --coverage"
68
+ "test:ci": "npm run test -- --coverage",
69
+ "test:debug": "npm run test -- --silent=false --verbose=true",
70
+ "test:e2e": "vitest --no-threads ./e2e"
69
71
  },
70
72
  "jest": {
71
73
  "coverageReporters": [
@@ -82,7 +84,7 @@
82
84
  "setupFilesAfterEnv": [
83
85
  "<rootDir>/src/__tests__/jest.setup.ts"
84
86
  ],
85
- "testRegex": ".*.(test|spec)\\.[jt]sx?$",
87
+ "testRegex": "src/__tests__/.*\\.(test|spec)\\.[jt]sx?$",
86
88
  "testTimeout": 30000,
87
89
  "transform": {
88
90
  "^.+\\.c?(t|j)sx?$": [
@@ -100,13 +102,13 @@
100
102
  "@cloudflare/kv-asset-handler": "^0.2.0",
101
103
  "@esbuild-plugins/node-globals-polyfill": "^0.1.1",
102
104
  "@esbuild-plugins/node-modules-polyfill": "^0.1.4",
103
- "@miniflare/core": "2.12.0",
104
- "@miniflare/d1": "2.12.0",
105
- "@miniflare/durable-objects": "2.12.0",
105
+ "@miniflare/core": "2.13.0",
106
+ "@miniflare/d1": "2.13.0",
107
+ "@miniflare/durable-objects": "2.13.0",
106
108
  "blake3-wasm": "^2.1.5",
107
109
  "chokidar": "^3.5.3",
108
110
  "esbuild": "0.16.3",
109
- "miniflare": "2.12.0",
111
+ "miniflare": "2.13.0",
110
112
  "nanoid": "^3.3.3",
111
113
  "path-to-regexp": "^6.2.0",
112
114
  "selfsigned": "^2.0.1",
@@ -118,7 +120,7 @@
118
120
  "@cloudflare/workers-types": "^4.20221111.1",
119
121
  "@iarna/toml": "^3.0.0",
120
122
  "@microsoft/api-extractor": "^7.28.3",
121
- "@miniflare/tre": "^3.0.0-next.8",
123
+ "@miniflare/tre": "3.0.0-next.13",
122
124
  "@types/better-sqlite3": "^7.6.0",
123
125
  "@types/busboy": "^1.5.0",
124
126
  "@types/command-exists": "^1.2.0",
@@ -174,13 +176,16 @@
174
176
  "remove-accents-esm": "^0.0.1",
175
177
  "semiver": "^1.1.0",
176
178
  "serve-static": "^1.15.0",
179
+ "shellac": "^0.7.3",
177
180
  "signal-exit": "^3.0.7",
181
+ "strip-ansi": "^7.0.1",
178
182
  "supports-color": "^9.2.2",
179
183
  "timeago.js": "^4.0.2",
180
184
  "tmp-promise": "^3.0.3",
181
185
  "ts-dedent": "^2.2.0",
182
- "undici": "5.11.0",
186
+ "undici": "5.20.0",
183
187
  "update-check": "^1.5.4",
188
+ "vitest": "^0.29.2",
184
189
  "ws": "^8.5.0",
185
190
  "xdg-app-paths": "^7.3.0",
186
191
  "yargs": "^17.4.1",
@@ -0,0 +1,121 @@
1
+ import { fetch } from "undici";
2
+ import { unstable_dev } from "../api";
3
+
4
+ jest.unmock("undici");
5
+
6
+ /**
7
+ * a huge caveat to how testing multi-worker scripts works:
8
+ * you can't shutdown the first worker you spun up, or it'll kill the devRegistry
9
+ */
10
+ describe("multi-worker testing", () => {
11
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
12
+ let childWorker: any;
13
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
+ let parentWorker: any;
15
+
16
+ beforeAll(async () => {
17
+ childWorker = await unstable_dev(
18
+ "src/__tests__/helpers/worker-scripts/hello-world-worker.js",
19
+ {
20
+ config: "src/__tests__/helpers/worker-scripts/child-wrangler.toml",
21
+ experimental: {
22
+ disableExperimentalWarning: true,
23
+ },
24
+ }
25
+ );
26
+ parentWorker = await unstable_dev(
27
+ "src/__tests__/helpers/worker-scripts/parent-worker.js",
28
+ {
29
+ config: "src/__tests__/helpers/worker-scripts/parent-wrangler.toml",
30
+ experimental: {
31
+ disableExperimentalWarning: true,
32
+ },
33
+ }
34
+ );
35
+ });
36
+
37
+ afterAll(async () => {
38
+ await childWorker.stop();
39
+ await parentWorker.stop();
40
+ });
41
+
42
+ it("parentWorker and childWorker should be added devRegistry", async () => {
43
+ const resp = await fetch("http://localhost:6284/workers");
44
+ if (resp) {
45
+ const parsedResp = (await resp.json()) as {
46
+ parent: unknown;
47
+ child: unknown;
48
+ };
49
+ expect(parsedResp.parent).toBeTruthy();
50
+ expect(parsedResp.child).toBeTruthy();
51
+ }
52
+ });
53
+
54
+ it("childWorker should return Hello World itself", async () => {
55
+ const resp = await childWorker.fetch();
56
+ if (resp) {
57
+ const text = await resp.text();
58
+ expect(text).toMatchInlineSnapshot(`"Hello World!"`);
59
+ }
60
+ });
61
+
62
+ it("parentWorker should return Hello World by invoking the child worker", async () => {
63
+ const resp = await parentWorker.fetch();
64
+ if (resp) {
65
+ const parsedResp = await resp.text();
66
+ expect(parsedResp).toEqual("Parent worker sees: Hello World!");
67
+ }
68
+ });
69
+
70
+ it("should be able to stop and start the server with no warning logs", async () => {
71
+ // Spy on all the console methods
72
+ let logs = "";
73
+ (["debug", "info", "log", "warn", "error"] as const).forEach((method) =>
74
+ jest
75
+ .spyOn(console, method)
76
+ .mockImplementation((...args: unknown[]) => (logs += `\n${args}`))
77
+ );
78
+
79
+ // Spy on the std out that is written to by Miniflare 2
80
+ jest
81
+ .spyOn(process.stdout, "write")
82
+ .mockImplementation((chunk: unknown) => ((logs += `\n${chunk}`), true));
83
+
84
+ async function startWorker() {
85
+ return await unstable_dev(
86
+ "src/__tests__/helpers/worker-scripts/hello-world-worker.js",
87
+ {
88
+ // We need the wrangler.toml config to specify a Worker name
89
+ // otherwise unstable_dev will not register the worker with the DevRegistry
90
+ config: "src/__tests__/helpers/worker-scripts/child-wrangler.toml",
91
+ // We need debug logs because this is where the message is written if registering the worker fails.
92
+ logLevel: "debug",
93
+ experimental: {
94
+ disableExperimentalWarning: true,
95
+ },
96
+ }
97
+ );
98
+ }
99
+
100
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
101
+ let worker: any;
102
+ try {
103
+ worker = await startWorker();
104
+
105
+ // Stop the worker and start it again
106
+ await worker.stop();
107
+ await new Promise((r) => setTimeout(r, 2000));
108
+
109
+ worker = await startWorker();
110
+
111
+ const resp = await worker.fetch();
112
+ expect(resp).not.toBe(undefined);
113
+
114
+ expect(logs).not.toMatch(
115
+ /Failed to register worker in local service registry/
116
+ );
117
+ } finally {
118
+ await worker?.stop();
119
+ }
120
+ }, 10000);
121
+ });
@@ -5,7 +5,7 @@ describe("parseRequestInput for fetch on unstable dev", () => {
5
5
  it("should allow no input to be passed in", () => {
6
6
  const [input, _] = parseRequestInput("0.0.0.0", 8080);
7
7
 
8
- expect(input).toMatchInlineSnapshot(`"http://0.0.0.0:8080"`);
8
+ expect(input).toMatchInlineSnapshot(`"http://0.0.0.0:8080/"`);
9
9
  });
10
10
 
11
11
  it("should allow string of pathname to be passed in", () => {
@@ -14,14 +14,24 @@ describe("parseRequestInput for fetch on unstable dev", () => {
14
14
  expect(input).toMatchInlineSnapshot(`"http://0.0.0.0:8080/test"`);
15
15
  });
16
16
 
17
+ it("should allow string of pathname and querystring to be passed in", () => {
18
+ const [input, _] = parseRequestInput("0.0.0.0", 8080, "/test?q=testparam");
19
+
20
+ expect(input).toMatchInlineSnapshot(
21
+ `"http://0.0.0.0:8080/test?q=testparam"`
22
+ );
23
+ });
24
+
17
25
  it("should allow full url to be passed in as string and stripped", () => {
18
26
  const [input, _] = parseRequestInput(
19
27
  "0.0.0.0",
20
28
  8080,
21
- "http://cloudflare.com/test"
29
+ "http://cloudflare.com/test?q=testparam"
22
30
  );
23
31
 
24
- expect(input).toMatchInlineSnapshot(`"http://0.0.0.0:8080/test"`);
32
+ expect(input).toMatchInlineSnapshot(
33
+ `"http://0.0.0.0:8080/test?q=testparam"`
34
+ );
25
35
  });
26
36
 
27
37
  it("should allow URL object without pathname to be passed in and stripped", () => {
@@ -44,18 +54,30 @@ describe("parseRequestInput for fetch on unstable dev", () => {
44
54
  expect(input).toMatchInlineSnapshot(`"http://0.0.0.0:8080/test"`);
45
55
  });
46
56
 
57
+ it("should allow URL object with pathname and querystring to be passed in and stripped", () => {
58
+ const [input, _] = parseRequestInput(
59
+ "0.0.0.0",
60
+ 8080,
61
+ new URL("http://cloudflare.com/test?q=testparam")
62
+ );
63
+
64
+ expect(input).toMatchInlineSnapshot(
65
+ `"http://0.0.0.0:8080/test?q=testparam"`
66
+ );
67
+ });
68
+
47
69
  it("should allow request object to be passed in", () => {
48
70
  const [input, init] = parseRequestInput(
49
71
  "0.0.0.0",
50
72
  8080,
51
- new Request("http://cloudflare.com/test", { method: "POST" })
73
+ new Request("http://cloudflare.com/test?q=testparam", { method: "POST" })
52
74
  );
53
75
 
54
76
  expect(init).toBeUndefined();
55
77
  expect(input).toBeInstanceOf(Request);
56
78
  // We don't expect the request to be modified
57
79
  expect((input as Request).url).toMatchInlineSnapshot(
58
- `"http://cloudflare.com/test"`
80
+ `"http://cloudflare.com/test?q=testparam"`
59
81
  );
60
82
  expect((input as Request).method).toMatchInlineSnapshot(`"POST"`);
61
83
  });
@@ -40,6 +40,7 @@ describe("normalizeAndValidateConfig()", () => {
40
40
  jsx_fragment: "React.Fragment",
41
41
  tsconfig: undefined,
42
42
  kv_namespaces: [],
43
+ send_email: [],
43
44
  legacy_env: true,
44
45
  logfwdr: {
45
46
  bindings: [],
@@ -65,7 +66,8 @@ describe("normalizeAndValidateConfig()", () => {
65
66
  crons: [],
66
67
  },
67
68
  unsafe: {
68
- bindings: [],
69
+ bindings: undefined,
70
+ metadata: undefined,
69
71
  },
70
72
  dispatch_namespaces: [],
71
73
  mtls_certificates: [],
@@ -902,6 +904,17 @@ describe("normalizeAndValidateConfig()", () => {
902
904
  preview_id: "KV_PREVIEW_1",
903
905
  },
904
906
  ],
907
+ send_email: [
908
+ { name: "SEB_TARGET", destination_address: "teste@example.com" },
909
+ { name: "SEB_UNRESTRICTED" },
910
+ {
911
+ name: "SEB_ALLOWLIST",
912
+ allowed_destination_addresses: [
913
+ "email1@example.com",
914
+ "email2@example.com",
915
+ ],
916
+ },
917
+ ],
905
918
  r2_buckets: [
906
919
  { binding: "R2_BINDING_1", bucket_name: "R2_BUCKET_1" },
907
920
  {
@@ -935,6 +948,7 @@ describe("normalizeAndValidateConfig()", () => {
935
948
  extra: "UNSAFE_EXTRA_1",
936
949
  },
937
950
  ],
951
+ metadata: undefined,
938
952
  },
939
953
  no_bundle: true,
940
954
  minify: true,
@@ -1627,6 +1641,39 @@ describe("normalizeAndValidateConfig()", () => {
1627
1641
  });
1628
1642
  });
1629
1643
 
1644
+ it("should error if send_email.bindings are not valid", () => {
1645
+ const { diagnostics } = normalizeAndValidateConfig(
1646
+ {
1647
+ send_email: [
1648
+ {},
1649
+ { binding: "VALID" },
1650
+ { name: "SEB", destination_address: 123 },
1651
+ {
1652
+ name: "SEB2",
1653
+ allowed_destination_addresses: 123,
1654
+ },
1655
+ {
1656
+ name: "SEB3",
1657
+ destination_address: "email@example.com",
1658
+ allowed_destination_addresses: ["email@example.com"],
1659
+ },
1660
+ ],
1661
+ } as unknown as RawConfig,
1662
+ undefined,
1663
+ { env: undefined }
1664
+ );
1665
+
1666
+ expect(diagnostics.hasWarnings()).toBe(false);
1667
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
1668
+ "Processing wrangler configuration:
1669
+ - \\"send_email[0]\\" bindings should have a string \\"name\\" field but got {}.
1670
+ - \\"send_email[1]\\" bindings should have a string \\"name\\" field but got {\\"binding\\":\\"VALID\\"}.
1671
+ - \\"send_email[2]\\" bindings should, optionally, have a string \\"destination_address\\" field but got {\\"name\\":\\"SEB\\",\\"destination_address\\":123}.
1672
+ - \\"send_email[3]\\" bindings should, optionally, have a []string \\"allowed_destination_addresses\\" field but got {\\"name\\":\\"SEB2\\",\\"allowed_destination_addresses\\":123}.
1673
+ - \\"send_email[4]\\" bindings should have either a \\"destination_address\\" or \\"allowed_destination_addresses\\" field, but not both."
1674
+ `);
1675
+ });
1676
+
1630
1677
  describe("[d1_databases]", () => {
1631
1678
  it("should error if d1_databases is an object", () => {
1632
1679
  const { diagnostics } = normalizeAndValidateConfig(
@@ -1789,6 +1836,7 @@ describe("normalizeAndValidateConfig()", () => {
1789
1836
  max_batch_timeout: null,
1790
1837
  max_retries: "hello",
1791
1838
  dead_letter_queue: 5,
1839
+ max_concurrency: "hello",
1792
1840
  },
1793
1841
  ],
1794
1842
  },
@@ -1804,20 +1852,21 @@ describe("normalizeAndValidateConfig()", () => {
1804
1852
  );
1805
1853
  expect(diagnostics.hasWarnings()).toBe(true);
1806
1854
  expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
1807
- "Processing wrangler configuration:
1808
- - Unexpected fields found in queues field: \\"invalidField\\"
1809
- - Unexpected fields found in queues.consumers[2] field: \\"invalidField\\""
1810
- `);
1855
+ "Processing wrangler configuration:
1856
+ - Unexpected fields found in queues field: \\"invalidField\\"
1857
+ - Unexpected fields found in queues.consumers[2] field: \\"invalidField\\""
1858
+ `);
1811
1859
 
1812
1860
  expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
1813
- "Processing wrangler configuration:
1814
- - \\"queues.consumers[0]\\" should have a string \\"queue\\" field but got {}.
1815
- - \\"queues.consumers[1]\\" should have a string \\"queue\\" field but got {\\"queue\\":22}.
1816
- - \\"queues.consumers[3]\\" should, optionally, have a number \\"max_batch_size\\" field but got {\\"queue\\":\\"myQueue\\",\\"max_batch_size\\":\\"3\\",\\"max_batch_timeout\\":null,\\"max_retries\\":\\"hello\\",\\"dead_letter_queue\\":5}.
1817
- - \\"queues.consumers[3]\\" should, optionally, have a number \\"max_batch_timeout\\" field but got {\\"queue\\":\\"myQueue\\",\\"max_batch_size\\":\\"3\\",\\"max_batch_timeout\\":null,\\"max_retries\\":\\"hello\\",\\"dead_letter_queue\\":5}.
1818
- - \\"queues.consumers[3]\\" should, optionally, have a number \\"max_retries\\" field but got {\\"queue\\":\\"myQueue\\",\\"max_batch_size\\":\\"3\\",\\"max_batch_timeout\\":null,\\"max_retries\\":\\"hello\\",\\"dead_letter_queue\\":5}.
1819
- - \\"queues.consumers[3]\\" should, optionally, have a string \\"dead_letter_queue\\" field but got {\\"queue\\":\\"myQueue\\",\\"max_batch_size\\":\\"3\\",\\"max_batch_timeout\\":null,\\"max_retries\\":\\"hello\\",\\"dead_letter_queue\\":5}."
1820
- `);
1861
+ "Processing wrangler configuration:
1862
+ - \\"queues.consumers[0]\\" should have a string \\"queue\\" field but got {}.
1863
+ - \\"queues.consumers[1]\\" should have a string \\"queue\\" field but got {\\"queue\\":22}.
1864
+ - \\"queues.consumers[3]\\" should, optionally, have a number \\"max_batch_size\\" field but got {\\"queue\\":\\"myQueue\\",\\"max_batch_size\\":\\"3\\",\\"max_batch_timeout\\":null,\\"max_retries\\":\\"hello\\",\\"dead_letter_queue\\":5,\\"max_concurrency\\":\\"hello\\"}.
1865
+ - \\"queues.consumers[3]\\" should, optionally, have a number \\"max_batch_timeout\\" field but got {\\"queue\\":\\"myQueue\\",\\"max_batch_size\\":\\"3\\",\\"max_batch_timeout\\":null,\\"max_retries\\":\\"hello\\",\\"dead_letter_queue\\":5,\\"max_concurrency\\":\\"hello\\"}.
1866
+ - \\"queues.consumers[3]\\" should, optionally, have a number \\"max_retries\\" field but got {\\"queue\\":\\"myQueue\\",\\"max_batch_size\\":\\"3\\",\\"max_batch_timeout\\":null,\\"max_retries\\":\\"hello\\",\\"dead_letter_queue\\":5,\\"max_concurrency\\":\\"hello\\"}.
1867
+ - \\"queues.consumers[3]\\" should, optionally, have a string \\"dead_letter_queue\\" field but got {\\"queue\\":\\"myQueue\\",\\"max_batch_size\\":\\"3\\",\\"max_batch_timeout\\":null,\\"max_retries\\":\\"hello\\",\\"dead_letter_queue\\":5,\\"max_concurrency\\":\\"hello\\"}.
1868
+ - \\"queues.consumers[3]\\" should, optionally, have a number \\"max_concurrency\\" field but got {\\"queue\\":\\"myQueue\\",\\"max_batch_size\\":\\"3\\",\\"max_batch_timeout\\":null,\\"max_retries\\":\\"hello\\",\\"dead_letter_queue\\":5,\\"max_concurrency\\":\\"hello\\"}."
1869
+ `);
1821
1870
  });
1822
1871
  });
1823
1872
 
@@ -2329,7 +2378,7 @@ describe("normalizeAndValidateConfig()", () => {
2329
2378
  `);
2330
2379
  });
2331
2380
 
2332
- it("should error if unsafe.bindings is not defined", () => {
2381
+ it("should error if neither unsafe.bindings nor unsafe.metadata are defined", () => {
2333
2382
  const { diagnostics } = normalizeAndValidateConfig(
2334
2383
  { unsafe: {} } as unknown as RawConfig,
2335
2384
  undefined,
@@ -2341,9 +2390,37 @@ describe("normalizeAndValidateConfig()", () => {
2341
2390
  - \\"unsafe\\" fields are experimental and may change or break at any time."
2342
2391
  `);
2343
2392
  expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
2393
+ "Processing wrangler configuration:
2394
+ - The field \\"unsafe\\" should contain at least one of \\"bindings\\" or \\"metadata\\" properties but got {}."
2395
+ `);
2396
+ });
2397
+
2398
+ it("should not error if at least unsafe.bindings is defined", () => {
2399
+ const { diagnostics } = normalizeAndValidateConfig(
2400
+ { unsafe: { bindings: [] } } as unknown as RawConfig,
2401
+ undefined,
2402
+ { env: undefined }
2403
+ );
2404
+
2405
+ expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
2406
+ "Processing wrangler configuration:
2407
+ - \\"unsafe\\" fields are experimental and may change or break at any time."
2408
+ `);
2409
+ expect(diagnostics.hasErrors()).toBe(false);
2410
+ });
2411
+
2412
+ it("should not error if at least unsafe.metadata is defined", () => {
2413
+ const { diagnostics } = normalizeAndValidateConfig(
2414
+ { unsafe: { metadata: {} } } as unknown as RawConfig,
2415
+ undefined,
2416
+ { env: undefined }
2417
+ );
2418
+
2419
+ expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
2344
2420
  "Processing wrangler configuration:
2345
- - The field \\"unsafe\\" is missing the required \\"bindings\\" property."
2421
+ - \\"unsafe\\" fields are experimental and may change or break at any time."
2346
2422
  `);
2423
+ expect(diagnostics.hasErrors()).toBe(false);
2347
2424
  });
2348
2425
 
2349
2426
  it("should error if unsafe.bindings is an object", () => {
@@ -2453,6 +2530,74 @@ describe("normalizeAndValidateConfig()", () => {
2453
2530
  - binding should have a string \\"type\\" field."
2454
2531
  `);
2455
2532
  });
2533
+
2534
+ it("should error if unsafe.metadata is an array", () => {
2535
+ const { diagnostics } = normalizeAndValidateConfig(
2536
+ { unsafe: { metadata: [] } } as unknown as RawConfig,
2537
+ undefined,
2538
+ { env: undefined }
2539
+ );
2540
+
2541
+ expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
2542
+ "Processing wrangler configuration:
2543
+ - \\"unsafe\\" fields are experimental and may change or break at any time."
2544
+ `);
2545
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
2546
+ "Processing wrangler configuration:
2547
+ - The field \\"unsafe.metadata\\" should be an object but got []."
2548
+ `);
2549
+ });
2550
+
2551
+ it("should error if unsafe.metadata is a string", () => {
2552
+ const { diagnostics } = normalizeAndValidateConfig(
2553
+ { unsafe: { metadata: "BAD" } } as unknown as RawConfig,
2554
+ undefined,
2555
+ { env: undefined }
2556
+ );
2557
+
2558
+ expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
2559
+ "Processing wrangler configuration:
2560
+ - \\"unsafe\\" fields are experimental and may change or break at any time."
2561
+ `);
2562
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
2563
+ "Processing wrangler configuration:
2564
+ - The field \\"unsafe.metadata\\" should be an object but got \\"BAD\\"."
2565
+ `);
2566
+ });
2567
+
2568
+ it("should error if unsafe.metadata is a number", () => {
2569
+ const { diagnostics } = normalizeAndValidateConfig(
2570
+ { unsafe: { metadata: 999 } } as unknown as RawConfig,
2571
+ undefined,
2572
+ { env: undefined }
2573
+ );
2574
+
2575
+ expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
2576
+ "Processing wrangler configuration:
2577
+ - \\"unsafe\\" fields are experimental and may change or break at any time."
2578
+ `);
2579
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
2580
+ "Processing wrangler configuration:
2581
+ - The field \\"unsafe.metadata\\" should be an object but got 999."
2582
+ `);
2583
+ });
2584
+
2585
+ it("should error if unsafe.metadata is null", () => {
2586
+ const { diagnostics } = normalizeAndValidateConfig(
2587
+ { unsafe: { metadata: null } } as unknown as RawConfig,
2588
+ undefined,
2589
+ { env: undefined }
2590
+ );
2591
+
2592
+ expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
2593
+ "Processing wrangler configuration:
2594
+ - \\"unsafe\\" fields are experimental and may change or break at any time."
2595
+ `);
2596
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
2597
+ "Processing wrangler configuration:
2598
+ - The field \\"unsafe.metadata\\" should be an object but got null."
2599
+ `);
2600
+ });
2456
2601
  });
2457
2602
 
2458
2603
  describe("(deprecated)", () => {
@@ -2836,7 +2981,10 @@ describe("normalizeAndValidateConfig()", () => {
2836
2981
  const r2_buckets: RawConfig["r2_buckets"] = [];
2837
2982
  const analytics_engine_datasets: RawConfig["analytics_engine_datasets"] =
2838
2983
  [];
2839
- const unsafe: RawConfig["unsafe"] = { bindings: [] };
2984
+ const unsafe: RawConfig["unsafe"] = {
2985
+ bindings: undefined,
2986
+ metadata: undefined,
2987
+ };
2840
2988
  const rawConfig: RawConfig = {
2841
2989
  define,
2842
2990
  vars,
@@ -3784,7 +3932,7 @@ describe("normalizeAndValidateConfig()", () => {
3784
3932
  `);
3785
3933
  });
3786
3934
 
3787
- it("should error if unsafe.bindings is not defined", () => {
3935
+ it("should error if neither unsafe.bindings nor unsafe.metadata are defined", () => {
3788
3936
  const { diagnostics } = normalizeAndValidateConfig(
3789
3937
  { env: { ENV1: { unsafe: {} } } } as unknown as RawConfig,
3790
3938
  undefined,
@@ -3798,11 +3946,47 @@ describe("normalizeAndValidateConfig()", () => {
3798
3946
  - \\"unsafe\\" fields are experimental and may change or break at any time."
3799
3947
  `);
3800
3948
  expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
3949
+ "Processing wrangler configuration:
3950
+
3951
+ - \\"env.ENV1\\" environment configuration
3952
+ - The field \\"env.ENV1.unsafe\\" should contain at least one of \\"bindings\\" or \\"metadata\\" properties but got {}."
3953
+ `);
3954
+ });
3955
+
3956
+ it("should not error if at least unsafe.bindings is undefined", () => {
3957
+ const { diagnostics } = normalizeAndValidateConfig(
3958
+ {
3959
+ env: { ENV1: { unsafe: { bindings: [] } } },
3960
+ } as unknown as RawConfig,
3961
+ undefined,
3962
+ { env: "ENV1" }
3963
+ );
3964
+
3965
+ expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
3801
3966
  "Processing wrangler configuration:
3802
3967
 
3803
3968
  - \\"env.ENV1\\" environment configuration
3804
- - The field \\"env.ENV1.unsafe\\" is missing the required \\"bindings\\" property."
3969
+ - \\"unsafe\\" fields are experimental and may change or break at any time."
3805
3970
  `);
3971
+ expect(diagnostics.hasErrors()).toBe(false);
3972
+ });
3973
+
3974
+ it("should not error if at least unsafe.metadata is undefined", () => {
3975
+ const { diagnostics } = normalizeAndValidateConfig(
3976
+ {
3977
+ env: { ENV1: { unsafe: { metadata: {} } } },
3978
+ } as unknown as RawConfig,
3979
+ undefined,
3980
+ { env: "ENV1" }
3981
+ );
3982
+
3983
+ expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
3984
+ "Processing wrangler configuration:
3985
+
3986
+ - \\"env.ENV1\\" environment configuration
3987
+ - \\"unsafe\\" fields are experimental and may change or break at any time."
3988
+ `);
3989
+ expect(diagnostics.hasErrors()).toBe(false);
3806
3990
  });
3807
3991
 
3808
3992
  it("should error if unsafe.bindings is an object", () => {
@@ -3944,6 +4128,98 @@ describe("normalizeAndValidateConfig()", () => {
3944
4128
  - binding should have a string \\"type\\" field."
3945
4129
  `);
3946
4130
  });
4131
+
4132
+ it("should error if unsafe.metadata is an array", () => {
4133
+ const { diagnostics } = normalizeAndValidateConfig(
4134
+ {
4135
+ env: { ENV1: { unsafe: { metadata: [] } } },
4136
+ } as unknown as RawConfig,
4137
+ undefined,
4138
+ { env: "ENV1" }
4139
+ );
4140
+
4141
+ expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
4142
+ "Processing wrangler configuration:
4143
+
4144
+ - \\"env.ENV1\\" environment configuration
4145
+ - \\"unsafe\\" fields are experimental and may change or break at any time."
4146
+ `);
4147
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
4148
+ "Processing wrangler configuration:
4149
+
4150
+ - \\"env.ENV1\\" environment configuration
4151
+ - The field \\"env.ENV1.unsafe.metadata\\" should be an object but got []."
4152
+ `);
4153
+ });
4154
+
4155
+ it("should error if unsafe.metadata is a string", () => {
4156
+ const { diagnostics } = normalizeAndValidateConfig(
4157
+ {
4158
+ env: { ENV1: { unsafe: { metadata: "BAD" } } },
4159
+ } as unknown as RawConfig,
4160
+ undefined,
4161
+ { env: "ENV1" }
4162
+ );
4163
+
4164
+ expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
4165
+ "Processing wrangler configuration:
4166
+
4167
+ - \\"env.ENV1\\" environment configuration
4168
+ - \\"unsafe\\" fields are experimental and may change or break at any time."
4169
+ `);
4170
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
4171
+ "Processing wrangler configuration:
4172
+
4173
+ - \\"env.ENV1\\" environment configuration
4174
+ - The field \\"env.ENV1.unsafe.metadata\\" should be an object but got \\"BAD\\"."
4175
+ `);
4176
+ });
4177
+
4178
+ it("should error if unsafe.metadata is a number", () => {
4179
+ const { diagnostics } = normalizeAndValidateConfig(
4180
+ {
4181
+ env: { ENV1: { unsafe: { metadata: 999 } } },
4182
+ } as unknown as RawConfig,
4183
+ undefined,
4184
+ { env: "ENV1" }
4185
+ );
4186
+
4187
+ expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
4188
+ "Processing wrangler configuration:
4189
+
4190
+ - \\"env.ENV1\\" environment configuration
4191
+ - \\"unsafe\\" fields are experimental and may change or break at any time."
4192
+ `);
4193
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
4194
+ "Processing wrangler configuration:
4195
+
4196
+ - \\"env.ENV1\\" environment configuration
4197
+ - The field \\"env.ENV1.unsafe.metadata\\" should be an object but got 999."
4198
+ `);
4199
+ });
4200
+
4201
+ it("should error if unsafe.metadata is null", () => {
4202
+ const { diagnostics } = normalizeAndValidateConfig(
4203
+ {
4204
+ env: { ENV1: { unsafe: { metadata: null } } },
4205
+ } as unknown as RawConfig,
4206
+ undefined,
4207
+ { env: "ENV1" }
4208
+ );
4209
+
4210
+ expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
4211
+ "Processing wrangler configuration:
4212
+
4213
+ - \\"env.ENV1\\" environment configuration
4214
+ - \\"unsafe\\" fields are experimental and may change or break at any time."
4215
+ `);
4216
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
4217
+ "Processing wrangler configuration:
4218
+
4219
+ - \\"env.ENV1\\" environment configuration
4220
+ - The field \\"env.ENV1.unsafe.metadata\\" should be an object but got null."
4221
+ `);
4222
+ });
3947
4223
  });
3948
4224
 
3949
4225
  describe("(deprecated)", () => {