alchemy 0.6.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.
- package/lib/cloudflare/bindings.d.ts +21 -7
- package/lib/cloudflare/bindings.js +0 -9
- package/lib/cloudflare/bound.d.ts +4 -1
- package/lib/cloudflare/d1-database.d.ts +134 -0
- package/lib/cloudflare/d1-database.js +161 -0
- package/lib/cloudflare/index.d.ts +4 -0
- package/lib/cloudflare/index.js +4 -0
- package/lib/cloudflare/kv-namespace.d.ts +0 -1
- package/lib/cloudflare/kv-namespace.js +0 -3
- package/lib/cloudflare/queue.d.ts +147 -0
- package/lib/cloudflare/queue.js +192 -0
- package/lib/cloudflare/worker.js +78 -34
- package/lib/cloudflare/workflow.d.ts +46 -0
- package/lib/cloudflare/workflow.js +29 -0
- package/lib/cloudflare/wrangler.json.d.ts +82 -185
- package/lib/cloudflare/wrangler.json.js +124 -17
- package/lib/internal/docs/providers.js +1 -1
- package/package.json +1 -1
- package/src/cloudflare/bindings.ts +27 -18
- package/src/cloudflare/bound.ts +10 -1
- package/src/cloudflare/d1-database.ts +354 -0
- package/src/cloudflare/durable-object-namespace.ts +1 -1
- package/src/cloudflare/index.ts +4 -0
- package/src/cloudflare/kv-namespace.ts +0 -6
- package/src/cloudflare/queue.ts +371 -0
- package/src/cloudflare/worker.ts +85 -45
- package/src/cloudflare/workflow.ts +79 -0
- package/src/cloudflare/wrangler.json.ts +239 -218
- package/src/internal/docs/providers.ts +1 -1
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import type { Context } from "../context";
|
|
2
|
+
import { Resource } from "../resource";
|
|
3
|
+
import {
|
|
4
|
+
CloudflareApi,
|
|
5
|
+
createCloudflareApi,
|
|
6
|
+
type CloudflareApiOptions,
|
|
7
|
+
} from "./api";
|
|
8
|
+
import { CloudflareApiError, handleApiError } from "./api-error";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Properties for creating or updating a D1 Database
|
|
12
|
+
*/
|
|
13
|
+
export interface D1DatabaseProps extends CloudflareApiOptions {
|
|
14
|
+
/**
|
|
15
|
+
* Name of the database
|
|
16
|
+
*/
|
|
17
|
+
name: string;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Optional primary location hint for the database
|
|
21
|
+
* Indicates the primary geographical location data will be stored
|
|
22
|
+
*/
|
|
23
|
+
primaryLocationHint?:
|
|
24
|
+
| "wnam"
|
|
25
|
+
| "enam"
|
|
26
|
+
| "weur"
|
|
27
|
+
| "eeur"
|
|
28
|
+
| "apac"
|
|
29
|
+
| "auto"
|
|
30
|
+
| string;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Read replication configuration
|
|
34
|
+
* Only mutable property during updates
|
|
35
|
+
*/
|
|
36
|
+
readReplication?: {
|
|
37
|
+
/**
|
|
38
|
+
* Read replication mode
|
|
39
|
+
* - auto: Automatic read replication
|
|
40
|
+
* - disabled: No read replication
|
|
41
|
+
*/
|
|
42
|
+
mode: "auto" | "disabled";
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Whether to delete the database.
|
|
47
|
+
* If set to false, the database will remain but the resource will be removed from state
|
|
48
|
+
*
|
|
49
|
+
* @default true
|
|
50
|
+
*/
|
|
51
|
+
delete?: boolean;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Output returned after D1 Database creation/update
|
|
56
|
+
*/
|
|
57
|
+
export interface D1Database
|
|
58
|
+
extends Resource<"cloudflare::D1Database">,
|
|
59
|
+
D1DatabaseProps {
|
|
60
|
+
type: "d1";
|
|
61
|
+
/**
|
|
62
|
+
* The unique ID of the database (UUID)
|
|
63
|
+
*/
|
|
64
|
+
id: string;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* File size of the database
|
|
68
|
+
*/
|
|
69
|
+
fileSize: number;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Number of tables in the database
|
|
73
|
+
*/
|
|
74
|
+
numTables: number;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Version of the database
|
|
78
|
+
*/
|
|
79
|
+
version: string;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Read replication configuration
|
|
83
|
+
*/
|
|
84
|
+
readReplication?: {
|
|
85
|
+
/**
|
|
86
|
+
* Read replication mode
|
|
87
|
+
*/
|
|
88
|
+
mode: "auto" | "disabled";
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Creates and manages Cloudflare D1 Databases.
|
|
94
|
+
*
|
|
95
|
+
* D1 Databases provide serverless SQL databases built on SQLite with
|
|
96
|
+
* automatic data replication for high availability.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* // Create a basic D1 database with default settings
|
|
100
|
+
* const basicDatabase = await D1Database("my-app-db", {
|
|
101
|
+
* name: "my-app-db"
|
|
102
|
+
* });
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* // Create a database with location hint for optimal performance
|
|
106
|
+
* const westUsDatabase = await D1Database("west-us-db", {
|
|
107
|
+
* name: "west-us-db",
|
|
108
|
+
* primaryLocationHint: "wnam"
|
|
109
|
+
* });
|
|
110
|
+
*
|
|
111
|
+
* @see https://developers.cloudflare.com/d1/
|
|
112
|
+
*/
|
|
113
|
+
export const D1Database = Resource(
|
|
114
|
+
"cloudflare::D1Database",
|
|
115
|
+
async function (
|
|
116
|
+
this: Context<D1Database>,
|
|
117
|
+
id: string,
|
|
118
|
+
props: D1DatabaseProps
|
|
119
|
+
): Promise<D1Database> {
|
|
120
|
+
const api = await createCloudflareApi(props);
|
|
121
|
+
const databaseName = props.name || id;
|
|
122
|
+
|
|
123
|
+
if (this.phase === "delete") {
|
|
124
|
+
console.log("Deleting D1 database:", databaseName);
|
|
125
|
+
if (props.delete !== false) {
|
|
126
|
+
// Delete D1 database
|
|
127
|
+
console.log("Deleting D1 database:", databaseName);
|
|
128
|
+
await deleteDatabase(api, this.output?.id);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Return void (a deleted database has no content)
|
|
132
|
+
return this.destroy();
|
|
133
|
+
} else {
|
|
134
|
+
let dbData: CloudflareD1Response;
|
|
135
|
+
|
|
136
|
+
if (this.phase === "create") {
|
|
137
|
+
console.log("Creating D1 database:", databaseName);
|
|
138
|
+
dbData = await createDatabase(api, databaseName, props);
|
|
139
|
+
} else {
|
|
140
|
+
// Update operation
|
|
141
|
+
if (this.output?.id) {
|
|
142
|
+
console.log("Updating D1 database:", databaseName);
|
|
143
|
+
// Update the database with new properties
|
|
144
|
+
dbData = await updateDatabase(api, this.output.id, props);
|
|
145
|
+
} else {
|
|
146
|
+
// If no ID exists, fall back to creating a new database
|
|
147
|
+
console.log(
|
|
148
|
+
"No existing database ID found, creating new D1 database:",
|
|
149
|
+
databaseName
|
|
150
|
+
);
|
|
151
|
+
dbData = await createDatabase(api, databaseName, props);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return this({
|
|
156
|
+
type: "d1",
|
|
157
|
+
id: dbData.result.uuid || "",
|
|
158
|
+
name: databaseName,
|
|
159
|
+
fileSize: dbData.result.file_size,
|
|
160
|
+
numTables: dbData.result.num_tables,
|
|
161
|
+
version: dbData.result.version,
|
|
162
|
+
readReplication: dbData.result.read_replication,
|
|
163
|
+
primaryLocationHint: props.primaryLocationHint,
|
|
164
|
+
accountId: api.accountId,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
interface CloudflareD1Response {
|
|
171
|
+
result: {
|
|
172
|
+
uuid?: string;
|
|
173
|
+
name: string;
|
|
174
|
+
file_size: number;
|
|
175
|
+
num_tables: number;
|
|
176
|
+
version: string;
|
|
177
|
+
primary_location_hint?: string;
|
|
178
|
+
read_replication?: {
|
|
179
|
+
mode: "auto" | "disabled";
|
|
180
|
+
};
|
|
181
|
+
};
|
|
182
|
+
success: boolean;
|
|
183
|
+
errors: Array<{ code: number; message: string }>;
|
|
184
|
+
messages: string[];
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Create a new D1 database
|
|
189
|
+
*/
|
|
190
|
+
export async function createDatabase(
|
|
191
|
+
api: CloudflareApi,
|
|
192
|
+
databaseName: string,
|
|
193
|
+
props: D1DatabaseProps
|
|
194
|
+
): Promise<CloudflareD1Response> {
|
|
195
|
+
// Create new D1 database
|
|
196
|
+
const createPayload: any = {
|
|
197
|
+
name: databaseName,
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
if (props.primaryLocationHint) {
|
|
201
|
+
createPayload.primary_location_hint = props.primaryLocationHint;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const createResponse = await api.post(
|
|
205
|
+
`/accounts/${api.accountId}/d1/database`,
|
|
206
|
+
createPayload
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
if (!createResponse.ok) {
|
|
210
|
+
return await handleApiError(
|
|
211
|
+
createResponse,
|
|
212
|
+
"creating",
|
|
213
|
+
"D1 database",
|
|
214
|
+
databaseName
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return (await createResponse.json()) as CloudflareD1Response;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Get a D1 database
|
|
223
|
+
*/
|
|
224
|
+
export async function getDatabase(
|
|
225
|
+
api: CloudflareApi,
|
|
226
|
+
databaseId?: string
|
|
227
|
+
): Promise<CloudflareD1Response> {
|
|
228
|
+
if (!databaseId) {
|
|
229
|
+
throw new Error("Database ID is required");
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const response = await api.get(
|
|
233
|
+
`/accounts/${api.accountId}/d1/database/${databaseId}`
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
if (!response.ok) {
|
|
237
|
+
return await handleApiError(response, "getting", "D1 database", databaseId);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return (await response.json()) as CloudflareD1Response;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Delete a D1 database
|
|
245
|
+
*/
|
|
246
|
+
export async function deleteDatabase(
|
|
247
|
+
api: CloudflareApi,
|
|
248
|
+
databaseId?: string
|
|
249
|
+
): Promise<void> {
|
|
250
|
+
if (!databaseId) {
|
|
251
|
+
console.log("No database ID provided, skipping delete");
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Delete D1 database
|
|
256
|
+
const deleteResponse = await api.delete(
|
|
257
|
+
`/accounts/${api.accountId}/d1/database/${databaseId}`
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
if (!deleteResponse.ok && deleteResponse.status !== 404) {
|
|
261
|
+
const errorData: any = await deleteResponse.json().catch(() => ({
|
|
262
|
+
errors: [{ message: deleteResponse.statusText }],
|
|
263
|
+
}));
|
|
264
|
+
throw new CloudflareApiError(
|
|
265
|
+
`Error deleting D1 database '${databaseId}': ${errorData.errors?.[0]?.message || deleteResponse.statusText}`,
|
|
266
|
+
deleteResponse
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* List all D1 databases in an account
|
|
273
|
+
*/
|
|
274
|
+
export async function listDatabases(
|
|
275
|
+
api: CloudflareApi
|
|
276
|
+
): Promise<{ name: string; id: string }[]> {
|
|
277
|
+
const response = await api.get(`/accounts/${api.accountId}/d1/database`);
|
|
278
|
+
|
|
279
|
+
if (!response.ok) {
|
|
280
|
+
throw new CloudflareApiError(
|
|
281
|
+
`Failed to list databases: ${response.statusText}`,
|
|
282
|
+
response
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const data = (await response.json()) as {
|
|
287
|
+
success: boolean;
|
|
288
|
+
errors?: Array<{ code: number; message: string }>;
|
|
289
|
+
result?: Array<{
|
|
290
|
+
name: string;
|
|
291
|
+
uuid: string;
|
|
292
|
+
}>;
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
if (!data.success) {
|
|
296
|
+
const errorMessage = data.errors?.[0]?.message || "Unknown error";
|
|
297
|
+
throw new Error(`Failed to list databases: ${errorMessage}`);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Transform API response
|
|
301
|
+
return (data.result || []).map((db) => ({
|
|
302
|
+
name: db.name,
|
|
303
|
+
id: db.uuid,
|
|
304
|
+
}));
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Update a D1 database
|
|
309
|
+
*
|
|
310
|
+
* Note: According to Cloudflare API, only read_replication.mode can be modified during updates.
|
|
311
|
+
*/
|
|
312
|
+
export async function updateDatabase(
|
|
313
|
+
api: CloudflareApi,
|
|
314
|
+
databaseId: string,
|
|
315
|
+
props: D1DatabaseProps
|
|
316
|
+
): Promise<CloudflareD1Response> {
|
|
317
|
+
// Get current database state to check for non-mutable changes
|
|
318
|
+
const currentDB = await getDatabase(api, databaseId);
|
|
319
|
+
|
|
320
|
+
// Only read_replication can be modified in update
|
|
321
|
+
if (
|
|
322
|
+
props.primaryLocationHint &&
|
|
323
|
+
props.primaryLocationHint !== currentDB.result.primary_location_hint
|
|
324
|
+
) {
|
|
325
|
+
throw new Error(
|
|
326
|
+
"Cannot update primaryLocationHint after database creation. Only readReplication.mode can be modified."
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const updatePayload: any = {};
|
|
331
|
+
|
|
332
|
+
// Only include read_replication in update payload
|
|
333
|
+
if (props.readReplication) {
|
|
334
|
+
updatePayload.read_replication = {
|
|
335
|
+
mode: props.readReplication.mode,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const updateResponse = await api.patch(
|
|
340
|
+
`/accounts/${api.accountId}/d1/database/${databaseId}`,
|
|
341
|
+
updatePayload
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
if (!updateResponse.ok) {
|
|
345
|
+
return await handleApiError(
|
|
346
|
+
updateResponse,
|
|
347
|
+
"updating",
|
|
348
|
+
"D1 database",
|
|
349
|
+
databaseId
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return (await updateResponse.json()) as CloudflareD1Response;
|
|
354
|
+
}
|
|
@@ -42,7 +42,7 @@ export class DurableObjectNamespace implements DurableObjectNamespaceInput {
|
|
|
42
42
|
|
|
43
43
|
constructor(
|
|
44
44
|
public readonly id: string,
|
|
45
|
-
input: DurableObjectNamespaceInput
|
|
45
|
+
input: DurableObjectNamespaceInput
|
|
46
46
|
) {
|
|
47
47
|
this.className = input.className;
|
|
48
48
|
this.scriptName = input.scriptName;
|
package/src/cloudflare/index.ts
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
export * from "./account-api-token";
|
|
2
2
|
export * from "./api";
|
|
3
|
+
export * from "./api-error";
|
|
3
4
|
export * from "./assets";
|
|
4
5
|
export * from "./bindings";
|
|
5
6
|
export * from "./bucket";
|
|
6
7
|
export * from "./custom-domain";
|
|
8
|
+
export * from "./d1-database";
|
|
7
9
|
export * from "./dns-records";
|
|
8
10
|
export * from "./durable-object-namespace";
|
|
9
11
|
export * from "./kv-namespace";
|
|
10
12
|
export * from "./permission-groups";
|
|
13
|
+
export * from "./queue";
|
|
11
14
|
export * from "./r2-rest-state-store";
|
|
12
15
|
export * from "./worker";
|
|
16
|
+
export { Workflow } from "./workflow";
|
|
13
17
|
export * from "./wrangler.json";
|
|
14
18
|
export * from "./zone";
|
|
@@ -8,12 +8,6 @@ import {
|
|
|
8
8
|
} from "./api";
|
|
9
9
|
import { handleApiError } from "./api-error";
|
|
10
10
|
|
|
11
|
-
export function isKVNamespace(resource: any): resource is KVNamespace {
|
|
12
|
-
return (
|
|
13
|
-
resource && typeof resource === "object" && resource.type === "kv_namespace"
|
|
14
|
-
);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
11
|
/**
|
|
18
12
|
* Properties for creating or updating a KV Namespace
|
|
19
13
|
*/
|