alchemy 0.77.4 → 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 +4 -4
  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 +7 -4
  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
@@ -18,88 +18,106 @@ export namespace CloudflareAuth {
18
18
  name: string;
19
19
  };
20
20
  export const ALL_SCOPES = {
21
- // TODO: Verify descriptions marked with `//*`
22
- "access:read": "Read Cloudflare Access", //*
23
- "access:write": "Write Cloudflare Access", //*
21
+ "access:read":
22
+ "See Cloudflare Access data such as zones, applications, certificates, device postures, groups, identity providers, login counts, organizations, policies, service tokens, and users",
23
+ "access:write":
24
+ "See and change Cloudflare Access data such as zones, applications, certificates, device postures, groups, identity providers, login counts, organizations, policies, service tokens, and users",
24
25
  "account:read":
25
- "See your account info such as account details, analytics, and memberships.",
26
- "agw:read": "Read API Gateway", //*
27
- "agw:run": "Run API Gateway", //*
28
- "ai:read": "Read access to Workers AI catalog and assets",
29
- "ai:write": "See and change Workers AI catalog and assets",
30
- "aiaudit:read": "Read AI Audit", //*
31
- "aiaudit:write": "Write AI Audit", //*
32
- "aig:read": "Read AI Gateway", //*
33
- "aig:write": "Write AI Gateway", //*
34
- "auditlogs:read": "Read audit logs", //*
35
- "browser:read": "Read Browser", //*
36
- "browser:write": "Write Browser", //*
37
- "cfone:read": "Read Cloudflare One", //*
38
- "cfone:write": "Write Cloudflare One", //*
39
- "cloudchamber:write": "Manage Cloudchamber",
40
- "constellation:write": "Write Constellation", //*
41
- "containers:write": "Manage Workers Containers",
42
- "d1:write": "See and change D1 Databases.",
43
- "dex:read": "Read DEX", //*
44
- "dex:write": "Write DEX", //*
45
- "dns_analytics:read": "Read DNS analytics",
46
- "dns_records:edit": "Edit DNS records",
47
- "dns_records:read": "Read DNS records",
48
- "dns_settings:read": "Read DNS settings",
49
- "firstpartytags:write": "Write First Party Tags", //*
50
- "lb:edit": "Edit Load Balancer", //*
51
- "lb:read": "Read Load Balancer", //*
52
- "logpush:read": "Read Logpush", //*
53
- "logpush:write": "Write Logpush", //*
54
- "notification:read": "Read Notifications", //*
55
- "notification:write": "Write Notifications", //*
56
- "pages:read": "Read access to Pages projects, settings, and deployments.",
57
- "pages:write": "See and change Pages projects, settings, and deployments.",
58
- "pipelines:read": "Read access to Pipelines configurations and data",
59
- "pipelines:setup": "Setup access to Pipelines configurations and data",
60
- "pipelines:write": "See and change Pipelines configurations and data",
61
- "query_cache:write": "Write Query Cache", //*
62
- "queues:write": "See and change Queues settings and data",
63
- "r2_catalog:write": "Write R2 Catalog", //*
64
- "radar:read": "Read Radar", //*
65
- "rag:read": "Read RAG", //*
66
- "rag:write": "Write RAG", //*
67
- "secrets_store:read":
68
- "Read access to secrets + stores within the Secrets Store",
69
- "secrets_store:write":
70
- "See and change secrets + stores within the Secrets Store",
71
- "sso-connector:read": "Read SSO Connector", //*
72
- "sso-connector:write": "Write SSO Connector", //*
73
- "ssl_certs:write": "See and manage mTLS certificates for your account",
74
- "teams:pii": "Read Teams PII", //*
75
- "teams:read": "Read Teams", //*
76
- "teams:secure_location": "Read Secure Location", //*
77
- "teams:write": "Write Teams", //*
78
- "url_scanner:read": "Read URL Scanner", //*
79
- "url_scanner:write": "Write URL Scanner", //*
26
+ "See your account info such as account details, analytics, and memberships",
27
+ "agw:read": "Grants read level access to Agents Gateway",
28
+ "agw:run": "Grants run level access to Agents Gateway",
29
+ "agw:write": "Grants read and write level access to Agents Gateway",
30
+ "ai:read": "Grants read level access to Workers AI",
31
+ "ai:write": "Grants write level access to Workers AI",
32
+ "aiaudit:read": "Grants read level access to AI Audit",
33
+ "aiaudit:write": "Grants write level access to AI Audit",
34
+ "aig:read": "Grants read level access to AI Gateway",
35
+ "aig:write": "Grants write level access to AI Gateway",
36
+ "auditlogs:read": "View Cloudflare Account Audit Logs",
37
+ "browser:read": "Grants read level access to Browser Rendering",
38
+ "browser:write": "Grants write level access to Browser Rendering",
39
+ "cfone:read": "Grants read level access to Cloudforce One data",
40
+ "cfone:write": "Grants write level access to Cloudforce One data",
41
+ "cloudchamber:write": "See and make changes to Cloudchamber",
42
+ "connectivity:admin":
43
+ "See, change, and bind to Connectivity Directory services, including creating services targeting Cloudflare Tunnel",
44
+ "connectivity:bind":
45
+ "read, list, and bind to Connectivity Directory services, as well as read and list Cloudflare Tunnels",
46
+ "connectivity:read":
47
+ "See Connectivity Directory services and Cloudflare Tunnels",
48
+ "constellation:write":
49
+ "Grants write access to Constellation configuration and models",
50
+ "containers:write": "See and make changes to Workers Containers",
51
+ "d1:write": "See and make changes to D1",
52
+ "dex:read": "Grants read level access to Cloudflare DEX",
53
+ "dex:write": "Grants write level access to Cloudflare DEX",
54
+ "dns_analytics:read":
55
+ "Grants read level access to Cloudflare DNS Analytics",
56
+ "dns_records:edit": "Grants edit level access to dns records",
57
+ "dns_records:read": "Grants read level access to dns records",
58
+ "dns_settings:read": "Grants read level access to Cloudflare DNS Settings",
59
+ "firstpartytags:write":
60
+ "Can see, edit and publish Google tag gateway configuration.",
61
+ "lb:edit": "Grants edit level access to lb and lb pools",
62
+ "lb:read": "Grants read level access to lb and lb pools",
63
+ "logpush:read": "See Cloudflare Logpush data",
64
+ "logpush:write": "See and change Cloudflare Logpush data",
65
+ "notification:read": "View Cloudflare Notifications",
66
+ "notification:write": "View and Modify Cloudflare Notifications",
67
+ "pages:read": "See Cloudflare Pages projects, settings and deployments",
68
+ "pages:write":
69
+ "See and change Cloudflare Pages projects, settings and deployments",
70
+ "pipelines:read": "Grants read level access to Cloudflare Pipelines",
71
+ "pipelines:setup":
72
+ "Grants permission to generate R2 tokens for Workers Pipelines",
73
+ "pipelines:write": "Grants write level access to Cloudflare Pipelines",
74
+ "query_cache:write": "See and make changes to Hyperdrive",
75
+ "queues:write": "See and change Cloudflare Queues settings and data",
76
+ "r2_catalog:write": "Grants write level access to R2 Data Catalog",
77
+ "radar:read": "Grants access to read Cloudflare Radar data",
78
+ "rag:read": "Grants read level access to Auto Rag",
79
+ "rag:write": "Grants write level access to Auto Rag",
80
+ "secrets_store:read": "Grants read level access to Secrets Store",
81
+ "secrets_store:write": "Grants write level access to Secrets Store",
82
+ "ssl_certs:write":
83
+ "Grants read and write access to SSL MTLS certificates or Certificate Store",
84
+ "sso-connector:read": "See Cloudflare SSO connectors",
85
+ "sso-connector:write":
86
+ "See Cloudflare SSO connectors to toggle activation and deactivation of SSO",
87
+ "teams:pii": "See personally identifiable Cloudflare Teams data",
88
+ "teams:read":
89
+ "See Cloudflare Teams data such as zones, gateway, and argo tunnel details",
90
+ "teams:secure_location":
91
+ "See all DNS Location data but can only change secure DNS Locations",
92
+ "teams:write":
93
+ "See and change Cloudflare Teams data such as zones, gateway, and argo tunnel details",
94
+ "url_scanner:read": "Grants read level access to URL Scanner",
95
+ "url_scanner:write": "Grants write level access to URL Scanner",
80
96
  "user:read":
81
- "See your user info such as name, email address, and account memberships.",
82
- "vectorize:write": "Write Vectorize", //*
97
+ "See your user info such as name, email address, and account memberships",
98
+ "vectorize:write": "See and make changes to Vectorize",
99
+ "workers:read":
100
+ "See Cloudflare Workers data such as zones, KV storage, R2 storage, scripts, and routes",
83
101
  "workers:write":
84
- "See and change Cloudflare Workers data such as zones, KV storage, namespaces, scripts, and routes.",
85
- "workers_builds:read": "Read Workers Builds", //*
86
- "workers_builds:write": "Write Workers Builds", //*
102
+ "See and change Cloudflare Workers data such as zones, KV storage, R2 storage, scripts, and routes",
103
+ "workers_builds:read":
104
+ "See Cloudflare Workers Builds data such as builds, build configuration, and build logs",
105
+ "workers_builds:write":
106
+ "See and change Cloudflare Workers Builds data such as builds, build configuration, and build logs",
87
107
  "workers_kv:write":
88
- "See and change Cloudflare Workers KV Storage data such as keys and namespaces.",
89
- "workers_observability:read": "Read Workers Observability", //*
90
- "workers_observability:write": "Write Workers Observability", //*
108
+ "See and change Cloudflare Workers KV Storage data such as keys and namespaces",
109
+ "workers_observability:read":
110
+ "Grants read access to Cloudflare Workers Observability",
91
111
  "workers_observability_telemetry:write":
92
- "Write Workers Observability Telemetry", //*
112
+ "Grants write access to Cloudflare Workers Observability Telemetry API",
93
113
  "workers_routes:write":
94
- "See and change Cloudflare Workers data such as filters and routes.",
114
+ "See and change Cloudflare Workers data such as filters and routes",
95
115
  "workers_scripts:write":
96
- "See and change Cloudflare Workers scripts, durable objects, subdomains, triggers, and tail data.",
97
- "workers_tail:read": "See Cloudflare Workers tail and script data.",
98
- "zone:read": "Grants read level access to account zone.",
99
- // Not granted yet
100
- // "connectivity:admin":
101
- // "See, change, and bind to Connectivity Directory services, including creating services targeting Cloudflare Tunnel.",
102
- };
116
+ "See and change Cloudflare Workers scripts, durable objects, subdomains, triggers, and tail data",
117
+ "workers_tail:read": "See Cloudflare Workers tail and script data",
118
+ "zone:read": "Grants read level access to account zone",
119
+ } as const;
120
+ export type Scope = keyof typeof ALL_SCOPES;
103
121
  export const DEFAULT_SCOPES = [
104
122
  "account:read",
105
123
  "user:read",
@@ -119,6 +137,7 @@ export namespace CloudflareAuth {
119
137
  "containers:write",
120
138
  "cloudchamber:write",
121
139
  "vectorize:write",
140
+ "connectivity:admin",
122
141
  ];
123
142
 
124
143
  /**
@@ -16,6 +16,7 @@ export const getCloudflareEnvProxy = async <E>(
16
16
 
17
17
  export const getPlatformProxyOptions = (
18
18
  input: GetPlatformProxyOptions = {},
19
+ throws: boolean = true,
19
20
  ): GetPlatformProxyOptions => {
20
21
  const persist =
21
22
  input.persist === false
@@ -37,7 +38,10 @@ export const getPlatformProxyOptions = (
37
38
  }
38
39
  return {
39
40
  ...input,
40
- configPath: validateConfigPath(input.configPath ?? getDefaultConfigPath()),
41
+ configPath: validateConfigPath(
42
+ input.configPath ?? getDefaultConfigPath(),
43
+ throws,
44
+ ),
41
45
  persist,
42
46
  };
43
47
  };
@@ -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;
@@ -387,10 +387,12 @@ const _D1Database = Resource(
387
387
  );
388
388
  }
389
389
  if (
390
- props.jurisdiction &&
391
- props.jurisdiction !== this.output?.jurisdiction
390
+ (props.jurisdiction ?? "default") !==
391
+ (this.output?.jurisdiction ?? "default")
392
392
  ) {
393
- return this.replace();
393
+ throw new Error(
394
+ `Cannot update jurisdiction from '${this.output.jurisdiction}' to '${props.jurisdiction}' after database creation.`,
395
+ );
394
396
  }
395
397
  // Update the database with new properties
396
398
  dbData = await updateDatabase(api, this.output.id, props);
@@ -469,7 +471,8 @@ export async function createDatabase(
469
471
  // Create new D1 database
470
472
  const createPayload: any = {
471
473
  name: databaseName,
472
- jurisdiction: props.jurisdiction,
474
+ jurisdiction:
475
+ props.jurisdiction !== "default" ? props.jurisdiction : undefined,
473
476
  };
474
477
 
475
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.4</p>
86
+ <p class="text-sm text-slate-500">Alchemy 0.78.0</p>
87
87
  </div>
88
88
  </div>
89
89
  </body>