alchemy 0.7.1 → 0.7.2

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.
@@ -9,6 +9,7 @@ import type { R2Bucket } from "./bucket";
9
9
  import type { D1Database } from "./d1-database";
10
10
  import type { DurableObjectNamespace } from "./durable-object-namespace";
11
11
  import type { KVNamespace } from "./kv-namespace";
12
+ import type { Pipeline } from "./pipeline";
12
13
  import type { Queue } from "./queue";
13
14
  import type { Worker } from "./worker";
14
15
  import type { Workflow } from "./workflow";
@@ -18,11 +19,11 @@ export type Bindings = {
18
19
  /**
19
20
  * L2 Binding Resources.
20
21
  */
21
- export type Binding = Assets | D1Database | DurableObjectNamespace | KVNamespace | Queue | R2Bucket | Secret | string | Worker | Workflow;
22
+ export type Binding = Assets | D1Database | DurableObjectNamespace | KVNamespace | Pipeline | Queue | R2Bucket | Secret | string | Worker | Workflow;
22
23
  /**
23
24
  * Union type for all Worker binding types (API spec)
24
25
  */
25
- export type WorkerBindingSpec = WorkerBindingAI | WorkerBindingAnalyticsEngine | WorkerBindingAssets | WorkerBindingBrowserRendering | WorkerBindingD1 | WorkerBindingDispatchNamespace | WorkerBindingDurableObjectNamespace | WorkerBindingHyperdrive | WorkerBindingJson | WorkerBindingKVNamespace | WorkerBindingMTLSCertificate | WorkerBindingPlainText | WorkerBindingQueue | WorkerBindingR2Bucket | WorkerBindingSecretText | WorkerBindingService | WorkerBindingStaticContent | WorkerBindingTailConsumer | WorkerBindingVectorize | WorkerBindingVersionMetadata | WorkerBindingWasmModule | WorkerBindingWorkflow;
26
+ export type WorkerBindingSpec = WorkerBindingAI | WorkerBindingAnalyticsEngine | WorkerBindingAssets | WorkerBindingBrowserRendering | WorkerBindingD1 | WorkerBindingDispatchNamespace | WorkerBindingDurableObjectNamespace | WorkerBindingHyperdrive | WorkerBindingJson | WorkerBindingKVNamespace | WorkerBindingMTLSCertificate | WorkerBindingPipeline | WorkerBindingPlainText | WorkerBindingQueue | WorkerBindingR2Bucket | WorkerBindingSecretText | WorkerBindingService | WorkerBindingStaticContent | WorkerBindingTailConsumer | WorkerBindingVectorize | WorkerBindingVersionMetadata | WorkerBindingWasmModule | WorkerBindingWorkflow;
26
27
  /**
27
28
  * AI binding type
28
29
  */
@@ -278,3 +279,14 @@ export interface WorkerBindingWorkflow {
278
279
  */
279
280
  script_name?: string;
280
281
  }
282
+ /**
283
+ * Pipeline binding type
284
+ */
285
+ export interface WorkerBindingPipeline {
286
+ /** The name of the binding */
287
+ name: string;
288
+ /** Type identifier for Pipeline binding */
289
+ type: "pipelines";
290
+ /** Pipeline name */
291
+ pipeline: string;
292
+ }
@@ -1,3 +1,4 @@
1
+ import type { Pipeline } from "cloudflare:pipelines";
1
2
  import type { Secret } from "../secret";
2
3
  import type { Assets } from "./assets";
3
4
  import type { Binding } from "./bindings";
@@ -5,7 +6,8 @@ import type { R2Bucket as _R2Bucket } from "./bucket";
5
6
  import type { D1Database as _D1Database } from "./d1-database";
6
7
  import type { DurableObjectNamespace as _DurableObjectNamespace } from "./durable-object-namespace";
7
8
  import type { KVNamespace as _KVNamespace } from "./kv-namespace";
9
+ import type { Pipeline as _Pipeline } from "./pipeline";
8
10
  import type { Queue as _Queue } from "./queue";
9
11
  import type { Worker as _Worker } from "./worker";
10
12
  import type { Workflow as _Workflow } from "./workflow";
11
- export type Bound<T extends Binding> = T extends _DurableObjectNamespace ? DurableObjectNamespace : T extends _KVNamespace ? KVNamespace : T extends _Worker ? Worker : T extends _R2Bucket ? R2Bucket : T extends Secret ? string : T extends Assets ? Service : T extends _Workflow<infer P> ? Workflow<P> : T extends _D1Database ? D1Database : T extends _Queue ? Queue : Service;
13
+ export type Bound<T extends Binding> = T extends _DurableObjectNamespace ? DurableObjectNamespace : T extends _KVNamespace ? KVNamespace : T extends _Worker ? Worker : T extends _R2Bucket ? R2Bucket : T extends Secret ? string : T extends Assets ? Service : T extends _Workflow<infer P> ? Workflow<P> : T extends _D1Database ? D1Database : T extends _Queue ? Queue : T extends _Pipeline<infer R> ? Pipeline<R> : Service;
@@ -205,6 +205,7 @@ export async function emptyBucket(r2, bucketName, jurisdiction) {
205
205
  let totalDeleted = 0;
206
206
  try {
207
207
  do {
208
+ console.log(`Listing objects in bucket ${bucketName}`);
208
209
  // List objects in the bucket
209
210
  const { objects, continuationToken: nextToken } = await listObjects(r2, bucketName, continuationToken, jurisdiction);
210
211
  continuationToken = nextToken;
@@ -33,6 +33,13 @@ export interface D1DatabaseProps extends CloudflareApiOptions {
33
33
  * @default true
34
34
  */
35
35
  delete?: boolean;
36
+ /**
37
+ * Whether to adopt an existing database with the same name if it exists
38
+ * If true and a database with the same name exists, it will be adopted rather than creating a new one
39
+ *
40
+ * @default false
41
+ */
42
+ adopt?: boolean;
36
43
  }
37
44
  /**
38
45
  * Output returned after D1 Database creation/update
@@ -84,6 +91,16 @@ export interface D1Database extends Resource<"cloudflare::D1Database">, D1Databa
84
91
  * primaryLocationHint: "wnam"
85
92
  * });
86
93
  *
94
+ * @example
95
+ * // Adopt an existing database if it already exists instead of failing
96
+ * const existingDb = await D1Database("existing-db", {
97
+ * name: "existing-db",
98
+ * adopt: true,
99
+ * readReplication: {
100
+ * mode: "auto"
101
+ * }
102
+ * });
103
+ *
87
104
  * @see https://developers.cloudflare.com/d1/
88
105
  */
89
106
  export declare const D1Database: (((this: any, id: string, props?: {}) => never) & (new (_: never) => never)) | ((this: Context<D1Database>, id: string, props: D1DatabaseProps) => Promise<D1Database>);
@@ -121,7 +138,7 @@ export declare function deleteDatabase(api: CloudflareApi, databaseId?: string):
121
138
  /**
122
139
  * List all D1 databases in an account
123
140
  */
124
- export declare function listDatabases(api: CloudflareApi): Promise<{
141
+ export declare function listDatabases(api: CloudflareApi, name?: string): Promise<{
125
142
  name: string;
126
143
  id: string;
127
144
  }[]>;
@@ -20,6 +20,16 @@ import { CloudflareApiError, handleApiError } from "./api-error";
20
20
  * primaryLocationHint: "wnam"
21
21
  * });
22
22
  *
23
+ * @example
24
+ * // Adopt an existing database if it already exists instead of failing
25
+ * const existingDb = await D1Database("existing-db", {
26
+ * name: "existing-db",
27
+ * adopt: true,
28
+ * readReplication: {
29
+ * mode: "auto"
30
+ * }
31
+ * });
32
+ *
23
33
  * @see https://developers.cloudflare.com/d1/
24
34
  */
25
35
  export const D1Database = Resource("cloudflare::D1Database", async function (id, props) {
@@ -39,7 +49,34 @@ export const D1Database = Resource("cloudflare::D1Database", async function (id,
39
49
  let dbData;
40
50
  if (this.phase === "create") {
41
51
  console.log("Creating D1 database:", databaseName);
42
- dbData = await createDatabase(api, databaseName, props);
52
+ try {
53
+ dbData = await createDatabase(api, databaseName, props);
54
+ }
55
+ catch (error) {
56
+ // Check if this is a "database already exists" error and adopt is enabled
57
+ if (props.adopt &&
58
+ error instanceof CloudflareApiError &&
59
+ error.message.includes("already exists")) {
60
+ console.log(`Database ${databaseName} already exists, adopting it`);
61
+ // Find the existing database by name
62
+ const databases = await listDatabases(api, databaseName);
63
+ const existingDb = databases.find((db) => db.name === databaseName);
64
+ if (!existingDb) {
65
+ throw new Error(`Failed to find existing database '${databaseName}' for adoption`);
66
+ }
67
+ // Get the database details using its ID
68
+ dbData = await getDatabase(api, existingDb.id);
69
+ // Update the database with the provided properties
70
+ if (props.readReplication) {
71
+ console.log(`Updating adopted database ${databaseName} with new properties`);
72
+ dbData = await updateDatabase(api, existingDb.id, props);
73
+ }
74
+ }
75
+ else {
76
+ // Re-throw the error if adopt is false or it's not a "database already exists" error
77
+ throw error;
78
+ }
79
+ }
43
80
  }
44
81
  else {
45
82
  // Update operation
@@ -117,8 +154,10 @@ export async function deleteDatabase(api, databaseId) {
117
154
  /**
118
155
  * List all D1 databases in an account
119
156
  */
120
- export async function listDatabases(api) {
121
- const response = await api.get(`/accounts/${api.accountId}/d1/database`);
157
+ export async function listDatabases(api, name) {
158
+ // Construct query string if name is provided
159
+ const queryParams = name ? `?name=${encodeURIComponent(name)}` : "";
160
+ const response = await api.get(`/accounts/${api.accountId}/d1/database${queryParams}`);
122
161
  if (!response.ok) {
123
162
  throw new CloudflareApiError(`Failed to list databases: ${response.statusText}`, response);
124
163
  }
@@ -10,6 +10,7 @@ export * from "./dns-records";
10
10
  export * from "./durable-object-namespace";
11
11
  export * from "./kv-namespace";
12
12
  export * from "./permission-groups";
13
+ export * from "./pipeline";
13
14
  export * from "./queue";
14
15
  export * from "./r2-rest-state-store";
15
16
  export * from "./worker";
@@ -10,6 +10,7 @@ export * from "./dns-records";
10
10
  export * from "./durable-object-namespace";
11
11
  export * from "./kv-namespace";
12
12
  export * from "./permission-groups";
13
+ export * from "./pipeline";
13
14
  export * from "./queue";
14
15
  export * from "./r2-rest-state-store";
15
16
  export * from "./worker";
@@ -0,0 +1,335 @@
1
+ import type { Context } from "../context";
2
+ import { Resource } from "../resource";
3
+ import type { Secret } from "../secret";
4
+ import { CloudflareApi, type CloudflareApiOptions } from "./api";
5
+ /**
6
+ * Settings for compression of pipeline output
7
+ */
8
+ export interface CompressionSettings {
9
+ /**
10
+ * Type of compression to use for pipeline output
11
+ * @default "gzip"
12
+ */
13
+ type: "gzip" | "none";
14
+ }
15
+ /**
16
+ * Settings for batching behavior of pipeline output
17
+ */
18
+ export interface BatchSettings {
19
+ /**
20
+ * Maximum size of batch in megabytes before delivery (1-100 MB)
21
+ * @default 100
22
+ */
23
+ maxMb?: number;
24
+ /**
25
+ * Maximum number of rows in a batch before delivery (1-10,000,000 rows)
26
+ * @default 10000000
27
+ */
28
+ maxRows?: number;
29
+ /**
30
+ * Maximum duration of a batch in seconds before delivery (1-300 seconds)
31
+ * @default 300
32
+ */
33
+ maxSeconds?: number;
34
+ }
35
+ /**
36
+ * Configuration for a pipeline HTTP source
37
+ */
38
+ export interface HttpSource {
39
+ /**
40
+ * Format of the source data
41
+ * @default "json"
42
+ */
43
+ format: "json";
44
+ /**
45
+ * Type of source
46
+ */
47
+ type: "http";
48
+ /**
49
+ * Whether authentication is required
50
+ * @default true
51
+ */
52
+ authentication?: boolean;
53
+ /**
54
+ * CORS configuration for HTTP endpoint source
55
+ */
56
+ cors?: {
57
+ /**
58
+ * Allowed origins for CORS requests
59
+ * @default ["*"]
60
+ */
61
+ origins: string[];
62
+ };
63
+ }
64
+ /**
65
+ * Configuration for a pipeline binding source
66
+ */
67
+ export interface BindingSource {
68
+ /**
69
+ * Format of the source data
70
+ * @default "json"
71
+ */
72
+ format: "json";
73
+ /**
74
+ * Type of source
75
+ */
76
+ type: "binding";
77
+ }
78
+ /**
79
+ * Configuration for a pipeline source
80
+ */
81
+ export type PipelineSource = HttpSource | BindingSource;
82
+ /**
83
+ * Configuration for an R2 destination
84
+ */
85
+ export interface R2DestinationConfig {
86
+ /**
87
+ * Type of destination (R2)
88
+ */
89
+ type: "r2";
90
+ /**
91
+ * Format of the output data
92
+ * @default "json"
93
+ */
94
+ format: "json" | "ndjson";
95
+ /**
96
+ * Path configuration for the R2 destination
97
+ */
98
+ path: {
99
+ /**
100
+ * R2 bucket name
101
+ */
102
+ bucket: string;
103
+ /**
104
+ * Optional prefix for files in the bucket
105
+ */
106
+ prefix?: string;
107
+ /**
108
+ * Optional filename pattern
109
+ * @default "${slug}${extension}"
110
+ */
111
+ filename?: string;
112
+ /**
113
+ * Optional filepath pattern
114
+ * @default "${date}/${hour}"
115
+ */
116
+ filepath?: string;
117
+ };
118
+ /**
119
+ * Compression settings
120
+ */
121
+ compression?: CompressionSettings;
122
+ /**
123
+ * Batch settings
124
+ */
125
+ batch?: BatchSettings;
126
+ /**
127
+ * Credentials for the R2 bucket
128
+ * Required for R2 destinations
129
+ */
130
+ credentials: {
131
+ /**
132
+ * Access key ID for the R2 bucket
133
+ */
134
+ accessKeyId: Secret;
135
+ /**
136
+ * Secret access key for the R2 bucket
137
+ */
138
+ secretAccessKey: Secret;
139
+ /**
140
+ * Endpoint for the R2 bucket
141
+ */
142
+ endpoint?: string;
143
+ };
144
+ }
145
+ /**
146
+ * Allowed destination types
147
+ */
148
+ export type PipelineDestination = R2DestinationConfig;
149
+ /**
150
+ * Properties for creating or updating a Pipeline
151
+ */
152
+ export interface PipelineProps extends CloudflareApiOptions {
153
+ /**
154
+ * Name of the pipeline
155
+ */
156
+ name: string;
157
+ /**
158
+ * Source configuration for the pipeline
159
+ */
160
+ source: PipelineSource[];
161
+ /**
162
+ * Destination configuration for the pipeline
163
+ */
164
+ destination: PipelineDestination;
165
+ /**
166
+ * Compression settings for the pipeline
167
+ * @default { type: "gzip" }
168
+ */
169
+ compression?: CompressionSettings;
170
+ /**
171
+ * Whether to delete the pipeline.
172
+ * If set to false, the pipeline will remain but the resource will be removed from state
173
+ *
174
+ * @default true
175
+ */
176
+ delete?: boolean;
177
+ }
178
+ /**
179
+ * Base type for pipeline records
180
+ */
181
+ export interface PipelineRecord {
182
+ [key: string]: any;
183
+ }
184
+ /**
185
+ * Output returned after Pipeline creation/update
186
+ */
187
+ export interface Pipeline<T extends PipelineRecord = PipelineRecord> extends Resource<"cloudflare::Pipeline">, PipelineProps {
188
+ /**
189
+ * Type identifier for the Pipeline resource
190
+ */
191
+ type: "pipeline";
192
+ /**
193
+ * The unique ID of the pipeline
194
+ */
195
+ id: string;
196
+ /**
197
+ * HTTP endpoint URL for the pipeline
198
+ */
199
+ endpoint: string;
200
+ /**
201
+ * Version of the pipeline
202
+ */
203
+ version: number;
204
+ }
205
+ /**
206
+ * Creates and manages Cloudflare Pipelines.
207
+ *
208
+ * Pipelines provide a managed data pipeline service that lets you collect, transform,
209
+ * and route data to various destinations like R2 buckets.
210
+ *
211
+ * @example
212
+ * // Create a basic pipeline with an R2 bucket destination
213
+ * const bucket = await R2Bucket("logs-bucket", {
214
+ * name: "logs-bucket"
215
+ * });
216
+ *
217
+ * const accessKey = alchemy.secret(process.env.R2_ACCESS_KEY_ID!);
218
+ * const secretKey = alchemy.secret(process.env.R2_SECRET_ACCESS_KEY!);
219
+ *
220
+ * const pipeline = await Pipeline("logs-pipeline", {
221
+ * name: "logs-pipeline",
222
+ * destination: {
223
+ * type: "r2",
224
+ * format: "json",
225
+ * path: {
226
+ * bucket: bucket.name,
227
+ * prefix: "app-logs",
228
+ * },
229
+ * credentials: {
230
+ * accessKeyId: accessKey,
231
+ * secretAccessKey: secretKey
232
+ * }
233
+ * },
234
+ * batch: {
235
+ * maxMb: 50,
236
+ * maxSeconds: 60
237
+ * }
238
+ * });
239
+ *
240
+ * @example
241
+ * // Create a pipeline with custom source configuration
242
+ * const customPipeline = await Pipeline("custom-pipeline", {
243
+ * name: "custom-pipeline",
244
+ * source: [{
245
+ * type: "http",
246
+ * format: "json",
247
+ * authentication: true,
248
+ * cors: {
249
+ * origins: ["https://example.com"]
250
+ * }
251
+ * }],
252
+ * destination: {
253
+ * type: "r2",
254
+ * format: "json",
255
+ * path: {
256
+ * bucket: "my-bucket",
257
+ * prefix: "data"
258
+ * },
259
+ * credentials: {
260
+ * accessKeyId: alchemy.secret(process.env.R2_ACCESS_KEY_ID!),
261
+ * secretAccessKey: alchemy.secret(process.env.R2_SECRET_ACCESS_KEY!)
262
+ * },
263
+ * compression: {
264
+ * type: "gzip"
265
+ * }
266
+ * }
267
+ * });
268
+ *
269
+ * @see https://developers.cloudflare.com/pipelines/
270
+ */
271
+ export declare const Pipeline: (((this: any, id: string, props?: {}) => never) & (new (_: never) => never)) | (<T extends PipelineRecord = PipelineRecord>(this: Context<Pipeline<T>>, id: string, props: PipelineProps) => Promise<Pipeline<T>>);
272
+ interface CloudflarePipelineResponse {
273
+ result: {
274
+ id: string;
275
+ name: string;
276
+ endpoint: string;
277
+ version: number;
278
+ source: Array<{
279
+ type: "http" | "binding";
280
+ format: string;
281
+ authentication?: boolean;
282
+ cors?: {
283
+ origins: string[];
284
+ };
285
+ }>;
286
+ destination: {
287
+ type: string;
288
+ format: string;
289
+ path?: {
290
+ bucket: string;
291
+ prefix?: string;
292
+ filename?: string;
293
+ filepath?: string;
294
+ };
295
+ compression?: {
296
+ type: string;
297
+ };
298
+ batch: {
299
+ max_bytes?: number;
300
+ max_rows?: number;
301
+ max_duration_s?: number;
302
+ };
303
+ };
304
+ };
305
+ success: boolean;
306
+ errors: Array<{
307
+ code: number;
308
+ message: string;
309
+ }>;
310
+ messages: string[];
311
+ }
312
+ /**
313
+ * Get a pipeline
314
+ */
315
+ export declare function getPipeline(api: CloudflareApi, pipelineName: string): Promise<CloudflarePipelineResponse>;
316
+ /**
317
+ * Delete a pipeline
318
+ */
319
+ export declare function deletePipeline(api: CloudflareApi, pipelineName: string): Promise<void>;
320
+ /**
321
+ * Create a new pipeline
322
+ */
323
+ export declare function createPipeline(api: CloudflareApi, pipelineName: string, props: PipelineProps): Promise<CloudflarePipelineResponse>;
324
+ /**
325
+ * Update a pipeline
326
+ */
327
+ export declare function updatePipeline(api: CloudflareApi, pipelineName: string, props: PipelineProps): Promise<CloudflarePipelineResponse>;
328
+ /**
329
+ * List all pipelines in an account
330
+ */
331
+ export declare function listPipelines(api: CloudflareApi): Promise<{
332
+ name: string;
333
+ id: string;
334
+ }[]>;
335
+ export {};