wrangler 2.1.14 → 2.2.0

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 (75) hide show
  1. package/miniflare-dist/index.mjs +3 -1
  2. package/package.json +2 -1
  3. package/src/__tests__/access.test.ts +25 -0
  4. package/src/__tests__/api-dev.test.ts +1 -1
  5. package/src/__tests__/api-devregistry.test.js +2 -2
  6. package/src/__tests__/configuration.test.ts +119 -2
  7. package/src/__tests__/d1.test.ts +2 -0
  8. package/src/__tests__/deployments.test.ts +22 -22
  9. package/src/__tests__/dev.test.tsx +167 -15
  10. package/src/__tests__/helpers/msw/handlers/access.ts +13 -0
  11. package/src/__tests__/helpers/msw/handlers/deployments.ts +22 -43
  12. package/src/__tests__/helpers/msw/handlers/zones.ts +22 -0
  13. package/src/__tests__/helpers/msw/index.ts +4 -0
  14. package/src/__tests__/index.test.ts +42 -33
  15. package/src/__tests__/init.test.ts +88 -4
  16. package/src/__tests__/jest.setup.ts +11 -0
  17. package/src/__tests__/kv.test.ts +400 -400
  18. package/src/__tests__/pages.test.ts +140 -28
  19. package/src/__tests__/publish.test.ts +1161 -647
  20. package/src/__tests__/pubsub.test.ts +3 -0
  21. package/src/__tests__/queues.test.ts +371 -0
  22. package/src/__tests__/r2.test.ts +57 -52
  23. package/src/__tests__/worker-namespace.test.ts +15 -10
  24. package/src/bundle-reporter.tsx +41 -2
  25. package/src/bundle.ts +59 -30
  26. package/src/cli.ts +0 -1
  27. package/src/config/environment.ts +50 -0
  28. package/src/config/index.ts +41 -0
  29. package/src/config/validation.ts +173 -0
  30. package/src/create-worker-preview.ts +10 -3
  31. package/src/create-worker-upload-form.ts +12 -0
  32. package/src/d1/backups.tsx +11 -5
  33. package/src/d1/execute.tsx +52 -47
  34. package/src/d1/index.ts +2 -1
  35. package/src/delete.ts +7 -10
  36. package/src/deployments.ts +73 -0
  37. package/src/deprecated/index.ts +9 -24
  38. package/src/dev/dev-vars.ts +11 -8
  39. package/src/dev/dev.tsx +12 -0
  40. package/src/dev/local.tsx +26 -0
  41. package/src/dev/remote.tsx +2 -0
  42. package/src/dev/start-server.ts +7 -0
  43. package/src/dev/use-esbuild.ts +12 -5
  44. package/src/dev.tsx +12 -9
  45. package/src/dispatch-namespace.ts +4 -3
  46. package/src/index.tsx +61 -45
  47. package/src/init.ts +4 -4
  48. package/src/inspect.ts +21 -1
  49. package/src/is-interactive.ts +4 -0
  50. package/src/kv/index.ts +5 -54
  51. package/src/logger.ts +12 -0
  52. package/src/pages/constants.ts +2 -0
  53. package/src/pages/upload.tsx +42 -15
  54. package/src/proxy.ts +38 -6
  55. package/src/publish/index.ts +11 -8
  56. package/src/publish/publish.ts +151 -30
  57. package/src/pubsub/pubsub-commands.tsx +3 -2
  58. package/src/queues/cli/commands/consumer/add.ts +71 -0
  59. package/src/queues/cli/commands/consumer/index.ts +22 -0
  60. package/src/queues/cli/commands/consumer/remove.ts +38 -0
  61. package/src/queues/cli/commands/create.ts +25 -0
  62. package/src/queues/cli/commands/delete.ts +26 -0
  63. package/src/queues/cli/commands/index.ts +33 -0
  64. package/src/queues/cli/commands/list.ts +25 -0
  65. package/src/queues/client.ts +135 -0
  66. package/src/secret/index.ts +14 -39
  67. package/src/tail/index.ts +5 -8
  68. package/src/user/access.ts +69 -0
  69. package/src/worker.ts +7 -0
  70. package/src/yargs-types.ts +15 -2
  71. package/src/zones.ts +31 -5
  72. package/templates/pages-template-plugin.ts +4 -0
  73. package/templates/pages-template-worker.ts +21 -4
  74. package/wrangler-dist/cli.d.ts +42 -0
  75. package/wrangler-dist/cli.js +4559 -3228
@@ -1,7 +1,6 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { mkdir } from "node:fs/promises";
3
3
  import path from "node:path";
4
- import chalk from "chalk";
5
4
  import { render, Static, Text } from "ink";
6
5
  import Table from "ink-table";
7
6
  import { npxImport } from "npx-import";
@@ -52,7 +51,7 @@ type QueryResult = {
52
51
  query?: string;
53
52
  };
54
53
  // Max number of bytes to send in a single /execute call
55
- const QUERY_LIMIT = 1_000_000; // 1MB
54
+ const QUERY_LIMIT = 10_000;
56
55
 
57
56
  export function Options(yargs: Argv): Argv<ExecuteArgs> {
58
57
  return Name(yargs)
@@ -119,28 +118,23 @@ export const Handler = withConfig<ExecuteArgs>(
119
118
  if (!response) return;
120
119
 
121
120
  if (isInteractive) {
121
+ // Render table if single result
122
122
  render(
123
123
  <Static items={response}>
124
124
  {(result) => {
125
- const { results, duration, query } = result;
125
+ // batch results
126
+ if (!Array.isArray(result)) {
127
+ const { results, query } = result;
126
128
 
127
- if (Array.isArray(results) && results.length > 0) {
128
- const shortQuery = shorten(query, 48);
129
- return (
130
- <>
131
- {shortQuery ? <Text dimColor>{shortQuery}</Text> : null}
132
- <Table data={results}></Table>
133
- </>
134
- );
135
- } else {
136
- const shortQuery = shorten(query, 24);
137
- return (
138
- <Text>
139
- Executed{" "}
140
- {shortQuery ? <Text dimColor>{shortQuery}</Text> : "command"}{" "}
141
- in {duration}ms.
142
- </Text>
143
- );
129
+ if (Array.isArray(results) && results.length > 0) {
130
+ const shortQuery = shorten(query, 48);
131
+ return (
132
+ <>
133
+ {shortQuery ? <Text dimColor>{shortQuery}</Text> : null}
134
+ <Table data={results}></Table>
135
+ </>
136
+ );
137
+ }
144
138
  }
145
139
  }}
146
140
  </Static>
@@ -187,7 +181,7 @@ async function executeLocally(
187
181
  await mkdir(dbDir, { recursive: true });
188
182
  }
189
183
 
190
- console.log(`Loading DB at ${readableRelative(dbPath)}`);
184
+ logger.log(`🌀 Loading DB at ${readableRelative(dbPath)}`);
191
185
  const db = await createSQLiteDB(dbPath);
192
186
 
193
187
  const results: QueryResult[] = [];
@@ -206,17 +200,14 @@ async function executeRemotely(
206
200
  batches: string[]
207
201
  ) {
208
202
  if (batches.length > 1) {
209
- const warning =
210
- chalk.red(`WARNING! `) +
211
- `Too much SQL to send at once, this execution will be sent as ${batches.length} batches.`;
203
+ const warning = `⚠️ Too much SQL to send at once, this execution will be sent as ${batches.length} batches.`;
212
204
 
213
205
  if (isInteractive) {
214
206
  const ok = await confirm(
215
- `${warning}\nNOTE: each batch is sent individually and may leave your DB in an unexpected state if a later batch fails.\n${chalk.green(
216
- `Make sure you have a recent backup.`
217
- )}\nOk to proceed?`
207
+ `${warning}\nℹ️ Each batch is sent individually and may leave your DB in an unexpected state if a later batch fails.\n⚠️ Make sure you have a recent backup. Ok to proceed?`
218
208
  );
219
209
  if (!ok) return null;
210
+ logger.log(`🌀 Let's go`);
220
211
  } else {
221
212
  console.error(warning);
222
213
  }
@@ -230,7 +221,7 @@ async function executeRemotely(
230
221
  );
231
222
 
232
223
  if (isInteractive) {
233
- console.log(`Executing on ${name} (${db.uuid}):`);
224
+ logger.log(`🌀 Executing on ${name} (${db.uuid}):`);
234
225
  } else {
235
226
  // Pipe to error so we don't break jq
236
227
  console.error(`Executing on ${name} (${db.uuid}):`);
@@ -238,24 +229,37 @@ async function executeRemotely(
238
229
 
239
230
  const results: QueryResult[] = [];
240
231
  for (const sql of batches) {
241
- results.push(
242
- ...(await fetchResult<QueryResult[]>(
243
- `/accounts/${accountId}/d1/database/${db.uuid}/query`,
244
- {
245
- method: "POST",
246
- headers: {
247
- "Content-Type": "application/json",
248
- },
249
- body: JSON.stringify({ sql }),
250
- }
251
- ))
232
+ const result = await fetchResult<QueryResult[]>(
233
+ `/accounts/${accountId}/d1/database/${db.uuid}/query`,
234
+ {
235
+ method: "POST",
236
+ headers: {
237
+ "Content-Type": "application/json",
238
+ },
239
+ body: JSON.stringify({ sql }),
240
+ }
252
241
  );
242
+ result.map(logResult);
243
+ results.push(...result);
253
244
  }
254
245
  return results;
255
246
  }
256
247
 
248
+ function logResult(r: QueryResult | QueryResult[]) {
249
+ logger.log(
250
+ `🚣 Executed ${Array.isArray(r) ? r.length : "1"} command(s) in ${
251
+ Array.isArray(r)
252
+ ? r
253
+ .map((d: QueryResult) => d.duration)
254
+ .reduce((a: number, b: number) => a + b, 0)
255
+ : r.duration
256
+ }ms`
257
+ );
258
+ }
259
+
257
260
  function splitSql(splitter: (query: SQLQuery) => SQLQuery[], sql: SQLQuery) {
258
261
  // We have no interpolations, so convert everything to text
262
+ logger.log(`🌀 Mapping SQL input into an array of statements`);
259
263
  return splitter(sql).map(
260
264
  (q) =>
261
265
  q.format({
@@ -267,16 +271,17 @@ function splitSql(splitter: (query: SQLQuery) => SQLQuery[], sql: SQLQuery) {
267
271
 
268
272
  function batchSplit(splitter: typeof splitSqlQuery, sql: SQLQuery) {
269
273
  const queries = splitSql(splitter, sql);
270
-
274
+ logger.log(`🌀 Parsing ${queries.length} statements`);
271
275
  const batches: string[] = [];
272
- for (const query of queries) {
273
- const last = batches.at(-1);
274
- if (!last || last.length + query.length > QUERY_LIMIT) {
275
- batches.push(query);
276
- } else {
277
- batches.splice(-1, 1, [last, query].join("; "));
278
- }
276
+ const nbatches = Math.floor(queries.length / QUERY_LIMIT);
277
+ for (let i = 0; i <= nbatches; i++) {
278
+ batches.push(
279
+ queries.slice(i * QUERY_LIMIT, (i + 1) * QUERY_LIMIT).join("; ")
280
+ );
279
281
  }
282
+ logger.log(
283
+ `🌀 We are sending ${batches.length} batch(es) to D1 (limited to ${QUERY_LIMIT} statements per batch)`
284
+ );
280
285
  return batches;
281
286
  }
282
287
 
package/src/d1/index.ts CHANGED
@@ -4,9 +4,10 @@ import * as Delete from "./delete";
4
4
  import * as Execute from "./execute";
5
5
  import * as List from "./list";
6
6
  import { d1BetaWarning } from "./utils";
7
+ import type { CommonYargsOptions } from "../yargs-types";
7
8
  import type { Argv } from "yargs";
8
9
 
9
- export const d1 = (yargs: Argv) => {
10
+ export const d1 = (yargs: Argv<CommonYargsOptions>) => {
10
11
  return (
11
12
  yargs
12
13
  .command("list", "List D1 databases", List.Options, List.Handler)
package/src/delete.ts CHANGED
@@ -11,17 +11,14 @@ import * as metrics from "./metrics";
11
11
  import { requireAuth } from "./user";
12
12
  import { getScriptName, printWranglerBanner } from "./index";
13
13
  import type { ConfigPath } from "./index";
14
- import type { YargsOptionsToInterface } from "./yargs-types";
15
- import type { Argv, ArgumentsCamelCase } from "yargs";
14
+ import type {
15
+ CommonYargsOptions,
16
+ YargsOptionsToInterface,
17
+ } from "./yargs-types";
18
+ import type { Argv } from "yargs";
16
19
 
17
- export function deleteOptions(yargs: Argv) {
20
+ export function deleteOptions(yargs: Argv<CommonYargsOptions>) {
18
21
  return yargs
19
- .option("env", {
20
- type: "string",
21
- requiresArg: true,
22
- describe: "Perform on a specific environment",
23
- alias: "e",
24
- })
25
22
  .positional("script", {
26
23
  describe: "The path to an entry point for your worker",
27
24
  type: "string",
@@ -45,7 +42,7 @@ export function deleteOptions(yargs: Argv) {
45
42
 
46
43
  type DeleteArgs = YargsOptionsToInterface<typeof deleteOptions>;
47
44
 
48
- export async function deleteHandler(args: ArgumentsCamelCase<DeleteArgs>) {
45
+ export async function deleteHandler(args: DeleteArgs) {
49
46
  await printWranglerBanner();
50
47
 
51
48
  const configPath =
@@ -0,0 +1,73 @@
1
+ import { fetchResult } from "./cfetch";
2
+ import { logger } from "./logger";
3
+ import type { ServiceMetadataRes } from "./init";
4
+
5
+ export type DeploymentListRes = {
6
+ latest: {
7
+ id: string;
8
+ number: string;
9
+ metadata: {
10
+ author_id: string;
11
+ author_email: string;
12
+ source: "api" | "dash" | "wrangler" | "terraform" | "other";
13
+ created_on: string;
14
+ modified_on: string;
15
+ };
16
+ resources: {
17
+ script: string;
18
+ bindings: unknown[];
19
+ };
20
+ };
21
+ items: {
22
+ id: string;
23
+ number: string;
24
+ metadata: {
25
+ author_id: string;
26
+ author_email: string;
27
+ source: "api" | "dash" | "wrangler" | "terraform" | "other";
28
+ created_on: string;
29
+ modified_on: string;
30
+ };
31
+ }[];
32
+ };
33
+
34
+ export async function deployments(
35
+ accountId: string,
36
+ scriptName: string | undefined
37
+ ) {
38
+ const scriptMetadata = await fetchResult<ServiceMetadataRes>(
39
+ `/accounts/${accountId}/workers/services/${scriptName}`
40
+ );
41
+
42
+ const scriptTag = scriptMetadata.default_environment.script.tag;
43
+ const { items: deploys } = await fetchResult<DeploymentListRes>(
44
+ `/accounts/${accountId}/workers/versions/by-script/${scriptTag}`
45
+ );
46
+
47
+ const versionMessages = deploys.map(
48
+ (versions) =>
49
+ `\nDeployment ID: ${versions.id}
50
+ Created on: ${versions.metadata.created_on}
51
+ Author: ${versions.metadata.author_email}
52
+ Source: ${sourceStr(versions.metadata.source)}\n`
53
+ );
54
+
55
+ versionMessages[0] += "🟩Active";
56
+ logger.log(...versionMessages.reverse());
57
+ }
58
+
59
+ // TODO Include emoji/icon for each source
60
+ function sourceStr(source: string): string {
61
+ switch (source) {
62
+ case "api":
63
+ return "API";
64
+ case "dash":
65
+ return "Dashboard";
66
+ case "wrangler":
67
+ return "Wrangler";
68
+ case "terraform":
69
+ return "Terraform";
70
+ default:
71
+ return "Other";
72
+ }
73
+ }
@@ -10,7 +10,10 @@ import {
10
10
  import { initHandler } from "../init";
11
11
  import { logger } from "../logger";
12
12
  import { formatMessage } from "../parse";
13
- import type { YargsOptionsToInterface } from "../yargs-types";
13
+ import type {
14
+ CommonYargsOptions,
15
+ YargsOptionsToInterface,
16
+ } from "../yargs-types";
14
17
  import type { Argv, ArgumentsCamelCase, BuilderCallback } from "yargs";
15
18
 
16
19
  // https://github.com/cloudflare/wrangler/blob/master/src/cli/mod.rs#L106-L123
@@ -21,7 +24,7 @@ interface GenerateArgs {
21
24
  site?: boolean;
22
25
  }
23
26
 
24
- export function generateOptions(yargs: Argv) {
27
+ export function generateOptions(yargs: Argv<CommonYargsOptions>) {
25
28
  return yargs
26
29
  .positional("name", {
27
30
  describe: "Name of the Workers project",
@@ -298,11 +301,8 @@ function parseTemplatePath(templatePath: string): {
298
301
  return { remote, subdirectory };
299
302
  }
300
303
 
301
- export function buildOptions(yargs: Argv) {
302
- return yargs.option("env", {
303
- describe: "Perform on a specific environment",
304
- type: "string",
305
- });
304
+ export function buildOptions(yargs: Argv<CommonYargsOptions>) {
305
+ return yargs;
306
306
  }
307
307
  type BuildArgs = YargsOptionsToInterface<typeof buildOptions>;
308
308
  export async function buildHandler(buildArgs: BuildArgs) {
@@ -340,7 +340,7 @@ export const configHandler = () => {
340
340
  );
341
341
  };
342
342
 
343
- export function previewOptions(yargs: Argv) {
343
+ export function previewOptions(yargs: Argv<CommonYargsOptions>) {
344
344
  return yargs
345
345
  .positional("method", {
346
346
  type: "string",
@@ -350,11 +350,6 @@ export function previewOptions(yargs: Argv) {
350
350
  type: "string",
351
351
  describe: "Body string to post to your preview worker request.",
352
352
  })
353
- .option("env", {
354
- type: "string",
355
- requiresArg: true,
356
- describe: "Perform on a specific environment",
357
- })
358
353
  .option("watch", {
359
354
  default: true,
360
355
  describe: "Enable live preview",
@@ -376,11 +371,6 @@ export const route: BuilderCallback<unknown, unknown> = (routeYargs: Argv) => {
376
371
  "List the routes associated with a zone",
377
372
  (yargs) => {
378
373
  return yargs
379
- .option("env", {
380
- type: "string",
381
- requiresArg: true,
382
- describe: "Perform on a specific environment",
383
- })
384
374
  .option("zone", {
385
375
  type: "string",
386
376
  requiresArg: true,
@@ -416,11 +406,6 @@ export const route: BuilderCallback<unknown, unknown> = (routeYargs: Argv) => {
416
406
  type: "string",
417
407
  requiresArg: true,
418
408
  describe: "zone id",
419
- })
420
- .option("env", {
421
- type: "string",
422
- requiresArg: true,
423
- describe: "Perform on a specific environment",
424
409
  });
425
410
  },
426
411
  () => {
@@ -442,7 +427,7 @@ export const routeHandler = () => {
442
427
  throw new DeprecationError(`${deprecationNotice}\n${shouldDo}`);
443
428
  };
444
429
 
445
- export const subdomainOptions = (yargs: Argv) => {
430
+ export const subdomainOptions = (yargs: Argv<CommonYargsOptions>) => {
446
431
  return yargs.positional("name", { type: "string" });
447
432
  };
448
433
 
@@ -1,6 +1,5 @@
1
- import * as fs from "node:fs";
2
1
  import * as path from "node:path";
3
- import dotenv from "dotenv";
2
+ import { loadDotEnv } from "../config";
4
3
  import { logger } from "../logger";
5
4
  import type { Config } from "../config";
6
5
 
@@ -14,21 +13,25 @@ import type { Config } from "../config";
14
13
  * It is useful during development, to provide these types of variable locally.
15
14
  * When running `wrangler dev` we will look for a file called `.dev.vars`, situated
16
15
  * next to the `wrangler.toml` file (or in the current working directory if there is no
17
- * `wrangler.toml`).
16
+ * `wrangler.toml`). If the `--env <env>` option is set, we'll first look for
17
+ * `.dev.vars.<env>`.
18
18
  *
19
19
  * Any values in this file, formatted like a `dotenv` file, will add to or override `vars`
20
20
  * bindings provided in the `wrangler.toml`.
21
21
  */
22
- export function getVarsForDev(config: Config): Config["vars"] {
22
+ export function getVarsForDev(
23
+ config: Config,
24
+ env: string | undefined
25
+ ): Config["vars"] {
23
26
  const configDir = path.resolve(path.dirname(config.configPath ?? "."));
24
27
  const devVarsPath = path.resolve(configDir, ".dev.vars");
25
- if (fs.existsSync(devVarsPath)) {
26
- const devVarsRelativePath = path.relative(process.cwd(), devVarsPath);
28
+ const loaded = loadDotEnv(devVarsPath, env);
29
+ if (loaded !== undefined) {
30
+ const devVarsRelativePath = path.relative(process.cwd(), loaded.path);
27
31
  logger.log(`Using vars defined in ${devVarsRelativePath}`);
28
- const devVars = dotenv.parse(fs.readFileSync(devVarsPath, "utf8"));
29
32
  return {
30
33
  ...config.vars,
31
- ...devVars,
34
+ ...loaded.parsed,
32
35
  };
33
36
  } else {
34
37
  return config.vars;
package/src/dev/dev.tsx CHANGED
@@ -130,6 +130,7 @@ export type DevProps = {
130
130
  bindings: CfWorkerInit["bindings"];
131
131
  define: Config["define"];
132
132
  crons: Config["triggers"]["crons"];
133
+ queueConsumers: Config["queues"]["consumers"];
133
134
  isWorkersSite: boolean;
134
135
  assetPaths: AssetPaths | undefined;
135
136
  assetsConfig: Config["assets"];
@@ -289,6 +290,16 @@ function DevSession(props: DevSessionProps) {
289
290
  experimentalLocalStubCache: props.local && props.experimentalLocal,
290
291
  });
291
292
 
293
+ // TODO(queues) support remote wrangler dev
294
+ if (
295
+ !props.local &&
296
+ (props.bindings.queues?.length || props.queueConsumers?.length)
297
+ ) {
298
+ logger.warn(
299
+ "Queues are currently in Beta and are not supported in wrangler dev remote mode."
300
+ );
301
+ }
302
+
292
303
  return props.local ? (
293
304
  <Local
294
305
  name={props.name}
@@ -307,6 +318,7 @@ function DevSession(props: DevSessionProps) {
307
318
  localPersistencePath={props.localPersistencePath}
308
319
  liveReload={props.liveReload}
309
320
  crons={props.crons}
321
+ queueConsumers={props.queueConsumers}
310
322
  localProtocol={props.localProtocol}
311
323
  localUpstream={props.localUpstream}
312
324
  logPrefix={props.logPrefix}
package/src/dev/local.tsx CHANGED
@@ -30,6 +30,7 @@ import type {
30
30
  CfKvNamespace,
31
31
  CfR2Bucket,
32
32
  CfVars,
33
+ CfQueue,
33
34
  CfD1Database,
34
35
  } from "../worker";
35
36
  import type { EsbuildBundle } from "./use-esbuild";
@@ -57,6 +58,7 @@ export interface LocalProps {
57
58
  localPersistencePath: string | null;
58
59
  liveReload: boolean;
59
60
  crons: Config["triggers"]["crons"];
61
+ queueConsumers: Config["queues"]["consumers"];
60
62
  localProtocol: "http" | "https";
61
63
  localUpstream: string | undefined;
62
64
  inspect: boolean;
@@ -94,6 +96,7 @@ function useLocalWorker({
94
96
  liveReload,
95
97
  ip,
96
98
  crons,
99
+ queueConsumers,
97
100
  localProtocol,
98
101
  localUpstream,
99
102
  inspect,
@@ -184,6 +187,8 @@ function useLocalWorker({
184
187
  usageModel,
185
188
  kv_namespaces: bindings?.kv_namespaces,
186
189
  r2_buckets: bindings?.r2_buckets,
190
+ queueBindings: bindings?.queues,
191
+ queueConsumers: queueConsumers,
187
192
  d1_databases: bindings?.d1_databases,
188
193
  internalDurableObjects,
189
194
  externalDurableObjects,
@@ -329,6 +334,8 @@ function useLocalWorker({
329
334
  port,
330
335
  inspectorPort,
331
336
  ip,
337
+ queueConsumers,
338
+ bindings.queues,
332
339
  bindings.durable_objects,
333
340
  bindings.kv_namespaces,
334
341
  bindings.r2_buckets,
@@ -452,6 +459,8 @@ interface SetupMiniflareOptionsProps {
452
459
  compatibilityFlags: string[] | undefined;
453
460
  usageModel: "bundled" | "unbound" | undefined;
454
461
  kv_namespaces: CfKvNamespace[] | undefined;
462
+ queueBindings: CfQueue[] | undefined;
463
+ queueConsumers: Config["queues"]["consumers"];
455
464
  r2_buckets: CfR2Bucket[] | undefined;
456
465
  d1_databases: CfD1Database[] | undefined;
457
466
  internalDurableObjects: CfDurableObject[];
@@ -482,6 +491,8 @@ export function setupMiniflareOptions({
482
491
  compatibilityFlags,
483
492
  usageModel,
484
493
  kv_namespaces,
494
+ queueBindings,
495
+ queueConsumers,
485
496
  r2_buckets,
486
497
  d1_databases,
487
498
  internalDurableObjects,
@@ -521,6 +532,21 @@ export function setupMiniflareOptions({
521
532
  compatibilityFlags,
522
533
  usageModel,
523
534
  kvNamespaces: kv_namespaces?.map((kv) => kv.binding),
535
+ queueBindings: queueBindings?.map((queue) => {
536
+ return { name: queue.binding, queueName: queue.queue_name };
537
+ }),
538
+ queueConsumers: queueConsumers?.map((consumer) => {
539
+ const waitMs = consumer.max_batch_timeout
540
+ ? 1000 * consumer.max_batch_timeout
541
+ : undefined;
542
+ return {
543
+ queueName: consumer.queue,
544
+ maxBatchSize: consumer.max_batch_size,
545
+ maxWaitMs: waitMs,
546
+ maxRetries: consumer.max_retries,
547
+ deadLetterQueue: consumer.dead_letter_queue,
548
+ };
549
+ }),
524
550
  r2Buckets: r2_buckets?.map((r2) => r2.binding),
525
551
  durableObjects: Object.fromEntries(
526
552
  internalDurableObjects.map((binding) => [
@@ -104,6 +104,7 @@ export function Remote(props: RemoteProps) {
104
104
  port: props.inspectorPort,
105
105
  logToTerminal: true,
106
106
  sourceMapPath: props.sourceMapPath,
107
+ host: previewToken?.host,
107
108
  });
108
109
 
109
110
  const errorHandler = useErrorHandler();
@@ -569,6 +570,7 @@ async function createRemoteWorkerInit(props: {
569
570
  compatibility_flags: props.compatibilityFlags,
570
571
  usage_model: props.usageModel,
571
572
  keepVars: true,
573
+ logpush: false,
572
574
  };
573
575
 
574
576
  return init;
@@ -114,6 +114,7 @@ export async function startDevServer(
114
114
  localPersistencePath: props.localPersistencePath,
115
115
  liveReload: props.liveReload,
116
116
  crons: props.crons,
117
+ queueConsumers: props.queueConsumers,
117
118
  localProtocol: props.localProtocol,
118
119
  localUpstream: props.localUpstream,
119
120
  logPrefix: props.logPrefix,
@@ -225,10 +226,12 @@ async function runEsbuild({
225
226
  resolvedEntryPointPath,
226
227
  bundleType,
227
228
  modules,
229
+ dependencies,
228
230
  sourceMapPath,
229
231
  }: Awaited<ReturnType<typeof bundleWorker>> = noBundle
230
232
  ? {
231
233
  modules: [],
234
+ dependencies: {},
232
235
  resolvedEntryPointPath: entry.file,
233
236
  bundleType: entry.format === "modules" ? "esm" : "commonjs",
234
237
  stop: undefined,
@@ -264,6 +267,7 @@ async function runEsbuild({
264
267
  path: resolvedEntryPointPath,
265
268
  type: bundleType,
266
269
  modules,
270
+ dependencies,
267
271
  sourceMapPath,
268
272
  };
269
273
  }
@@ -285,6 +289,7 @@ export async function startLocalServer({
285
289
  liveReload,
286
290
  ip,
287
291
  crons,
292
+ queueConsumers,
288
293
  localProtocol,
289
294
  localUpstream,
290
295
  inspect,
@@ -364,6 +369,8 @@ export async function startLocalServer({
364
369
  compatibilityFlags,
365
370
  usageModel,
366
371
  kv_namespaces: bindings?.kv_namespaces,
372
+ queueBindings: bindings?.queues,
373
+ queueConsumers,
367
374
  r2_buckets: bindings?.r2_buckets,
368
375
  d1_databases: bindings?.d1_databases,
369
376
  internalDurableObjects,
@@ -2,13 +2,13 @@ import assert from "node:assert";
2
2
  import { watch } from "chokidar";
3
3
  import { useApp } from "ink";
4
4
  import { useState, useEffect } from "react";
5
- import { bundleWorker } from "../bundle";
6
- import { logger } from "../logger";
5
+ import { bundleWorker, rewriteNodeCompatBuildFailure } from "../bundle";
6
+ import { logBuildFailure, logger } from "../logger";
7
7
  import type { Config } from "../config";
8
8
  import type { WorkerRegistry } from "../dev-registry";
9
9
  import type { Entry } from "../entry";
10
10
  import type { CfModule } from "../worker";
11
- import type { WatchMode } from "esbuild";
11
+ import type { WatchMode, Metafile } from "esbuild";
12
12
 
13
13
  export type EsbuildBundle = {
14
14
  id: number;
@@ -16,6 +16,7 @@ export type EsbuildBundle = {
16
16
  entry: Entry;
17
17
  type: "esm" | "commonjs";
18
18
  modules: CfModule[];
19
+ dependencies: Metafile["outputs"][string]["inputs"];
19
20
  sourceMapPath: string | undefined;
20
21
  };
21
22
 
@@ -83,8 +84,11 @@ export function useEsbuild({
83
84
 
84
85
  const watchMode: WatchMode = {
85
86
  async onRebuild(error) {
86
- if (error) logger.error("Watch build failed:", error);
87
- else {
87
+ if (error !== null) {
88
+ if (!nodeCompat) rewriteNodeCompatBuildFailure(error);
89
+ logBuildFailure(error);
90
+ logger.error("Watch build failed:", error.message);
91
+ } else {
88
92
  updateBundle();
89
93
  }
90
94
  },
@@ -97,11 +101,13 @@ export function useEsbuild({
97
101
  resolvedEntryPointPath,
98
102
  bundleType,
99
103
  modules,
104
+ dependencies,
100
105
  stop,
101
106
  sourceMapPath,
102
107
  }: Awaited<ReturnType<typeof bundleWorker>> = noBundle
103
108
  ? {
104
109
  modules: [],
110
+ dependencies: {},
105
111
  resolvedEntryPointPath: entry.file,
106
112
  bundleType: entry.format === "modules" ? "esm" : "commonjs",
107
113
  stop: undefined,
@@ -155,6 +161,7 @@ export function useEsbuild({
155
161
  path: resolvedEntryPointPath,
156
162
  type: bundleType,
157
163
  modules,
164
+ dependencies,
158
165
  sourceMapPath,
159
166
  });
160
167
  }