alchemy 0.77.5 → 0.78.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 (67) hide show
  1. package/bin/alchemy.js +2414 -323
  2. package/bin/commands/util.ts +234 -315
  3. package/bin/services/execute-alchemy.ts +8 -33
  4. package/lib/cloudflare/assets.js +1 -1
  5. package/lib/cloudflare/assets.js.map +1 -1
  6. package/lib/cloudflare/astro/plugin.d.ts.map +1 -1
  7. package/lib/cloudflare/astro/plugin.js +3 -1
  8. package/lib/cloudflare/astro/plugin.js.map +1 -1
  9. package/lib/cloudflare/auth.d.ts +73 -68
  10. package/lib/cloudflare/auth.d.ts.map +1 -1
  11. package/lib/cloudflare/auth.js +73 -72
  12. package/lib/cloudflare/auth.js.map +1 -1
  13. package/lib/cloudflare/cloudflare-env-proxy.d.ts +1 -1
  14. package/lib/cloudflare/cloudflare-env-proxy.d.ts.map +1 -1
  15. package/lib/cloudflare/cloudflare-env-proxy.js +2 -2
  16. package/lib/cloudflare/cloudflare-env-proxy.js.map +1 -1
  17. package/lib/cloudflare/compatibility-date.gen.d.ts +1 -1
  18. package/lib/cloudflare/compatibility-date.gen.js +1 -1
  19. package/lib/cloudflare/container.d.ts +0 -3
  20. package/lib/cloudflare/container.d.ts.map +1 -1
  21. package/lib/cloudflare/container.js +10 -4
  22. package/lib/cloudflare/container.js.map +1 -1
  23. package/lib/cloudflare/d1-database.d.ts.map +1 -1
  24. package/lib/cloudflare/d1-database.js +3 -2
  25. package/lib/cloudflare/d1-database.js.map +1 -1
  26. package/lib/cloudflare/index.d.ts +2 -2
  27. package/lib/cloudflare/index.d.ts.map +1 -1
  28. package/lib/cloudflare/index.js +2 -2
  29. package/lib/cloudflare/index.js.map +1 -1
  30. package/lib/cloudflare/permission-groups.d.ts +35 -0
  31. package/lib/cloudflare/permission-groups.d.ts.map +1 -1
  32. package/lib/cloudflare/permission-groups.js +42 -0
  33. package/lib/cloudflare/permission-groups.js.map +1 -1
  34. package/lib/cloudflare/worker-assets.d.ts.map +1 -1
  35. package/lib/cloudflare/worker-assets.js +22 -1
  36. package/lib/cloudflare/worker-assets.js.map +1 -1
  37. package/lib/cloudflare/worker-bundle.d.ts.map +1 -1
  38. package/lib/cloudflare/worker-bundle.js +1 -0
  39. package/lib/cloudflare/worker-bundle.js.map +1 -1
  40. package/lib/cloudflare/worker.d.ts +2 -0
  41. package/lib/cloudflare/worker.d.ts.map +1 -1
  42. package/lib/cloudflare/worker.js.map +1 -1
  43. package/lib/util/detect-node-runtime.d.ts.map +1 -1
  44. package/lib/util/detect-node-runtime.js +0 -2
  45. package/lib/util/detect-node-runtime.js.map +1 -1
  46. package/lib/util/detect-package-manager.d.ts +1 -1
  47. package/lib/util/detect-package-manager.d.ts.map +1 -1
  48. package/lib/util/detect-package-manager.js +11 -8
  49. package/lib/util/detect-package-manager.js.map +1 -1
  50. package/package.json +5 -1
  51. package/src/cloudflare/assets.ts +1 -1
  52. package/src/cloudflare/astro/plugin.ts +8 -1
  53. package/src/cloudflare/auth.ts +94 -75
  54. package/src/cloudflare/cloudflare-env-proxy.ts +5 -1
  55. package/src/cloudflare/compatibility-date.gen.ts +1 -1
  56. package/src/cloudflare/container.ts +10 -7
  57. package/src/cloudflare/d1-database.ts +6 -2
  58. package/src/cloudflare/index.ts +2 -2
  59. package/src/cloudflare/permission-groups.ts +42 -0
  60. package/src/cloudflare/worker-assets.ts +34 -1
  61. package/src/cloudflare/worker-bundle.ts +1 -0
  62. package/src/cloudflare/worker.ts +2 -0
  63. package/src/util/detect-node-runtime.ts +0 -2
  64. package/src/util/detect-package-manager.ts +12 -8
  65. package/templates/tanstack-start/alchemy.run.ts +8 -2
  66. package/templates/tanstack-start/src/routes/demo.start.server-funcs.tsx +11 -16
  67. package/workers/tunnel-proxy.js +1 -1
@@ -4,4 +4,4 @@
4
4
  /**
5
5
  * The default Cloudflare Workers compatibility date, set based on the latest workerd release at the time of build.
6
6
  */
7
- export const DEFAULT_COMPATIBILITY_DATE = "2025-11-11";
7
+ export const DEFAULT_COMPATIBILITY_DATE = "2025-11-19";
@@ -221,6 +221,11 @@ export async function Container<T>(
221
221
  ...props,
222
222
  name: `cloudflare-dev/${name}`, // prefix used by Miniflare
223
223
  tag,
224
+ build: props.build ?? {
225
+ dockerfile: "Dockerfile",
226
+ platform: "linux/amd64",
227
+ context: process.cwd(),
228
+ },
224
229
  } as ImageProps);
225
230
 
226
231
  return {
@@ -240,7 +245,11 @@ export async function Container<T>(
240
245
  platform: props.build?.platform ?? "linux/amd64",
241
246
  ...props.build,
242
247
  }
243
- : undefined,
248
+ : {
249
+ dockerfile: "Dockerfile",
250
+ platform: "linux/amd64",
251
+ context: process.cwd(),
252
+ },
244
253
  image: props.image,
245
254
  registry: {
246
255
  server: "registry.cloudflare.com",
@@ -562,9 +571,6 @@ export const ContainerApplication = Resource(
562
571
  logs: {
563
572
  enabled: true,
564
573
  },
565
- logging: {
566
- enabled: true,
567
- },
568
574
  },
569
575
  };
570
576
  if (this.phase === "update" && this.output?.id) {
@@ -1039,9 +1045,6 @@ interface CreateRolloutApplicationResponse {
1039
1045
  logs?: {
1040
1046
  enabled: boolean;
1041
1047
  };
1042
- logging?: {
1043
- enabled: boolean;
1044
- };
1045
1048
  };
1046
1049
  };
1047
1050
  target_configuration: DeploymentConfiguration;
@@ -386,7 +386,10 @@ const _D1Database = Resource(
386
386
  `Cannot update primaryLocationHint from '${this.output.primaryLocationHint}' to '${props.primaryLocationHint}' after database creation.`,
387
387
  );
388
388
  }
389
- if (props.jurisdiction !== this.output?.jurisdiction) {
389
+ if (
390
+ (props.jurisdiction ?? "default") !==
391
+ (this.output?.jurisdiction ?? "default")
392
+ ) {
390
393
  throw new Error(
391
394
  `Cannot update jurisdiction from '${this.output.jurisdiction}' to '${props.jurisdiction}' after database creation.`,
392
395
  );
@@ -468,7 +471,8 @@ export async function createDatabase(
468
471
  // Create new D1 database
469
472
  const createPayload: any = {
470
473
  name: databaseName,
471
- jurisdiction: props.jurisdiction,
474
+ jurisdiction:
475
+ props.jurisdiction !== "default" ? props.jurisdiction : undefined,
472
476
  };
473
477
 
474
478
  if (props.primaryLocationHint) {
@@ -69,11 +69,11 @@ export * from "./vectorize-metadata-index.ts";
69
69
  export * from "./version-metadata.ts";
70
70
  export * from "./vite/vite.ts";
71
71
  export * from "./website.ts";
72
- export { WorkerLoader } from "./worker-loader.ts";
72
+ export * from "./worker-loader.ts";
73
73
  export * from "./worker-ref.ts";
74
74
  export * from "./worker-stub.ts";
75
75
  export * from "./worker-subdomain.ts";
76
76
  export * from "./worker.ts";
77
- export { Workflow } from "./workflow.ts";
77
+ export * from "./workflow.ts";
78
78
  export * from "./wrangler.json.ts";
79
79
  export * from "./zone.ts";
@@ -359,6 +359,18 @@ export const PERMISSION_GROUPS = [
359
359
  description: "Grants write access to Account Rulesets",
360
360
  scopes: ["com.cloudflare.api.account"],
361
361
  },
362
+ {
363
+ id: "ece81d7757ff406cad61f221a4920d55",
364
+ name: "Account Security Center Insights Read",
365
+ description: "Grants read access to Security Center Insights",
366
+ scopes: ["com.cloudflare.api.account"],
367
+ },
368
+ {
369
+ id: "cfa2e2893226455c9b945914969dff7c",
370
+ name: "Account Security Center Insights Write",
371
+ description: "Grants write access to Security Center Insights",
372
+ scopes: ["com.cloudflare.api.account"],
373
+ },
362
374
  {
363
375
  id: "c1fde68c7bcc44588cbb6ddbc16d6480",
364
376
  name: "Account Settings Read",
@@ -527,6 +539,18 @@ export const PERMISSION_GROUPS = [
527
539
  description: "Grants write access to Browser Rendering API",
528
540
  scopes: ["com.cloudflare.api.account"],
529
541
  },
542
+ {
543
+ id: "da5fe84b9c5446a0a5955b673fafa288",
544
+ name: "CASB Read",
545
+ description: "Grants read access to Cloudflare CASB",
546
+ scopes: ["com.cloudflare.api.account"],
547
+ },
548
+ {
549
+ id: "b9ed086b20864ad89c5aac24cdd02365",
550
+ name: "CASB Write",
551
+ description: "Grants write access to Cloudflare CASB",
552
+ scopes: ["com.cloudflare.api.account"],
553
+ },
530
554
  {
531
555
  id: "47b936e2027140c0a474cac8887b8c29",
532
556
  name: "CF Agents Read",
@@ -970,6 +994,18 @@ export const PERMISSION_GROUPS = [
970
994
  description: "Grants full access to Email Security dmarc reports\n",
971
995
  scopes: ["com.cloudflare.api.account.zone"],
972
996
  },
997
+ {
998
+ id: "2d5b4b1f6c89487bb7184c2c1dcd3bf1",
999
+ name: "Email Sending Read",
1000
+ description: "Grants access to reading data from Email Sending",
1001
+ scopes: ["com.cloudflare.api.account"],
1002
+ },
1003
+ {
1004
+ id: "5df633d6b41c42bcaf5b4a62b9d14b64",
1005
+ name: "Email Sending Write",
1006
+ description: "Grants access to reading and writing data from Email Sending",
1007
+ scopes: ["com.cloudflare.api.account"],
1008
+ },
973
1009
  {
974
1010
  id: "4ec32dfcb35641c5bb32d5ef1ab963b4",
975
1011
  name: "Firewall Services Read",
@@ -1426,6 +1462,12 @@ export const PERMISSION_GROUPS = [
1426
1462
  description: "Grants admin access to Realtime configuration",
1427
1463
  scopes: ["com.cloudflare.api.account"],
1428
1464
  },
1465
+ {
1466
+ id: "9bb90620717647a39679e1d951f140d6",
1467
+ name: "Registrar Domains Read",
1468
+ description: "Grants read access to Cloudflare Registrar domains",
1469
+ scopes: ["com.cloudflare.api.account"],
1470
+ },
1429
1471
  {
1430
1472
  id: "93ae59e7a40c4287a57ff6e501186a63",
1431
1473
  name: "Response Compression Read",
@@ -1,4 +1,5 @@
1
1
  import fs from "node:fs/promises";
2
+ import path from "pathe";
2
3
  import { AsyncQueue } from "../util/async-queue.ts";
3
4
  import { withExponentialBackoff } from "../util/retry.ts";
4
5
  import { extractCloudflareResult } from "./api-response.ts";
@@ -38,7 +39,10 @@ export async function uploadAssets(
38
39
  },
39
40
  ): Promise<AssetUploadResult> {
40
41
  // Process the assets configuration once at the beginning
41
- const processedConfig = createAssetConfig(assetConfig);
42
+ const processedConfig = await createAssetConfigWithFiles(
43
+ assets.path,
44
+ assetConfig,
45
+ );
42
46
 
43
47
  const manifest: Record<string, FileMetadata> = {};
44
48
  const filesByHash = new Map<string, Assets.FileMetadata>();
@@ -95,6 +99,35 @@ export async function uploadAssets(
95
99
  };
96
100
  }
97
101
 
102
+ async function createAssetConfigWithFiles(
103
+ assetsPath: string,
104
+ config: AssetsConfig = {},
105
+ ): Promise<AssetsConfig> {
106
+ const [_headers, _redirects] = await Promise.all([
107
+ readFileOrProperty("_headers"),
108
+ readFileOrProperty("_redirects"),
109
+ ]);
110
+ return createAssetConfig({
111
+ ...config,
112
+ _headers,
113
+ _redirects,
114
+ });
115
+
116
+ async function readFileOrProperty(
117
+ property: "_headers" | "_redirects",
118
+ ): Promise<string | undefined> {
119
+ const content = await fs
120
+ .readFile(path.join(assetsPath, property), "utf-8")
121
+ .catch(() => undefined);
122
+ if (content && config[property]) {
123
+ throw new Error(
124
+ `Cannot use both ${property} file and ${property} property`,
125
+ );
126
+ }
127
+ return content ?? config[property];
128
+ }
129
+ }
130
+
98
131
  /**
99
132
  * Creates asset configuration object from provided config or defaults
100
133
  */
@@ -351,6 +351,7 @@ export namespace WorkerBundleSource {
351
351
  ".js": "jsx",
352
352
  ".mjs": "jsx",
353
353
  ".cjs": "jsx",
354
+ ".sql": "text",
354
355
  },
355
356
  format,
356
357
  sourceRoot: this.props.outdir,
@@ -71,11 +71,13 @@ import { createEmptyWorker, exists } from "./worker-stub.ts";
71
71
  export interface AssetsConfig {
72
72
  /**
73
73
  * The contents of a _headers file (used to attach custom headers on asset responses)
74
+ * @default the `_headers` file in the assets directory, if present
74
75
  */
75
76
  _headers?: string;
76
77
 
77
78
  /**
78
79
  * The contents of a _redirects file (used to apply redirects or proxy paths ahead of asset serving)
80
+ * @default the `_redirects` file in the assets directory, if present
79
81
  */
80
82
  _redirects?: string;
81
83
 
@@ -6,7 +6,5 @@ export type Runtime = "bun" | "deno" | "node";
6
6
  export function detectRuntime(): Runtime {
7
7
  if (typeof Bun !== "undefined" && Bun !== null) return "bun";
8
8
  if (typeof Deno !== "undefined" && Deno !== null) return "deno";
9
- if (typeof process !== "undefined" && process.release?.name === "node")
10
- return "node";
11
9
  return "node";
12
10
  }
@@ -1,20 +1,24 @@
1
+ import path from "pathe";
1
2
  import { exists } from "./exists.ts";
3
+ import { findWorkspaceRoot } from "./find-workspace-root.ts";
2
4
 
3
5
  export type PackageManager = "bun" | "pnpm" | "yarn" | "npm" | "deno";
4
6
 
5
7
  export async function detectPackageManager(
6
- path: string = process.cwd(),
8
+ root: string = process.cwd(),
7
9
  ): Promise<PackageManager> {
8
- if (await exists(`${path}/deno.lock`)) return "deno";
10
+ root = await findWorkspaceRoot(root);
11
+
12
+ if (await exists(path.resolve(root, "deno.lock"))) return "deno";
9
13
  if (
10
- (await exists(`${path}/deno.json`)) ||
11
- (await exists(`${path}/deno.jsonc`))
14
+ (await exists(path.resolve(root, "deno.json"))) ||
15
+ (await exists(path.resolve(root, "deno.jsonc")))
12
16
  )
13
17
  return "deno";
14
- if (await exists(`${path}/bun.lockb`)) return "bun";
15
- if (await exists(`${path}/bun.lock`)) return "bun";
16
- if (await exists(`${path}/pnpm-lock.yaml`)) return "pnpm";
17
- if (await exists(`${path}/yarn.lock`)) return "yarn";
18
+ if (await exists(path.resolve(root, "bun.lockb"))) return "bun";
19
+ if (await exists(path.resolve(root, "bun.lock"))) return "bun";
20
+ if (await exists(path.resolve(root, "pnpm-lock.yaml"))) return "pnpm";
21
+ if (await exists(path.resolve(root, "yarn.lock"))) return "yarn";
18
22
 
19
23
  if (process.env.npm_execpath?.includes("bun")) {
20
24
  return "bun";
@@ -1,9 +1,15 @@
1
1
  import alchemy from "alchemy";
2
- import { TanStackStart } from "alchemy/cloudflare";
2
+ import { R2Bucket, TanStackStart } from "alchemy/cloudflare";
3
3
 
4
4
  const app = await alchemy("{projectName}");
5
5
 
6
- export const website = await TanStackStart("website");
6
+ const r2 = await R2Bucket("r2");
7
+
8
+ export const website = await TanStackStart("website", {
9
+ bindings: {
10
+ R2: r2,
11
+ },
12
+ });
7
13
 
8
14
  console.log({
9
15
  url: website.url,
@@ -1,22 +1,17 @@
1
- import fs from "node:fs";
2
- import { useCallback, useState } from "react";
3
1
  import { createFileRoute, useRouter } from "@tanstack/react-router";
4
2
  import { createServerFn } from "@tanstack/react-start";
3
+ import { env } from "cloudflare:workers";
4
+ import { useCallback, useState } from "react";
5
5
 
6
- const filePath = "todos.json";
6
+ const key = "todos.json";
7
7
 
8
- async function readTodos() {
9
- return JSON.parse(
10
- await fs.promises.readFile(filePath, "utf-8").catch(() =>
11
- JSON.stringify(
12
- [
13
- { id: 1, name: "Get groceries" },
14
- { id: 2, name: "Buy a new phone" },
15
- ],
16
- null,
17
- 2,
18
- ),
19
- ),
8
+ async function readTodos(): Promise<{ id: number; name: string }[]> {
9
+ const todos = await env.R2.get(key);
10
+ return (
11
+ (await todos?.json()) ?? [
12
+ { id: 1, name: "Get groceries" },
13
+ { id: 2, name: "Buy a new phone" },
14
+ ]
20
15
  );
21
16
  }
22
17
 
@@ -29,7 +24,7 @@ const addTodo = createServerFn({ method: "POST" })
29
24
  .handler(async ({ data }) => {
30
25
  const todos = await readTodos();
31
26
  todos.push({ id: todos.length + 1, name: data });
32
- await fs.promises.writeFile(filePath, JSON.stringify(todos, null, 2));
27
+ await env.R2.put(key, JSON.stringify(todos, null, 2));
33
28
  return todos;
34
29
  });
35
30
 
@@ -83,7 +83,7 @@ const renderErrorHtml = (props) => `
83
83
  Alchemy</a>.</p>
84
84
  </div>
85
85
  <div class="bg-slate-200 px-5 py-3 flex flex-col w-full max-w-lg">
86
- <p class="text-sm text-slate-500">Alchemy 0.77.5</p>
86
+ <p class="text-sm text-slate-500">Alchemy 0.78.0</p>
87
87
  </div>
88
88
  </div>
89
89
  </body>