alchemy 0.15.5 → 0.15.6
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.
- package/lib/cloudflare/index.d.ts +1 -0
- package/lib/cloudflare/index.d.ts.map +1 -1
- package/lib/cloudflare/index.js +1 -0
- package/lib/cloudflare/index.js.map +1 -1
- package/lib/cloudflare/kv-namespace.d.ts +39 -0
- package/lib/cloudflare/kv-namespace.d.ts.map +1 -1
- package/lib/cloudflare/kv-namespace.js +95 -26
- package/lib/cloudflare/kv-namespace.js.map +1 -1
- package/lib/cloudflare/route.d.ts +102 -0
- package/lib/cloudflare/route.d.ts.map +1 -0
- package/lib/cloudflare/route.js +130 -0
- package/lib/cloudflare/route.js.map +1 -0
- package/lib/cloudflare/worker.d.ts +6 -0
- package/lib/cloudflare/worker.d.ts.map +1 -1
- package/lib/cloudflare/worker.js +6 -0
- package/lib/cloudflare/worker.js.map +1 -1
- package/lib/cloudflare/wrangler.json.d.ts +8 -0
- package/lib/cloudflare/wrangler.json.d.ts.map +1 -1
- package/lib/cloudflare/wrangler.json.js +17 -0
- package/lib/cloudflare/wrangler.json.js.map +1 -1
- package/lib/internal/docs/providers.d.ts.map +1 -1
- package/lib/internal/docs/providers.js +8 -2
- package/lib/internal/docs/providers.js.map +1 -1
- package/lib/scope.d.ts +0 -1
- package/lib/scope.d.ts.map +1 -1
- package/lib/scope.js +3 -2
- package/lib/scope.js.map +1 -1
- package/package.json +1 -1
- package/src/cloudflare/index.ts +1 -0
- package/src/cloudflare/kv-namespace.ts +162 -36
- package/src/cloudflare/route.ts +268 -0
- package/src/cloudflare/worker.ts +6 -0
- package/src/cloudflare/wrangler.json.ts +27 -0
- package/src/internal/docs/providers.ts +11 -2
- package/src/scope.ts +6 -1
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import type { Context } from "../context.js";
|
|
2
|
+
import { Resource } from "../resource.js";
|
|
3
|
+
import { CloudflareApiError, handleApiError } from "./api-error.js";
|
|
4
|
+
import {
|
|
5
|
+
createCloudflareApi,
|
|
6
|
+
type CloudflareApi,
|
|
7
|
+
type CloudflareApiOptions,
|
|
8
|
+
} from "./api.js";
|
|
9
|
+
import type { Worker } from "./worker.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Properties for creating or updating a Route
|
|
13
|
+
*/
|
|
14
|
+
export interface RouteProps extends CloudflareApiOptions {
|
|
15
|
+
/**
|
|
16
|
+
* URL pattern for the route
|
|
17
|
+
* @example "example.com/*"
|
|
18
|
+
*/
|
|
19
|
+
pattern: string;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Worker script for the route
|
|
23
|
+
* This can be a Worker resource or script name as a string
|
|
24
|
+
*/
|
|
25
|
+
script: Worker | string;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Zone ID for the route
|
|
29
|
+
*/
|
|
30
|
+
zoneId: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Output returned after Route creation/update
|
|
35
|
+
*/
|
|
36
|
+
export interface Route extends Resource<"cloudflare::Route">, RouteProps {
|
|
37
|
+
/**
|
|
38
|
+
* The unique ID of the route
|
|
39
|
+
*/
|
|
40
|
+
id: string;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The URL pattern for the route
|
|
44
|
+
*/
|
|
45
|
+
pattern: string;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The Worker script name for the route
|
|
49
|
+
*/
|
|
50
|
+
script: string;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The Zone ID for the route
|
|
54
|
+
*/
|
|
55
|
+
zoneId: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Creates and manages Cloudflare Worker Routes.
|
|
60
|
+
*
|
|
61
|
+
* Routes map URL patterns to Worker scripts, allowing you to control which
|
|
62
|
+
* requests are handled by your Workers.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* // Create a route that maps all requests on a domain to a Worker
|
|
66
|
+
* const basicRoute = await Route("main-route", {
|
|
67
|
+
* pattern: "example.com/*",
|
|
68
|
+
* script: "my-worker",
|
|
69
|
+
* zoneId: "your-zone-id"
|
|
70
|
+
* });
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* // Create a route using a Worker resource
|
|
74
|
+
* const worker = await Worker("api-worker", {
|
|
75
|
+
* script: `
|
|
76
|
+
* export default {
|
|
77
|
+
* fetch(request, env) {
|
|
78
|
+
* return new Response("Hello from API!");
|
|
79
|
+
* }
|
|
80
|
+
* }
|
|
81
|
+
* `
|
|
82
|
+
* });
|
|
83
|
+
*
|
|
84
|
+
* const apiRoute = await Route("api-route", {
|
|
85
|
+
* pattern: "api.example.com/*",
|
|
86
|
+
* script: worker,
|
|
87
|
+
* zoneId: "your-zone-id"
|
|
88
|
+
* });
|
|
89
|
+
*
|
|
90
|
+
* @see https://developers.cloudflare.com/workers/configuration/routes/
|
|
91
|
+
*/
|
|
92
|
+
export const Route = Resource(
|
|
93
|
+
"cloudflare::Route",
|
|
94
|
+
async function (
|
|
95
|
+
this: Context<Route>,
|
|
96
|
+
id: string,
|
|
97
|
+
props: RouteProps,
|
|
98
|
+
): Promise<Route> {
|
|
99
|
+
const api = await createCloudflareApi(props);
|
|
100
|
+
|
|
101
|
+
// Get script name from script prop (either a string or a Worker resource)
|
|
102
|
+
const scriptName =
|
|
103
|
+
typeof props.script === "string" ? props.script : props.script.name;
|
|
104
|
+
|
|
105
|
+
// Get zone ID from props
|
|
106
|
+
const { zoneId } = props;
|
|
107
|
+
|
|
108
|
+
if (this.phase === "delete") {
|
|
109
|
+
console.log("Deleting Route:", props.pattern);
|
|
110
|
+
|
|
111
|
+
// Only delete if we have an ID
|
|
112
|
+
if (this.output?.id) {
|
|
113
|
+
await deleteRoute(api, zoneId, this.output.id);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Return void (a deleted route has no content)
|
|
117
|
+
return this.destroy();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
let routeData: CloudflareRouteResponse;
|
|
121
|
+
|
|
122
|
+
if (this.phase === "update" && this.output?.id) {
|
|
123
|
+
console.log("Updating Route:", props.pattern);
|
|
124
|
+
|
|
125
|
+
// Update existing route
|
|
126
|
+
routeData = await updateRoute(
|
|
127
|
+
api,
|
|
128
|
+
zoneId,
|
|
129
|
+
this.output.id,
|
|
130
|
+
props.pattern,
|
|
131
|
+
scriptName,
|
|
132
|
+
);
|
|
133
|
+
} else {
|
|
134
|
+
console.log("Creating Route:", props.pattern);
|
|
135
|
+
|
|
136
|
+
// Create new route
|
|
137
|
+
routeData = await createRoute(api, zoneId, props.pattern, scriptName);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Return the route resource
|
|
141
|
+
return this({
|
|
142
|
+
id: routeData.result.id,
|
|
143
|
+
pattern: routeData.result.pattern,
|
|
144
|
+
script: routeData.result.script,
|
|
145
|
+
zoneId,
|
|
146
|
+
});
|
|
147
|
+
},
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
interface CloudflareRouteResponse {
|
|
151
|
+
result: {
|
|
152
|
+
id: string;
|
|
153
|
+
pattern: string;
|
|
154
|
+
script: string;
|
|
155
|
+
};
|
|
156
|
+
success: boolean;
|
|
157
|
+
errors: Array<{ code: number; message: string }>;
|
|
158
|
+
messages: string[];
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Create a new route
|
|
163
|
+
*/
|
|
164
|
+
async function createRoute(
|
|
165
|
+
api: CloudflareApi,
|
|
166
|
+
zoneId: string,
|
|
167
|
+
pattern: string,
|
|
168
|
+
script: string,
|
|
169
|
+
): Promise<CloudflareRouteResponse> {
|
|
170
|
+
const createResponse = await api.post(`/zones/${zoneId}/workers/routes`, {
|
|
171
|
+
pattern,
|
|
172
|
+
script,
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
if (!createResponse.ok) {
|
|
176
|
+
return await handleApiError(createResponse, "creating", "Route", pattern);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return (await createResponse.json()) as CloudflareRouteResponse;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Update a route
|
|
184
|
+
*/
|
|
185
|
+
async function updateRoute(
|
|
186
|
+
api: CloudflareApi,
|
|
187
|
+
zoneId: string,
|
|
188
|
+
routeId: string,
|
|
189
|
+
pattern: string,
|
|
190
|
+
script: string,
|
|
191
|
+
): Promise<CloudflareRouteResponse> {
|
|
192
|
+
const updateResponse = await api.put(
|
|
193
|
+
`/zones/${zoneId}/workers/routes/${routeId}`,
|
|
194
|
+
{
|
|
195
|
+
pattern,
|
|
196
|
+
script,
|
|
197
|
+
},
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
if (!updateResponse.ok) {
|
|
201
|
+
return await handleApiError(updateResponse, "updating", "Route", pattern);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return (await updateResponse.json()) as CloudflareRouteResponse;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Delete a route
|
|
209
|
+
*/
|
|
210
|
+
async function deleteRoute(
|
|
211
|
+
api: CloudflareApi,
|
|
212
|
+
zoneId: string,
|
|
213
|
+
routeId: string,
|
|
214
|
+
): Promise<void> {
|
|
215
|
+
const deleteResponse = await api.delete(
|
|
216
|
+
`/zones/${zoneId}/workers/routes/${routeId}`,
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
if (!deleteResponse.ok && deleteResponse.status !== 404) {
|
|
220
|
+
const errorData: any = await deleteResponse.json().catch(() => ({
|
|
221
|
+
errors: [{ message: deleteResponse.statusText }],
|
|
222
|
+
}));
|
|
223
|
+
|
|
224
|
+
throw new CloudflareApiError(
|
|
225
|
+
`Error deleting Route '${routeId}': ${errorData.errors?.[0]?.message || deleteResponse.statusText}`,
|
|
226
|
+
deleteResponse,
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Get a route by ID
|
|
233
|
+
*/
|
|
234
|
+
export async function getRoute(
|
|
235
|
+
api: CloudflareApi,
|
|
236
|
+
zoneId: string,
|
|
237
|
+
routeId: string,
|
|
238
|
+
): Promise<CloudflareRouteResponse> {
|
|
239
|
+
const response = await api.get(`/zones/${zoneId}/workers/routes/${routeId}`);
|
|
240
|
+
|
|
241
|
+
if (!response.ok) {
|
|
242
|
+
throw new CloudflareApiError(
|
|
243
|
+
`Failed to get route ${routeId}: ${response.statusText}`,
|
|
244
|
+
response,
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return (await response.json()) as CloudflareRouteResponse;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* List all routes for a zone
|
|
253
|
+
*/
|
|
254
|
+
export async function listRoutes(
|
|
255
|
+
api: CloudflareApi,
|
|
256
|
+
zoneId: string,
|
|
257
|
+
): Promise<CloudflareRouteResponse> {
|
|
258
|
+
const response = await api.get(`/zones/${zoneId}/workers/routes`);
|
|
259
|
+
|
|
260
|
+
if (!response.ok) {
|
|
261
|
+
throw new CloudflareApiError(
|
|
262
|
+
`Failed to list routes: ${response.statusText}`,
|
|
263
|
+
response,
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return (await response.json()) as CloudflareRouteResponse;
|
|
268
|
+
}
|
package/src/cloudflare/worker.ts
CHANGED
|
@@ -275,6 +275,12 @@ export interface Worker<B extends Bindings = Bindings>
|
|
|
275
275
|
* url: true
|
|
276
276
|
* });
|
|
277
277
|
*
|
|
278
|
+
* await Route("route", {
|
|
279
|
+
* zoneId: zone.zoneId,
|
|
280
|
+
* worker: api,
|
|
281
|
+
* pattern: "api.example.com/*",
|
|
282
|
+
* });
|
|
283
|
+
*
|
|
278
284
|
* @example
|
|
279
285
|
* // Create a real-time chat worker using Durable Objects
|
|
280
286
|
* // for state management:
|
|
@@ -268,6 +268,15 @@ export interface WranglerJsonSpec {
|
|
|
268
268
|
binding: string;
|
|
269
269
|
};
|
|
270
270
|
|
|
271
|
+
/**
|
|
272
|
+
* Migrations
|
|
273
|
+
*/
|
|
274
|
+
migrations?: {
|
|
275
|
+
tag: string;
|
|
276
|
+
new_sqlite_classes?: string[];
|
|
277
|
+
new_classes?: string[];
|
|
278
|
+
}[];
|
|
279
|
+
|
|
271
280
|
/**
|
|
272
281
|
* Workflow bindings
|
|
273
282
|
*/
|
|
@@ -327,6 +336,9 @@ function processBindings(
|
|
|
327
336
|
consumers: [],
|
|
328
337
|
};
|
|
329
338
|
|
|
339
|
+
const new_sqlite_classes: string[] = [];
|
|
340
|
+
const new_classes: string[] = [];
|
|
341
|
+
|
|
330
342
|
const vectorizeIndexes: { binding: string; index_name: string }[] = [];
|
|
331
343
|
|
|
332
344
|
for (const eventSource of eventSources ?? []) {
|
|
@@ -383,6 +395,11 @@ function processBindings(
|
|
|
383
395
|
script_name: doBinding.scriptName,
|
|
384
396
|
environment: doBinding.environment,
|
|
385
397
|
});
|
|
398
|
+
if (doBinding.sqlite) {
|
|
399
|
+
new_sqlite_classes.push(doBinding.className);
|
|
400
|
+
} else {
|
|
401
|
+
new_classes.push(doBinding.className);
|
|
402
|
+
}
|
|
386
403
|
} else if (binding.type === "r2_bucket") {
|
|
387
404
|
r2Buckets.push({
|
|
388
405
|
binding: bindingName,
|
|
@@ -466,4 +483,14 @@ function processBindings(
|
|
|
466
483
|
if (vectorizeIndexes.length > 0) {
|
|
467
484
|
spec.vectorize_indexes = vectorizeIndexes;
|
|
468
485
|
}
|
|
486
|
+
|
|
487
|
+
if (new_sqlite_classes.length > 0 || new_classes.length > 0) {
|
|
488
|
+
spec.migrations = [
|
|
489
|
+
{
|
|
490
|
+
tag: "v1",
|
|
491
|
+
new_sqlite_classes,
|
|
492
|
+
new_classes,
|
|
493
|
+
},
|
|
494
|
+
];
|
|
495
|
+
}
|
|
469
496
|
}
|
|
@@ -6,6 +6,14 @@ import { Document } from "../../ai/document.js";
|
|
|
6
6
|
import { alchemy } from "../../alchemy.js";
|
|
7
7
|
import { Folder } from "../../fs/folder.js";
|
|
8
8
|
|
|
9
|
+
function getArg(arg: string) {
|
|
10
|
+
const idx = process.argv.findIndex((a) => a === arg);
|
|
11
|
+
return idx > -1 ? process.argv[idx + 1] : undefined;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const onlyProviderName = getArg("--provider");
|
|
15
|
+
const onlyResourceName = getArg("--resource");
|
|
16
|
+
|
|
9
17
|
export interface DocsProps {
|
|
10
18
|
/**
|
|
11
19
|
* The output directory for the docs.
|
|
@@ -117,7 +125,7 @@ async function generateProviderDocs({
|
|
|
117
125
|
reasoningEffort: "high",
|
|
118
126
|
},
|
|
119
127
|
},
|
|
120
|
-
freeze:
|
|
128
|
+
freeze: onlyProviderName === undefined || onlyProviderName !== providerName,
|
|
121
129
|
temperature: 0.1,
|
|
122
130
|
schema: type({
|
|
123
131
|
groups: type({
|
|
@@ -177,7 +185,8 @@ async function generateProviderDocs({
|
|
|
177
185
|
providerDocsDir,
|
|
178
186
|
`${g.filename.replace(".ts", "").replace(".md", "")}.md`,
|
|
179
187
|
),
|
|
180
|
-
freeze:
|
|
188
|
+
freeze:
|
|
189
|
+
onlyResourceName !== undefined && onlyResourceName !== g.identifier,
|
|
181
190
|
model: {
|
|
182
191
|
id: "claude-3-5-sonnet-latest",
|
|
183
192
|
provider: "anthropic",
|
package/src/scope.ts
CHANGED
|
@@ -47,10 +47,15 @@ export class Scope {
|
|
|
47
47
|
|
|
48
48
|
private isErrored = false;
|
|
49
49
|
|
|
50
|
-
constructor(
|
|
50
|
+
constructor(options: ScopeOptions) {
|
|
51
51
|
this.appName = options.appName;
|
|
52
52
|
this.stage = options?.stage ?? DEFAULT_STAGE;
|
|
53
53
|
this.scopeName = options.scopeName ?? null;
|
|
54
|
+
if (this.scopeName?.includes(":")) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
`Scope name ${this.scopeName} cannot contain double colons`,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
54
59
|
this.parent = options.parent ?? Scope.get();
|
|
55
60
|
this.quiet = options.quiet ?? this.parent?.quiet ?? false;
|
|
56
61
|
if (this.parent && !this.scopeName) {
|