alchemy 0.57.2 → 0.58.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.
@@ -0,0 +1,448 @@
1
+ import type { Context } from "../context.ts";
2
+ import { Resource } from "../resource.ts";
3
+ import type { Secret } from "../secret.ts";
4
+ import { type CloudflareApi, type CloudflareApiOptions } from "./api.ts";
5
+ /**
6
+ * Tunnel data as returned by Cloudflare API
7
+ */
8
+ interface CloudflareTunnel {
9
+ id: string;
10
+ account_tag: string;
11
+ created_at: string;
12
+ deleted_at: string | null;
13
+ name: string;
14
+ metadata?: Record<string, any>;
15
+ credentials_file?: {
16
+ AccountTag: string;
17
+ TunnelID: string;
18
+ TunnelName: string;
19
+ TunnelSecret: string;
20
+ };
21
+ token?: string;
22
+ }
23
+ /**
24
+ * Properties for creating or updating a Cloudflare Tunnel
25
+ *
26
+ * @remarks
27
+ * This interface includes all configuration options supported by Cloudflare Tunnels
28
+ * for both remotely-managed (configSrc: 'cloudflare') and locally-managed tunnels.
29
+ */
30
+ export interface TunnelProps extends CloudflareApiOptions {
31
+ /**
32
+ * Name for the tunnel
33
+ *
34
+ * Note: Tunnel names are immutable and cannot be changed after creation.
35
+ * When updating a tunnel, any name change will be ignored.
36
+ *
37
+ * @default id
38
+ */
39
+ name?: string;
40
+ /**
41
+ * Secret for the tunnel
42
+ * If not provided, will be generated automatically
43
+ */
44
+ tunnelSecret?: Secret<string>;
45
+ /**
46
+ * Optional metadata object for the tunnel
47
+ */
48
+ metadata?: Record<string, any>;
49
+ /**
50
+ * Configuration source
51
+ * - 'cloudflare' - Use Cloudflare configuration (default, managed via API)
52
+ * - 'local' - Use local configuration (managed via config file)
53
+ *
54
+ * @default 'cloudflare'
55
+ * @see https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/get-started/tunnel-useful-terms/#remotely-managed-tunnel
56
+ * @see https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/get-started/tunnel-useful-terms/#locally-managed-tunnel
57
+ */
58
+ configSrc?: "cloudflare" | "local";
59
+ /**
60
+ * Ingress rules defining how requests are routed
61
+ * Must include a catch-all rule at the end
62
+ * Only used when configSrc is 'cloudflare'
63
+ *
64
+ * @see https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/configure-tunnels/origin-configuration/#ingress-rules
65
+ */
66
+ ingress?: IngressRule[];
67
+ /**
68
+ * WarpRouting configuration for private network access
69
+ * Only used when configSrc is 'cloudflare'
70
+ *
71
+ * @see https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/private-net/
72
+ */
73
+ warpRouting?: {
74
+ enabled?: boolean;
75
+ };
76
+ /**
77
+ * Origin request configuration to apply to all rules
78
+ * Only used when configSrc is 'cloudflare'
79
+ *
80
+ * @see https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/configure-tunnels/origin-configuration/#origin-request-parameters
81
+ */
82
+ originRequest?: OriginRequestConfig;
83
+ /**
84
+ * Whether to adopt an existing tunnel with the same name if it exists
85
+ * If true and a tunnel with the same name exists, it will be adopted rather than creating a new one
86
+ *
87
+ * @default false
88
+ */
89
+ adopt?: boolean;
90
+ /**
91
+ * Whether to delete the tunnel.
92
+ * If set to false, the tunnel will remain but the resource will be removed from state
93
+ *
94
+ * @default true
95
+ */
96
+ delete?: boolean;
97
+ }
98
+ /**
99
+ * Tunnel configuration for routing traffic
100
+ */
101
+ export interface TunnelConfig {
102
+ /**
103
+ * Ingress rules defining how requests are routed
104
+ * Must include a catch-all rule at the end
105
+ */
106
+ ingress?: IngressRule[];
107
+ /**
108
+ * WarpRouting configuration for private network access
109
+ */
110
+ warpRouting?: {
111
+ enabled?: boolean;
112
+ };
113
+ /**
114
+ * Origin request configuration to apply to all rules
115
+ */
116
+ originRequest?: OriginRequestConfig;
117
+ }
118
+ /**
119
+ * Ingress rule defining how a hostname is routed
120
+ *
121
+ * @see https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/configure-tunnels/origin-configuration/#ingress-rules
122
+ */
123
+ export interface IngressRule {
124
+ /**
125
+ * Hostname to match for this rule
126
+ * Use service: "http_status:404" as catch-all
127
+ */
128
+ hostname?: string;
129
+ /**
130
+ * Service to route to (e.g., "http://localhost:8000" or "http_status:404")
131
+ *
132
+ * @see https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/configure-tunnels/origin-configuration/#supported-protocols
133
+ */
134
+ service: string;
135
+ /**
136
+ * Path to match for this rule
137
+ */
138
+ path?: string;
139
+ /**
140
+ * Origin request configuration for this specific rule
141
+ */
142
+ originRequest?: OriginRequestConfig;
143
+ }
144
+ /**
145
+ * Origin request configuration
146
+ *
147
+ * @see https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/configure-tunnels/origin-configuration/#origin-request-parameters
148
+ */
149
+ export interface OriginRequestConfig {
150
+ /**
151
+ * Timeout for origin server to respond to a request
152
+ */
153
+ connectTimeout?: number;
154
+ /**
155
+ * Timeout for closing the connection to the origin server
156
+ */
157
+ tlsTimeout?: number;
158
+ /**
159
+ * Timeout for TCP connections to the origin server
160
+ */
161
+ tcpKeepAlive?: number;
162
+ /**
163
+ * Disable keep-alive connections
164
+ */
165
+ noHappyEyeballs?: boolean;
166
+ /**
167
+ * Keep connections open after a request
168
+ */
169
+ keepAliveConnections?: number;
170
+ /**
171
+ * Timeout for keep-alive connections
172
+ */
173
+ keepAliveTimeout?: number;
174
+ /**
175
+ * HTTP/2 origin support
176
+ */
177
+ http2Origin?: boolean;
178
+ /**
179
+ * Headers to add to origin requests
180
+ */
181
+ httpHostHeader?: string;
182
+ /**
183
+ * CA pool for origin TLS verification
184
+ */
185
+ caPool?: string;
186
+ /**
187
+ * Disable TLS verification
188
+ */
189
+ noTLSVerify?: boolean;
190
+ /**
191
+ * Disable chunked encoding
192
+ */
193
+ disableChunkedEncoding?: boolean;
194
+ /**
195
+ * Rewrite the Host header
196
+ */
197
+ bastionMode?: boolean;
198
+ /**
199
+ * Proxy protocol version
200
+ */
201
+ proxyProtocol?: "off" | "v1" | "v2";
202
+ /**
203
+ * Proxy outgoing connections through a specified address
204
+ */
205
+ proxyAddress?: string;
206
+ /**
207
+ * Port to use for proxy connections
208
+ */
209
+ proxyPort?: number;
210
+ /**
211
+ * Type of proxy to use
212
+ */
213
+ proxyType?: string;
214
+ /**
215
+ * Enable TCP keep-alive for connection pooling
216
+ */
217
+ tcpKeepAliveInterval?: number;
218
+ }
219
+ export declare function isTunnel(resource: Resource): resource is Tunnel;
220
+ /**
221
+ * Output returned after Tunnel creation/update
222
+ */
223
+ export interface Tunnel extends Resource<"cloudflare::Tunnel">, Omit<TunnelProps, "delete" | "tunnelSecret"> {
224
+ /**
225
+ * The ID of the tunnel
226
+ */
227
+ tunnelId: string;
228
+ /**
229
+ * The account ID that owns the tunnel
230
+ */
231
+ accountTag: string;
232
+ /**
233
+ * Time at which the tunnel was created
234
+ */
235
+ createdAt: string;
236
+ /**
237
+ * Time at which the tunnel was deleted (null if active)
238
+ */
239
+ deletedAt: string | null;
240
+ /**
241
+ * Credentials for connecting to the tunnel
242
+ */
243
+ credentials: {
244
+ accountTag: string;
245
+ tunnelId: string;
246
+ tunnelName: string;
247
+ tunnelSecret: Secret<string>;
248
+ };
249
+ /**
250
+ * Token for running the tunnel
251
+ *
252
+ * @remarks
253
+ * Use this token with `cloudflared tunnel run --token <token>` to start the tunnel
254
+ *
255
+ * @see https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/get-started/create-remote-tunnel-api/#install-and-run-the-tunnel
256
+ */
257
+ token: Secret<string>;
258
+ /**
259
+ * DNS records automatically created for hostnames in ingress rules
260
+ * Maps hostname to DNS record ID
261
+ * @internal
262
+ */
263
+ dnsRecords?: Record<string, string>;
264
+ }
265
+ /**
266
+ * Creates and manages a Cloudflare Tunnel, which provides a secure connection between
267
+ * your origin server and Cloudflare's edge. This resource handles the tunnel lifecycle
268
+ * (create, update, delete) and configuration.
269
+ *
270
+ * @remarks
271
+ * After creating a tunnel, use the returned credentials and token to run the
272
+ * cloudflared connector on your origin server.
273
+ *
274
+ * When hostnames are specified in ingress rules, this resource automatically creates
275
+ * the required DNS CNAME records pointing to <tunnel-id>.cfargotunnel.com, following
276
+ * the Cloudflare API documentation for connecting applications (step 3a).
277
+ *
278
+ * @see https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/
279
+ * @see https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/get-started/create-remote-tunnel-api/
280
+ * @see https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/configure-tunnels/
281
+ *
282
+ * @example
283
+ * // Create a basic tunnel
284
+ * const tunnel = await Tunnel("my-app", {
285
+ * name: "my-app-tunnel"
286
+ * });
287
+ *
288
+ * // Run cloudflared with:
289
+ * // cloudflared tunnel run --token <tunnel.token.unencrypted>
290
+ *
291
+ * @example
292
+ * // Create a tunnel with ingress configuration for a web app
293
+ * // DNS records are automatically created for each hostname
294
+ * const webTunnel = await Tunnel("web-app", {
295
+ * name: "web-app-tunnel",
296
+ * ingress: [
297
+ * {
298
+ * hostname: "app.example.com",
299
+ * service: "http://localhost:3000"
300
+ * },
301
+ * {
302
+ * service: "http_status:404" // catch-all rule
303
+ * }
304
+ * ]
305
+ * });
306
+ * // A CNAME record for app.example.com → webTunnel.tunnelId.cfargotunnel.com
307
+ * // is automatically created in the appropriate zone
308
+ *
309
+ * @example
310
+ * // Create a tunnel with multiple services and origin configuration
311
+ * const apiTunnel = await Tunnel("api", {
312
+ * name: "api-tunnel",
313
+ * ingress: [
314
+ * {
315
+ * hostname: "api.example.com",
316
+ * path: "/v1/*",
317
+ * service: "http://localhost:8080",
318
+ * originRequest: {
319
+ * httpHostHeader: "api.internal",
320
+ * connectTimeout: 30
321
+ * }
322
+ * },
323
+ * {
324
+ * hostname: "api.example.com",
325
+ * path: "/v2/*",
326
+ * service: "http://localhost:8081"
327
+ * },
328
+ * {
329
+ * service: "http_status:404"
330
+ * }
331
+ * ]
332
+ * });
333
+ *
334
+ * @example
335
+ * // Create a tunnel for private network access with WARP
336
+ * const privateTunnel = await Tunnel("private-network", {
337
+ * name: "private-network-tunnel",
338
+ * warpRouting: {
339
+ * enabled: true
340
+ * }
341
+ * });
342
+ *
343
+ * @example
344
+ * // Create a tunnel with origin request configuration
345
+ * const secureTunnel = await Tunnel("secure", {
346
+ * name: "secure-tunnel",
347
+ * originRequest: {
348
+ * noTLSVerify: false,
349
+ * connectTimeout: 30,
350
+ * httpHostHeader: "internal.service"
351
+ * },
352
+ * ingress: [
353
+ * {
354
+ * hostname: "secure.example.com",
355
+ * service: "https://localhost:8443"
356
+ * },
357
+ * {
358
+ * service: "http_status:404"
359
+ * }
360
+ * ]
361
+ * });
362
+ *
363
+ * @example
364
+ * // Adopt an existing tunnel if it already exists
365
+ * const existingTunnel = await Tunnel("existing", {
366
+ * name: "existing-tunnel",
367
+ * adopt: true,
368
+ * ingress: [
369
+ * {
370
+ * hostname: "updated.example.com",
371
+ * service: "http://localhost:5000"
372
+ * },
373
+ * {
374
+ * service: "http_status:404"
375
+ * }
376
+ * ]
377
+ * });
378
+ *
379
+ * @example
380
+ * // Tunnel with automatic DNS record creation
381
+ * // The Tunnel resource automatically creates DNS records for hostnames in ingress rules
382
+ * const appTunnel = await Tunnel("app", {
383
+ * name: "app-tunnel",
384
+ * ingress: [
385
+ * {
386
+ * hostname: "app.example.com",
387
+ * service: "http://localhost:3000"
388
+ * },
389
+ * {
390
+ * hostname: "api.example.com",
391
+ * service: "http://localhost:8080"
392
+ * },
393
+ * {
394
+ * service: "http_status:404"
395
+ * }
396
+ * ]
397
+ * });
398
+ * // DNS CNAME records are automatically created:
399
+ * // - app.example.com → {tunnelId}.cfargotunnel.com
400
+ * // - api.example.com → {tunnelId}.cfargotunnel.com
401
+ *
402
+ * // Run the tunnel:
403
+ * // cloudflared tunnel run --token <appTunnel.token.unencrypted>
404
+ *
405
+ * @example
406
+ * // For advanced DNS control, you can still manually manage DNS records
407
+ * // by omitting hostnames from ingress rules:
408
+ * const tunnel = await Tunnel("manual-dns", {
409
+ * name: "manual-dns-tunnel",
410
+ * ingress: [
411
+ * {
412
+ * service: "http://localhost:3000"
413
+ * },
414
+ * {
415
+ * service: "http_status:404"
416
+ * }
417
+ * ]
418
+ * });
419
+ * // Then create DNS records separately with custom configuration
420
+ */
421
+ export declare const Tunnel: (((this: any, id: string, props?: {}) => never) & (new (_: never) => never)) | ((this: Context<Tunnel>, id: string, props: TunnelProps) => Promise<Tunnel>);
422
+ /**
423
+ * Get tunnel details
424
+ * @internal
425
+ */
426
+ export declare function getTunnel(api: CloudflareApi, tunnelId: string): Promise<CloudflareTunnel>;
427
+ /**
428
+ * Get tunnel configuration
429
+ * @internal
430
+ */
431
+ export declare function getTunnelConfiguration(api: CloudflareApi, tunnelId: string): Promise<TunnelConfig>;
432
+ /**
433
+ * List all tunnels with pagination support
434
+ * @internal
435
+ */
436
+ export declare function listTunnels(api: CloudflareApi, options?: {
437
+ /** Whether to include deleted tunnels */
438
+ includeDeleted?: boolean;
439
+ /** Maximum number of tunnels to return */
440
+ limit?: number;
441
+ }): Promise<CloudflareTunnel[]>;
442
+ /**
443
+ * Find a tunnel by name with pagination support
444
+ * @internal
445
+ */
446
+ export declare function findTunnelByName(api: CloudflareApi, name: string): Promise<CloudflareTunnel | null>;
447
+ export {};
448
+ //# sourceMappingURL=tunnel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tunnel.d.ts","sourceRoot":"","sources":["../../src/cloudflare/tunnel.ts"],"names":[],"mappings":"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"}