wrangler 0.0.0-e6e1daba → 0.0.0-e7cf0caa

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 (101) hide show
  1. package/bin/wrangler.js +1 -1
  2. package/miniflare-dist/index.mjs +1 -1
  3. package/package.json +14 -9
  4. package/src/__tests__/api-devregistry.test.ts +121 -0
  5. package/src/__tests__/api.test.ts +27 -5
  6. package/src/__tests__/configuration.test.ts +294 -18
  7. package/src/__tests__/d1/execute.test.ts +3 -1
  8. package/src/__tests__/d1/migrate.test.ts +2 -0
  9. package/src/__tests__/delete.test.ts +6 -0
  10. package/src/__tests__/deployments.test.ts +335 -95
  11. package/src/__tests__/dev.test.tsx +87 -57
  12. package/src/__tests__/generate.test.ts +3 -4
  13. package/src/__tests__/helpers/mock-console.ts +6 -0
  14. package/src/__tests__/helpers/msw/handlers/deployments.ts +70 -3
  15. package/src/__tests__/helpers/msw/index.ts +4 -2
  16. package/src/__tests__/helpers/worker-scripts/parent-worker.js +4 -1
  17. package/src/__tests__/index.test.ts +10 -4
  18. package/src/__tests__/init.test.ts +136 -104
  19. package/src/__tests__/jest.setup.ts +4 -0
  20. package/src/__tests__/kv.test.ts +9 -9
  21. package/src/__tests__/logger.test.ts +68 -2
  22. package/src/__tests__/middleware.scheduled.test.ts +2 -2
  23. package/src/__tests__/middleware.test.ts +4 -3
  24. package/src/__tests__/mtls-certificates.test.ts +5 -2
  25. package/src/__tests__/pages/deployment-list.test.ts +2 -0
  26. package/src/__tests__/pages/functions-build.test.ts +91 -41
  27. package/src/__tests__/pages/project-list.test.ts +2 -0
  28. package/src/__tests__/pages/project-upload.test.ts +43 -24
  29. package/src/__tests__/pages/publish.test.ts +267 -148
  30. package/src/__tests__/publish.test.ts +420 -164
  31. package/src/__tests__/pubsub.test.ts +3 -0
  32. package/src/__tests__/queues.test.ts +5 -2
  33. package/src/__tests__/test-old-node-version.js +3 -3
  34. package/src/__tests__/traverse-module-graph.test.ts +220 -0
  35. package/src/__tests__/tsconfig.tsbuildinfo +1 -1
  36. package/src/__tests__/type-generation.test.ts +1 -0
  37. package/src/__tests__/user.test.ts +1 -1
  38. package/src/__tests__/worker-namespace.test.ts +1 -0
  39. package/src/api/dev.ts +10 -22
  40. package/src/api/pages/create-worker-bundle-contents.ts +1 -0
  41. package/src/api/pages/publish.tsx +38 -51
  42. package/src/bundle.ts +44 -13
  43. package/src/config/environment.ts +36 -4
  44. package/src/config/index.ts +30 -2
  45. package/src/config/validation-helpers.ts +1 -1
  46. package/src/config/validation.ts +149 -5
  47. package/src/create-worker-upload-form.ts +28 -3
  48. package/src/d1/backups.tsx +15 -11
  49. package/src/d1/create.tsx +20 -16
  50. package/src/d1/execute.tsx +22 -19
  51. package/src/d1/list.tsx +2 -2
  52. package/src/d1/migrations/apply.tsx +35 -29
  53. package/src/d1/migrations/create.tsx +15 -12
  54. package/src/d1/migrations/list.tsx +10 -7
  55. package/src/deployments.ts +260 -8
  56. package/src/dev/dev.tsx +4 -4
  57. package/src/dev/local.tsx +14 -16
  58. package/src/dev/start-server.ts +12 -16
  59. package/src/dev/use-esbuild.ts +11 -13
  60. package/src/dev/validate-dev-props.ts +9 -0
  61. package/src/dev-registry.ts +2 -1
  62. package/src/dev.tsx +28 -13
  63. package/src/entry.ts +18 -8
  64. package/src/generate/index.ts +2 -2
  65. package/src/index.ts +76 -23
  66. package/src/init.ts +144 -134
  67. package/src/logger.ts +18 -3
  68. package/src/metrics/send-event.ts +2 -1
  69. package/src/miniflare-cli/index.ts +1 -1
  70. package/src/miniflare-cli/tsconfig.tsbuildinfo +1 -1
  71. package/src/module-collection.ts +91 -25
  72. package/src/pages/build.ts +277 -99
  73. package/src/pages/buildFunctions.ts +20 -10
  74. package/src/pages/deployments.tsx +3 -5
  75. package/src/pages/dev.ts +21 -21
  76. package/src/pages/errors.ts +1 -0
  77. package/src/pages/functions/buildPlugin.ts +57 -34
  78. package/src/pages/functions/buildWorker.ts +34 -31
  79. package/src/pages/functions/tsconfig.tsbuildinfo +1 -1
  80. package/src/pages/projects.tsx +2 -5
  81. package/src/pages/publish.tsx +1 -10
  82. package/src/pages/upload.tsx +29 -9
  83. package/src/paths.ts +1 -1
  84. package/src/publish/index.ts +1 -1
  85. package/src/publish/publish.ts +29 -34
  86. package/src/queues/cli/commands/consumer/add.ts +6 -0
  87. package/src/queues/client.ts +1 -0
  88. package/src/secret/index.ts +2 -1
  89. package/src/traverse-module-graph.ts +53 -0
  90. package/src/type-generation.ts +1 -1
  91. package/src/user/user.ts +1 -1
  92. package/src/utils/render.ts +93 -0
  93. package/src/worker.ts +18 -1
  94. package/templates/d1-beta-facade.js +5 -2
  95. package/templates/gitignore +1 -0
  96. package/templates/middleware/middleware-miniflare3-json-error.ts +17 -5
  97. package/templates/tsconfig.init.json +1 -2
  98. package/templates/tsconfig.tsbuildinfo +1 -1
  99. package/wrangler-dist/cli.d.ts +33 -13
  100. package/wrangler-dist/cli.js +20732 -19984
  101. package/src/__tests__/api-devregistry.test.js +0 -64
@@ -200,7 +200,7 @@ export const upload = async (
200
200
  let attempts = 0;
201
201
  const getMissingHashes = async (skipCaching: boolean): Promise<string[]> => {
202
202
  if (skipCaching) {
203
- console.debug("Force skipping cache");
203
+ logger.debug("Force skipping cache");
204
204
  return files.map(({ hash }) => hash);
205
205
  }
206
206
 
@@ -276,9 +276,7 @@ export const upload = async (
276
276
  }
277
277
 
278
278
  let counter = fileMap.size - sortedFiles.length;
279
- const { rerender, unmount } = render(
280
- <Progress done={counter} total={fileMap.size} />
281
- );
279
+ const { rerender, unmount } = renderProgress(counter, fileMap.size);
282
280
 
283
281
  const queue = new PQueue({ concurrency: BULK_UPLOAD_CONCURRENCY });
284
282
 
@@ -302,7 +300,7 @@ export const upload = async (
302
300
  );
303
301
 
304
302
  try {
305
- console.debug("POST /pages/assets/upload");
303
+ logger.debug("POST /pages/assets/upload");
306
304
  const res = await fetchResult(`/pages/assets/upload`, {
307
305
  method: "POST",
308
306
  headers: {
@@ -311,10 +309,10 @@ export const upload = async (
311
309
  },
312
310
  body: JSON.stringify(payload),
313
311
  });
314
- console.debug("result:", res);
312
+ logger.debug("result:", res);
315
313
  } catch (e) {
316
314
  if (attempts < MAX_UPLOAD_ATTEMPTS) {
317
- console.debug("failed:", e, "retrying...");
315
+ logger.debug("failed:", e, "retrying...");
318
316
  // Exponential backoff, 1 second first time, then 2 second, then 4 second etc.
319
317
  await new Promise((resolvePromise) =>
320
318
  setTimeout(resolvePromise, Math.pow(2, attempts++) * 1000)
@@ -326,7 +324,7 @@ export const upload = async (
326
324
  }
327
325
  return doUpload();
328
326
  } else {
329
- console.debug("failed:", e);
327
+ logger.debug("failed:", e);
330
328
  throw e;
331
329
  }
332
330
  }
@@ -336,7 +334,7 @@ export const upload = async (
336
334
  doUpload().then(
337
335
  () => {
338
336
  counter += bucket.files.length;
339
- rerender(<Progress done={counter} total={fileMap.size} />);
337
+ rerender(counter, fileMap.size);
340
338
  },
341
339
  (error) => {
342
340
  return Promise.reject(
@@ -437,6 +435,28 @@ function formatTime(duration: number) {
437
435
  return `(${(duration / 1000).toFixed(2)} sec)`;
438
436
  }
439
437
 
438
+ function renderProgress(done: number, total: number) {
439
+ if (isInteractive()) {
440
+ const { rerender, unmount } = render(
441
+ <Progress done={done} total={total} />
442
+ );
443
+ return {
444
+ // eslint-disable-next-line no-shadow
445
+ rerender(done: number, total: number) {
446
+ rerender(<Progress done={done} total={total} />);
447
+ },
448
+ unmount,
449
+ };
450
+ } else {
451
+ // eslint-disable-next-line no-shadow
452
+ const rerender = (done: number, total: number) => {
453
+ logger.log(`Uploading... (${done}/${total})`);
454
+ };
455
+ rerender(done, total);
456
+ return { rerender, unmount() {} };
457
+ }
458
+ }
459
+
440
460
  function Progress({ done, total }: { done: number; total: number }) {
441
461
  return (
442
462
  <>
package/src/paths.ts CHANGED
@@ -59,7 +59,7 @@ export function readableRelative(to: string) {
59
59
  declare const __RELATIVE_PACKAGE_PATH__: string;
60
60
 
61
61
  /**
62
- * Use this function (rather than node.js constants like `__dirname`) to specify
62
+ * Use this function (rather than Node.js constants like `__dirname`) to specify
63
63
  * paths that are relative to the base path of the Wrangler package.
64
64
  *
65
65
  * It is important to use this function because it reliably maps to the root of the package
@@ -158,7 +158,7 @@ export function publishOptions(yargs: CommonYargsArgv) {
158
158
  type: "boolean",
159
159
  })
160
160
  .option("node-compat", {
161
- describe: "Enable node.js compatibility",
161
+ describe: "Enable Node.js compatibility",
162
162
  type: "boolean",
163
163
  })
164
164
  .option("dry-run", {
@@ -12,6 +12,7 @@ import {
12
12
  import { fetchListResult, fetchResult } from "../cfetch";
13
13
  import { printBindings } from "../config";
14
14
  import { createWorkerUploadForm } from "../create-worker-upload-form";
15
+ import { addHyphens } from "../deployments";
15
16
  import { confirm } from "../dialogs";
16
17
  import { getMigrationsToUpload } from "../durable";
17
18
  import { logger } from "../logger";
@@ -20,6 +21,7 @@ import { ParseError } from "../parse";
20
21
  import { getQueue, putConsumer } from "../queues/client";
21
22
  import { getWorkersDevSubdomain } from "../routes";
22
23
  import { syncAssets } from "../sites";
24
+ import traverseModuleGraph from "../traverse-module-graph";
23
25
  import { identifyD1BindingsAsBeta } from "../worker";
24
26
  import { getZoneForRoute } from "../zones";
25
27
  import type { FetchError } from "../cfetch";
@@ -30,7 +32,6 @@ import type {
30
32
  ZoneNameRoute,
31
33
  CustomDomainRoute,
32
34
  } from "../config/environment";
33
- import type { DeploymentListResult } from "../deployments";
34
35
  import type { Entry } from "../entry";
35
36
  import type { PutConsumerBody } from "../queues/client";
36
37
  import type { AssetPaths } from "../sites";
@@ -335,17 +336,25 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
335
336
 
336
337
  const jsxFactory = props.jsxFactory || config.jsx_factory;
337
338
  const jsxFragment = props.jsxFragment || config.jsx_fragment;
338
- const keepVars = props.keepVars ?? config.keep_vars;
339
+ const keepVars = props.keepVars || config.keep_vars;
339
340
 
340
341
  const minify = props.minify ?? config.minify;
341
342
 
342
- const nodeCompat = props.nodeCompat ?? config.node_compat;
343
- if (nodeCompat) {
343
+ const legacyNodeCompat = props.nodeCompat ?? config.node_compat;
344
+ if (legacyNodeCompat) {
344
345
  logger.warn(
345
- "Enabling node.js compatibility mode for built-ins and globals. This is experimental and has serious tradeoffs. Please see https://github.com/ionic-team/rollup-plugin-node-polyfills/ for more details."
346
+ "Enabling Node.js compatibility mode for built-ins and globals. This is experimental and has serious tradeoffs. Please see https://github.com/ionic-team/rollup-plugin-node-polyfills/ for more details."
346
347
  );
347
348
  }
348
349
 
350
+ const compatibilityFlags =
351
+ props.compatibilityFlags ?? config.compatibility_flags;
352
+ const nodejsCompat = compatibilityFlags.includes("nodejs_compat");
353
+ assert(
354
+ !(legacyNodeCompat && nodejsCompat),
355
+ "The `nodejs_compat` compatibility flag cannot be used in conjunction with the legacy `--node-compat` flag. If you want to use the Workers runtime Node.js compatibility features, please remove the `--node-compat` argument from your CLI command or `node_compat = true` from your config file."
356
+ );
357
+
349
358
  // Warn if user tries minify or node-compat with no-bundle
350
359
  if (props.noBundle && minify) {
351
360
  logger.warn(
@@ -353,7 +362,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
353
362
  );
354
363
  }
355
364
 
356
- if (props.noBundle && nodeCompat) {
365
+ if (props.noBundle && legacyNodeCompat) {
357
366
  logger.warn(
358
367
  "`--node-compat` and `--no-bundle` can't be used together. If you want to polyfill Node.js built-ins and disable Wrangler's bundling, please polyfill as part of your own bundling process."
359
368
  );
@@ -393,7 +402,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
393
402
  : `/accounts/${accountId}/workers/scripts/${scriptName}`;
394
403
 
395
404
  let available_on_subdomain: boolean | undefined = undefined; // we'll set this later
396
- let scriptTag: string | null = null;
405
+ let deploymentId: string | null = null;
397
406
 
398
407
  const { format } = props.entry;
399
408
 
@@ -455,14 +464,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
455
464
  resolvedEntryPointPath,
456
465
  bundleType,
457
466
  }: Awaited<ReturnType<typeof bundleWorker>> = props.noBundle
458
- ? // we can skip the whole bundling step and mock a bundle here
459
- {
460
- modules: [],
461
- dependencies: {},
462
- resolvedEntryPointPath: props.entry.file,
463
- bundleType: props.entry.format === "modules" ? "esm" : "commonjs",
464
- stop: undefined,
465
- }
467
+ ? await traverseModuleGraph(props.entry, props.rules)
466
468
  : await bundleWorker(
467
469
  props.entry,
468
470
  typeof destination === "string" ? destination : destination.path,
@@ -478,7 +480,8 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
478
480
  rules: props.rules,
479
481
  tsconfig: props.tsconfig ?? config.tsconfig,
480
482
  minify,
481
- nodeCompat,
483
+ legacyNodeCompat,
484
+ nodejsCompat,
482
485
  define: { ...config.define, ...props.defines },
483
486
  checkFetch: false,
484
487
  assets: config.assets && {
@@ -532,6 +535,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
532
535
  ? { binding: "__STATIC_CONTENT", id: assets.namespace }
533
536
  : []
534
537
  ),
538
+ send_email: config.send_email,
535
539
  vars: { ...config.vars, ...props.vars },
536
540
  wasm_modules: config.wasm_modules,
537
541
  text_blobs: {
@@ -553,7 +557,10 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
553
557
  dispatch_namespaces: config.dispatch_namespaces,
554
558
  mtls_certificates: config.mtls_certificates,
555
559
  logfwdr: config.logfwdr,
556
- unsafe: config.unsafe?.bindings,
560
+ unsafe: {
561
+ bindings: config.unsafe.bindings,
562
+ metadata: config.unsafe.metadata,
563
+ },
557
564
  };
558
565
 
559
566
  if (assets.manifest) {
@@ -575,8 +582,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
575
582
  migrations,
576
583
  modules,
577
584
  compatibility_date: props.compatibilityDate ?? config.compatibility_date,
578
- compatibility_flags:
579
- props.compatibilityFlags ?? config.compatibility_flags,
585
+ compatibility_flags: compatibilityFlags,
580
586
  usage_model: config.usage_model,
581
587
  keepVars,
582
588
  logpush: props.logpush !== undefined ? props.logpush : config.logpush,
@@ -621,7 +627,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
621
627
  id: string | null;
622
628
  etag: string | null;
623
629
  pipeline_hash: string | null;
624
- tag: string | null;
630
+ deployment_id: string | null;
625
631
  }>(
626
632
  workerUrl,
627
633
  {
@@ -638,7 +644,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
638
644
  );
639
645
 
640
646
  available_on_subdomain = result.available_on_subdomain;
641
- scriptTag = result.tag;
647
+ deploymentId = addHyphens(result.deployment_id) ?? result.deployment_id;
642
648
 
643
649
  if (config.first_party_worker) {
644
650
  // Print some useful information returned after publishing
@@ -828,19 +834,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
828
834
  logger.log("No publish targets for", workerName, formatTime(deployMs));
829
835
  }
830
836
 
831
- try {
832
- const deploymentsList = await fetchResult<DeploymentListResult>(
833
- `/accounts/${accountId}/workers/deployments/by-script/${scriptTag}`
834
- );
835
-
836
- logger.log("Current Deployment ID:", deploymentsList.latest.id);
837
- } catch (e) {
838
- if ((e as { code: number }).code === 10023) {
839
- // TODO: remove this try/catch once deployments is completely rolled out
840
- } else {
841
- throw e;
842
- }
843
- }
837
+ logger.log("Current Deployment ID:", deploymentId);
844
838
  }
845
839
 
846
840
  export function helpIfErrorIsSizeOrScriptStartup(
@@ -1049,6 +1043,7 @@ function updateQueueConsumers(config: Config): Promise<string[]>[] {
1049
1043
  max_wait_time_ms: consumer.max_batch_timeout
1050
1044
  ? 1000 * consumer.max_batch_timeout
1051
1045
  : undefined,
1046
+ max_concurrency: consumer.max_concurrency,
1052
1047
  },
1053
1048
  };
1054
1049
 
@@ -37,6 +37,11 @@ export function options(yargs: CommonYargsArgv) {
37
37
  type: "string",
38
38
  describe: "Queue to send messages that failed to be consumed",
39
39
  },
40
+ "max-concurrency": {
41
+ type: "number",
42
+ describe:
43
+ "The maximum number of concurrent consumer Worker invocations. Must be a positive integer",
44
+ },
40
45
  });
41
46
  }
42
47
 
@@ -55,6 +60,7 @@ export async function handler(
55
60
  max_wait_time_ms: args.batchTimeout // API expects milliseconds
56
61
  ? 1000 * args.batchTimeout
57
62
  : undefined,
63
+ max_concurrency: args.maxConcurrency,
58
64
  },
59
65
  dead_letter_queue: args.deadLetterQueue,
60
66
  };
@@ -90,6 +90,7 @@ export interface ConsumerSettings {
90
90
  batch_size?: number;
91
91
  max_retries?: number;
92
92
  max_wait_time_ms?: number;
93
+ max_concurrency?: number | null;
93
94
  }
94
95
 
95
96
  export interface ConsumerResponse extends PostConsumerBody {
@@ -100,6 +100,7 @@ export const secret = (secretYargs: CommonYargsArgv) => {
100
100
  },
101
101
  bindings: {
102
102
  kv_namespaces: [],
103
+ send_email: [],
103
104
  vars: {},
104
105
  durable_objects: { bindings: [] },
105
106
  queues: [],
@@ -113,7 +114,7 @@ export const secret = (secretYargs: CommonYargsArgv) => {
113
114
  dispatch_namespaces: [],
114
115
  mtls_certificates: [],
115
116
  logfwdr: { schema: undefined, bindings: [] },
116
- unsafe: [],
117
+ unsafe: { bindings: undefined, metadata: undefined },
117
118
  },
118
119
  modules: [],
119
120
  migrations: undefined,
@@ -0,0 +1,53 @@
1
+ import { readdir } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import chalk from "chalk";
4
+ import { logger } from "./logger";
5
+ import { matchFiles, parseRules } from "./module-collection";
6
+ import type { BundleResult } from "./bundle";
7
+ import type { Config } from "./config";
8
+ import type { Entry } from "./entry";
9
+
10
+ async function getFiles(root: string, relativeTo: string): Promise<string[]> {
11
+ const files = [];
12
+ for (const file of await readdir(root, { withFileTypes: true })) {
13
+ if (file.isDirectory()) {
14
+ files.push(...(await getFiles(path.join(root, file.name), relativeTo)));
15
+ } else {
16
+ files.push(path.relative(relativeTo, path.join(root, file.name)));
17
+ }
18
+ }
19
+ return files;
20
+ }
21
+
22
+ export default async function traverseModuleGraph(
23
+ entry: Entry,
24
+ rules: Config["rules"]
25
+ ): Promise<BundleResult> {
26
+ const files = await getFiles(entry.moduleRoot, entry.moduleRoot);
27
+ const relativeEntryPoint = path.relative(entry.moduleRoot, entry.file);
28
+
29
+ const modules = (await matchFiles(files, entry.moduleRoot, parseRules(rules)))
30
+ .filter((m) => m.name !== relativeEntryPoint)
31
+ .map((m) => ({
32
+ ...m,
33
+ name: m.name,
34
+ }));
35
+
36
+ const bundleType = entry.format === "modules" ? "esm" : "commonjs";
37
+
38
+ if (modules.length > 0) {
39
+ logger.info(`Uploading additional modules:`);
40
+ modules.forEach(({ name, type }) => {
41
+ logger.info(`- ${chalk.blue(name)} (${chalk.green(type ?? "")})`);
42
+ });
43
+ }
44
+
45
+ return {
46
+ modules,
47
+ dependencies: {},
48
+ resolvedEntryPointPath: entry.file,
49
+ bundleType,
50
+ stop: undefined,
51
+ sourceMapPath: undefined,
52
+ };
53
+ }
@@ -89,7 +89,7 @@ export async function generateTypes(
89
89
  }
90
90
  }
91
91
 
92
- if (configToDTS.unsafe) {
92
+ if (configToDTS.unsafe?.bindings) {
93
93
  for (const unsafe of configToDTS.unsafe.bindings) {
94
94
  envTypeStructure.push(`${unsafe.name}: any;`);
95
95
  }
package/src/user/user.ts CHANGED
@@ -1153,7 +1153,7 @@ export async function requireAuth(config: {
1153
1153
  if (!loggedIn) {
1154
1154
  if (!isInteractive() || CI.isCI()) {
1155
1155
  throw new Error(
1156
- "In a non-interactive environment, it's necessary to set a CLOUDFLARE_API_TOKEN environment variable for wrangler to work. Please go to https://developers.cloudflare.com/api/tokens/create/ for instructions on how to create an api token, and assign its value to CLOUDFLARE_API_TOKEN."
1156
+ "In a non-interactive environment, it's necessary to set a CLOUDFLARE_API_TOKEN environment variable for wrangler to work. Please go to https://developers.cloudflare.com/fundamentals/api/get-started/create-token/ for instructions on how to create an api token, and assign its value to CLOUDFLARE_API_TOKEN."
1157
1157
  );
1158
1158
  } else {
1159
1159
  // didn't login, let's just quit
@@ -0,0 +1,93 @@
1
+ import { EventEmitter } from "events";
2
+ import { render as inkRender } from "ink";
3
+ import type { ReactElement } from "react";
4
+ import type { WriteStream } from "tty";
5
+
6
+ export function renderToString(tree: ReactElement): string {
7
+ const { output, cleanup } = render(tree);
8
+ cleanup();
9
+ return output;
10
+ }
11
+
12
+ // The code below is mostly copied from ink-render-string.
13
+ // See https://github.com/zhanwang626/ink-render-string.git.
14
+
15
+ // MIT License
16
+
17
+ // Copyright (c) 2021 Zhan Wang
18
+
19
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
20
+ // of this software and associated documentation files (the "Software"), to deal
21
+ // in the Software without restriction, including without limitation the rights
22
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23
+ // copies of the Software, and to permit persons to whom the Software is
24
+ // furnished to do so, subject to the following conditions:
25
+
26
+ // The above copyright notice and this permission notice shall be included in all
27
+ // copies or substantial portions of the Software.
28
+
29
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35
+ // SOFTWARE.
36
+
37
+ interface Instance {
38
+ output: string;
39
+ unmount: () => void;
40
+ cleanup: () => void;
41
+ stdout: OutputStream;
42
+ stderr: OutputStream;
43
+ frames: string[];
44
+ }
45
+
46
+ const LASTFRAME_UNDEFINED = "value of stdout.lastframe() is undefined";
47
+
48
+ export class OutputStream extends EventEmitter {
49
+ readonly frames: string[] = [];
50
+ private _lastFrame?: string;
51
+
52
+ get columns(): number {
53
+ return this._originalStream.columns;
54
+ }
55
+ get rows(): number {
56
+ return this._originalStream.rows;
57
+ }
58
+
59
+ constructor(private _originalStream: WriteStream) {
60
+ super();
61
+ }
62
+
63
+ write = (frame: string) => {
64
+ this.frames.push(frame);
65
+ this._lastFrame = frame;
66
+ };
67
+
68
+ lastFrame = () => {
69
+ return this._lastFrame;
70
+ };
71
+ }
72
+
73
+ export const render = (tree: ReactElement): Instance => {
74
+ const stdout = new OutputStream(process.stdout);
75
+ const stderr = new OutputStream(process.stderr);
76
+
77
+ const instance = inkRender(tree, {
78
+ stdout: stdout as unknown as WriteStream,
79
+ stderr: stderr as unknown as WriteStream,
80
+ debug: true,
81
+ exitOnCtrlC: false,
82
+ patchConsole: false,
83
+ });
84
+
85
+ return {
86
+ output: stdout.lastFrame() || LASTFRAME_UNDEFINED,
87
+ stdout,
88
+ stderr,
89
+ cleanup: instance.cleanup,
90
+ unmount: instance.unmount,
91
+ frames: stdout.frames,
92
+ };
93
+ };
package/src/worker.ts CHANGED
@@ -79,6 +79,15 @@ export interface CfKvNamespace {
79
79
  id: string;
80
80
  }
81
81
 
82
+ /**
83
+ * A binding to send email.
84
+ */
85
+ export interface CfSendEmailBindings {
86
+ name: string;
87
+ destination_address?: string;
88
+ allowed_destination_addresses?: string[];
89
+ }
90
+
82
91
  /**
83
92
  * A binding to a wasm module (in service-worker format)
84
93
  */
@@ -174,6 +183,13 @@ interface CfUnsafeBinding {
174
183
  type: string;
175
184
  }
176
185
 
186
+ type CfUnsafeMetadata = Record<string, unknown>;
187
+
188
+ interface CfUnsafe {
189
+ bindings: CfUnsafeBinding[] | undefined;
190
+ metadata: CfUnsafeMetadata | undefined;
191
+ }
192
+
177
193
  export interface CfDurableObjectMigrations {
178
194
  old_tag?: string;
179
195
  new_tag: string;
@@ -209,6 +225,7 @@ export interface CfWorkerInit {
209
225
  bindings: {
210
226
  vars: CfVars | undefined;
211
227
  kv_namespaces: CfKvNamespace[] | undefined;
228
+ send_email: CfSendEmailBindings[] | undefined;
212
229
  wasm_modules: CfWasmModuleBindings | undefined;
213
230
  text_blobs: CfTextBlobBindings | undefined;
214
231
  data_blobs: CfDataBlobBindings | undefined;
@@ -221,7 +238,7 @@ export interface CfWorkerInit {
221
238
  dispatch_namespaces: CfDispatchNamespace[] | undefined;
222
239
  mtls_certificates: CfMTlsCertificate[] | undefined;
223
240
  logfwdr: CfLogfwdr | undefined;
224
- unsafe: CfUnsafeBinding[] | undefined;
241
+ unsafe: CfUnsafe | undefined;
225
242
  };
226
243
  migrations: CfDurableObjectMigrations | undefined;
227
244
  compatibility_date: string | undefined;
@@ -11,7 +11,7 @@ var D1Database = class {
11
11
  return new D1PreparedStatement(this, query);
12
12
  }
13
13
  async dump() {
14
- const response = await this.binding.fetch("/dump", {
14
+ const response = await this.binding.fetch("http://d1/dump", {
15
15
  method: "POST",
16
16
  headers: {
17
17
  "content-type": "application/json",
@@ -79,7 +79,7 @@ var D1Database = class {
79
79
  params,
80
80
  }
81
81
  );
82
- const response = await this.binding.fetch(endpoint, {
82
+ const response = await this.binding.fetch(new URL(endpoint, "http://d1"), {
83
83
  method: "POST",
84
84
  headers: {
85
85
  "content-type": "application/json",
@@ -237,5 +237,8 @@ var shim_default = {
237
237
  async trace(traces, env, ctx) {
238
238
  return worker.trace(traces, getMaskedEnv(env), ctx);
239
239
  },
240
+ async email(message, env, ctx) {
241
+ return worker.email(message, getMaskedEnv(env), ctx);
242
+ },
240
243
  };
241
244
  export { shim_default as default };
@@ -169,3 +169,4 @@ dist
169
169
  # wrangler project
170
170
 
171
171
  .dev.vars
172
+ .wrangler/
@@ -1,15 +1,27 @@
1
1
  import type { Middleware } from "./common";
2
2
 
3
+ interface JsonError {
4
+ message?: string;
5
+ name?: string;
6
+ stack?: string;
7
+ cause?: JsonError;
8
+ }
9
+
10
+ function reduceError(e: any): JsonError {
11
+ return {
12
+ name: e?.name,
13
+ message: e?.message ?? String(e),
14
+ stack: e?.stack,
15
+ cause: e?.cause === undefined ? undefined : reduceError(e.cause),
16
+ };
17
+ }
18
+
3
19
  // See comment in `bundle.ts` for details on why this is needed
4
20
  const jsonError: Middleware = async (request, env, _ctx, middlewareCtx) => {
5
21
  try {
6
22
  return await middlewareCtx.next(request, env);
7
23
  } catch (e: any) {
8
- const error = {
9
- name: e?.name,
10
- message: e?.message ?? String(e),
11
- stack: e?.stack,
12
- };
24
+ const error = reduceError(e);
13
25
  return Response.json(error, {
14
26
  status: 500,
15
27
  headers: { "MF-Experimental-Error-Stack": "true" },
@@ -34,8 +34,7 @@
34
34
  // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
35
35
  // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */
36
36
  "types": [
37
- "@cloudflare/workers-types",
38
- "vitest"
37
+ "@cloudflare/workers-types"
39
38
  ] /* Specify type package names to be included without being referenced in a source file. */,
40
39
  // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
41
40
  "resolveJsonModule": true /* Enable importing .json files */,