wrangler 0.0.0-e7a0cd5b → 0.0.0-e87ad068

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 (255) hide show
  1. package/README.md +3 -3
  2. package/bin/wrangler.js +30 -10
  3. package/miniflare-dist/index.mjs +254 -128
  4. package/package.json +27 -19
  5. package/src/__tests__/api-dev.test.ts +65 -36
  6. package/src/__tests__/api-devregistry.test.ts +121 -0
  7. package/src/__tests__/api.test.ts +27 -5
  8. package/src/__tests__/configuration.test.ts +797 -98
  9. package/src/__tests__/constellation.test.ts +371 -0
  10. package/src/__tests__/d1/d1.test.ts +82 -0
  11. package/src/__tests__/d1/execute.test.ts +66 -0
  12. package/src/__tests__/d1/migrate.test.ts +257 -0
  13. package/src/__tests__/d1/splitter.test.ts +255 -0
  14. package/src/__tests__/delete.test.ts +91 -49
  15. package/src/__tests__/deployments.test.ts +330 -56
  16. package/src/__tests__/dev.test.tsx +198 -90
  17. package/src/__tests__/generate.test.ts +12 -10
  18. package/src/__tests__/guess-worker-format.test.ts +35 -0
  19. package/src/__tests__/helpers/end-event-loop.ts +6 -0
  20. package/src/__tests__/helpers/mock-auth-domain.ts +20 -0
  21. package/src/__tests__/helpers/mock-console.ts +6 -0
  22. package/src/__tests__/helpers/mock-dialogs.ts +71 -85
  23. package/src/__tests__/helpers/mock-get-pages-upload-token.ts +25 -0
  24. package/src/__tests__/helpers/mock-get-zone-from-host.ts +8 -5
  25. package/src/__tests__/helpers/mock-known-routes.ts +7 -2
  26. package/src/__tests__/helpers/mock-kv.ts +29 -16
  27. package/src/__tests__/helpers/mock-oauth-flow.ts +103 -92
  28. package/src/__tests__/helpers/mock-process.ts +8 -13
  29. package/src/__tests__/helpers/mock-set-timeout.ts +16 -0
  30. package/src/__tests__/helpers/msw/blob-worker.cjs +19 -0
  31. package/src/__tests__/helpers/msw/handlers/deployments.ts +128 -27
  32. package/src/__tests__/helpers/msw/handlers/namespaces.ts +18 -41
  33. package/src/__tests__/helpers/msw/handlers/oauth.ts +13 -18
  34. package/src/__tests__/helpers/msw/handlers/r2.ts +14 -34
  35. package/src/__tests__/helpers/msw/handlers/script.ts +9 -28
  36. package/src/__tests__/helpers/msw/handlers/user.ts +13 -24
  37. package/src/__tests__/helpers/msw/handlers/zones.ts +6 -8
  38. package/src/__tests__/helpers/msw/index.ts +32 -1
  39. package/src/__tests__/helpers/msw/read-file-sync.js +61 -0
  40. package/src/__tests__/helpers/string-dynamic-values-matcher.ts +28 -0
  41. package/src/__tests__/helpers/worker-scripts/parent-worker.js +4 -1
  42. package/src/__tests__/index.test.ts +92 -72
  43. package/src/__tests__/init.test.ts +1033 -631
  44. package/src/__tests__/jest.setup.ts +19 -42
  45. package/src/__tests__/kv.test.ts +355 -231
  46. package/src/__tests__/logger.test.ts +68 -2
  47. package/src/__tests__/logout.test.ts +47 -0
  48. package/src/__tests__/metrics.test.ts +94 -51
  49. package/src/__tests__/middleware.scheduled.test.ts +42 -32
  50. package/src/__tests__/middleware.test.ts +212 -122
  51. package/src/__tests__/mtls-certificates.test.ts +589 -0
  52. package/src/__tests__/pages/deployment-list.test.ts +80 -0
  53. package/src/__tests__/pages/functions-build.test.ts +529 -0
  54. package/src/__tests__/pages/pages.test.ts +81 -0
  55. package/src/__tests__/pages/project-create.test.ts +63 -0
  56. package/src/__tests__/pages/project-list.test.ts +110 -0
  57. package/src/__tests__/pages/project-upload.test.ts +500 -0
  58. package/src/__tests__/pages/publish.test.ts +2864 -0
  59. package/src/__tests__/pages-deployment-tail.test.ts +957 -0
  60. package/src/__tests__/parse.test.ts +106 -0
  61. package/src/__tests__/publish.test.ts +2227 -1005
  62. package/src/__tests__/pubsub.test.ts +226 -100
  63. package/src/__tests__/queues.test.ts +238 -77
  64. package/src/__tests__/r2.test.ts +55 -57
  65. package/src/__tests__/secret.test.ts +179 -130
  66. package/src/__tests__/tail.test.ts +313 -113
  67. package/src/__tests__/test-old-node-version.js +3 -3
  68. package/src/__tests__/traverse-module-graph.test.ts +220 -0
  69. package/src/__tests__/tsconfig-sanity.ts +12 -0
  70. package/src/__tests__/tsconfig.json +8 -0
  71. package/src/__tests__/tsconfig.tsbuildinfo +1 -0
  72. package/src/__tests__/type-generation.test.ts +43 -26
  73. package/src/__tests__/unstableDev.test.ts +61 -0
  74. package/src/__tests__/user.test.ts +35 -72
  75. package/src/__tests__/whoami.test.tsx +8 -114
  76. package/src/__tests__/worker-namespace.test.ts +83 -69
  77. package/src/api/dev.ts +180 -60
  78. package/src/api/index.ts +10 -1
  79. package/src/api/mtls-certificate.ts +148 -0
  80. package/src/api/pages/create-worker-bundle-contents.ts +80 -0
  81. package/src/api/pages/index.ts +5 -0
  82. package/src/api/pages/publish.tsx +371 -0
  83. package/src/bundle.ts +315 -148
  84. package/src/cfetch/index.ts +0 -2
  85. package/src/cfetch/internal.ts +33 -33
  86. package/src/cli.ts +4 -4
  87. package/src/config/config.ts +14 -7
  88. package/src/config/environment.ts +120 -24
  89. package/src/config/index.ts +117 -35
  90. package/src/config/validation-helpers.ts +1 -1
  91. package/src/config/validation.ts +430 -19
  92. package/src/constellation/createProject.tsx +51 -0
  93. package/src/constellation/deleteProject.ts +51 -0
  94. package/src/constellation/deleteProjectModel.ts +76 -0
  95. package/src/constellation/index.ts +75 -0
  96. package/src/constellation/listCatalog.tsx +35 -0
  97. package/src/constellation/listModel.tsx +49 -0
  98. package/src/constellation/listProject.tsx +28 -0
  99. package/src/constellation/listRuntime.tsx +28 -0
  100. package/src/constellation/options.ts +17 -0
  101. package/src/constellation/types.ts +17 -0
  102. package/src/constellation/uploadModel.tsx +84 -0
  103. package/src/constellation/utils.ts +90 -0
  104. package/src/create-worker-preview.ts +19 -8
  105. package/src/create-worker-upload-form.ts +81 -18
  106. package/src/d1/backups.tsx +47 -46
  107. package/src/d1/constants.ts +5 -0
  108. package/src/d1/create.tsx +85 -37
  109. package/src/d1/{delete.tsx → delete.ts} +12 -15
  110. package/src/d1/execute.tsx +228 -156
  111. package/src/d1/index.ts +31 -7
  112. package/src/d1/list.tsx +27 -13
  113. package/src/d1/migrations/apply.tsx +235 -0
  114. package/src/d1/migrations/create.tsx +79 -0
  115. package/src/d1/migrations/helpers.ts +171 -0
  116. package/src/d1/migrations/index.ts +3 -0
  117. package/src/d1/migrations/list.tsx +95 -0
  118. package/src/d1/migrations/options.ts +23 -0
  119. package/src/d1/options.ts +12 -2
  120. package/src/d1/splitter.ts +161 -0
  121. package/src/d1/types.ts +32 -0
  122. package/src/d1/utils.ts +12 -2
  123. package/src/delete.ts +8 -16
  124. package/src/deployments.ts +340 -46
  125. package/src/deprecated/index.ts +8 -302
  126. package/src/dev/dev.tsx +66 -46
  127. package/src/dev/local.tsx +339 -104
  128. package/src/dev/remote.tsx +36 -3
  129. package/src/dev/start-server.ts +146 -101
  130. package/src/dev/use-esbuild.ts +82 -56
  131. package/src/dev/validate-dev-props.ts +9 -0
  132. package/src/{dev-registry.tsx → dev-registry.ts} +13 -1
  133. package/src/dev.tsx +202 -125
  134. package/src/dialogs.ts +136 -0
  135. package/src/dispatch-namespace.ts +21 -17
  136. package/src/docs/helpers.ts +50 -0
  137. package/src/docs/index.ts +54 -0
  138. package/src/entry.ts +26 -11
  139. package/src/environment-variables/factory.ts +89 -0
  140. package/src/environment-variables/misc-variables.ts +30 -0
  141. package/src/generate/index.ts +303 -0
  142. package/src/git-client.ts +15 -4
  143. package/src/{index.tsx → index.ts} +148 -63
  144. package/src/init.ts +301 -197
  145. package/src/inspect.ts +122 -6
  146. package/src/jest.d.ts +4 -0
  147. package/src/kv/helpers.ts +7 -4
  148. package/src/kv/index.ts +15 -17
  149. package/src/logger.ts +36 -5
  150. package/src/metrics/metrics-config.ts +3 -3
  151. package/src/metrics/send-event.ts +6 -1
  152. package/src/miniflare-cli/assets.ts +64 -28
  153. package/src/miniflare-cli/index.ts +6 -9
  154. package/src/miniflare-cli/tsconfig.json +9 -0
  155. package/src/miniflare-cli/tsconfig.tsbuildinfo +1 -0
  156. package/src/miniflare-cli/types.ts +11 -0
  157. package/src/module-collection.ts +92 -26
  158. package/src/mtls-certificate/cli.ts +155 -0
  159. package/src/pages/build.ts +423 -0
  160. package/src/pages/buildFunctions.ts +140 -0
  161. package/src/pages/deployment-tails.ts +281 -0
  162. package/src/pages/deployments.tsx +11 -33
  163. package/src/pages/{dev.tsx → dev.ts} +192 -126
  164. package/src/pages/errors.ts +1 -0
  165. package/src/pages/functions/buildPlugin.ts +73 -69
  166. package/src/pages/functions/buildWorker.ts +342 -117
  167. package/src/pages/functions/tsconfig.json +8 -0
  168. package/src/pages/functions/tsconfig.tsbuildinfo +1 -0
  169. package/src/pages/{functions.tsx → functions.ts} +8 -4
  170. package/src/pages/{index.tsx → index.ts} +11 -3
  171. package/src/pages/projects.tsx +12 -22
  172. package/src/pages/prompt-select-project.tsx +31 -0
  173. package/src/pages/publish.tsx +45 -214
  174. package/src/pages/types.ts +6 -9
  175. package/src/pages/upload.tsx +58 -14
  176. package/src/pages/utils.ts +14 -3
  177. package/src/parse.ts +23 -1
  178. package/src/paths.ts +1 -1
  179. package/src/proxy.ts +5 -0
  180. package/src/publish/index.ts +20 -16
  181. package/src/publish/publish.ts +81 -48
  182. package/src/pubsub/{pubsub-commands.tsx → pubsub-commands.ts} +19 -20
  183. package/src/queues/cli/commands/consumer/add.ts +24 -24
  184. package/src/queues/cli/commands/consumer/index.ts +3 -6
  185. package/src/queues/cli/commands/consumer/remove.ts +11 -18
  186. package/src/queues/cli/commands/create.ts +8 -8
  187. package/src/queues/cli/commands/delete.ts +8 -8
  188. package/src/queues/cli/commands/index.ts +6 -4
  189. package/src/queues/cli/commands/list.ts +8 -8
  190. package/src/queues/client.ts +1 -0
  191. package/src/queues/utils.ts +18 -0
  192. package/src/r2/constants.ts +4 -0
  193. package/src/r2/helpers.ts +1 -0
  194. package/src/r2/index.ts +40 -28
  195. package/src/secret/index.ts +18 -17
  196. package/src/{sites.tsx → sites.ts} +165 -53
  197. package/src/tail/createTail.ts +103 -3
  198. package/src/tail/filters.ts +1 -1
  199. package/src/tail/index.ts +8 -11
  200. package/src/tail/printing.ts +27 -0
  201. package/src/traverse-module-graph.ts +54 -0
  202. package/src/tsconfig-sanity.ts +16 -0
  203. package/src/type-generation.ts +85 -57
  204. package/src/user/access.ts +0 -1
  205. package/src/user/auth-variables.ts +113 -0
  206. package/src/user/choose-account.tsx +1 -31
  207. package/src/user/index.ts +0 -1
  208. package/src/user/{user.tsx → user.ts} +124 -82
  209. package/src/utils/render.ts +93 -0
  210. package/src/{whoami.tsx → whoami.ts} +37 -71
  211. package/src/worker.ts +61 -3
  212. package/src/yargs-types.ts +22 -5
  213. package/templates/__tests__/tsconfig-sanity.ts +12 -0
  214. package/templates/__tests__/tsconfig.json +8 -0
  215. package/templates/__tests__/tsconfig.tsbuildinfo +1 -0
  216. package/templates/checked-fetch.js +9 -3
  217. package/templates/d1-beta-facade.js +95 -25
  218. package/templates/facade.d.ts +14 -0
  219. package/templates/first-party-worker-module-facade.ts +4 -3
  220. package/templates/format-dev-errors.ts +8 -6
  221. package/templates/gitignore +1 -0
  222. package/templates/init-tests/test-jest-new-worker.js +3 -5
  223. package/templates/init-tests/test-vitest-new-worker.js +3 -5
  224. package/templates/init-tests/test-vitest-new-worker.ts +25 -0
  225. package/templates/middleware/loader-modules.ts +2 -2
  226. package/templates/middleware/loader-sw.ts +51 -31
  227. package/templates/middleware/middleware-miniflare3-json-error.ts +32 -0
  228. package/templates/new-worker.ts +3 -0
  229. package/templates/pages-dev-pipeline.ts +4 -1
  230. package/templates/pages-dev-util.ts +4 -1
  231. package/templates/pages-shim.ts +4 -1
  232. package/templates/pages-template-plugin.ts +19 -10
  233. package/templates/serve-static-assets.ts +17 -14
  234. package/templates/service-bindings-module-facade.js +1 -0
  235. package/templates/tsconfig-sanity.ts +11 -0
  236. package/templates/tsconfig.init.json +105 -0
  237. package/templates/tsconfig.json +5 -103
  238. package/templates/tsconfig.tsbuildinfo +1 -0
  239. package/wrangler-dist/cli.d.ts +902 -77
  240. package/wrangler-dist/cli.js +131685 -140615
  241. package/wrangler-dist/wasm-sync.wasm +0 -0
  242. package/src/__tests__/api-devregistry.test.js +0 -57
  243. package/src/__tests__/d1.test.ts +0 -72
  244. package/src/__tests__/dialogs.test.tsx +0 -40
  245. package/src/__tests__/helpers/mock-cfetch.ts +0 -333
  246. package/src/__tests__/pages.test.ts +0 -2223
  247. package/src/d1/types.tsx +0 -14
  248. package/src/dialogs.tsx +0 -163
  249. package/src/environment-variables.ts +0 -50
  250. package/src/pages/build.tsx +0 -238
  251. package/src/user/env-vars.ts +0 -46
  252. package/templates/experimental-local-cache-stubs.js +0 -27
  253. /package/src/{bundle-reporter.tsx → bundle-reporter.ts} +0 -0
  254. /package/src/dev/{get-local-persistence-path.tsx → get-local-persistence-path.ts} +0 -0
  255. /package/src/pages/{hash.tsx → hash.ts} +0 -0
@@ -26,6 +26,7 @@ describe("normalizeAndValidateConfig()", () => {
26
26
  compatibility_flags: [],
27
27
  configPath: undefined,
28
28
  d1_databases: [],
29
+ constellation: [],
29
30
  dev: {
30
31
  ip: "0.0.0.0",
31
32
  local_protocol: "http",
@@ -40,6 +41,7 @@ describe("normalizeAndValidateConfig()", () => {
40
41
  jsx_fragment: "React.Fragment",
41
42
  tsconfig: undefined,
42
43
  kv_namespaces: [],
44
+ send_email: [],
43
45
  legacy_env: true,
44
46
  logfwdr: {
45
47
  bindings: [],
@@ -55,21 +57,24 @@ describe("normalizeAndValidateConfig()", () => {
55
57
  },
56
58
  r2_buckets: [],
57
59
  services: [],
60
+ analytics_engine_datasets: [],
58
61
  route: undefined,
59
62
  routes: undefined,
60
63
  rules: [],
61
64
  site: undefined,
62
65
  text_blobs: undefined,
66
+ browser: undefined,
63
67
  triggers: {
64
68
  crons: [],
65
69
  },
66
70
  unsafe: {
67
- bindings: [],
71
+ bindings: undefined,
72
+ metadata: undefined,
68
73
  },
69
74
  dispatch_namespaces: [],
75
+ mtls_certificates: [],
70
76
  usage_model: undefined,
71
77
  vars: {},
72
- metadata_binding: undefined,
73
78
  define: {},
74
79
  wasm_modules: undefined,
75
80
  data_blobs: undefined,
@@ -81,6 +86,8 @@ describe("normalizeAndValidateConfig()", () => {
81
86
  first_party_worker: undefined,
82
87
  keep_vars: undefined,
83
88
  logpush: undefined,
89
+ placement: undefined,
90
+ tail_consumers: undefined,
84
91
  });
85
92
  expect(diagnostics.hasErrors()).toBe(false);
86
93
  expect(diagnostics.hasWarnings()).toBe(false);
@@ -901,6 +908,17 @@ describe("normalizeAndValidateConfig()", () => {
901
908
  preview_id: "KV_PREVIEW_1",
902
909
  },
903
910
  ],
911
+ send_email: [
912
+ { name: "SEB_TARGET", destination_address: "teste@example.com" },
913
+ { name: "SEB_UNRESTRICTED" },
914
+ {
915
+ name: "SEB_ALLOWLIST",
916
+ allowed_destination_addresses: [
917
+ "email1@example.com",
918
+ "email2@example.com",
919
+ ],
920
+ },
921
+ ],
904
922
  r2_buckets: [
905
923
  { binding: "R2_BINDING_1", bucket_name: "R2_BUCKET_1" },
906
924
  {
@@ -916,6 +934,15 @@ describe("normalizeAndValidateConfig()", () => {
916
934
  environment: "SERVICE_BINDING_ENVIRONMENT_1",
917
935
  },
918
936
  ],
937
+ analytics_engine_datasets: [
938
+ {
939
+ binding: "AE_BINDING_1",
940
+ dataset: "DATASET_1",
941
+ },
942
+ {
943
+ binding: "AE_BINDING_2",
944
+ },
945
+ ],
919
946
  unsafe: {
920
947
  bindings: [
921
948
  { name: "UNSAFE_BINDING_1", type: "UNSAFE_TYPE_1" },
@@ -925,12 +952,16 @@ describe("normalizeAndValidateConfig()", () => {
925
952
  extra: "UNSAFE_EXTRA_1",
926
953
  },
927
954
  ],
955
+ metadata: undefined,
928
956
  },
929
957
  no_bundle: true,
930
958
  minify: true,
931
959
  node_compat: true,
932
960
  first_party_worker: true,
933
961
  logpush: true,
962
+ placement: {
963
+ mode: "smart",
964
+ },
934
965
  };
935
966
 
936
967
  const { config, diagnostics } = normalizeAndValidateConfig(
@@ -1006,6 +1037,9 @@ describe("normalizeAndValidateConfig()", () => {
1006
1037
  node_compat: "INVALID",
1007
1038
  first_party_worker: "INVALID",
1008
1039
  logpush: "INVALID",
1040
+ placement: {
1041
+ mode: "INVALID",
1042
+ },
1009
1043
  } as unknown as RawEnvironment;
1010
1044
 
1011
1045
  const { config, diagnostics } = normalizeAndValidateConfig(
@@ -1069,6 +1103,7 @@ describe("normalizeAndValidateConfig()", () => {
1069
1103
  - Expected \\"name\\" to be of type string, alphanumeric and lowercase with dashes only but got 111.
1070
1104
  - Expected \\"main\\" to be of type string but got 1333.
1071
1105
  - Expected \\"usage_model\\" field to be one of [\\"bundled\\",\\"unbound\\"] but got \\"INVALID\\".
1106
+ - Expected \\"placement.mode\\" field to be one of [\\"off\\",\\"smart\\"] but got \\"INVALID\\".
1072
1107
  - The field \\"define.DEF1\\" should be a string but got 1777.
1073
1108
  - Expected \\"no_bundle\\" to be of type boolean but got \\"INVALID\\".
1074
1109
  - Expected \\"minify\\" to be of type boolean but got \\"INVALID\\".
@@ -1527,6 +1562,64 @@ describe("normalizeAndValidateConfig()", () => {
1527
1562
  });
1528
1563
  });
1529
1564
 
1565
+ describe("[browser]", () => {
1566
+ it("should error if browser is an array", () => {
1567
+ const { diagnostics } = normalizeAndValidateConfig(
1568
+ { browser: [] } as unknown as RawConfig,
1569
+ undefined,
1570
+ { env: undefined }
1571
+ );
1572
+
1573
+ expect(diagnostics.hasWarnings()).toBe(false);
1574
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
1575
+ "Processing wrangler configuration:
1576
+ - The field \\"browser\\" should be an object but got []."
1577
+ `);
1578
+ });
1579
+
1580
+ it("should error if browser is a string", () => {
1581
+ const { diagnostics } = normalizeAndValidateConfig(
1582
+ { browser: "BAD" } as unknown as RawConfig,
1583
+ undefined,
1584
+ { env: undefined }
1585
+ );
1586
+
1587
+ expect(diagnostics.hasWarnings()).toBe(false);
1588
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
1589
+ "Processing wrangler configuration:
1590
+ - The field \\"browser\\" should be an object but got \\"BAD\\"."
1591
+ `);
1592
+ });
1593
+
1594
+ it("should error if browser is a number", () => {
1595
+ const { diagnostics } = normalizeAndValidateConfig(
1596
+ { browser: 999 } as unknown as RawConfig,
1597
+ undefined,
1598
+ { env: undefined }
1599
+ );
1600
+
1601
+ expect(diagnostics.hasWarnings()).toBe(false);
1602
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
1603
+ "Processing wrangler configuration:
1604
+ - The field \\"browser\\" should be an object but got 999."
1605
+ `);
1606
+ });
1607
+
1608
+ it("should error if browser is null", () => {
1609
+ const { diagnostics } = normalizeAndValidateConfig(
1610
+ { browser: null } as unknown as RawConfig,
1611
+ undefined,
1612
+ { env: undefined }
1613
+ );
1614
+
1615
+ expect(diagnostics.hasWarnings()).toBe(false);
1616
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
1617
+ "Processing wrangler configuration:
1618
+ - The field \\"browser\\" should be an object but got null."
1619
+ `);
1620
+ });
1621
+ });
1622
+
1530
1623
  describe("[kv_namespaces]", () => {
1531
1624
  it("should error if kv_namespaces is an object", () => {
1532
1625
  const { diagnostics } = normalizeAndValidateConfig(
@@ -1617,6 +1710,39 @@ describe("normalizeAndValidateConfig()", () => {
1617
1710
  });
1618
1711
  });
1619
1712
 
1713
+ it("should error if send_email.bindings are not valid", () => {
1714
+ const { diagnostics } = normalizeAndValidateConfig(
1715
+ {
1716
+ send_email: [
1717
+ {},
1718
+ { binding: "VALID" },
1719
+ { name: "SEB", destination_address: 123 },
1720
+ {
1721
+ name: "SEB2",
1722
+ allowed_destination_addresses: 123,
1723
+ },
1724
+ {
1725
+ name: "SEB3",
1726
+ destination_address: "email@example.com",
1727
+ allowed_destination_addresses: ["email@example.com"],
1728
+ },
1729
+ ],
1730
+ } as unknown as RawConfig,
1731
+ undefined,
1732
+ { env: undefined }
1733
+ );
1734
+
1735
+ expect(diagnostics.hasWarnings()).toBe(false);
1736
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
1737
+ "Processing wrangler configuration:
1738
+ - \\"send_email[0]\\" bindings should have a string \\"name\\" field but got {}.
1739
+ - \\"send_email[1]\\" bindings should have a string \\"name\\" field but got {\\"binding\\":\\"VALID\\"}.
1740
+ - \\"send_email[2]\\" bindings should, optionally, have a string \\"destination_address\\" field but got {\\"name\\":\\"SEB\\",\\"destination_address\\":123}.
1741
+ - \\"send_email[3]\\" bindings should, optionally, have a []string \\"allowed_destination_addresses\\" field but got {\\"name\\":\\"SEB2\\",\\"allowed_destination_addresses\\":123}.
1742
+ - \\"send_email[4]\\" bindings should have either a \\"destination_address\\" or \\"allowed_destination_addresses\\" field, but not both."
1743
+ `);
1744
+ });
1745
+
1620
1746
  describe("[d1_databases]", () => {
1621
1747
  it("should error if d1_databases is an object", () => {
1622
1748
  const { diagnostics } = normalizeAndValidateConfig(
@@ -1707,6 +1833,100 @@ describe("normalizeAndValidateConfig()", () => {
1707
1833
  });
1708
1834
  });
1709
1835
 
1836
+ describe("[constellation]", () => {
1837
+ it("should error if constellation is an object", () => {
1838
+ const { diagnostics } = normalizeAndValidateConfig(
1839
+ { constellation: {} } as unknown as RawConfig,
1840
+ undefined,
1841
+ { env: undefined }
1842
+ );
1843
+
1844
+ expect(diagnostics.hasWarnings()).toBe(false);
1845
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
1846
+ "Processing wrangler configuration:
1847
+ - The field \\"constellation\\" should be an array but got {}."
1848
+ `);
1849
+ });
1850
+
1851
+ it("should error if constellation is a string", () => {
1852
+ const { diagnostics } = normalizeAndValidateConfig(
1853
+ { constellation: "BAD" } as unknown as RawConfig,
1854
+ undefined,
1855
+ { env: undefined }
1856
+ );
1857
+
1858
+ expect(diagnostics.hasWarnings()).toBe(false);
1859
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
1860
+ "Processing wrangler configuration:
1861
+ - The field \\"constellation\\" should be an array but got \\"BAD\\"."
1862
+ `);
1863
+ });
1864
+
1865
+ it("should error if constellation is a number", () => {
1866
+ const { diagnostics } = normalizeAndValidateConfig(
1867
+ { constellation: 999 } as unknown as RawConfig,
1868
+ undefined,
1869
+ { env: undefined }
1870
+ );
1871
+
1872
+ expect(diagnostics.hasWarnings()).toBe(false);
1873
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
1874
+ "Processing wrangler configuration:
1875
+ - The field \\"constellation\\" should be an array but got 999."
1876
+ `);
1877
+ });
1878
+
1879
+ it("should error if constellation is null", () => {
1880
+ const { diagnostics } = normalizeAndValidateConfig(
1881
+ { constellation: null } as unknown as RawConfig,
1882
+ undefined,
1883
+ { env: undefined }
1884
+ );
1885
+
1886
+ expect(diagnostics.hasWarnings()).toBe(false);
1887
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
1888
+ "Processing wrangler configuration:
1889
+ - The field \\"constellation\\" should be an array but got null."
1890
+ `);
1891
+ });
1892
+
1893
+ it("should accept valid bindings", () => {
1894
+ const { diagnostics } = normalizeAndValidateConfig(
1895
+ {
1896
+ constellation: [{ binding: "VALID", project_id: "ID" }],
1897
+ } as unknown as RawConfig,
1898
+ undefined,
1899
+ { env: undefined }
1900
+ );
1901
+
1902
+ expect(diagnostics.hasErrors()).toBe(false);
1903
+ });
1904
+
1905
+ it("should error if constellation.bindings are not valid", () => {
1906
+ const { diagnostics } = normalizeAndValidateConfig(
1907
+ {
1908
+ constellation: [
1909
+ {},
1910
+ { binding: "VALID" },
1911
+ { binding: 2000, project: 2111 },
1912
+ ],
1913
+ } as unknown as RawConfig,
1914
+ undefined,
1915
+ { env: undefined }
1916
+ );
1917
+
1918
+ expect(diagnostics.hasWarnings()).toBe(false);
1919
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
1920
+ "Processing wrangler configuration:
1921
+ - \\"constellation[0]\\" bindings should have a string \\"binding\\" field but got {}.
1922
+ - \\"constellation[0]\\" bindings must have a \\"project_id\\" field but got {}.
1923
+ - \\"constellation[1]\\" bindings must have a \\"project_id\\" field but got {\\"binding\\":\\"VALID\\"}.
1924
+ - \\"constellation[2]\\" bindings should have a string \\"binding\\" field but got {\\"binding\\":2000,\\"project\\":2111}.
1925
+ - \\"constellation[2]\\" bindings must have a \\"project_id\\" field but got {\\"binding\\":2000,\\"project\\":2111}."
1926
+ `);
1927
+ });
1928
+ });
1929
+
1710
1930
  describe("[queues]", () => {
1711
1931
  it("should error if queues is not an object", () => {
1712
1932
  const { config, diagnostics } = normalizeAndValidateConfig(
@@ -1779,6 +1999,7 @@ describe("normalizeAndValidateConfig()", () => {
1779
1999
  max_batch_timeout: null,
1780
2000
  max_retries: "hello",
1781
2001
  dead_letter_queue: 5,
2002
+ max_concurrency: "hello",
1782
2003
  },
1783
2004
  ],
1784
2005
  },
@@ -1794,20 +2015,21 @@ describe("normalizeAndValidateConfig()", () => {
1794
2015
  );
1795
2016
  expect(diagnostics.hasWarnings()).toBe(true);
1796
2017
  expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
1797
- "Processing wrangler configuration:
1798
- - Unexpected fields found in queues field: \\"invalidField\\"
1799
- - Unexpected fields found in queues.consumers[2] field: \\"invalidField\\""
1800
- `);
2018
+ "Processing wrangler configuration:
2019
+ - Unexpected fields found in queues field: \\"invalidField\\"
2020
+ - Unexpected fields found in queues.consumers[2] field: \\"invalidField\\""
2021
+ `);
1801
2022
 
1802
2023
  expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
1803
- "Processing wrangler configuration:
1804
- - \\"queues.consumers[0]\\" should have a string \\"queue\\" field but got {}.
1805
- - \\"queues.consumers[1]\\" should have a string \\"queue\\" field but got {\\"queue\\":22}.
1806
- - \\"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}.
1807
- - \\"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}.
1808
- - \\"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}.
1809
- - \\"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}."
1810
- `);
2024
+ "Processing wrangler configuration:
2025
+ - \\"queues.consumers[0]\\" should have a string \\"queue\\" field but got {}.
2026
+ - \\"queues.consumers[1]\\" should have a string \\"queue\\" field but got {\\"queue\\":22}.
2027
+ - \\"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\\"}.
2028
+ - \\"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\\"}.
2029
+ - \\"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\\"}.
2030
+ - \\"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\\"}.
2031
+ - \\"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\\"}."
2032
+ `);
1811
2033
  });
1812
2034
  });
1813
2035
 
@@ -2029,28 +2251,93 @@ describe("normalizeAndValidateConfig()", () => {
2029
2251
  });
2030
2252
  });
2031
2253
 
2032
- describe("[dispatch_namespaces]", () => {
2033
- it("should log an experimental warning when dispatch_namespaces is used", () => {
2254
+ describe("[analytics_engine_datasets]", () => {
2255
+ it("should error if analytics_engine_datasets is an object", () => {
2256
+ const { diagnostics } = normalizeAndValidateConfig(
2257
+ { analytics_engine_datasets: {} } as unknown as RawConfig,
2258
+ undefined,
2259
+ { env: undefined }
2260
+ );
2261
+
2262
+ expect(diagnostics.hasWarnings()).toBe(false);
2263
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
2264
+ "Processing wrangler configuration:
2265
+ - The field \\"analytics_engine_datasets\\" should be an array but got {}."
2266
+ `);
2267
+ });
2268
+
2269
+ it("should error if analytics_engine_datasets is a string", () => {
2270
+ const { diagnostics } = normalizeAndValidateConfig(
2271
+ { analytics_engine_datasets: "BAD" } as unknown as RawConfig,
2272
+ undefined,
2273
+ { env: undefined }
2274
+ );
2275
+
2276
+ expect(diagnostics.hasWarnings()).toBe(false);
2277
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
2278
+ "Processing wrangler configuration:
2279
+ - The field \\"analytics_engine_datasets\\" should be an array but got \\"BAD\\"."
2280
+ `);
2281
+ });
2282
+
2283
+ it("should error if analytics_engine_datasets is a number", () => {
2284
+ const { diagnostics } = normalizeAndValidateConfig(
2285
+ { analytics_engine_datasets: 999 } as unknown as RawConfig,
2286
+ undefined,
2287
+ { env: undefined }
2288
+ );
2289
+
2290
+ expect(diagnostics.hasWarnings()).toBe(false);
2291
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
2292
+ "Processing wrangler configuration:
2293
+ - The field \\"analytics_engine_datasets\\" should be an array but got 999."
2294
+ `);
2295
+ });
2296
+
2297
+ it("should error if analytics_engine_datasets is null", () => {
2298
+ const { diagnostics } = normalizeAndValidateConfig(
2299
+ { analytics_engine_datasets: null } as unknown as RawConfig,
2300
+ undefined,
2301
+ { env: undefined }
2302
+ );
2303
+
2304
+ expect(diagnostics.hasWarnings()).toBe(false);
2305
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
2306
+ "Processing wrangler configuration:
2307
+ - The field \\"analytics_engine_datasets\\" should be an array but got null."
2308
+ `);
2309
+ });
2310
+
2311
+ it("should error if analytics_engine_datasets.bindings are not valid", () => {
2034
2312
  const { diagnostics } = normalizeAndValidateConfig(
2035
2313
  {
2036
- dispatch_namespaces: [
2314
+ analytics_engine_datasets: [
2315
+ {},
2316
+ { binding: 2333, dataset: 2444 },
2037
2317
  {
2038
- binding: "BINDING_1",
2039
- namespace: "NAMESPACE_1",
2318
+ binding: "AE_BINDING_2",
2319
+ dataset: 2555,
2040
2320
  },
2321
+ { binding: "AE_BINDING_1", dataset: "" },
2041
2322
  ],
2042
2323
  } as unknown as RawConfig,
2043
2324
  undefined,
2044
2325
  { env: undefined }
2045
2326
  );
2046
- expect(diagnostics.hasWarnings()).toBe(true);
2047
- expect(diagnostics.hasErrors()).toBe(false);
2048
- expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
2049
- "Processing wrangler configuration:
2050
- - \\"dispatch_namespaces\\" fields are experimental and may change or break at any time."
2051
- `);
2327
+
2328
+ expect(diagnostics.hasWarnings()).toBe(false);
2329
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
2330
+ "Processing wrangler configuration:
2331
+ - \\"analytics_engine_datasets[0]\\" bindings should have a string \\"binding\\" field but got {}.
2332
+ - \\"analytics_engine_datasets[1]\\" bindings should have a string \\"binding\\" field but got {\\"binding\\":2333,\\"dataset\\":2444}.
2333
+ - \\"analytics_engine_datasets[1]\\" bindings should, optionally, have a string \\"dataset\\" field but got {\\"binding\\":2333,\\"dataset\\":2444}.
2334
+ - \\"analytics_engine_datasets[2]\\" bindings should, optionally, have a string \\"dataset\\" field but got {\\"binding\\":\\"AE_BINDING_2\\",\\"dataset\\":2555}.
2335
+ - \\"analytics_engine_datasets[3]\\" bindings should, optionally, have a string \\"dataset\\" field but got {\\"binding\\":\\"AE_BINDING_1\\",\\"dataset\\":\\"\\"}."
2336
+ `);
2052
2337
  });
2338
+ });
2053
2339
 
2340
+ describe("[dispatch_namespaces]", () => {
2054
2341
  it("should error if dispatch_namespaces is not an array", () => {
2055
2342
  const { diagnostics } = normalizeAndValidateConfig(
2056
2343
  {
@@ -2060,12 +2347,8 @@ describe("normalizeAndValidateConfig()", () => {
2060
2347
  { env: undefined }
2061
2348
  );
2062
2349
 
2063
- expect(diagnostics.hasWarnings()).toBe(true);
2350
+ expect(diagnostics.hasWarnings()).toBe(false);
2064
2351
  expect(diagnostics.hasErrors()).toBe(true);
2065
- expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
2066
- "Processing wrangler configuration:
2067
- - \\"dispatch_namespaces\\" fields are experimental and may change or break at any time."
2068
- `);
2069
2352
  expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
2070
2353
  "Processing wrangler configuration:
2071
2354
  - The field \\"dispatch_namespaces\\" should be an array but got \\"just a string\\"."
@@ -2104,12 +2387,8 @@ describe("normalizeAndValidateConfig()", () => {
2104
2387
  undefined,
2105
2388
  { env: undefined }
2106
2389
  );
2107
- expect(diagnostics.hasWarnings()).toBe(true);
2390
+ expect(diagnostics.hasWarnings()).toBe(false);
2108
2391
  expect(diagnostics.hasErrors()).toBe(true);
2109
- expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
2110
- "Processing wrangler configuration:
2111
- - \\"dispatch_namespaces\\" fields are experimental and may change or break at any time."
2112
- `);
2113
2392
  expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
2114
2393
  "Processing wrangler configuration:
2115
2394
  - \\"dispatch_namespaces[0]\\" binding should be objects, but got \\"a string\\"
@@ -2124,27 +2403,96 @@ describe("normalizeAndValidateConfig()", () => {
2124
2403
  });
2125
2404
  });
2126
2405
 
2127
- describe("[unsafe.bindings]", () => {
2128
- it("should error if unsafe is an array", () => {
2406
+ describe("[mtls_certificates]", () => {
2407
+ it("should error if mtls_certificates is not an array", () => {
2129
2408
  const { diagnostics } = normalizeAndValidateConfig(
2130
- { unsafe: [] } as unknown as RawConfig,
2409
+ {
2410
+ mtls_certificates: "just a string",
2411
+ } as unknown as RawConfig,
2131
2412
  undefined,
2132
2413
  { env: undefined }
2133
2414
  );
2134
2415
 
2135
- expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
2136
- "Processing wrangler configuration:
2137
- - \\"unsafe\\" fields are experimental and may change or break at any time."
2138
- `);
2416
+ expect(diagnostics.hasWarnings()).toBe(false);
2417
+ expect(diagnostics.hasErrors()).toBe(true);
2139
2418
  expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
2140
- "Processing wrangler configuration:
2141
- - The field \\"unsafe\\" should be an object but got []."
2142
- `);
2419
+ "Processing wrangler configuration:
2420
+ - The field \\"mtls_certificates\\" should be an array but got \\"just a string\\"."
2421
+ `);
2143
2422
  });
2144
2423
 
2145
- it("should error if unsafe is a string", () => {
2424
+ it("should error on non valid mtls_certificates", () => {
2146
2425
  const { diagnostics } = normalizeAndValidateConfig(
2147
- { unsafe: "BAD" } as unknown as RawConfig,
2426
+ {
2427
+ mtls_certificates: [
2428
+ "a string",
2429
+ 123,
2430
+ false,
2431
+ {
2432
+ binding: 123,
2433
+ namespace: 123,
2434
+ },
2435
+ {
2436
+ binding: "CERT_ONE",
2437
+ id: "1234",
2438
+ },
2439
+ {
2440
+ binding: "CERT_TWO",
2441
+ certificate_id: 1234,
2442
+ },
2443
+ // this one is valid
2444
+ {
2445
+ binding: "CERT_THREE",
2446
+ certificate_id: "1234",
2447
+ },
2448
+ {
2449
+ binding: true,
2450
+ service: "1234",
2451
+ },
2452
+ ],
2453
+ } as unknown as RawConfig,
2454
+ undefined,
2455
+ { env: undefined }
2456
+ );
2457
+
2458
+ expect(diagnostics.hasWarnings()).toBe(false);
2459
+ expect(diagnostics.hasErrors()).toBe(true);
2460
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
2461
+ "Processing wrangler configuration:
2462
+ - \\"mtls_certificates\\" bindings should be objects, but got \\"a string\\"
2463
+ - \\"mtls_certificates\\" bindings should be objects, but got 123
2464
+ - \\"mtls_certificates\\" bindings should be objects, but got false
2465
+ - \\"mtls_certificates[3]\\" bindings should have a string \\"binding\\" field but got {\\"binding\\":123,\\"namespace\\":123}.
2466
+ - \\"mtls_certificates[3]\\" bindings should have a string \\"certificate_id\\" field but got {\\"binding\\":123,\\"namespace\\":123}.
2467
+ - \\"mtls_certificates[4]\\" bindings should have a string \\"certificate_id\\" field but got {\\"binding\\":\\"CERT_ONE\\",\\"id\\":\\"1234\\"}.
2468
+ - \\"mtls_certificates[5]\\" bindings should have a string \\"certificate_id\\" field but got {\\"binding\\":\\"CERT_TWO\\",\\"certificate_id\\":1234}.
2469
+ - \\"mtls_certificates[7]\\" bindings should have a string \\"binding\\" field but got {\\"binding\\":true,\\"service\\":\\"1234\\"}.
2470
+ - \\"mtls_certificates[7]\\" bindings should have a string \\"certificate_id\\" field but got {\\"binding\\":true,\\"service\\":\\"1234\\"}."
2471
+ `);
2472
+ });
2473
+ });
2474
+
2475
+ describe("[unsafe.bindings]", () => {
2476
+ it("should error if unsafe is an array", () => {
2477
+ const { diagnostics } = normalizeAndValidateConfig(
2478
+ { unsafe: [] } as unknown as RawConfig,
2479
+ undefined,
2480
+ { env: undefined }
2481
+ );
2482
+
2483
+ expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
2484
+ "Processing wrangler configuration:
2485
+ - \\"unsafe\\" fields are experimental and may change or break at any time."
2486
+ `);
2487
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
2488
+ "Processing wrangler configuration:
2489
+ - The field \\"unsafe\\" should be an object but got []."
2490
+ `);
2491
+ });
2492
+
2493
+ it("should error if unsafe is a string", () => {
2494
+ const { diagnostics } = normalizeAndValidateConfig(
2495
+ { unsafe: "BAD" } as unknown as RawConfig,
2148
2496
  undefined,
2149
2497
  { env: undefined }
2150
2498
  );
@@ -2193,7 +2541,7 @@ describe("normalizeAndValidateConfig()", () => {
2193
2541
  `);
2194
2542
  });
2195
2543
 
2196
- it("should error if unsafe.bindings is not defined", () => {
2544
+ it("should error if neither unsafe.bindings nor unsafe.metadata are defined", () => {
2197
2545
  const { diagnostics } = normalizeAndValidateConfig(
2198
2546
  { unsafe: {} } as unknown as RawConfig,
2199
2547
  undefined,
@@ -2205,9 +2553,37 @@ describe("normalizeAndValidateConfig()", () => {
2205
2553
  - \\"unsafe\\" fields are experimental and may change or break at any time."
2206
2554
  `);
2207
2555
  expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
2556
+ "Processing wrangler configuration:
2557
+ - The field \\"unsafe\\" should contain at least one of \\"bindings\\" or \\"metadata\\" properties but got {}."
2558
+ `);
2559
+ });
2560
+
2561
+ it("should not error if at least unsafe.bindings is defined", () => {
2562
+ const { diagnostics } = normalizeAndValidateConfig(
2563
+ { unsafe: { bindings: [] } } as unknown as RawConfig,
2564
+ undefined,
2565
+ { env: undefined }
2566
+ );
2567
+
2568
+ expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
2569
+ "Processing wrangler configuration:
2570
+ - \\"unsafe\\" fields are experimental and may change or break at any time."
2571
+ `);
2572
+ expect(diagnostics.hasErrors()).toBe(false);
2573
+ });
2574
+
2575
+ it("should not error if at least unsafe.metadata is defined", () => {
2576
+ const { diagnostics } = normalizeAndValidateConfig(
2577
+ { unsafe: { metadata: {} } } as unknown as RawConfig,
2578
+ undefined,
2579
+ { env: undefined }
2580
+ );
2581
+
2582
+ expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
2208
2583
  "Processing wrangler configuration:
2209
- - The field \\"unsafe\\" is missing the required \\"bindings\\" property."
2584
+ - \\"unsafe\\" fields are experimental and may change or break at any time."
2210
2585
  `);
2586
+ expect(diagnostics.hasErrors()).toBe(false);
2211
2587
  });
2212
2588
 
2213
2589
  it("should error if unsafe.bindings is an object", () => {
@@ -2317,6 +2693,74 @@ describe("normalizeAndValidateConfig()", () => {
2317
2693
  - binding should have a string \\"type\\" field."
2318
2694
  `);
2319
2695
  });
2696
+
2697
+ it("should error if unsafe.metadata is an array", () => {
2698
+ const { diagnostics } = normalizeAndValidateConfig(
2699
+ { unsafe: { metadata: [] } } as unknown as RawConfig,
2700
+ undefined,
2701
+ { env: undefined }
2702
+ );
2703
+
2704
+ expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
2705
+ "Processing wrangler configuration:
2706
+ - \\"unsafe\\" fields are experimental and may change or break at any time."
2707
+ `);
2708
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
2709
+ "Processing wrangler configuration:
2710
+ - The field \\"unsafe.metadata\\" should be an object but got []."
2711
+ `);
2712
+ });
2713
+
2714
+ it("should error if unsafe.metadata is a string", () => {
2715
+ const { diagnostics } = normalizeAndValidateConfig(
2716
+ { unsafe: { metadata: "BAD" } } as unknown as RawConfig,
2717
+ undefined,
2718
+ { env: undefined }
2719
+ );
2720
+
2721
+ expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
2722
+ "Processing wrangler configuration:
2723
+ - \\"unsafe\\" fields are experimental and may change or break at any time."
2724
+ `);
2725
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
2726
+ "Processing wrangler configuration:
2727
+ - The field \\"unsafe.metadata\\" should be an object but got \\"BAD\\"."
2728
+ `);
2729
+ });
2730
+
2731
+ it("should error if unsafe.metadata is a number", () => {
2732
+ const { diagnostics } = normalizeAndValidateConfig(
2733
+ { unsafe: { metadata: 999 } } as unknown as RawConfig,
2734
+ undefined,
2735
+ { env: undefined }
2736
+ );
2737
+
2738
+ expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
2739
+ "Processing wrangler configuration:
2740
+ - \\"unsafe\\" fields are experimental and may change or break at any time."
2741
+ `);
2742
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
2743
+ "Processing wrangler configuration:
2744
+ - The field \\"unsafe.metadata\\" should be an object but got 999."
2745
+ `);
2746
+ });
2747
+
2748
+ it("should error if unsafe.metadata is null", () => {
2749
+ const { diagnostics } = normalizeAndValidateConfig(
2750
+ { unsafe: { metadata: null } } as unknown as RawConfig,
2751
+ undefined,
2752
+ { env: undefined }
2753
+ );
2754
+
2755
+ expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
2756
+ "Processing wrangler configuration:
2757
+ - \\"unsafe\\" fields are experimental and may change or break at any time."
2758
+ `);
2759
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
2760
+ "Processing wrangler configuration:
2761
+ - The field \\"unsafe.metadata\\" should be an object but got null."
2762
+ `);
2763
+ });
2320
2764
  });
2321
2765
 
2322
2766
  describe("(deprecated)", () => {
@@ -2490,7 +2934,6 @@ describe("normalizeAndValidateConfig()", () => {
2490
2934
  minify: false,
2491
2935
  node_compat: false,
2492
2936
  first_party_worker: false,
2493
- metadata_binding: "METADATA_ENV",
2494
2937
  logpush: false,
2495
2938
  };
2496
2939
  const rawConfig: RawConfig = {
@@ -2515,7 +2958,6 @@ describe("normalizeAndValidateConfig()", () => {
2515
2958
  minify: true,
2516
2959
  node_compat: true,
2517
2960
  first_party_worker: true,
2518
- metadata_binding: "METADATA_RAW",
2519
2961
  logpush: true,
2520
2962
  env: {
2521
2963
  ENV1: rawEnv,
@@ -2700,16 +3142,20 @@ describe("normalizeAndValidateConfig()", () => {
2700
3142
  };
2701
3143
  const kv_namespaces: RawConfig["kv_namespaces"] = [];
2702
3144
  const r2_buckets: RawConfig["r2_buckets"] = [];
2703
- const unsafe: RawConfig["unsafe"] = { bindings: [] };
2704
- const metadata_binding: RawConfig["metadata_binding"] = "";
3145
+ const analytics_engine_datasets: RawConfig["analytics_engine_datasets"] =
3146
+ [];
3147
+ const unsafe: RawConfig["unsafe"] = {
3148
+ bindings: undefined,
3149
+ metadata: undefined,
3150
+ };
2705
3151
  const rawConfig: RawConfig = {
2706
3152
  define,
2707
3153
  vars,
2708
3154
  durable_objects,
2709
3155
  kv_namespaces,
2710
3156
  r2_buckets,
3157
+ analytics_engine_datasets,
2711
3158
  unsafe,
2712
- metadata_binding,
2713
3159
  env: {
2714
3160
  ENV1: {},
2715
3161
  },
@@ -2728,6 +3174,7 @@ describe("normalizeAndValidateConfig()", () => {
2728
3174
  durable_objects,
2729
3175
  kv_namespaces,
2730
3176
  r2_buckets,
3177
+ analytics_engine_datasets,
2731
3178
  unsafe,
2732
3179
  })
2733
3180
  );
@@ -2739,9 +3186,6 @@ describe("normalizeAndValidateConfig()", () => {
2739
3186
  - \\"vars\\" exists at the top level, but not on \\"env.ENV1\\".
2740
3187
  This is not what you probably want, since \\"vars\\" is not inherited by environments.
2741
3188
  Please add \\"vars\\" to \\"env.ENV1\\".
2742
- - \\"metadata_binding\\" exists at the top level, but not on \\"env.ENV1\\".
2743
- This is not what you probably want, since \\"metadata_binding\\" is not inherited by environments.
2744
- Please add \\"metadata_binding\\" to \\"env.ENV1\\".
2745
3189
  - \\"define\\" exists at the top level, but not on \\"env.ENV1\\".
2746
3190
  This is not what you probably want, since \\"define\\" is not inherited by environments.
2747
3191
  Please add \\"define\\" to \\"env.ENV1\\".
@@ -2754,6 +3198,9 @@ describe("normalizeAndValidateConfig()", () => {
2754
3198
  - \\"r2_buckets\\" exists at the top level, but not on \\"env.ENV1\\".
2755
3199
  This is not what you probably want, since \\"r2_buckets\\" is not inherited by environments.
2756
3200
  Please add \\"r2_buckets\\" to \\"env.ENV1\\".
3201
+ - \\"analytics_engine_datasets\\" exists at the top level, but not on \\"env.ENV1\\".
3202
+ This is not what you probably want, since \\"analytics_engine_datasets\\" is not inherited by environments.
3203
+ Please add \\"analytics_engine_datasets\\" to \\"env.ENV1\\".
2757
3204
  - \\"unsafe\\" exists at the top level, but not on \\"env.ENV1\\".
2758
3205
  This is not what you probably want, since \\"unsafe\\" is not inherited by environments.
2759
3206
  Please add \\"unsafe\\" to \\"env.ENV1\\"."
@@ -3455,6 +3902,114 @@ describe("normalizeAndValidateConfig()", () => {
3455
3902
  });
3456
3903
  });
3457
3904
 
3905
+ describe("[analytics_engine_datasets]", () => {
3906
+ it("should error if analytics_engine_datasets is an object", () => {
3907
+ const { diagnostics } = normalizeAndValidateConfig(
3908
+ {
3909
+ env: { ENV1: { analytics_engine_datasets: {} } },
3910
+ } as unknown as RawConfig,
3911
+ undefined,
3912
+ { env: "ENV1" }
3913
+ );
3914
+
3915
+ expect(diagnostics.hasWarnings()).toBe(false);
3916
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
3917
+ "Processing wrangler configuration:
3918
+
3919
+ - \\"env.ENV1\\" environment configuration
3920
+ - The field \\"env.ENV1.analytics_engine_datasets\\" should be an array but got {}."
3921
+ `);
3922
+ });
3923
+
3924
+ it("should error if analytics_engine_datasets is a string", () => {
3925
+ const { diagnostics } = normalizeAndValidateConfig(
3926
+ {
3927
+ env: { ENV1: { analytics_engine_datasets: "BAD" } },
3928
+ } as unknown as RawConfig,
3929
+ undefined,
3930
+ { env: "ENV1" }
3931
+ );
3932
+
3933
+ expect(diagnostics.hasWarnings()).toBe(false);
3934
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
3935
+ "Processing wrangler configuration:
3936
+
3937
+ - \\"env.ENV1\\" environment configuration
3938
+ - The field \\"env.ENV1.analytics_engine_datasets\\" should be an array but got \\"BAD\\"."
3939
+ `);
3940
+ });
3941
+
3942
+ it("should error if analytics_engine_datasets is a number", () => {
3943
+ const { diagnostics } = normalizeAndValidateConfig(
3944
+ {
3945
+ env: { ENV1: { analytics_engine_datasets: 999 } },
3946
+ } as unknown as RawConfig,
3947
+ undefined,
3948
+ { env: "ENV1" }
3949
+ );
3950
+
3951
+ expect(diagnostics.hasWarnings()).toBe(false);
3952
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
3953
+ "Processing wrangler configuration:
3954
+
3955
+ - \\"env.ENV1\\" environment configuration
3956
+ - The field \\"env.ENV1.analytics_engine_datasets\\" should be an array but got 999."
3957
+ `);
3958
+ });
3959
+
3960
+ it("should error if analytics_engine_datasets is null", () => {
3961
+ const { diagnostics } = normalizeAndValidateConfig(
3962
+ {
3963
+ env: { ENV1: { analytics_engine_datasets: null } },
3964
+ } as unknown as RawConfig,
3965
+ undefined,
3966
+ { env: "ENV1" }
3967
+ );
3968
+
3969
+ expect(diagnostics.hasWarnings()).toBe(false);
3970
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
3971
+ "Processing wrangler configuration:
3972
+
3973
+ - \\"env.ENV1\\" environment configuration
3974
+ - The field \\"env.ENV1.analytics_engine_datasets\\" should be an array but got null."
3975
+ `);
3976
+ });
3977
+
3978
+ it("should error if analytics_engine_datasets.bindings are not valid", () => {
3979
+ const { diagnostics } = normalizeAndValidateConfig(
3980
+ {
3981
+ env: {
3982
+ ENV1: {
3983
+ analytics_engine_datasets: [
3984
+ {},
3985
+ { binding: 2333, dataset: 2444 },
3986
+ {
3987
+ binding: "AE_BINDING_2",
3988
+ dataset: 2555,
3989
+ },
3990
+ { binding: "AE_BINDING_1", dataset: "" },
3991
+ ],
3992
+ },
3993
+ },
3994
+ } as unknown as RawConfig,
3995
+ undefined,
3996
+ { env: "ENV1" }
3997
+ );
3998
+
3999
+ expect(diagnostics.hasWarnings()).toBe(false);
4000
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
4001
+ "Processing wrangler configuration:
4002
+
4003
+ - \\"env.ENV1\\" environment configuration
4004
+ - \\"env.ENV1.analytics_engine_datasets[0]\\" bindings should have a string \\"binding\\" field but got {}.
4005
+ - \\"env.ENV1.analytics_engine_datasets[1]\\" bindings should have a string \\"binding\\" field but got {\\"binding\\":2333,\\"dataset\\":2444}.
4006
+ - \\"env.ENV1.analytics_engine_datasets[1]\\" bindings should, optionally, have a string \\"dataset\\" field but got {\\"binding\\":2333,\\"dataset\\":2444}.
4007
+ - \\"env.ENV1.analytics_engine_datasets[2]\\" bindings should, optionally, have a string \\"dataset\\" field but got {\\"binding\\":\\"AE_BINDING_2\\",\\"dataset\\":2555}.
4008
+ - \\"env.ENV1.analytics_engine_datasets[3]\\" bindings should, optionally, have a string \\"dataset\\" field but got {\\"binding\\":\\"AE_BINDING_1\\",\\"dataset\\":\\"\\"}."
4009
+ `);
4010
+ });
4011
+ });
4012
+
3458
4013
  describe("[unsafe.bindings]", () => {
3459
4014
  it("should error if unsafe is an array", () => {
3460
4015
  const { diagnostics } = normalizeAndValidateConfig(
@@ -3540,7 +4095,7 @@ describe("normalizeAndValidateConfig()", () => {
3540
4095
  `);
3541
4096
  });
3542
4097
 
3543
- it("should error if unsafe.bindings is not defined", () => {
4098
+ it("should error if neither unsafe.bindings nor unsafe.metadata are defined", () => {
3544
4099
  const { diagnostics } = normalizeAndValidateConfig(
3545
4100
  { env: { ENV1: { unsafe: {} } } } as unknown as RawConfig,
3546
4101
  undefined,
@@ -3554,11 +4109,47 @@ describe("normalizeAndValidateConfig()", () => {
3554
4109
  - \\"unsafe\\" fields are experimental and may change or break at any time."
3555
4110
  `);
3556
4111
  expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
4112
+ "Processing wrangler configuration:
4113
+
4114
+ - \\"env.ENV1\\" environment configuration
4115
+ - The field \\"env.ENV1.unsafe\\" should contain at least one of \\"bindings\\" or \\"metadata\\" properties but got {}."
4116
+ `);
4117
+ });
4118
+
4119
+ it("should not error if at least unsafe.bindings is undefined", () => {
4120
+ const { diagnostics } = normalizeAndValidateConfig(
4121
+ {
4122
+ env: { ENV1: { unsafe: { bindings: [] } } },
4123
+ } as unknown as RawConfig,
4124
+ undefined,
4125
+ { env: "ENV1" }
4126
+ );
4127
+
4128
+ expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
3557
4129
  "Processing wrangler configuration:
3558
4130
 
3559
4131
  - \\"env.ENV1\\" environment configuration
3560
- - The field \\"env.ENV1.unsafe\\" is missing the required \\"bindings\\" property."
4132
+ - \\"unsafe\\" fields are experimental and may change or break at any time."
3561
4133
  `);
4134
+ expect(diagnostics.hasErrors()).toBe(false);
4135
+ });
4136
+
4137
+ it("should not error if at least unsafe.metadata is undefined", () => {
4138
+ const { diagnostics } = normalizeAndValidateConfig(
4139
+ {
4140
+ env: { ENV1: { unsafe: { metadata: {} } } },
4141
+ } as unknown as RawConfig,
4142
+ undefined,
4143
+ { env: "ENV1" }
4144
+ );
4145
+
4146
+ expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
4147
+ "Processing wrangler configuration:
4148
+
4149
+ - \\"env.ENV1\\" environment configuration
4150
+ - \\"unsafe\\" fields are experimental and may change or break at any time."
4151
+ `);
4152
+ expect(diagnostics.hasErrors()).toBe(false);
3562
4153
  });
3563
4154
 
3564
4155
  it("should error if unsafe.bindings is an object", () => {
@@ -3700,6 +4291,153 @@ describe("normalizeAndValidateConfig()", () => {
3700
4291
  - binding should have a string \\"type\\" field."
3701
4292
  `);
3702
4293
  });
4294
+
4295
+ it("should error if unsafe.metadata is an array", () => {
4296
+ const { diagnostics } = normalizeAndValidateConfig(
4297
+ {
4298
+ env: { ENV1: { unsafe: { metadata: [] } } },
4299
+ } as unknown as RawConfig,
4300
+ undefined,
4301
+ { env: "ENV1" }
4302
+ );
4303
+
4304
+ expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
4305
+ "Processing wrangler configuration:
4306
+
4307
+ - \\"env.ENV1\\" environment configuration
4308
+ - \\"unsafe\\" fields are experimental and may change or break at any time."
4309
+ `);
4310
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
4311
+ "Processing wrangler configuration:
4312
+
4313
+ - \\"env.ENV1\\" environment configuration
4314
+ - The field \\"env.ENV1.unsafe.metadata\\" should be an object but got []."
4315
+ `);
4316
+ });
4317
+
4318
+ it("should error if unsafe.metadata is a string", () => {
4319
+ const { diagnostics } = normalizeAndValidateConfig(
4320
+ {
4321
+ env: { ENV1: { unsafe: { metadata: "BAD" } } },
4322
+ } as unknown as RawConfig,
4323
+ undefined,
4324
+ { env: "ENV1" }
4325
+ );
4326
+
4327
+ expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
4328
+ "Processing wrangler configuration:
4329
+
4330
+ - \\"env.ENV1\\" environment configuration
4331
+ - \\"unsafe\\" fields are experimental and may change or break at any time."
4332
+ `);
4333
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
4334
+ "Processing wrangler configuration:
4335
+
4336
+ - \\"env.ENV1\\" environment configuration
4337
+ - The field \\"env.ENV1.unsafe.metadata\\" should be an object but got \\"BAD\\"."
4338
+ `);
4339
+ });
4340
+
4341
+ it("should error if unsafe.metadata is a number", () => {
4342
+ const { diagnostics } = normalizeAndValidateConfig(
4343
+ {
4344
+ env: { ENV1: { unsafe: { metadata: 999 } } },
4345
+ } as unknown as RawConfig,
4346
+ undefined,
4347
+ { env: "ENV1" }
4348
+ );
4349
+
4350
+ expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
4351
+ "Processing wrangler configuration:
4352
+
4353
+ - \\"env.ENV1\\" environment configuration
4354
+ - \\"unsafe\\" fields are experimental and may change or break at any time."
4355
+ `);
4356
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
4357
+ "Processing wrangler configuration:
4358
+
4359
+ - \\"env.ENV1\\" environment configuration
4360
+ - The field \\"env.ENV1.unsafe.metadata\\" should be an object but got 999."
4361
+ `);
4362
+ });
4363
+
4364
+ it("should error if unsafe.metadata is null", () => {
4365
+ const { diagnostics } = normalizeAndValidateConfig(
4366
+ {
4367
+ env: { ENV1: { unsafe: { metadata: null } } },
4368
+ } as unknown as RawConfig,
4369
+ undefined,
4370
+ { env: "ENV1" }
4371
+ );
4372
+
4373
+ expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
4374
+ "Processing wrangler configuration:
4375
+
4376
+ - \\"env.ENV1\\" environment configuration
4377
+ - \\"unsafe\\" fields are experimental and may change or break at any time."
4378
+ `);
4379
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
4380
+ "Processing wrangler configuration:
4381
+
4382
+ - \\"env.ENV1\\" environment configuration
4383
+ - The field \\"env.ENV1.unsafe.metadata\\" should be an object but got null."
4384
+ `);
4385
+ });
4386
+ });
4387
+
4388
+ describe("[tail_consumers]", () => {
4389
+ it("should error if tail_consumers is not an array", () => {
4390
+ const { diagnostics } = normalizeAndValidateConfig(
4391
+ {
4392
+ tail_consumers: "this sure isn't an array",
4393
+ } as unknown as RawConfig,
4394
+ undefined,
4395
+ { env: undefined }
4396
+ );
4397
+
4398
+ expect(diagnostics.hasWarnings()).toBe(false);
4399
+ expect(diagnostics.hasErrors()).toBe(true);
4400
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
4401
+ "Processing wrangler configuration:
4402
+ - Expected \\"tail_consumers\\" to be an array but got \\"this sure isn't an array\\"."
4403
+ `);
4404
+ });
4405
+
4406
+ it("should error on invalid tail_consumers", () => {
4407
+ const { diagnostics } = normalizeAndValidateConfig(
4408
+ {
4409
+ tail_consumers: [
4410
+ "some string",
4411
+ 456,
4412
+ {
4413
+ binding: "other",
4414
+ namespace: "shape",
4415
+ },
4416
+ { service: {} },
4417
+ {
4418
+ service: 123,
4419
+ environment: "prod",
4420
+ },
4421
+ // these are valid
4422
+ { service: "tail_listener" },
4423
+ { service: "listener_two", environment: "production" },
4424
+ ],
4425
+ } as unknown as RawConfig,
4426
+ undefined,
4427
+ { env: undefined }
4428
+ );
4429
+
4430
+ expect(diagnostics.hasWarnings()).toBe(false);
4431
+ expect(diagnostics.hasErrors()).toBe(true);
4432
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
4433
+ "Processing wrangler configuration:
4434
+ - \\"tail_consumers[0]\\" should be an object but got \\"some string\\".
4435
+ - \\"tail_consumers[1]\\" should be an object but got 456.
4436
+ - \\"tail_consumers[2].service\\" is a required field.
4437
+ - Expected \\"tail_consumers[3].service\\" to be of type string but got {}.
4438
+ - Expected \\"tail_consumers[4].service\\" to be of type string but got 123."
4439
+ `);
4440
+ });
3703
4441
  });
3704
4442
 
3705
4443
  describe("(deprecated)", () => {
@@ -3979,45 +4717,6 @@ describe("normalizeAndValidateConfig()", () => {
3979
4717
  expect(result2.diagnostics.hasWarnings()).toBe(false);
3980
4718
  });
3981
4719
  });
3982
- describe("[metadata_binding]", () => {
3983
- it("should error if metadata_binding is a number", () => {
3984
- const { diagnostics } = normalizeAndValidateConfig(
3985
- { env: { ENV1: { metadata_binding: 999 } } } as unknown as RawConfig,
3986
- undefined,
3987
- { env: "ENV1" }
3988
- );
3989
-
3990
- expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
3991
- "Processing wrangler configuration:
3992
- "
3993
- `);
3994
- expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
3995
- "Processing wrangler configuration:
3996
-
3997
- - \\"env.ENV1\\" environment configuration
3998
- - Expected \\"metadata_binding\\" to be of type string but got 999."
3999
- `);
4000
- });
4001
-
4002
- it("should error if metadata_binding is null", () => {
4003
- const { diagnostics } = normalizeAndValidateConfig(
4004
- { env: { ENV1: { metadata_binding: null } } } as unknown as RawConfig,
4005
- undefined,
4006
- { env: "ENV1" }
4007
- );
4008
-
4009
- expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
4010
- "Processing wrangler configuration:
4011
- "
4012
- `);
4013
- expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
4014
- "Processing wrangler configuration:
4015
-
4016
- - \\"env.ENV1\\" environment configuration
4017
- - Expected \\"metadata_binding\\" to be of type string but got null."
4018
- `);
4019
- });
4020
- });
4021
4720
  });
4022
4721
  });
4023
4722