alchemy 0.7.0 → 0.7.1

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.
@@ -1,309 +1,330 @@
1
- import * as fs from "fs/promises";
2
1
  import type { Context } from "../context";
2
+ import { StaticJsonFile } from "../fs";
3
3
  import { Resource } from "../resource";
4
+ import type { Bindings } from "./bindings";
5
+ import type { DurableObjectNamespace } from "./durable-object-namespace";
6
+ import type { Worker } from "./worker";
4
7
 
5
8
  /**
6
9
  * Properties for wrangler.json configuration file
7
10
  */
8
11
  export interface WranglerJsonProps {
12
+ name?: string;
9
13
  /**
10
- * The name of your worker
14
+ * The worker to generate the wrangler.json file for
11
15
  */
12
- name: string;
13
-
16
+ worker: Worker;
14
17
  /**
15
- * The directory containing your worker entry point
16
- * @default "src"
18
+ * Path to write the wrangler.json file to
19
+ *
20
+ * @default cwd/wrangler.json
17
21
  */
18
- main?: string;
22
+ path?: string;
23
+ }
19
24
 
25
+ /**
26
+ * Output returned after WranglerJson creation/update
27
+ */
28
+ export interface WranglerJson
29
+ extends Resource<"cloudflare::WranglerJson">,
30
+ WranglerJsonProps {
20
31
  /**
21
- * The entry point for your worker
22
- * @default "index.ts" or "index.js"
32
+ * Time at which the file was created
23
33
  */
24
- entrypoint?: string;
34
+ createdAt: number;
25
35
 
26
36
  /**
27
- * The directory to store build artifacts
28
- * @default "dist"
37
+ * Time at which the file was last updated
29
38
  */
30
- outdir?: string;
39
+ updatedAt: number;
31
40
 
32
41
  /**
33
- * The directory to serve static assets from
42
+ * Path to the wrangler.json file
34
43
  */
35
- assets?: string;
44
+ path: string;
45
+ }
36
46
 
47
+ /**
48
+ * Resource for managing wrangler.json configuration files
49
+ */
50
+ export const WranglerJson = Resource(
51
+ "cloudflare::WranglerJson",
52
+ async function (
53
+ this: Context<WranglerJson>,
54
+ id: string,
55
+ props: WranglerJsonProps
56
+ ): Promise<WranglerJson> {
57
+ // Default path is wrangler.json in current directory
58
+ const filePath = props.path || "wrangler.json";
59
+
60
+ if (this.phase === "delete") {
61
+ return this.destroy();
62
+ }
63
+
64
+ if (props.worker.entrypoint === undefined) {
65
+ throw new Error(
66
+ "Worker must have an entrypoint to generate a wrangler.json"
67
+ );
68
+ }
69
+
70
+ const worker = props.worker;
71
+
72
+ const spec: WranglerJsonSpec = {
73
+ name: worker.name,
74
+ // Use entrypoint as main if it exists
75
+ main: worker.entrypoint,
76
+ // see: https://developers.cloudflare.com/workers/configuration/compatibility-dates/
77
+ compatibility_date: "2022-04-05",
78
+ };
79
+
80
+ // Process bindings if they exist
81
+ if (worker.bindings) {
82
+ processBindings(spec, worker.bindings);
83
+ }
84
+
85
+ // Add environment variables as vars
86
+ if (worker.env) {
87
+ spec.vars = { ...worker.env };
88
+ }
89
+
90
+ await StaticJsonFile("wrangler.json", spec);
91
+
92
+ // Return the resource
93
+ return this({
94
+ ...props,
95
+ path: filePath,
96
+ createdAt: Date.now(),
97
+ updatedAt: Date.now(),
98
+ });
99
+ }
100
+ );
101
+
102
+ /**
103
+ * Wrangler.json configuration specification based on Cloudflare's schema
104
+ */
105
+ export interface WranglerJsonSpec {
37
106
  /**
38
- * Minify the worker script
39
- * @default true
107
+ * The name of the worker
40
108
  */
41
- minify?: boolean;
109
+ name: string;
42
110
 
43
111
  /**
44
- * Node.js compatibility mode
45
- * @default false
112
+ * Main entry point for the worker
46
113
  */
47
- node_compat?: boolean;
114
+ main?: string;
48
115
 
49
116
  /**
50
- * First-party worker service bindings
117
+ * A date in the form yyyy-mm-dd used to determine Workers runtime version
51
118
  */
52
- services?: Array<{
53
- /**
54
- * Binding name
55
- */
56
- name: string;
57
- /**
58
- * Service environment
59
- */
60
- environment?: string;
61
- }>;
119
+ compatibility_date?: string;
62
120
 
63
121
  /**
64
- * Worker environment variables
122
+ * A list of flags that enable features from upcoming Workers runtime
65
123
  */
66
- vars?: Record<string, string>;
124
+ compatibility_flags?: string[];
67
125
 
68
126
  /**
69
- * KV Namespace bindings
127
+ * Whether to enable a workers.dev URL for this worker
70
128
  */
71
- kv_namespaces?: Array<{
72
- /**
73
- * Binding name
74
- */
75
- binding: string;
76
- /**
77
- * KV namespace ID
78
- */
79
- id: string;
80
- /**
81
- * Preview KV namespace ID
82
- */
83
- preview_id?: string;
84
- }>;
129
+ workers_dev?: boolean;
85
130
 
86
131
  /**
87
- * R2 bucket bindings
132
+ * Routes to attach to the worker
88
133
  */
89
- r2_buckets?: Array<{
90
- /**
91
- * Binding name
92
- */
93
- binding: string;
94
- /**
95
- * Bucket name
96
- */
97
- bucket_name: string;
98
- /**
99
- * Preview bucket name
100
- */
101
- preview_bucket_name?: string;
102
- }>;
134
+ routes?: string[];
103
135
 
104
136
  /**
105
- * D1 database bindings
137
+ * KV Namespace bindings
106
138
  */
107
- d1_databases?: Array<{
108
- /**
109
- * Binding name
110
- */
139
+ kv_namespaces?: {
111
140
  binding: string;
112
- /**
113
- * Database name
114
- */
115
- database_name: string;
116
- /**
117
- * Database ID
118
- */
119
- database_id: string;
120
- /**
121
- * Preview database ID
122
- */
123
- preview_database_id?: string;
124
- }>;
141
+ id: string;
142
+ }[];
125
143
 
126
144
  /**
127
145
  * Durable Object bindings
128
146
  */
129
147
  durable_objects?: {
130
- /**
131
- * Durable Object bindings
132
- */
133
- bindings: Array<{
134
- /**
135
- * Binding name
136
- */
148
+ bindings: {
137
149
  name: string;
138
- /**
139
- * Class name
140
- */
141
150
  class_name: string;
142
- /**
143
- * Script name
144
- */
145
151
  script_name?: string;
146
- /**
147
- * Environment name
148
- */
149
152
  environment?: string;
150
- }>;
153
+ }[];
151
154
  };
152
155
 
153
156
  /**
154
- * Queue bindings
155
- */
156
- queues?: {
157
- /**
158
- * Producer bindings
159
- */
160
- producers?: Array<{
161
- /**
162
- * Binding name
163
- */
164
- binding: string;
165
- /**
166
- * Queue name
167
- */
168
- queue: string;
169
- }>;
170
- /**
171
- * Consumer configuration
172
- */
173
- consumers?: Array<{
174
- /**
175
- * Queue name
176
- */
177
- queue: string;
178
- /**
179
- * Maximum batch size
180
- */
181
- max_batch_size?: number;
182
- /**
183
- * Maximum batch timeout
184
- */
185
- max_batch_timeout?: number;
186
- /**
187
- * Maximum retries
188
- */
189
- max_retries?: number;
190
- /**
191
- * Dead letter queue
192
- */
193
- dead_letter_queue?: string;
194
- }>;
195
- };
196
-
197
- /**
198
- * Analytics Engine bindings
157
+ * R2 bucket bindings
199
158
  */
200
- analytics_engine_datasets?: Array<{
201
- /**
202
- * Binding name
203
- */
159
+ r2_buckets?: {
204
160
  binding: string;
205
- /**
206
- * Dataset name
207
- */
208
- dataset?: string;
209
- }>;
161
+ bucket_name: string;
162
+ }[];
210
163
 
211
164
  /**
212
- * Route configuration
165
+ * Queue bindings
213
166
  */
214
- routes?: Array<string>;
167
+ queues?: {
168
+ binding: string;
169
+ queue: string;
170
+ }[];
215
171
 
216
172
  /**
217
- * Triggers configuration
173
+ * Service bindings
218
174
  */
219
- triggers?: {
220
- /**
221
- * Cron triggers
222
- */
223
- crons: Array<string>;
224
- };
175
+ services?: {
176
+ binding: string;
177
+ service: string;
178
+ environment?: string;
179
+ }[];
225
180
 
226
181
  /**
227
- * Worker compatibility date
182
+ * Workflow bindings
228
183
  */
229
- compatibility_date?: string;
184
+ workflows?: {
185
+ name: string;
186
+ binding: string;
187
+ class_name: string;
188
+ }[];
230
189
 
231
190
  /**
232
- * Worker compatibility flags
191
+ * Plain text bindings (vars)
233
192
  */
234
- compatibility_flags?: Array<string>;
193
+ vars?: Record<string, string>;
235
194
 
236
195
  /**
237
- * Worker usage model
238
- * "bundled" - Includes resources like CPU and memory. Better for consistent workloads.
239
- * "unbound" - Pay only for what you use. Better for sporadic workloads.
196
+ * Assets bindings
240
197
  */
241
- usage_model?: "bundled" | "unbound";
198
+ assets?: {
199
+ directory: string;
200
+ binding: string;
201
+ };
242
202
 
243
203
  /**
244
- * Path to the wrangler.json file
245
- * @internal
204
+ * Workflow bindings
246
205
  */
247
- path?: string;
248
- }
206
+ wasm_modules?: Record<string, string>;
249
207
 
250
- /**
251
- * Output returned after WranglerJson creation/update
252
- */
253
- export interface WranglerJson
254
- extends Resource<"cloudflare::WranglerJson">,
255
- WranglerJsonProps {
256
208
  /**
257
- * Time at which the file was created
209
+ * Safe mode configuration
258
210
  */
259
- createdAt: number;
211
+ node_compat?: boolean;
260
212
 
261
213
  /**
262
- * Time at which the file was last updated
214
+ * Whether to minify the worker script
263
215
  */
264
- updatedAt: number;
216
+ minify?: boolean;
265
217
  }
266
218
 
267
219
  /**
268
- * Resource for managing wrangler.json configuration files
220
+ * Process worker bindings into wrangler.json format
269
221
  */
270
- export const WranglerJson = Resource(
271
- "cloudflare::WranglerJson",
272
- async function (
273
- this: Context<WranglerJson>,
274
- id: string,
275
- props: WranglerJsonProps,
276
- ): Promise<WranglerJson> {
277
- // Default path is wrangler.json in current directory
278
- const filePath = props.path || "wrangler.json";
222
+ function processBindings(spec: WranglerJsonSpec, bindings: Bindings): void {
223
+ // Arrays to collect different binding types
224
+ const kvNamespaces: { binding: string; id: string }[] = [];
225
+ const durableObjects: {
226
+ name: string;
227
+ class_name: string;
228
+ script_name?: string;
229
+ environment?: string;
230
+ }[] = [];
231
+ const r2Buckets: { binding: string; bucket_name: string }[] = [];
232
+ const services: { binding: string; service: string; environment?: string }[] =
233
+ [];
234
+ const secrets: string[] = [];
235
+ const workflows: { name: string; binding: string; class_name: string }[] = [];
236
+ const d1Databases: {
237
+ binding: string;
238
+ database_id: string;
239
+ database_name: string;
240
+ }[] = [];
241
+ const queues: { binding: string; queue: string }[] = [];
279
242
 
280
- if (this.phase === "delete") {
281
- try {
282
- await fs.unlink(filePath);
283
- } catch (error) {
284
- // Ignore errors if file doesn't exist
285
- if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
286
- throw error;
287
- }
243
+ // Process each binding
244
+ for (const [bindingName, binding] of Object.entries(bindings)) {
245
+ if (typeof binding === "string") {
246
+ // Plain text binding - add to vars
247
+ if (!spec.vars) {
248
+ spec.vars = {};
288
249
  }
289
- return this.destroy();
250
+ spec.vars[bindingName] = binding;
251
+ } else if (binding.type === "kv_namespace") {
252
+ // KV Namespace binding
253
+ kvNamespaces.push({
254
+ binding: bindingName,
255
+ id: binding.namespaceId,
256
+ });
257
+ } else if (
258
+ typeof binding === "object" &&
259
+ binding.type === "durable_object_namespace"
260
+ ) {
261
+ // Durable Object binding
262
+ const doBinding = binding as DurableObjectNamespace;
263
+ durableObjects.push({
264
+ name: bindingName,
265
+ class_name: doBinding.className,
266
+ script_name: doBinding.scriptName,
267
+ environment: doBinding.environment,
268
+ });
269
+ } else if (binding.type === "r2_bucket") {
270
+ r2Buckets.push({
271
+ binding: bindingName,
272
+ bucket_name: binding.name,
273
+ });
274
+ } else if (binding.type === "service") {
275
+ // Service binding
276
+ services.push({
277
+ binding: bindingName,
278
+ service: binding.id,
279
+ });
280
+ } else if (binding.type === "secret") {
281
+ // Secret binding
282
+ secrets.push(bindingName);
283
+ } else if (binding.type === "assets") {
284
+ spec.assets = {
285
+ directory: binding.path,
286
+ binding: bindingName,
287
+ };
288
+ } else if (binding.type === "workflow") {
289
+ workflows.push({
290
+ name: bindingName,
291
+ binding: binding.id,
292
+ class_name: binding.className,
293
+ });
294
+ } else if (binding.type === "d1") {
295
+ d1Databases.push({
296
+ binding: bindingName,
297
+ database_id: binding.id,
298
+ database_name: binding.name,
299
+ });
300
+ } else if (binding.type === "queue") {
301
+ queues.push({
302
+ binding: bindingName,
303
+ queue: binding.name,
304
+ });
290
305
  }
306
+ }
291
307
 
292
- // Create or update the file
293
- const config = {
294
- ...props,
295
- // Remove internal path property
296
- path: undefined,
308
+ // Add collected bindings to the spec
309
+ if (kvNamespaces.length > 0) {
310
+ spec.kv_namespaces = kvNamespaces;
311
+ }
312
+
313
+ if (durableObjects.length > 0) {
314
+ spec.durable_objects = {
315
+ bindings: durableObjects,
297
316
  };
317
+ }
298
318
 
299
- // Write the file
300
- await fs.writeFile(filePath, JSON.stringify(config, null, 2));
319
+ if (r2Buckets.length > 0) {
320
+ spec.r2_buckets = r2Buckets;
321
+ }
301
322
 
302
- // Return the resource
303
- return this({
304
- ...props,
305
- createdAt: Date.now(),
306
- updatedAt: Date.now(),
307
- });
308
- },
309
- );
323
+ if (services.length > 0) {
324
+ spec.services = services;
325
+ }
326
+
327
+ if (queues.length > 0) {
328
+ spec.queues = queues;
329
+ }
330
+ }