wrangler 0.0.0-e30d1013 → 0.0.0-e311bbbf

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 (236) hide show
  1. package/README.md +3 -3
  2. package/bin/wrangler.js +30 -10
  3. package/miniflare-dist/index.mjs +126 -74
  4. package/package.json +26 -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 +567 -39
  9. package/src/__tests__/d1/d1.test.ts +82 -0
  10. package/src/__tests__/d1/execute.test.ts +66 -0
  11. package/src/__tests__/d1/migrate.test.ts +157 -0
  12. package/src/__tests__/d1/splitter.test.ts +255 -0
  13. package/src/__tests__/delete.test.ts +91 -49
  14. package/src/__tests__/deployments.test.ts +330 -52
  15. package/src/__tests__/dev.test.tsx +198 -90
  16. package/src/__tests__/generate.test.ts +8 -5
  17. package/src/__tests__/guess-worker-format.test.ts +35 -0
  18. package/src/__tests__/helpers/end-event-loop.ts +6 -0
  19. package/src/__tests__/helpers/mock-auth-domain.ts +20 -0
  20. package/src/__tests__/helpers/mock-console.ts +6 -0
  21. package/src/__tests__/helpers/mock-dialogs.ts +71 -85
  22. package/src/__tests__/helpers/mock-get-pages-upload-token.ts +25 -0
  23. package/src/__tests__/helpers/mock-get-zone-from-host.ts +8 -5
  24. package/src/__tests__/helpers/mock-known-routes.ts +7 -2
  25. package/src/__tests__/helpers/mock-kv.ts +29 -16
  26. package/src/__tests__/helpers/mock-oauth-flow.ts +103 -92
  27. package/src/__tests__/helpers/mock-process.ts +8 -13
  28. package/src/__tests__/helpers/mock-set-timeout.ts +16 -0
  29. package/src/__tests__/helpers/msw/blob-worker.cjs +19 -0
  30. package/src/__tests__/helpers/msw/handlers/deployments.ts +127 -26
  31. package/src/__tests__/helpers/msw/handlers/namespaces.ts +18 -41
  32. package/src/__tests__/helpers/msw/handlers/oauth.ts +13 -18
  33. package/src/__tests__/helpers/msw/handlers/r2.ts +14 -34
  34. package/src/__tests__/helpers/msw/handlers/script.ts +9 -28
  35. package/src/__tests__/helpers/msw/handlers/user.ts +13 -24
  36. package/src/__tests__/helpers/msw/handlers/zones.ts +6 -8
  37. package/src/__tests__/helpers/msw/index.ts +32 -1
  38. package/src/__tests__/helpers/msw/read-file-sync.js +61 -0
  39. package/src/__tests__/helpers/string-dynamic-values-matcher.ts +28 -0
  40. package/src/__tests__/helpers/worker-scripts/parent-worker.js +4 -1
  41. package/src/__tests__/index.test.ts +90 -72
  42. package/src/__tests__/init.test.ts +927 -631
  43. package/src/__tests__/jest.setup.ts +19 -42
  44. package/src/__tests__/kv.test.ts +355 -231
  45. package/src/__tests__/logger.test.ts +68 -2
  46. package/src/__tests__/logout.test.ts +47 -0
  47. package/src/__tests__/metrics.test.ts +94 -51
  48. package/src/__tests__/middleware.scheduled.test.ts +42 -32
  49. package/src/__tests__/middleware.test.ts +212 -122
  50. package/src/__tests__/mtls-certificates.test.ts +588 -0
  51. package/src/__tests__/pages/deployment-list.test.ts +80 -0
  52. package/src/__tests__/pages/functions-build.test.ts +452 -0
  53. package/src/__tests__/pages/pages.test.ts +81 -0
  54. package/src/__tests__/pages/project-create.test.ts +63 -0
  55. package/src/__tests__/pages/project-list.test.ts +110 -0
  56. package/src/__tests__/pages/project-upload.test.ts +500 -0
  57. package/src/__tests__/pages/publish.test.ts +2864 -0
  58. package/src/__tests__/pages-deployment-tail.test.ts +955 -0
  59. package/src/__tests__/parse.test.ts +106 -0
  60. package/src/__tests__/publish.test.ts +1620 -830
  61. package/src/__tests__/pubsub.test.ts +226 -100
  62. package/src/__tests__/queues.test.ts +237 -76
  63. package/src/__tests__/r2.test.ts +37 -56
  64. package/src/__tests__/secret.test.ts +179 -130
  65. package/src/__tests__/tail.test.ts +267 -109
  66. package/src/__tests__/test-old-node-version.js +3 -3
  67. package/src/__tests__/traverse-module-graph.test.ts +220 -0
  68. package/src/__tests__/tsconfig-sanity.ts +12 -0
  69. package/src/__tests__/tsconfig.json +8 -0
  70. package/src/__tests__/tsconfig.tsbuildinfo +1 -0
  71. package/src/__tests__/type-generation.test.ts +11 -2
  72. package/src/__tests__/user.test.ts +35 -72
  73. package/src/__tests__/whoami.test.tsx +8 -114
  74. package/src/__tests__/worker-namespace.test.ts +83 -69
  75. package/src/api/dev.ts +168 -60
  76. package/src/api/index.ts +10 -1
  77. package/src/api/mtls-certificate.ts +148 -0
  78. package/src/api/pages/create-worker-bundle-contents.ts +76 -0
  79. package/src/api/pages/index.ts +5 -0
  80. package/src/api/pages/publish.tsx +355 -0
  81. package/src/bundle.ts +221 -128
  82. package/src/cfetch/index.ts +0 -2
  83. package/src/cfetch/internal.ts +16 -21
  84. package/src/cli.ts +4 -4
  85. package/src/config/config.ts +14 -7
  86. package/src/config/environment.ts +73 -16
  87. package/src/config/index.ts +99 -30
  88. package/src/config/validation-helpers.ts +1 -1
  89. package/src/config/validation.ts +248 -9
  90. package/src/create-worker-preview.ts +19 -8
  91. package/src/create-worker-upload-form.ts +48 -5
  92. package/src/d1/backups.tsx +43 -42
  93. package/src/d1/create.tsx +26 -21
  94. package/src/d1/{delete.tsx → delete.ts} +11 -14
  95. package/src/d1/execute.tsx +183 -170
  96. package/src/d1/index.ts +7 -6
  97. package/src/d1/list.tsx +22 -10
  98. package/src/d1/migrations/apply.tsx +212 -0
  99. package/src/d1/migrations/create.tsx +79 -0
  100. package/src/d1/migrations/helpers.ts +172 -0
  101. package/src/d1/migrations/index.ts +3 -0
  102. package/src/d1/migrations/list.tsx +95 -0
  103. package/src/d1/migrations/options.ts +23 -0
  104. package/src/d1/options.ts +3 -3
  105. package/src/d1/splitter.ts +161 -0
  106. package/src/d1/{types.tsx → types.ts} +1 -0
  107. package/src/d1/utils.ts +2 -1
  108. package/src/delete.ts +8 -16
  109. package/src/deployments.ts +333 -42
  110. package/src/deprecated/index.ts +8 -302
  111. package/src/dev/dev.tsx +57 -44
  112. package/src/dev/local.tsx +248 -74
  113. package/src/dev/remote.tsx +32 -3
  114. package/src/dev/start-server.ts +81 -66
  115. package/src/dev/use-esbuild.ts +17 -18
  116. package/src/dev/validate-dev-props.ts +9 -0
  117. package/src/{dev-registry.tsx → dev-registry.ts} +2 -1
  118. package/src/dev.tsx +178 -117
  119. package/src/dialogs.ts +136 -0
  120. package/src/dispatch-namespace.ts +21 -17
  121. package/src/docs/index.ts +97 -0
  122. package/src/entry.ts +19 -11
  123. package/src/environment-variables/factory.ts +88 -0
  124. package/src/environment-variables/misc-variables.ts +30 -0
  125. package/src/generate/index.ts +298 -0
  126. package/src/git-client.ts +15 -4
  127. package/src/{index.tsx → index.ts} +134 -59
  128. package/src/init.ts +249 -187
  129. package/src/inspect.ts +16 -1
  130. package/src/jest.d.ts +4 -0
  131. package/src/kv/index.ts +15 -17
  132. package/src/logger.ts +32 -5
  133. package/src/metrics/metrics-config.ts +3 -3
  134. package/src/metrics/send-event.ts +6 -1
  135. package/src/miniflare-cli/assets.ts +64 -28
  136. package/src/miniflare-cli/index.ts +4 -7
  137. package/src/miniflare-cli/tsconfig.json +9 -0
  138. package/src/miniflare-cli/tsconfig.tsbuildinfo +1 -0
  139. package/src/miniflare-cli/types.ts +11 -0
  140. package/src/module-collection.ts +91 -25
  141. package/src/mtls-certificate/cli.ts +155 -0
  142. package/src/pages/build.ts +410 -0
  143. package/src/pages/buildFunctions.ts +140 -0
  144. package/src/pages/deployment-tails.ts +281 -0
  145. package/src/pages/deployments.tsx +11 -33
  146. package/src/pages/{dev.tsx → dev.ts} +147 -139
  147. package/src/pages/errors.ts +1 -0
  148. package/src/pages/functions/buildPlugin.ts +61 -54
  149. package/src/pages/functions/buildWorker.ts +159 -33
  150. package/src/pages/functions/tsconfig.json +8 -0
  151. package/src/pages/functions/tsconfig.tsbuildinfo +1 -0
  152. package/src/pages/{functions.tsx → functions.ts} +8 -4
  153. package/src/pages/{index.tsx → index.ts} +11 -3
  154. package/src/pages/projects.tsx +12 -22
  155. package/src/pages/prompt-select-project.tsx +31 -0
  156. package/src/pages/publish.tsx +46 -225
  157. package/src/pages/types.ts +6 -9
  158. package/src/pages/upload.tsx +58 -14
  159. package/src/pages/utils.ts +12 -1
  160. package/src/parse.ts +23 -1
  161. package/src/paths.ts +1 -1
  162. package/src/proxy.ts +5 -0
  163. package/src/publish/index.ts +20 -16
  164. package/src/publish/publish.ts +71 -46
  165. package/src/pubsub/{pubsub-commands.tsx → pubsub-commands.ts} +19 -20
  166. package/src/queues/cli/commands/consumer/add.ts +24 -24
  167. package/src/queues/cli/commands/consumer/index.ts +3 -6
  168. package/src/queues/cli/commands/consumer/remove.ts +11 -18
  169. package/src/queues/cli/commands/create.ts +8 -8
  170. package/src/queues/cli/commands/delete.ts +8 -8
  171. package/src/queues/cli/commands/index.ts +6 -4
  172. package/src/queues/cli/commands/list.ts +8 -8
  173. package/src/queues/client.ts +1 -0
  174. package/src/queues/utils.ts +18 -0
  175. package/src/r2/helpers.ts +1 -0
  176. package/src/r2/index.ts +28 -28
  177. package/src/secret/index.ts +14 -16
  178. package/src/{sites.tsx → sites.ts} +1 -1
  179. package/src/tail/createTail.ts +94 -3
  180. package/src/tail/filters.ts +1 -1
  181. package/src/tail/index.ts +8 -11
  182. package/src/tail/printing.ts +15 -0
  183. package/src/traverse-module-graph.ts +53 -0
  184. package/src/tsconfig-sanity.ts +16 -0
  185. package/src/type-generation.ts +28 -20
  186. package/src/user/access.ts +0 -1
  187. package/src/user/auth-variables.ts +113 -0
  188. package/src/user/choose-account.tsx +1 -31
  189. package/src/user/index.ts +0 -1
  190. package/src/user/{user.tsx → user.ts} +123 -82
  191. package/src/utils/render.ts +93 -0
  192. package/src/{whoami.tsx → whoami.ts} +37 -71
  193. package/src/worker.ts +30 -1
  194. package/src/yargs-types.ts +22 -5
  195. package/templates/__tests__/tsconfig-sanity.ts +12 -0
  196. package/templates/__tests__/tsconfig.json +8 -0
  197. package/templates/__tests__/tsconfig.tsbuildinfo +1 -0
  198. package/templates/checked-fetch.js +9 -3
  199. package/templates/d1-beta-facade.js +51 -3
  200. package/templates/facade.d.ts +14 -0
  201. package/templates/first-party-worker-module-facade.ts +4 -3
  202. package/templates/format-dev-errors.ts +8 -6
  203. package/templates/gitignore +1 -0
  204. package/templates/init-tests/test-jest-new-worker.js +3 -5
  205. package/templates/init-tests/test-vitest-new-worker.js +3 -5
  206. package/templates/init-tests/test-vitest-new-worker.ts +25 -0
  207. package/templates/middleware/loader-modules.ts +2 -2
  208. package/templates/middleware/loader-sw.ts +51 -31
  209. package/templates/middleware/middleware-miniflare3-json-error.ts +32 -0
  210. package/templates/new-worker.ts +3 -0
  211. package/templates/pages-dev-pipeline.ts +4 -1
  212. package/templates/pages-shim.ts +4 -1
  213. package/templates/pages-template-plugin.ts +19 -10
  214. package/templates/serve-static-assets.ts +17 -14
  215. package/templates/service-bindings-module-facade.js +1 -0
  216. package/templates/tsconfig-sanity.ts +11 -0
  217. package/templates/tsconfig.init.json +105 -0
  218. package/templates/tsconfig.json +5 -103
  219. package/templates/tsconfig.tsbuildinfo +1 -0
  220. package/wrangler-dist/cli.d.ts +820 -70
  221. package/wrangler-dist/cli.js +68161 -78780
  222. package/wrangler-dist/wasm-sync.wasm +0 -0
  223. package/src/__tests__/api-devregistry.test.js +0 -56
  224. package/src/__tests__/d1.test.ts +0 -74
  225. package/src/__tests__/dialogs.test.tsx +0 -40
  226. package/src/__tests__/helpers/mock-cfetch.ts +0 -333
  227. package/src/__tests__/pages.test.ts +0 -2387
  228. package/src/d1/migrations.tsx +0 -446
  229. package/src/dialogs.tsx +0 -169
  230. package/src/environment-variables.ts +0 -50
  231. package/src/pages/build.tsx +0 -270
  232. package/src/user/env-vars.ts +0 -46
  233. package/templates/experimental-local-cache-stubs.js +0 -27
  234. /package/src/{bundle-reporter.tsx → bundle-reporter.ts} +0 -0
  235. /package/src/dev/{get-local-persistence-path.tsx → get-local-persistence-path.ts} +0 -0
  236. /package/src/pages/{hash.tsx → hash.ts} +0 -0
package/src/dev/local.tsx CHANGED
@@ -1,13 +1,15 @@
1
1
  import assert from "node:assert";
2
2
  import { fork } from "node:child_process";
3
3
  import { realpathSync } from "node:fs";
4
- import { readFile, writeFile } from "node:fs/promises";
4
+ import { readFile } from "node:fs/promises";
5
5
  import path from "node:path";
6
6
  import chalk from "chalk";
7
7
  import getPort from "get-port";
8
8
  import { npxImport } from "npx-import";
9
9
  import { useState, useEffect, useRef } from "react";
10
10
  import onExit from "signal-exit";
11
+ import { fetch } from "undici";
12
+ import { performApiFetch } from "../cfetch/internal";
11
13
  import { registerWorker } from "../dev-registry";
12
14
  import useInspector from "../inspect";
13
15
  import { logger } from "../logger";
@@ -17,9 +19,11 @@ import {
17
19
  } from "../module-collection";
18
20
  import { getBasePath } from "../paths";
19
21
  import { waitForPortToBeAvailable } from "../proxy";
22
+ import { requireAuth } from "../user";
20
23
  import type { Config } from "../config";
21
24
  import type { WorkerRegistry } from "../dev-registry";
22
- import type { EnablePagesAssetsServiceBindingOptions } from "../miniflare-cli";
25
+ import type { LoggerLevel } from "../logger";
26
+ import type { EnablePagesAssetsServiceBindingOptions } from "../miniflare-cli/types";
23
27
  import type { AssetPaths } from "../sites";
24
28
  import type {
25
29
  CfWorkerInit,
@@ -38,6 +42,8 @@ import type { EsbuildBundle } from "./use-esbuild";
38
42
  import type {
39
43
  Miniflare as Miniflare3Type,
40
44
  MiniflareOptions as Miniflare3Options,
45
+ Log as Miniflare3LogType,
46
+ CloudflareFetch,
41
47
  } from "@miniflare/tre";
42
48
  import type { MiniflareOptions } from "miniflare";
43
49
  import type { ChildProcess } from "node:child_process";
@@ -52,8 +58,8 @@ export interface LocalProps {
52
58
  bindings: CfWorkerInit["bindings"];
53
59
  workerDefinitions: WorkerRegistry | undefined;
54
60
  assetPaths: AssetPaths | undefined;
55
- port: number;
56
- ip: string;
61
+ initialPort: number;
62
+ initialIp: string;
57
63
  rules: Config["rules"];
58
64
  inspectorPort: number;
59
65
  localPersistencePath: string | null;
@@ -64,18 +70,31 @@ export interface LocalProps {
64
70
  localUpstream: string | undefined;
65
71
  inspect: boolean;
66
72
  onReady: ((ip: string, port: number) => void) | undefined;
67
- logPrefix?: string;
68
73
  enablePagesAssetsServiceBinding?: EnablePagesAssetsServiceBindingOptions;
69
74
  testScheduled?: boolean;
70
- experimentalLocal?: boolean;
75
+ experimentalLocal: boolean | undefined;
76
+ accountId: string | undefined; // Account ID? In local mode??? :exploding_head:
77
+ experimentalLocalRemoteKv: boolean | undefined;
71
78
  }
72
79
 
80
+ type InspectorJSON = {
81
+ id: string;
82
+ title: string;
83
+ type: "node";
84
+ description: string;
85
+ webSocketDebuggerUrl: string;
86
+ devtoolsFrontendUrl: string;
87
+ devtoolsFrontendUrlCompat: string;
88
+ faviconUrl: string;
89
+ url: string;
90
+ }[];
91
+
73
92
  export function Local(props: LocalProps) {
74
93
  const { inspectorUrl } = useLocalWorker(props);
75
94
  useInspector({
76
95
  inspectorUrl,
77
96
  port: props.inspectorPort,
78
- logToTerminal: false,
97
+ logToTerminal: props.experimentalLocal ?? false,
79
98
  });
80
99
  return null;
81
100
  }
@@ -90,21 +109,21 @@ function useLocalWorker({
90
109
  bindings,
91
110
  workerDefinitions,
92
111
  assetPaths,
93
- port,
94
- inspectorPort,
112
+ initialPort,
95
113
  rules,
96
114
  localPersistencePath,
97
115
  liveReload,
98
- ip,
116
+ initialIp,
99
117
  crons,
100
118
  queueConsumers,
101
119
  localProtocol,
102
120
  localUpstream,
103
121
  inspect,
104
122
  onReady,
105
- logPrefix,
106
123
  enablePagesAssetsServiceBinding,
107
124
  experimentalLocal,
125
+ accountId,
126
+ experimentalLocalRemoteKv,
108
127
  }: LocalProps) {
109
128
  // TODO: pass vars via command line
110
129
  const local = useRef<ChildProcess>();
@@ -113,6 +132,11 @@ function useLocalWorker({
113
132
  const removeExperimentalLocalSignalExitListener = useRef<() => void>();
114
133
  const [inspectorUrl, setInspectorUrl] = useState<string | undefined>();
115
134
 
135
+ // Our inspector proxy server will be binding to `LocalProps`'s `inspectorPort`.
136
+ // If we attempted to bind Node.js/workerd to the same inspector port, we'd get a port already in use error.
137
+ // Therefore, generate a new random port for our runtime's to bind their inspector service to.
138
+ const runtimeInspectorPortRef = useRef<number>();
139
+
116
140
  useEffect(() => {
117
141
  if (bindings.services && bindings.services.length > 0) {
118
142
  logger.warn(
@@ -138,22 +162,6 @@ function useLocalWorker({
138
162
  async function startLocalWorker() {
139
163
  if (!bundle || !format) return;
140
164
 
141
- // port for the worker
142
- await waitForPortToBeAvailable(port, {
143
- retryPeriod: 200,
144
- timeout: 2000,
145
- abortSignal: abortController.signal,
146
- });
147
-
148
- // In local mode, we want to copy all referenced modules into
149
- // the output bundle directory before starting up
150
- for (const module of bundle.modules) {
151
- await writeFile(
152
- path.join(path.dirname(bundle.path), module.name),
153
- module.content
154
- );
155
- }
156
-
157
165
  const scriptPath = realpathSync(bundle.path);
158
166
 
159
167
  const upstream =
@@ -176,12 +184,15 @@ function useLocalWorker({
176
184
  bundle,
177
185
  });
178
186
 
187
+ runtimeInspectorPortRef.current ??= await getPort();
188
+ const runtimeInspectorPort = runtimeInspectorPortRef.current;
189
+
179
190
  const { forkOptions, miniflareCLIPath, options } = setupMiniflareOptions({
180
191
  workerName,
181
- port,
192
+ port: initialPort,
182
193
  scriptPath,
183
194
  localProtocol,
184
- ip,
195
+ ip: initialIp,
185
196
  format,
186
197
  rules,
187
198
  compatibilityDate,
@@ -203,23 +214,37 @@ function useLocalWorker({
203
214
  dataBlobBindings,
204
215
  crons,
205
216
  upstream,
206
- logPrefix,
207
217
  workerDefinitions,
208
218
  enablePagesAssetsServiceBinding,
209
219
  });
210
220
 
211
221
  if (experimentalLocal) {
212
- const mf3Options = await transformLocalOptions(options, format, bundle);
222
+ const log = await buildMiniflare3Logger();
223
+ const mf3Options = await transformMf2OptionsToMf3Options({
224
+ miniflare2Options: options,
225
+ format,
226
+ bundle,
227
+ log,
228
+ enablePagesAssetsServiceBinding,
229
+ kvNamespaces: bindings?.kv_namespaces,
230
+ r2Buckets: bindings?.r2_buckets,
231
+ d1Databases: bindings?.d1_databases,
232
+ authenticatedAccountId: accountId,
233
+ kvRemote: experimentalLocalRemoteKv,
234
+ inspectorPort: runtimeInspectorPort,
235
+ });
236
+
213
237
  const current = experimentalLocalRef.current;
238
+
214
239
  if (current === undefined) {
215
240
  // If we don't have an active Miniflare instance, create a new one
216
- const Miniflare = await getMiniflare3Constructor();
241
+ const { Miniflare } = await getMiniflare3();
217
242
  if (abortController.signal.aborted) return;
218
243
  const mf = new Miniflare(mf3Options);
219
244
  experimentalLocalRef.current = mf;
220
245
  removeExperimentalLocalSignalExitListener.current = onExit(() => {
221
246
  logger.log("⎔ Shutting down experimental local server.");
222
- mf.dispose();
247
+ void mf.dispose();
223
248
  experimentalLocalRef.current = undefined;
224
249
  });
225
250
  await mf.ready;
@@ -230,19 +255,61 @@ function useLocalWorker({
230
255
  logger.log("⎔ Reloading experimental local server.");
231
256
  await current.setOptions(mf3Options);
232
257
  }
258
+
259
+ try {
260
+ // fetch the inspector JSON response from the DevTools Inspector protocol
261
+ const inspectorJSONArr = (await (
262
+ await fetch(`http://127.0.0.1:${runtimeInspectorPort}/json`)
263
+ ).json()) as InspectorJSON;
264
+
265
+ const foundInspectorURL = inspectorJSONArr?.find((inspectorJSON) =>
266
+ inspectorJSON.id.startsWith("core:user")
267
+ )?.webSocketDebuggerUrl;
268
+ if (foundInspectorURL === undefined) {
269
+ setInspectorUrl(undefined);
270
+ } else {
271
+ const url = new URL(foundInspectorURL);
272
+ // Force inspector URL to be different on each reload so `useEffect`
273
+ // in `useInspector` is re-run to connect to newly restarted
274
+ // `workerd` server when updating options. Can't use a query param
275
+ // here as that seems to cause an infinite connection loop, can't
276
+ // use a hash as those are forbidden by `ws`, so username it is.
277
+ url.username = `${Date.now()}-${Math.floor(
278
+ Math.random() * Number.MAX_SAFE_INTEGER
279
+ )}`;
280
+ setInspectorUrl(url.toString());
281
+ }
282
+ } catch (error: unknown) {
283
+ logger.error("Error attempting to retrieve Debugger URL:", error);
284
+ }
285
+
233
286
  return;
234
287
  }
235
288
 
236
- const nodeOptions = setupNodeOptions({ inspect, ip, inspectorPort });
289
+ // Wait for the Worker port to be available. We don't want to do this in experimental local
290
+ // mode, as we only `dispose()` the Miniflare 3 instance, and shutdown the server when
291
+ // unmounting the component, not when props change. If we did, we'd just timeout every time.
292
+ await waitForPortToBeAvailable(initialPort, {
293
+ retryPeriod: 200,
294
+ timeout: 2000,
295
+ abortSignal: abortController.signal,
296
+ });
297
+
298
+ const nodeOptions = setupNodeOptions({
299
+ inspect,
300
+ inspectorPort: runtimeInspectorPort,
301
+ });
237
302
  logger.log("⎔ Starting a local server...");
238
303
 
304
+ const hasColourSupport =
305
+ chalk.supportsColor.hasBasic && process.env.FORCE_COLOR !== "0";
239
306
  const child = (local.current = fork(miniflareCLIPath, forkOptions, {
240
307
  cwd: path.dirname(scriptPath),
241
308
  execArgv: nodeOptions,
242
309
  stdio: "pipe",
243
310
  env: {
244
311
  ...process.env,
245
- FORCE_COLOR: chalk.supportsColor.hasBasic ? "1" : undefined,
312
+ FORCE_COLOR: hasColourSupport ? "1" : undefined,
246
313
  },
247
314
  }));
248
315
 
@@ -254,21 +321,21 @@ function useLocalWorker({
254
321
  await registerWorker(workerName, {
255
322
  protocol: localProtocol,
256
323
  mode: "local",
257
- port,
258
- host: ip,
324
+ port: message.port,
325
+ host: initialIp,
259
326
  durableObjects: internalDurableObjects.map((binding) => ({
260
327
  name: binding.name,
261
328
  className: binding.class_name,
262
329
  })),
263
330
  ...(message.durableObjectsPort
264
331
  ? {
265
- durableObjectsHost: ip,
332
+ durableObjectsHost: initialIp,
266
333
  durableObjectsPort: message.durableObjectsPort,
267
334
  }
268
335
  : {}),
269
336
  });
270
337
  }
271
- onReady?.(ip, message.mfPort);
338
+ onReady?.(initialIp, message.port);
272
339
  }
273
340
  });
274
341
 
@@ -320,7 +387,13 @@ function useLocalWorker({
320
387
  }
321
388
 
322
389
  startLocalWorker().catch((err) => {
323
- logger.error("local worker:", err);
390
+ if (err.code === "ERR_RUNTIME_FAILURE") {
391
+ // Don't log a full verbose stack-trace when Miniflare 3's workerd instance fails to start.
392
+ // workerd will log its own errors, and our stack trace won't have any useful information.
393
+ logger.error(err.message);
394
+ } else {
395
+ logger.error("local worker:", err);
396
+ }
324
397
  });
325
398
 
326
399
  return () => {
@@ -337,9 +410,8 @@ function useLocalWorker({
337
410
  bundle,
338
411
  workerName,
339
412
  format,
340
- port,
341
- inspectorPort,
342
- ip,
413
+ initialPort,
414
+ initialIp,
343
415
  queueConsumers,
344
416
  bindings.queues,
345
417
  bindings.durable_objects,
@@ -363,10 +435,11 @@ function useLocalWorker({
363
435
  localProtocol,
364
436
  localUpstream,
365
437
  inspect,
366
- logPrefix,
367
438
  onReady,
368
439
  enablePagesAssetsServiceBinding,
369
440
  experimentalLocal,
441
+ accountId,
442
+ experimentalLocalRemoteKv,
370
443
  ]);
371
444
 
372
445
  // Rather than disposing the Miniflare instance on every reload, only dispose
@@ -500,7 +573,6 @@ interface SetupMiniflareOptionsProps {
500
573
  dataBlobBindings: Record<string, string>;
501
574
  crons: Config["triggers"]["crons"];
502
575
  upstream: string | undefined;
503
- logPrefix: string | undefined;
504
576
  workerDefinitions: WorkerRegistry | undefined;
505
577
  enablePagesAssetsServiceBinding?: EnablePagesAssetsServiceBindingOptions;
506
578
  }
@@ -532,7 +604,6 @@ export function setupMiniflareOptions({
532
604
  dataBlobBindings,
533
605
  crons,
534
606
  upstream,
535
- logPrefix,
536
607
  workerDefinitions,
537
608
  enablePagesAssetsServiceBinding,
538
609
  }: SetupMiniflareOptionsProps): {
@@ -645,7 +716,6 @@ export function setupMiniflareOptions({
645
716
  crons,
646
717
  upstream,
647
718
  logLevel: logger.loggerLevel,
648
- logOptions: logPrefix ? { prefix: logPrefix } : undefined,
649
719
  enablePagesAssetsServiceBinding,
650
720
  };
651
721
  // The path to the Miniflare CLI assumes that this file is being run from
@@ -665,11 +735,9 @@ export function setupMiniflareOptions({
665
735
 
666
736
  export function setupNodeOptions({
667
737
  inspect,
668
- ip,
669
738
  inspectorPort,
670
739
  }: {
671
740
  inspect: boolean;
672
- ip: string;
673
741
  inspectorPort: number;
674
742
  }) {
675
743
  const nodeOptions = [
@@ -678,31 +746,123 @@ export function setupNodeOptions({
678
746
  // "--log=VERBOSE", // uncomment this to Miniflare to log "everything"!
679
747
  ];
680
748
  if (inspect) {
681
- nodeOptions.push("--inspect=" + `${ip}:${inspectorPort}`); // start Miniflare listening for a debugger to attach
749
+ nodeOptions.push("--inspect=" + `127.0.0.1:${inspectorPort}`); // start Miniflare listening for a debugger to attach
682
750
  }
683
751
  return nodeOptions;
684
752
  }
685
753
 
686
- function arrayToObject(values: string[] = []): Record<string, string> {
687
- return Object.fromEntries(values.map((value) => [value, value]));
754
+ export interface SetupMiniflare3Options {
755
+ // Regular Miniflare 2 options to transform
756
+ miniflare2Options: MiniflareOptions;
757
+ // Miniflare 3 requires all modules to be manually specified
758
+ format: CfScriptFormat;
759
+ bundle: EsbuildBundle;
760
+
761
+ // Miniflare's logger
762
+ log: Miniflare3LogType;
763
+
764
+ enablePagesAssetsServiceBinding?: EnablePagesAssetsServiceBindingOptions;
765
+
766
+ // Miniflare 3 accepts namespace/bucket names in addition to binding names.
767
+ // This means multiple workers persisting to the same location can have
768
+ // different binding names for the same namespace/bucket. Therefore, we need
769
+ // the full KV/R2 arrays. This is also required for remote KV storage, as
770
+ // we need actual namespace IDs to connect to.
771
+ kvNamespaces: CfKvNamespace[] | undefined;
772
+ r2Buckets: CfR2Bucket[] | undefined;
773
+ d1Databases: CfD1Database[] | undefined;
774
+
775
+ // Account ID to use for authenticated Cloudflare fetch. If true, prompt
776
+ // user for ID if multiple available.
777
+ authenticatedAccountId: string | true | undefined;
778
+ // Whether to read/write from/to real KV namespaces
779
+ kvRemote: boolean | undefined;
780
+
781
+ // Port to start DevTools inspector server on
782
+ inspectorPort: number;
688
783
  }
689
784
 
690
- export async function transformLocalOptions(
691
- mf2Options: MiniflareOptions,
692
- format: CfScriptFormat,
693
- bundle: EsbuildBundle
694
- ): Promise<Miniflare3Options> {
785
+ export async function buildMiniflare3Logger(): Promise<Miniflare3LogType> {
786
+ const { Log, NoOpLog, LogLevel } = await getMiniflare3();
787
+
788
+ let level = logger.loggerLevel.toUpperCase() as Uppercase<LoggerLevel>;
789
+ if (level === "LOG") level = "INFO";
790
+ const logLevel = LogLevel[level];
791
+
792
+ return logLevel === LogLevel.NONE ? new NoOpLog() : new Log(logLevel);
793
+ }
794
+
795
+ export async function transformMf2OptionsToMf3Options({
796
+ miniflare2Options,
797
+ format,
798
+ bundle,
799
+ log,
800
+ enablePagesAssetsServiceBinding,
801
+ kvNamespaces,
802
+ r2Buckets,
803
+ d1Databases,
804
+ authenticatedAccountId,
805
+ kvRemote,
806
+ inspectorPort,
807
+ }: SetupMiniflare3Options): Promise<Miniflare3Options> {
808
+ // Build authenticated Cloudflare API fetch function if required
809
+ let cloudflareFetch: CloudflareFetch | undefined;
810
+ if (kvRemote && authenticatedAccountId !== undefined) {
811
+ const { Response: Miniflare3Response } = await getMiniflare3();
812
+ const preferredAccountId =
813
+ authenticatedAccountId === true ? undefined : authenticatedAccountId;
814
+ const accountId = await requireAuth({ account_id: preferredAccountId });
815
+ cloudflareFetch = async (resource, searchParams, init) => {
816
+ resource = `/accounts/${accountId}/${resource}`;
817
+ const response = await performApiFetch(resource, init, searchParams);
818
+ return new Miniflare3Response(response.body, response);
819
+ };
820
+ }
821
+
695
822
  const options: Miniflare3Options = {
696
- ...mf2Options,
823
+ ...miniflare2Options,
697
824
  // Miniflare 3 distinguishes between binding name and namespace/bucket IDs.
698
- // For now, just use the same value as we did in Miniflare 2.
699
- // TODO: use defined KV preview ID if any
700
- kvNamespaces: arrayToObject(mf2Options.kvNamespaces),
701
- r2Buckets: arrayToObject(mf2Options.r2Buckets),
702
- inspectorPort: await getPort({ port: 9229 }),
825
+ kvNamespaces: Object.fromEntries(
826
+ kvNamespaces?.map(({ binding, id }) => [binding, id]) ?? []
827
+ ),
828
+ r2Buckets: Object.fromEntries(
829
+ r2Buckets?.map(({ binding, bucket_name }) => [binding, bucket_name]) ?? []
830
+ ),
831
+ d1Databases: Object.fromEntries(
832
+ d1Databases?.map(({ binding, database_id, preview_database_id }) => [
833
+ binding,
834
+ preview_database_id ?? database_id,
835
+ ]) ?? []
836
+ ),
837
+ inspectorPort,
703
838
  verbose: true,
839
+ cloudflareFetch,
840
+ log,
704
841
  };
705
842
 
843
+ if (enablePagesAssetsServiceBinding !== undefined) {
844
+ // `../miniflare-cli/assets` dynamically imports`@cloudflare/pages-shared/environment-polyfills`.
845
+ // `@cloudflare/pages-shared/environment-polyfills/types.ts` defines `global`
846
+ // augmentations that pollute the `import`-site's typing environment.
847
+ //
848
+ // We `require` instead of `import`ing here to avoid polluting the main
849
+ // `wrangler` TypeScript project with the `global` augmentations. This
850
+ // relies on the fact that `require` is untyped.
851
+ //
852
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
853
+ const generateASSETSBinding = require("../miniflare-cli/assets").default;
854
+ options.serviceBindings = {
855
+ ...options.serviceBindings,
856
+ ASSETS: (await generateASSETSBinding({
857
+ log,
858
+ ...enablePagesAssetsServiceBinding,
859
+ tre: true,
860
+ // We can get rid of this `any` easily once we do experimental-local/tre by default
861
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
862
+ })) as any,
863
+ };
864
+ }
865
+
706
866
  if (format === "modules") {
707
867
  // Manually specify all modules from the bundle. If we didn't do this,
708
868
  // Miniflare 3 would try collect them automatically again itself.
@@ -713,34 +873,48 @@ export async function transformLocalOptions(
713
873
  const root = path.dirname(bundle.path);
714
874
 
715
875
  assert.strictEqual(bundle.type, "esm");
876
+ // Required for source mapped paths to resolve correctly
877
+ options.modulesRoot = root;
716
878
  options.modules = [
717
879
  // Entrypoint
718
880
  {
719
881
  type: "ESModule",
720
- path: path.relative(root, bundle.path),
882
+ path: bundle.path,
721
883
  contents: await readFile(bundle.path, "utf-8"),
722
884
  },
723
885
  // Misc (WebAssembly, etc, ...)
724
886
  ...bundle.modules.map((module) => ({
725
887
  type: ModuleTypeToRuleType[module.type ?? "esm"],
726
- path: module.name,
888
+ path: path.resolve(root, module.name),
727
889
  contents: module.content,
728
890
  })),
729
891
  ];
730
892
  }
731
893
 
894
+ if (kvRemote) {
895
+ // `kvPersist` is always assigned a truthy value in `setupMiniflareOptions`
896
+ assert(options.kvPersist);
897
+ const kvRemoteCache =
898
+ options.kvPersist === true
899
+ ? // If storing in temporary directory, find this path from the bundle
900
+ // output path
901
+ path.join(path.dirname(bundle.path), ".mf", "kv-remote")
902
+ : // Otherwise, `kvPersist` looks like `.../kv`, so rewrite it to
903
+ // `kv-remote` since the expected metadata format for remote storage
904
+ // is different to local
905
+ path.join(path.dirname(options.kvPersist), "kv-remote");
906
+ options.kvPersist = `remote:?cache=${encodeURIComponent(kvRemoteCache)}`;
907
+ }
908
+
732
909
  return options;
733
910
  }
734
911
 
735
912
  // Caching of the `npx-import`ed `@miniflare/tre` package
736
913
  // eslint-disable-next-line @typescript-eslint/consistent-type-imports
737
- let Miniflare: typeof import("@miniflare/tre").Miniflare;
738
- export async function getMiniflare3Constructor(): Promise<typeof Miniflare> {
739
- if (Miniflare === undefined) {
740
- ({ Miniflare } = await npxImport<
741
- // eslint-disable-next-line @typescript-eslint/consistent-type-imports
742
- typeof import("@miniflare/tre")
743
- >("@miniflare/tre@3.0.0-next.5"));
744
- }
745
- return Miniflare;
914
+ let miniflare3Module: typeof import("@miniflare/tre");
915
+ export async function getMiniflare3(): Promise<
916
+ // eslint-disable-next-line @typescript-eslint/consistent-type-imports
917
+ typeof import("@miniflare/tre")
918
+ > {
919
+ return (miniflare3Module ??= await npxImport("@miniflare/tre@3.0.0-next.12"));
746
920
  }
@@ -1,5 +1,7 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
+ import { Text } from "ink";
4
+ import SelectInput from "ink-select-input";
3
5
  import React, { useState, useEffect, useRef } from "react";
4
6
  import { useErrorHandler } from "react-error-boundary";
5
7
  import { printBundleSize } from "../bundle-reporter";
@@ -10,9 +12,9 @@ import {
10
12
  import useInspector from "../inspect";
11
13
  import { logger } from "../logger";
12
14
  import { startPreviewServer, usePreviewServer } from "../proxy";
15
+ import { helpIfErrorIsSizeOrScriptStartup } from "../publish/publish";
13
16
  import { syncAssets } from "../sites";
14
17
  import {
15
- ChooseAccount,
16
18
  getAccountChoices,
17
19
  requireApiToken,
18
20
  saveAccountToCache,
@@ -302,13 +304,13 @@ export function useWorker(
302
304
  // TODO: Once we get service bindings working in the
303
305
  // edge preview server, we can define remote dev service bindings
304
306
  // and you can uncomment this code.
305
- // https://github.com/cloudflare/wrangler2/issues/1182
307
+ // https://github.com/cloudflare/workers-sdk/issues/1182
306
308
 
307
309
  /*
308
310
  if (name) {
309
311
  await registerWorker(name, {
310
312
  mode: "remote",
311
- // upstream protocol is always https (https://github.com/cloudflare/wrangler2/issues/583)
313
+ // upstream protocol is always https (https://github.com/cloudflare/workers-sdk/issues/583)
312
314
  protocol: "https",
313
315
  port: undefined,
314
316
  host: workerPreviewToken.host,
@@ -493,6 +495,7 @@ export async function getRemotePreviewToken(props: RemoteProps) {
493
495
  // code 10049 happens when the preview token expires
494
496
  logger.log("Preview token expired, restart server to fetch a new one");
495
497
  } else {
498
+ helpIfErrorIsSizeOrScriptStartup(err, props.bundle?.dependencies || {});
496
499
  logger.error("Error on remote worker:", err);
497
500
  }
498
501
  }
@@ -601,3 +604,29 @@ function getWorkerAccountAndContext(props: {
601
604
 
602
605
  return { workerAccount, workerContext };
603
606
  }
607
+
608
+ /**
609
+ * A component that allows the user to select from a list of available accounts.
610
+ */
611
+ function ChooseAccount(props: {
612
+ accounts: ChooseAccountItem[];
613
+ onSelect: (account: { name: string; id: string }) => void;
614
+ onError: (error: Error) => void;
615
+ }) {
616
+ return (
617
+ <>
618
+ <Text bold>Select an account from below:</Text>
619
+ <SelectInput
620
+ items={props.accounts.map((item) => ({
621
+ key: item.id,
622
+ label: item.name,
623
+ value: item,
624
+ }))}
625
+ onSelect={(item) => {
626
+ logger.log(`Using account: "${item.value.name} - ${item.value.id}"`);
627
+ props.onSelect({ id: item.value.id, name: item.value.name });
628
+ }}
629
+ />
630
+ </>
631
+ );
632
+ }