alchemy 0.53.0 → 0.55.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 (214) hide show
  1. package/bin/alchemy.js +196353 -0
  2. package/bin/alchemy.ts +2 -2
  3. package/bin/commands/init.ts +1190 -0
  4. package/bin/constants.ts +18 -0
  5. package/bin/services/execute-alchemy.ts +7 -2
  6. package/bin/services/package-manager.ts +1 -1
  7. package/bin/types.ts +14 -5
  8. package/lib/aws/ec2/index.d.ts +10 -0
  9. package/lib/aws/ec2/index.d.ts.map +1 -0
  10. package/lib/aws/ec2/index.js +10 -0
  11. package/lib/aws/ec2/index.js.map +1 -0
  12. package/lib/aws/ec2/internet-gateway-attachment.d.ts +150 -0
  13. package/lib/aws/ec2/internet-gateway-attachment.d.ts.map +1 -0
  14. package/lib/aws/ec2/internet-gateway-attachment.js +248 -0
  15. package/lib/aws/ec2/internet-gateway-attachment.js.map +1 -0
  16. package/lib/aws/ec2/internet-gateway.d.ts +132 -0
  17. package/lib/aws/ec2/internet-gateway.d.ts.map +1 -0
  18. package/lib/aws/ec2/internet-gateway.js +311 -0
  19. package/lib/aws/ec2/internet-gateway.js.map +1 -0
  20. package/lib/aws/ec2/nat-gateway.d.ts +145 -0
  21. package/lib/aws/ec2/nat-gateway.d.ts.map +1 -0
  22. package/lib/aws/ec2/nat-gateway.js +618 -0
  23. package/lib/aws/ec2/nat-gateway.js.map +1 -0
  24. package/lib/aws/ec2/route-table-association.d.ts +134 -0
  25. package/lib/aws/ec2/route-table-association.d.ts.map +1 -0
  26. package/lib/aws/ec2/route-table-association.js +364 -0
  27. package/lib/aws/ec2/route-table-association.js.map +1 -0
  28. package/lib/aws/ec2/route-table.d.ts +94 -0
  29. package/lib/aws/ec2/route-table.d.ts.map +1 -0
  30. package/lib/aws/ec2/route-table.js +253 -0
  31. package/lib/aws/ec2/route-table.js.map +1 -0
  32. package/lib/aws/ec2/route.d.ts +130 -0
  33. package/lib/aws/ec2/route.d.ts.map +1 -0
  34. package/lib/aws/ec2/route.js +253 -0
  35. package/lib/aws/ec2/route.js.map +1 -0
  36. package/lib/aws/ec2/security-group-rule.d.ts +84 -0
  37. package/lib/aws/ec2/security-group-rule.d.ts.map +1 -0
  38. package/lib/aws/ec2/security-group-rule.js +123 -0
  39. package/lib/aws/ec2/security-group-rule.js.map +1 -0
  40. package/lib/aws/ec2/security-group.d.ts +84 -0
  41. package/lib/aws/ec2/security-group.d.ts.map +1 -0
  42. package/lib/aws/ec2/security-group.js +207 -0
  43. package/lib/aws/ec2/security-group.js.map +1 -0
  44. package/lib/aws/ec2/subnet.d.ts +110 -0
  45. package/lib/aws/ec2/subnet.d.ts.map +1 -0
  46. package/lib/aws/ec2/subnet.js +336 -0
  47. package/lib/aws/ec2/subnet.js.map +1 -0
  48. package/lib/aws/ec2/utils.d.ts +15 -0
  49. package/lib/aws/ec2/utils.d.ts.map +1 -0
  50. package/lib/aws/ec2/utils.js +79 -0
  51. package/lib/aws/ec2/utils.js.map +1 -0
  52. package/lib/aws/ec2/vpc.d.ts +160 -0
  53. package/lib/aws/ec2/vpc.d.ts.map +1 -0
  54. package/lib/aws/ec2/vpc.js +453 -0
  55. package/lib/aws/ec2/vpc.js.map +1 -0
  56. package/lib/aws/index.d.ts +7 -0
  57. package/lib/aws/index.d.ts.map +1 -1
  58. package/lib/aws/index.js +8 -0
  59. package/lib/aws/index.js.map +1 -1
  60. package/lib/aws/utils.d.ts +25 -0
  61. package/lib/aws/utils.d.ts.map +1 -0
  62. package/lib/aws/utils.js +89 -0
  63. package/lib/aws/utils.js.map +1 -0
  64. package/lib/build-date.d.ts +6 -0
  65. package/lib/build-date.d.ts.map +1 -0
  66. package/lib/build-date.js +8 -0
  67. package/lib/build-date.js.map +1 -0
  68. package/lib/cloudflare/astro.d.ts.map +1 -1
  69. package/lib/cloudflare/astro.js +4 -3
  70. package/lib/cloudflare/astro.js.map +1 -1
  71. package/lib/cloudflare/bundle/externals.d.ts +3 -0
  72. package/lib/cloudflare/bundle/externals.d.ts.map +1 -0
  73. package/lib/cloudflare/bundle/externals.js +35 -0
  74. package/lib/cloudflare/bundle/externals.js.map +1 -0
  75. package/lib/cloudflare/bundle/internal-worker-bundle.d.ts +8 -1
  76. package/lib/cloudflare/bundle/internal-worker-bundle.d.ts.map +1 -1
  77. package/lib/cloudflare/bundle/internal-worker-bundle.js +11 -4
  78. package/lib/cloudflare/bundle/internal-worker-bundle.js.map +1 -1
  79. package/lib/cloudflare/bundle/plugin-hot-reload.d.ts +4 -1
  80. package/lib/cloudflare/bundle/plugin-hot-reload.d.ts.map +1 -1
  81. package/lib/cloudflare/bundle/plugin-hot-reload.js +27 -11
  82. package/lib/cloudflare/bundle/plugin-hot-reload.js.map +1 -1
  83. package/lib/cloudflare/bundle/plugin-hybrid-node-compat.js +5 -1
  84. package/lib/cloudflare/bundle/plugin-hybrid-node-compat.js.map +1 -1
  85. package/lib/cloudflare/bundle/plugin-wasm.d.ts +6 -2
  86. package/lib/cloudflare/bundle/plugin-wasm.d.ts.map +1 -1
  87. package/lib/cloudflare/bundle/plugin-wasm.js +21 -47
  88. package/lib/cloudflare/bundle/plugin-wasm.js.map +1 -1
  89. package/lib/cloudflare/bundle/validate-node-compat.d.ts +2 -1
  90. package/lib/cloudflare/bundle/validate-node-compat.d.ts.map +1 -1
  91. package/lib/cloudflare/bundle/validate-node-compat.js +5 -4
  92. package/lib/cloudflare/bundle/validate-node-compat.js.map +1 -1
  93. package/lib/cloudflare/compatibility-date.gen.d.ts +1 -1
  94. package/lib/cloudflare/compatibility-date.gen.js +1 -1
  95. package/lib/cloudflare/d1-database.js +1 -1
  96. package/lib/cloudflare/d1-database.js.map +1 -1
  97. package/lib/cloudflare/d1-migrations.d.ts.map +1 -1
  98. package/lib/cloudflare/d1-migrations.js +8 -10
  99. package/lib/cloudflare/d1-migrations.js.map +1 -1
  100. package/lib/cloudflare/miniflare/miniflare-worker-options.d.ts +3 -3
  101. package/lib/cloudflare/miniflare/miniflare-worker-options.d.ts.map +1 -1
  102. package/lib/cloudflare/miniflare/miniflare-worker-options.js +23 -6
  103. package/lib/cloudflare/miniflare/miniflare-worker-options.js.map +1 -1
  104. package/lib/cloudflare/miniflare/miniflare.d.ts.map +1 -1
  105. package/lib/cloudflare/miniflare/miniflare.js +2 -4
  106. package/lib/cloudflare/miniflare/miniflare.js.map +1 -1
  107. package/lib/cloudflare/miniflare/remote-binding-proxy.d.ts.map +1 -1
  108. package/lib/cloudflare/miniflare/remote-binding-proxy.js +4 -6
  109. package/lib/cloudflare/miniflare/remote-binding-proxy.js.map +1 -1
  110. package/lib/cloudflare/react-router.d.ts +4 -0
  111. package/lib/cloudflare/react-router.d.ts.map +1 -1
  112. package/lib/cloudflare/react-router.js +8 -5
  113. package/lib/cloudflare/react-router.js.map +1 -1
  114. package/lib/cloudflare/worker-bundle.d.ts +80 -0
  115. package/lib/cloudflare/worker-bundle.d.ts.map +1 -0
  116. package/lib/cloudflare/worker-bundle.js +266 -0
  117. package/lib/cloudflare/worker-bundle.js.map +1 -0
  118. package/lib/cloudflare/worker.d.ts +1 -1
  119. package/lib/cloudflare/worker.d.ts.map +1 -1
  120. package/lib/cloudflare/worker.js +81 -195
  121. package/lib/cloudflare/worker.js.map +1 -1
  122. package/lib/esbuild/bundle.d.ts +1 -0
  123. package/lib/esbuild/bundle.d.ts.map +1 -1
  124. package/lib/state/cloudflare-state-store.js +1 -5
  125. package/lib/state/cloudflare-state-store.js.map +1 -1
  126. package/lib/state/dofs-state-store/store.d.ts.map +1 -1
  127. package/lib/state/dofs-state-store/store.js +4 -4
  128. package/lib/state/dofs-state-store/store.js.map +1 -1
  129. package/lib/util/params.d.ts +5 -0
  130. package/lib/util/params.d.ts.map +1 -0
  131. package/lib/util/params.js +30 -0
  132. package/lib/util/params.js.map +1 -0
  133. package/lib/util/timeout.d.ts +31 -0
  134. package/lib/util/timeout.d.ts.map +1 -0
  135. package/lib/util/timeout.js +109 -0
  136. package/lib/util/timeout.js.map +1 -0
  137. package/lib/util/xml.d.ts +5 -0
  138. package/lib/util/xml.d.ts.map +1 -0
  139. package/lib/util/xml.js +8 -0
  140. package/lib/util/xml.js.map +1 -0
  141. package/package.json +12 -5
  142. package/src/aws/ec2/index.ts +9 -0
  143. package/src/aws/ec2/internet-gateway-attachment.ts +468 -0
  144. package/src/aws/ec2/internet-gateway.ts +557 -0
  145. package/src/aws/ec2/nat-gateway.ts +1123 -0
  146. package/src/aws/ec2/route-table-association.ts +747 -0
  147. package/src/aws/ec2/route-table.ts +452 -0
  148. package/src/aws/ec2/route.ts +447 -0
  149. package/src/aws/ec2/security-group-rule.ts +229 -0
  150. package/src/aws/ec2/security-group.ts +418 -0
  151. package/src/aws/ec2/subnet.ts +625 -0
  152. package/src/aws/ec2/utils.ts +102 -0
  153. package/src/aws/ec2/vpc.ts +969 -0
  154. package/src/aws/index.ts +9 -0
  155. package/src/aws/utils.ts +137 -0
  156. package/src/build-date.ts +8 -0
  157. package/src/cloudflare/astro.ts +4 -3
  158. package/src/cloudflare/bundle/externals.ts +36 -0
  159. package/src/cloudflare/bundle/internal-worker-bundle.ts +12 -3
  160. package/src/cloudflare/bundle/plugin-hot-reload.ts +32 -12
  161. package/src/cloudflare/bundle/plugin-hybrid-node-compat.ts +6 -1
  162. package/src/cloudflare/bundle/plugin-wasm.ts +26 -54
  163. package/src/cloudflare/bundle/validate-node-compat.ts +6 -5
  164. package/src/cloudflare/compatibility-date.gen.ts +1 -1
  165. package/src/cloudflare/d1-database.ts +1 -1
  166. package/src/cloudflare/d1-migrations.ts +19 -18
  167. package/src/cloudflare/miniflare/miniflare-worker-options.ts +27 -13
  168. package/src/cloudflare/miniflare/miniflare.ts +2 -4
  169. package/src/cloudflare/miniflare/remote-binding-proxy.ts +5 -7
  170. package/src/cloudflare/react-router.ts +14 -6
  171. package/src/cloudflare/worker-bundle.ts +364 -0
  172. package/src/cloudflare/worker.ts +104 -241
  173. package/src/state/cloudflare-state-store.ts +1 -5
  174. package/src/state/dofs-state-store/store.ts +4 -4
  175. package/src/util/params.ts +33 -0
  176. package/src/util/timeout.ts +178 -0
  177. package/src/util/xml.ts +7 -0
  178. package/templates/nuxt/package.json +1 -1
  179. package/templates/nuxt/types/env.d.ts +11 -0
  180. package/templates/react-router/workers/app.ts +2 -2
  181. package/templates/rwsdk/.cursor/rules/alchemy_cloudflare.mdc +385 -0
  182. package/templates/rwsdk/wrangler.jsonc +10 -7
  183. package/templates/typescript/tsconfig.json +1 -2
  184. package/bin/alchemy.mjs +0 -51766
  185. package/lib/cloudflare/bundle/esbuild.d.ts +0 -16
  186. package/lib/cloudflare/bundle/esbuild.d.ts.map +0 -1
  187. package/lib/cloudflare/bundle/esbuild.js +0 -159
  188. package/lib/cloudflare/bundle/esbuild.js.map +0 -1
  189. package/lib/cloudflare/bundle/fs.d.ts +0 -18
  190. package/lib/cloudflare/bundle/fs.d.ts.map +0 -1
  191. package/lib/cloudflare/bundle/fs.js +0 -78
  192. package/lib/cloudflare/bundle/fs.js.map +0 -1
  193. package/lib/cloudflare/bundle/index.d.ts +0 -3
  194. package/lib/cloudflare/bundle/index.d.ts.map +0 -1
  195. package/lib/cloudflare/bundle/index.js +0 -2
  196. package/lib/cloudflare/bundle/index.js.map +0 -1
  197. package/lib/cloudflare/bundle/inline.d.ts +0 -12
  198. package/lib/cloudflare/bundle/inline.d.ts.map +0 -1
  199. package/lib/cloudflare/bundle/inline.js +0 -32
  200. package/lib/cloudflare/bundle/inline.js.map +0 -1
  201. package/lib/cloudflare/bundle/normalize.d.ts +0 -21
  202. package/lib/cloudflare/bundle/normalize.d.ts.map +0 -1
  203. package/lib/cloudflare/bundle/normalize.js +0 -49
  204. package/lib/cloudflare/bundle/normalize.js.map +0 -1
  205. package/lib/cloudflare/bundle/shared.d.ts +0 -30
  206. package/lib/cloudflare/bundle/shared.d.ts.map +0 -1
  207. package/lib/cloudflare/bundle/shared.js +0 -34
  208. package/lib/cloudflare/bundle/shared.js.map +0 -1
  209. package/src/cloudflare/bundle/esbuild.ts +0 -194
  210. package/src/cloudflare/bundle/fs.ts +0 -103
  211. package/src/cloudflare/bundle/index.ts +0 -6
  212. package/src/cloudflare/bundle/inline.ts +0 -42
  213. package/src/cloudflare/bundle/normalize.ts +0 -76
  214. package/src/cloudflare/bundle/shared.ts +0 -73
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Timeout configuration interface
3
+ */
4
+ export interface TimeoutConfig {
5
+ maxAttempts: number;
6
+ delayMs: number;
7
+ }
8
+
9
+ /**
10
+ * Resource timeout error class
11
+ */
12
+ export class ResourceTimeoutError extends Error {
13
+ constructor(
14
+ resourceType: string,
15
+ resourceId: string,
16
+ timeoutMs: number,
17
+ operation: string,
18
+ ) {
19
+ super(
20
+ `${resourceType} ${resourceId} did not complete ${operation} within ${timeoutMs}ms`,
21
+ );
22
+ this.name = "ResourceTimeoutError";
23
+ }
24
+ }
25
+
26
+ /**
27
+ * Validate timeout configuration
28
+ */
29
+ export function validateTimeoutConfig(config: TimeoutConfig): void {
30
+ if (config.maxAttempts <= 0) {
31
+ throw new Error("maxAttempts must be greater than 0");
32
+ }
33
+ if (config.delayMs <= 0) {
34
+ throw new Error("delayMs must be greater than 0");
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Merge default timeout configuration with optional overrides
40
+ */
41
+ export function mergeTimeoutConfig(
42
+ defaultConfig: TimeoutConfig,
43
+ override?: Partial<TimeoutConfig>,
44
+ ): TimeoutConfig {
45
+ if (!override) {
46
+ return defaultConfig;
47
+ }
48
+
49
+ const merged = {
50
+ maxAttempts: override.maxAttempts ?? defaultConfig.maxAttempts,
51
+ delayMs: override.delayMs ?? defaultConfig.delayMs,
52
+ };
53
+
54
+ validateTimeoutConfig(merged);
55
+ return merged;
56
+ }
57
+
58
+ /**
59
+ * Wait for a resource to reach a desired state with configurable timeout
60
+ */
61
+ export async function waitForResourceState<T>(
62
+ checkFunction: () => Promise<T>,
63
+ isReady: (result: T) => boolean,
64
+ config: TimeoutConfig,
65
+ resourceId: string,
66
+ resourceType: string,
67
+ operation = "become ready",
68
+ ): Promise<void> {
69
+ validateTimeoutConfig(config);
70
+ const { maxAttempts, delayMs } = config;
71
+
72
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
73
+ try {
74
+ const result = await checkFunction();
75
+ if (isReady(result)) {
76
+ return;
77
+ }
78
+
79
+ console.log(
80
+ `${resourceType} ${resourceId} not ready, waiting... (attempt ${attempt}/${maxAttempts})`,
81
+ );
82
+
83
+ if (attempt < maxAttempts) {
84
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
85
+ }
86
+ } catch (error) {
87
+ console.log(
88
+ `Error checking ${resourceType} status (attempt ${attempt}/${maxAttempts}):`,
89
+ error,
90
+ );
91
+
92
+ // Check if this is a non-retryable error (contains "NonRetryableError" in the name)
93
+ if (error instanceof Error && error.name.includes("NonRetryableError")) {
94
+ throw error;
95
+ }
96
+
97
+ if (attempt === maxAttempts) {
98
+ throw error;
99
+ }
100
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
101
+ }
102
+ }
103
+
104
+ const totalTimeMs = maxAttempts * delayMs;
105
+ throw new ResourceTimeoutError(
106
+ resourceType,
107
+ resourceId,
108
+ totalTimeMs,
109
+ operation,
110
+ );
111
+ }
112
+ /**
113
+ * Generic function to wait for an operation to complete
114
+ * This is the most flexible version that can handle any type of operation
115
+ */
116
+ export async function waitForOperation<T>(
117
+ checkFunction: () => Promise<T>,
118
+ isComplete: (result: T) => boolean,
119
+ config: TimeoutConfig,
120
+ resourceId: string,
121
+ resourceType: string,
122
+ operation = "complete",
123
+ onError?: (error: any, attempt: number, maxAttempts: number) => boolean, // Return true to continue retrying
124
+ ): Promise<void> {
125
+ validateTimeoutConfig(config);
126
+ const { maxAttempts, delayMs } = config;
127
+
128
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
129
+ try {
130
+ const result = await checkFunction();
131
+
132
+ if (isComplete(result)) {
133
+ console.log(`${resourceType} ${resourceId} ${operation} completed`);
134
+ return;
135
+ }
136
+
137
+ console.log(
138
+ `${resourceType} ${resourceId} ${operation} in progress... (attempt ${attempt}/${maxAttempts})`,
139
+ );
140
+
141
+ if (attempt < maxAttempts) {
142
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
143
+ }
144
+ } catch (error) {
145
+ // Allow custom error handling
146
+ if (onError) {
147
+ const shouldContinue = onError(error, attempt, maxAttempts);
148
+ if (!shouldContinue) {
149
+ // If onError returns false, it means the operation completed successfully
150
+ // (e.g., resource was deleted and not found)
151
+ console.log(
152
+ `${resourceType} ${resourceId} ${operation} completed (via error handling)`,
153
+ );
154
+ return;
155
+ }
156
+ } else {
157
+ console.log(
158
+ `Error checking ${resourceType} ${operation} status (attempt ${attempt}/${maxAttempts}):`,
159
+ error,
160
+ );
161
+ }
162
+
163
+ if (attempt === maxAttempts) {
164
+ throw error;
165
+ }
166
+
167
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
168
+ }
169
+ }
170
+
171
+ const totalTimeMs = maxAttempts * delayMs;
172
+ throw new ResourceTimeoutError(
173
+ resourceType,
174
+ resourceId,
175
+ totalTimeMs,
176
+ operation,
177
+ );
178
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Extract value from XML string
3
+ */
4
+ export function extractXmlValue(xml: string, tagName: string): string {
5
+ const match = xml.match(new RegExp(`<${tagName}>(.*?)<\/${tagName}>`));
6
+ return match ? match[1] : "";
7
+ }
@@ -12,7 +12,7 @@
12
12
  "preview": "nuxt preview"
13
13
  },
14
14
  "dependencies": {
15
- "nuxt": "^3.17.5",
15
+ "nuxt": "^4.0.1",
16
16
  "vue": "^3.5.16",
17
17
  "vue-router": "^4.5.1"
18
18
  },
@@ -14,3 +14,14 @@ declare module "cloudflare:workers" {
14
14
  export interface Env extends CloudflareEnv {}
15
15
  }
16
16
  }
17
+
18
+ declare module "h3" {
19
+ interface H3EventContext {
20
+ cf: CfProperties;
21
+ cloudflare: {
22
+ request: Request;
23
+ env: CloudflareEnv;
24
+ context: ExecutionContext;
25
+ };
26
+ }
27
+ }
@@ -15,9 +15,9 @@ const requestHandler = createRequestHandler(
15
15
  );
16
16
 
17
17
  export default {
18
- async fetch(request, env, ctx) {
18
+ async fetch(request: Request, env: Env, ctx: ExecutionContext) {
19
19
  return requestHandler(request, {
20
20
  cloudflare: { env, ctx },
21
21
  });
22
22
  },
23
- } satisfies ExportedHandler<Env>;
23
+ }
@@ -0,0 +1,385 @@
1
+ ---
2
+ description: Core Alchemy development guidelines and patterns
3
+ globs: *.ts
4
+ alwaysApply: true
5
+ ---
6
+ # Alchemy - TypeScript-native Infrastructure as Code
7
+
8
+ Alchemy is a TypeScript-native Infrastructure-as-Code framework that allows you to define cloud resources using familiar TypeScript patterns. This guide focuses on Cloudflare integration.
9
+
10
+ ## Core Concepts
11
+
12
+ **Resources**: Async functions that manage cloud infrastructure lifecycle (create, update, delete)
13
+ **Bindings**: Type-safe connections between resources (workers, KV stores, databases)
14
+ **State Management**: Tracks resource state for consistent deployments
15
+ **Finalization**: Always call `app.finalize()` to clean up orphaned resources
16
+
17
+ ## Basic Setup
18
+
19
+ ### Project Structure
20
+ ```typescript
21
+ // alchemy.run.ts - Your infrastructure definition
22
+ import alchemy from "alchemy";
23
+ import { Worker, KVNamespace } from "alchemy/cloudflare";
24
+
25
+ const app = await alchemy("my-app");
26
+
27
+ export const worker = await Worker("api", {
28
+ entrypoint: "./src/worker.ts",
29
+ bindings: {
30
+ CACHE: await KVNamespace("cache", { title: "cache-store" })
31
+ }
32
+ });
33
+
34
+ console.log({ url: worker.url });
35
+ await app.finalize(); // ⚠️ ALWAYS call finalize()
36
+ ```
37
+
38
+ ### Environment Setup
39
+ ```bash
40
+ # .env - Required for secret encryption
41
+ ALCHEMY_PASSWORD=your-secure-password
42
+
43
+ # Optional: Use Cloudflare state store in production
44
+ ALCHEMY_STATE_STORE=cloudflare
45
+ ```
46
+
47
+ ### Commands
48
+ ```bash
49
+ bun alchemy deploy # Deploy
50
+ bun alchemy dev # Local development
51
+ bun alchemy destroy # Destroy all resources
52
+ ```
53
+
54
+ ## Workers
55
+
56
+ ### Basic Worker
57
+ ```typescript
58
+ const worker = await Worker("api", {
59
+ entrypoint: "./src/worker.ts",
60
+ url: true, // Get public URL
61
+ });
62
+ ```
63
+
64
+ ### Worker with Bindings
65
+ ```typescript
66
+ const worker = await Worker("api", {
67
+ entrypoint: "./src/worker.ts",
68
+ bindings: {
69
+ // Resource bindings
70
+ CACHE: kvNamespace,
71
+ STORAGE: r2Bucket,
72
+ COUNTER: durableObjectNamespace,
73
+ QUEUE: queue,
74
+ API: otherWorker,
75
+
76
+ // Environment variables
77
+ API_KEY: alchemy.secret(process.env.API_KEY),
78
+ DEBUG: "true"
79
+ }
80
+ });
81
+ ```
82
+
83
+ ### Worker Implementation
84
+ ```typescript
85
+ // src/worker.ts
86
+ import type { worker } from "../alchemy.run";
87
+
88
+ export default {
89
+ async fetch(request: Request, env: typeof worker.Env): Promise<Response> {
90
+ // Type-safe access to all bindings
91
+ const cached = await env.CACHE.get("key");
92
+ const apiKey = env.API_KEY;
93
+
94
+ return new Response("Hello World");
95
+ }
96
+ };
97
+ ```
98
+
99
+ ### Type Safety Setup
100
+ ```typescript
101
+ // types/env.d.ts
102
+ import type { worker } from "../alchemy.run";
103
+
104
+ declare module "cloudflare:workers" {
105
+ namespace Cloudflare {
106
+ export interface Env extends typeof worker.Env {}
107
+ }
108
+ }
109
+ ```
110
+
111
+ ## Durable Objects
112
+
113
+ ### Creating Durable Object Namespace
114
+ ```typescript
115
+ // Use 'new' instead of 'await' for DurableObjectNamespace
116
+ const counter = DurableObjectNamespace("counter", {
117
+ className: "Counter",
118
+ sqlite: true, // Enable SQLite storage
119
+ });
120
+
121
+ const worker = await Worker("api", {
122
+ entrypoint: "./src/worker.ts",
123
+ bindings: {
124
+ COUNTER: counter, // Bind to worker
125
+ }
126
+ });
127
+ ```
128
+
129
+ ### Durable Object Implementation
130
+ ```typescript
131
+ // src/counter.ts
132
+ import { DurableObject } from "cloudflare:workers";
133
+
134
+ export class Counter extends DurableObject {
135
+ async increment(): Promise<number> {
136
+ let count = (await this.ctx.storage.get("count")) || 0;
137
+ count++;
138
+ await this.ctx.storage.put("count", count);
139
+ return count;
140
+ }
141
+
142
+ async fetch(request: Request): Promise<Response> {
143
+ const count = await this.increment();
144
+ return new Response(JSON.stringify({ count }));
145
+ }
146
+ }
147
+ ```
148
+
149
+ ### Using Durable Objects in Worker
150
+ ```typescript
151
+ // src/worker.ts
152
+ export default {
153
+ async fetch(request: Request, env: typeof worker.Env) {
154
+ // Get a Durable Object instance
155
+ const id = env.COUNTER.idFromName("global-counter");
156
+ const obj = env.COUNTER.get(id);
157
+
158
+ // Call the Durable Object
159
+ return obj.fetch(request);
160
+ }
161
+ };
162
+ ```
163
+
164
+ ### Cross-Script Durable Objects
165
+ ```typescript
166
+ // Method 1: Re-export from provider worker
167
+ const sharedCounter = host.bindings.SHARED_COUNTER;
168
+
169
+ // Method 2: Reference by worker script name
170
+ const counter = DurableObjectNamespace("counter", {
171
+ className: "Counter",
172
+ scriptName: "provider-worker"
173
+ });
174
+ ```
175
+
176
+ ## Key Resources
177
+
178
+ ### KV Namespace
179
+ ```typescript
180
+ const cache = await KVNamespace("cache", {
181
+ title: "my-cache-store"
182
+ });
183
+
184
+ // Usage in worker
185
+ const value = await env.CACHE.get("key");
186
+ await env.CACHE.put("key", "value", { expirationTtl: 3600 });
187
+ ```
188
+
189
+ ### R2 Bucket
190
+ ```typescript
191
+ const storage = await R2Bucket("storage", {
192
+ allowPublicAccess: false
193
+ });
194
+
195
+ // Usage in worker
196
+ const object = await env.STORAGE.get("file.txt");
197
+ await env.STORAGE.put("file.txt", "content");
198
+ ```
199
+
200
+ ### Queue
201
+ ```typescript
202
+ // Typed queue
203
+ const queue = await Queue<{ name: string; email: string }>("notifications");
204
+
205
+ // Producer worker
206
+ const producer = await Worker("producer", {
207
+ bindings: { QUEUE: queue },
208
+ // Queue sending code
209
+ });
210
+
211
+ // Consumer worker
212
+ const consumer = await Worker("consumer", {
213
+ eventSources: [queue], // Register as consumer
214
+ // Queue processing code
215
+ });
216
+
217
+ // Worker implementation for queue processing
218
+ export default {
219
+ async queue(batch: typeof queue.Batch, env: Env) {
220
+ for (const message of batch.messages) {
221
+ console.log("Processing:", message.body);
222
+ message.ack(); // Acknowledge message
223
+ }
224
+ }
225
+ };
226
+ ```
227
+
228
+ ### Custom Domains
229
+ ```typescript
230
+ const worker = await Worker("api", {
231
+ entrypoint: "./src/worker.ts",
232
+ routes: [
233
+ { pattern: "api.example.com/*", zone: "example.com" },
234
+ { pattern: "example.com/api/*", zone: "example.com" }
235
+ ]
236
+ });
237
+ ```
238
+
239
+ ## Converting Existing Cloudflare Projects
240
+
241
+ ### 1. Migrate wrangler.toml Configuration
242
+ ```toml
243
+ # Old wrangler.toml
244
+ name = "my-worker"
245
+ main = "src/index.js"
246
+
247
+ [env.production]
248
+ kv_namespaces = [
249
+ { binding = "CACHE", id = "abc123" }
250
+ ]
251
+
252
+ [[env.production.r2_buckets]]
253
+ binding = "STORAGE"
254
+ bucket_name = "my-bucket"
255
+ ```
256
+
257
+ ```typescript
258
+ // New alchemy.run.ts
259
+ const worker = await Worker("my-worker", {
260
+ entrypoint: "./src/index.js",
261
+ bindings: {
262
+ CACHE: await KVNamespace("cache", {
263
+ title: "cache-store",
264
+ adopt: true // Use existing KV namespace
265
+ }),
266
+ STORAGE: await R2Bucket("storage", {
267
+ name: "my-bucket",
268
+ adopt: true // Use existing bucket
269
+ })
270
+ }
271
+ });
272
+ ```
273
+
274
+ ### 2. Enable Development Compatibility
275
+ ```typescript
276
+ // Generate wrangler.json for local development
277
+ await WranglerJson("wrangler.json", {
278
+ worker,
279
+ });
280
+ ```
281
+
282
+ ### 3. Adopt Existing Resources
283
+ ```typescript
284
+ // Use adopt: true to use existing resources instead of failing
285
+ const bucket = await R2Bucket("storage", {
286
+ name: "existing-bucket-name",
287
+ adopt: true // Won't fail if bucket already exists
288
+ });
289
+ ```
290
+
291
+ ### 4. State Store Migration
292
+ ```typescript
293
+ // Local development (default)
294
+ const app = await alchemy("my-app");
295
+
296
+ // Production with Cloudflare state store
297
+ const app = await alchemy("my-app", {
298
+ stateStore: process.env.NODE_ENV === "production"
299
+ ? (scope) => new DOStateStore(scope)
300
+ : undefined
301
+ });
302
+ ```
303
+
304
+ ## Advanced Patterns
305
+
306
+ ### Framework Integration (Vite/React/etc.)
307
+ ```typescript
308
+ const website = await Vite("website", {
309
+ main: "./src/worker.ts",
310
+ command: "bun run build", // Build command
311
+ bindings: {
312
+ API: await Worker("api", { entrypoint: "./api/worker.ts" }),
313
+ STORAGE: await R2Bucket("assets")
314
+ }
315
+ });
316
+ ```
317
+
318
+ ### Resource Scoping
319
+ ```typescript
320
+ // Organize resources into logical groups
321
+ await alchemy.run("backend", async () => {
322
+ await Worker("api", { entrypoint: "./api.ts" });
323
+ await KVNamespace("cache", { title: "api-cache" });
324
+ });
325
+
326
+ await alchemy.run("frontend", async () => {
327
+ await Vite("website", { main: "./src/worker.ts" });
328
+ });
329
+ ```
330
+
331
+ ### Testing Pattern
332
+ ```typescript
333
+ import { alchemy } from "../../src/alchemy";
334
+ const test = alchemy.test(import.meta, { prefix: "test" });
335
+
336
+ describe("Worker Tests", () => {
337
+ test("creates worker", async (scope) => {
338
+ const worker = await Worker("test-worker", {
339
+ entrypoint: "./src/worker.ts"
340
+ });
341
+
342
+ expect(worker.url).toBeTruthy();
343
+ // Resources auto-cleaned after test
344
+ });
345
+ });
346
+ ```
347
+
348
+ ## Best Practices
349
+
350
+ 1. **Always call finalize()**: `await app.finalize()` at the end of your script
351
+ 2. **Use adoption for migrations**: `adopt: true` when converting existing projects
352
+ 3. **Type-safe bindings**: Set up env.d.ts for full type safety
353
+ 4. **Resource naming**: Use consistent, descriptive names for resources
354
+ 5. **State store**: Use DOStateStore for production deployments
355
+ 6. **Secrets**: Use `alchemy.secret()` for sensitive values
356
+ 7. **Scoping**: Organize related resources using `alchemy.run()`
357
+ 8. **Testing**: Use `alchemy.test()` for integration tests
358
+
359
+ ## Common Issues
360
+
361
+ - **Missing finalize()**: Resources won't be cleaned up properly
362
+ - **Wrong DurableObject syntax**: Use `DurableObjectNamespace()` not `await`
363
+ - **Type errors**: Set up env.d.ts file for binding types
364
+ - **State conflicts**: Use different stages/prefixes for different environments
365
+ - **Resource adoption**: Use `adopt: true` when migrating existing resources
366
+
367
+ ## Development Workflow
368
+
369
+ ```bash
370
+ # Create new project
371
+ bunx alchemy create my-app --template=vite
372
+ cd my-app
373
+
374
+ # Set up authentication (one-time)
375
+ bun wrangler login
376
+
377
+ # Deploy infrastructure
378
+ bun run deploy
379
+
380
+ # Local development
381
+ bun run dev
382
+
383
+ # Clean up
384
+ bun run destroy
385
+ ```
@@ -6,8 +6,11 @@
6
6
  "assets": { "binding": "ASSETS", "directory": "dist/client" },
7
7
  "durable_objects": {
8
8
  "bindings": [
9
- { "name": "SESSION_DURABLE_OBJECT", "class_name": "SessionDurableObject" }
10
- ]
9
+ {
10
+ "name": "SESSION_DURABLE_OBJECT",
11
+ "class_name": "SessionDurableObject",
12
+ },
13
+ ],
11
14
  },
12
15
  "d1_databases": [
13
16
  {
@@ -15,14 +18,14 @@
15
18
  "database_id": "33c4fe7d-fbfe-4cef-959c-bb54292af712",
16
19
  "database_name": "my-alchemy-app-db",
17
20
  "migrations_dir": "migrations",
18
- "preview_database_id": "33c4fe7d-fbfe-4cef-959c-bb54292af712"
19
- }
21
+ "preview_database_id": "33c4fe7d-fbfe-4cef-959c-bb54292af712",
22
+ },
20
23
  ],
21
24
  "migrations": [
22
25
  {
23
26
  "tag": "v1",
24
27
  "new_sqlite_classes": [],
25
- "new_classes": ["SessionDurableObject"]
26
- }
27
- ]
28
+ "new_classes": ["SessionDurableObject"],
29
+ },
30
+ ],
28
31
  }
@@ -10,8 +10,7 @@
10
10
  "rewriteRelativeImportExtensions": true,
11
11
  "noEmit": true,
12
12
  "types": [
13
- "@cloudflare/workers-types",
14
- "@types/node"
13
+ "@cloudflare/workers-types"
15
14
  ]
16
15
  },
17
16
  "include": [