alchemy 0.92.2 → 0.93.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/bin/alchemy.js +2 -2
- package/lib/cloudflare/ai-search-namespace.d.ts +150 -0
- package/lib/cloudflare/ai-search-namespace.d.ts.map +1 -0
- package/lib/cloudflare/ai-search-namespace.js +278 -0
- package/lib/cloudflare/ai-search-namespace.js.map +1 -0
- package/lib/cloudflare/ai-search-token.d.ts +11 -1
- package/lib/cloudflare/ai-search-token.d.ts.map +1 -1
- package/lib/cloudflare/ai-search-token.js +35 -7
- package/lib/cloudflare/ai-search-token.js.map +1 -1
- package/lib/cloudflare/ai-search.d.ts +123 -18
- package/lib/cloudflare/ai-search.d.ts.map +1 -1
- package/lib/cloudflare/ai-search.js +222 -68
- package/lib/cloudflare/ai-search.js.map +1 -1
- package/lib/cloudflare/bindings.d.ts +29 -2
- package/lib/cloudflare/bindings.d.ts.map +1 -1
- package/lib/cloudflare/bindings.js.map +1 -1
- package/lib/cloudflare/bound.d.ts +4 -2
- package/lib/cloudflare/bound.d.ts.map +1 -1
- package/lib/cloudflare/container.d.ts +79 -6
- package/lib/cloudflare/container.d.ts.map +1 -1
- package/lib/cloudflare/container.js +130 -9
- package/lib/cloudflare/container.js.map +1 -1
- package/lib/cloudflare/custom-domain.d.ts +14 -0
- package/lib/cloudflare/custom-domain.d.ts.map +1 -1
- package/lib/cloudflare/custom-domain.js +3 -0
- package/lib/cloudflare/custom-domain.js.map +1 -1
- 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/miniflare/build-worker-options.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/build-worker-options.js +43 -0
- package/lib/cloudflare/miniflare/build-worker-options.js.map +1 -1
- package/lib/cloudflare/miniflare/remote-binding-proxy.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/remote-binding-proxy.js +31 -13
- package/lib/cloudflare/miniflare/remote-binding-proxy.js.map +1 -1
- package/lib/cloudflare/website.d.ts +4 -0
- package/lib/cloudflare/website.d.ts.map +1 -1
- package/lib/cloudflare/website.js +15 -1
- package/lib/cloudflare/website.js.map +1 -1
- package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
- package/lib/cloudflare/worker-metadata.js +25 -1
- package/lib/cloudflare/worker-metadata.js.map +1 -1
- package/lib/cloudflare/worker.d.ts +6 -0
- package/lib/cloudflare/worker.d.ts.map +1 -1
- package/lib/cloudflare/worker.js +3 -0
- package/lib/cloudflare/worker.js.map +1 -1
- package/lib/cloudflare/wrangler.json.d.ts.map +1 -1
- package/lib/cloudflare/wrangler.json.js +32 -0
- package/lib/cloudflare/wrangler.json.js.map +1 -1
- package/lib/docker/api.d.ts +4 -1
- package/lib/docker/api.d.ts.map +1 -1
- package/lib/docker/api.js +8 -2
- package/lib/docker/api.js.map +1 -1
- package/lib/docker/image.d.ts +1 -1
- package/lib/docker/image.d.ts.map +1 -1
- package/lib/docker/image.js +4 -44
- package/lib/docker/image.js.map +1 -1
- package/lib/docker/registry.d.ts +11 -0
- package/lib/docker/registry.d.ts.map +1 -0
- package/lib/docker/registry.js +41 -0
- package/lib/docker/registry.js.map +1 -0
- package/package.json +3 -3
- package/src/cloudflare/ai-search-namespace.ts +435 -0
- package/src/cloudflare/ai-search-token.ts +43 -9
- package/src/cloudflare/ai-search.ts +334 -73
- package/src/cloudflare/bindings.ts +33 -0
- package/src/cloudflare/bound.ts +87 -74
- package/src/cloudflare/container.ts +242 -18
- package/src/cloudflare/custom-domain.ts +25 -1
- package/src/cloudflare/index.ts +1 -0
- package/src/cloudflare/miniflare/build-worker-options.ts +50 -1
- package/src/cloudflare/miniflare/remote-binding-proxy.ts +58 -34
- package/src/cloudflare/website.ts +19 -1
- package/src/cloudflare/worker-metadata.ts +25 -1
- package/src/cloudflare/worker.ts +9 -0
- package/src/cloudflare/wrangler.json.ts +32 -0
- package/src/docker/api.ts +11 -2
- package/src/docker/image.ts +5 -56
- package/src/docker/registry.ts +62 -0
- package/workers/cloudflare-state-store.js +57 -57
- package/workers/tunnel-proxy.js +1 -1
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import type { Secret } from "../secret.ts";
|
|
7
7
|
import type { Ai } from "./ai.ts";
|
|
8
|
+
import type { AiSearchNamespace } from "./ai-search-namespace.ts";
|
|
9
|
+
import type { AiSearch } from "./ai-search.ts";
|
|
8
10
|
import type { AnalyticsEngineDataset } from "./analytics-engine.ts";
|
|
9
11
|
import type { Assets } from "./assets.ts";
|
|
10
12
|
import type { Bound } from "./bound.ts";
|
|
@@ -49,6 +51,8 @@ export declare namespace Bindings {
|
|
|
49
51
|
*/
|
|
50
52
|
export type Binding =
|
|
51
53
|
| Ai
|
|
54
|
+
| AiSearch
|
|
55
|
+
| AiSearchNamespace
|
|
52
56
|
| Assets
|
|
53
57
|
| Container
|
|
54
58
|
| CloudflareSecret
|
|
@@ -120,6 +124,8 @@ export function Json<const T>(value: T): Json<T> {
|
|
|
120
124
|
*/
|
|
121
125
|
export type WorkerBindingSpec =
|
|
122
126
|
| WorkerBindingAI
|
|
127
|
+
| WorkerBindingAiSearch
|
|
128
|
+
| WorkerBindingAiSearchNamespace
|
|
123
129
|
| WorkerBindingAnalyticsEngine
|
|
124
130
|
| WorkerBindingAssets
|
|
125
131
|
| WorkerBindingBrowserRendering
|
|
@@ -161,6 +167,33 @@ export interface WorkerBindingAI {
|
|
|
161
167
|
type: "ai";
|
|
162
168
|
}
|
|
163
169
|
|
|
170
|
+
/**
|
|
171
|
+
* AI Search single instance binding type.
|
|
172
|
+
* Binds directly to one specific AI Search instance (always scoped to default namespace).
|
|
173
|
+
*/
|
|
174
|
+
export interface WorkerBindingAiSearch {
|
|
175
|
+
/** The name of the binding */
|
|
176
|
+
name: string;
|
|
177
|
+
/** Type identifier for AI Search single instance binding */
|
|
178
|
+
type: "ai_search";
|
|
179
|
+
/** The AI Search instance name */
|
|
180
|
+
instance_name: string;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* AI Search namespace binding type.
|
|
185
|
+
* Scoped to a user-defined namespace of AI Search instances.
|
|
186
|
+
* Grants full access (CRUD + search + chat) to all instances within the namespace.
|
|
187
|
+
*/
|
|
188
|
+
export interface WorkerBindingAiSearchNamespace {
|
|
189
|
+
/** The name of the binding */
|
|
190
|
+
name: string;
|
|
191
|
+
/** Type identifier for AI Search namespace binding */
|
|
192
|
+
type: "ai_search_namespace";
|
|
193
|
+
/** The namespace name */
|
|
194
|
+
namespace: string;
|
|
195
|
+
}
|
|
196
|
+
|
|
164
197
|
/**
|
|
165
198
|
* Analytics Engine binding type
|
|
166
199
|
*/
|
package/src/cloudflare/bound.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
/// <reference types="@cloudflare/workers-types" />
|
|
1
2
|
import type { Pipeline } from "cloudflare:pipelines";
|
|
2
3
|
import type { Secret } from "../secret.ts";
|
|
3
4
|
import type { Ai as _Ai } from "./ai.ts";
|
|
5
|
+
import type { AiSearchNamespace as _AiSearchNamespace } from "./ai-search-namespace.ts";
|
|
6
|
+
import type { AiSearch as _AiSearch } from "./ai-search.ts";
|
|
4
7
|
import type { AnalyticsEngineDataset as _AnalyticsEngineDataset } from "./analytics-engine.ts";
|
|
5
8
|
import type { Assets } from "./assets.ts";
|
|
6
9
|
import type { Binding, Json, Self } from "./bindings.ts";
|
|
@@ -9,6 +12,7 @@ import type { R2Bucket as _R2Bucket } from "./bucket.ts";
|
|
|
9
12
|
import type { Container as _Container } from "./container.ts";
|
|
10
13
|
import type { D1Database as _D1Database } from "./d1-database.ts";
|
|
11
14
|
import type { DurableObjectNamespace as _DurableObjectNamespace } from "./durable-object-namespace.ts";
|
|
15
|
+
import type { EmailSender as _EmailSender } from "./email-sender.ts";
|
|
12
16
|
import type { HyperdriveRef } from "./hyperdrive-ref.ts";
|
|
13
17
|
import type { Hyperdrive as _Hyperdrive } from "./hyperdrive.ts";
|
|
14
18
|
import type { Images as _Images } from "./images.ts";
|
|
@@ -18,7 +22,6 @@ import type { RateLimit as _RateLimit } from "./rate-limit.ts";
|
|
|
18
22
|
import type { SecretKey } from "./secret-key.ts";
|
|
19
23
|
import type { SecretRef as CloudflareSecretRef } from "./secret-ref.ts";
|
|
20
24
|
import type { Secret as CloudflareSecret } from "./secret.ts";
|
|
21
|
-
import type { EmailSender as _EmailSender } from "./email-sender.ts";
|
|
22
25
|
import type { VectorizeIndex as _VectorizeIndex } from "./vectorize-index.ts";
|
|
23
26
|
import type { VersionMetadata as _VersionMetadata } from "./version-metadata.ts";
|
|
24
27
|
import type { VpcService as _VpcService } from "./vpc-service.ts";
|
|
@@ -39,78 +42,88 @@ type BoundWorker<
|
|
|
39
42
|
>]: Rpc.Provider<RPC, "fetch" | "connect">[property];
|
|
40
43
|
};
|
|
41
44
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
// NOTE: AiSearch / AiSearchNamespace MUST come FIRST in this conditional
|
|
46
|
+
// chain, before any structural discriminator like `{ type: "kv_namespace" }`.
|
|
47
|
+
// `AiSearch.type` is the data-source type (`"r2" | "web-crawler"`), NOT a
|
|
48
|
+
// binding-type discriminator — so we match by *resource-type import* via the
|
|
49
|
+
// `_AiSearch` / `_AiSearchNamespace` aliases (underscore-prefixed to avoid
|
|
50
|
+
// colliding with the identically-named Cloudflare runtime classes on the
|
|
51
|
+
// right-hand side of the conditional).
|
|
52
|
+
export type Bound<T extends Binding> = T extends _AiSearch
|
|
53
|
+
? AiSearchInstance
|
|
54
|
+
: T extends _AiSearchNamespace
|
|
55
|
+
? AiSearchNamespace
|
|
56
|
+
: T extends _DurableObjectNamespace<infer O>
|
|
57
|
+
? DurableObjectNamespace<O & Rpc.DurableObjectBranded>
|
|
58
|
+
: T extends { type: "kv_namespace" }
|
|
59
|
+
? KVNamespace
|
|
60
|
+
: T extends WorkerStub<infer RPC>
|
|
50
61
|
? BoundWorker<RPC>
|
|
51
|
-
: T extends
|
|
52
|
-
?
|
|
53
|
-
: T extends
|
|
54
|
-
?
|
|
55
|
-
: T extends
|
|
56
|
-
?
|
|
57
|
-
: T extends
|
|
58
|
-
?
|
|
59
|
-
: T extends
|
|
60
|
-
?
|
|
61
|
-
: T extends
|
|
62
|
-
?
|
|
63
|
-
: T extends
|
|
64
|
-
?
|
|
65
|
-
: T extends
|
|
66
|
-
?
|
|
67
|
-
: T extends
|
|
68
|
-
?
|
|
69
|
-
: T extends
|
|
70
|
-
?
|
|
71
|
-
: T extends
|
|
72
|
-
?
|
|
73
|
-
: T extends
|
|
74
|
-
?
|
|
75
|
-
: T extends
|
|
76
|
-
?
|
|
77
|
-
: T extends
|
|
78
|
-
?
|
|
79
|
-
: T extends
|
|
80
|
-
?
|
|
81
|
-
: T extends
|
|
82
|
-
?
|
|
83
|
-
: T extends
|
|
84
|
-
?
|
|
85
|
-
: T extends
|
|
86
|
-
?
|
|
87
|
-
: T extends
|
|
88
|
-
?
|
|
89
|
-
: T extends
|
|
90
|
-
?
|
|
91
|
-
: T extends
|
|
92
|
-
?
|
|
93
|
-
: T extends
|
|
94
|
-
?
|
|
95
|
-
: T extends
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
?
|
|
101
|
-
: T extends
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
: T extends _Container<
|
|
106
|
-
infer Obj
|
|
107
|
-
>
|
|
108
|
-
? DurableObjectNamespace<
|
|
109
|
-
Obj &
|
|
110
|
-
Rpc.DurableObjectBranded
|
|
62
|
+
: T extends _Worker<any, infer RPC> | WorkerRef<infer RPC>
|
|
63
|
+
? BoundWorker<RPC>
|
|
64
|
+
: T extends { type: "service" }
|
|
65
|
+
? Service
|
|
66
|
+
: T extends _R2Bucket
|
|
67
|
+
? R2Bucket
|
|
68
|
+
: T extends _Hyperdrive | HyperdriveRef
|
|
69
|
+
? Hyperdrive
|
|
70
|
+
: T extends Secret
|
|
71
|
+
? string
|
|
72
|
+
: T extends CloudflareSecret | CloudflareSecretRef
|
|
73
|
+
? SecretsStoreSecret
|
|
74
|
+
: T extends _EmailSender
|
|
75
|
+
? SendEmail
|
|
76
|
+
: T extends SecretKey
|
|
77
|
+
? CryptoKey
|
|
78
|
+
: T extends Assets
|
|
79
|
+
? Service
|
|
80
|
+
: T extends _Workflow<infer P>
|
|
81
|
+
? Workflow<P>
|
|
82
|
+
: T extends _D1Database
|
|
83
|
+
? D1Database
|
|
84
|
+
: T extends DispatchNamespace
|
|
85
|
+
? DispatchNamespace
|
|
86
|
+
: T extends _WorkerLoader
|
|
87
|
+
? WorkerLoader
|
|
88
|
+
: T extends _VectorizeIndex
|
|
89
|
+
? VectorizeIndex
|
|
90
|
+
: T extends _Queue<infer Body>
|
|
91
|
+
? Queue<Body>
|
|
92
|
+
: T extends _AnalyticsEngineDataset
|
|
93
|
+
? AnalyticsEngineDataset
|
|
94
|
+
: T extends _Pipeline<infer R>
|
|
95
|
+
? Pipeline<R>
|
|
96
|
+
: T extends _RateLimit
|
|
97
|
+
? RateLimit
|
|
98
|
+
: T extends string
|
|
99
|
+
? T
|
|
100
|
+
: T extends BrowserRendering
|
|
101
|
+
? Fetcher
|
|
102
|
+
: T extends _Ai<infer M>
|
|
103
|
+
? Ai<M>
|
|
104
|
+
: T extends _Images
|
|
105
|
+
? ImagesBinding
|
|
106
|
+
: T extends _VersionMetadata
|
|
107
|
+
? WorkerVersionMetadata
|
|
108
|
+
: T extends
|
|
109
|
+
| _Worker.DevDomain
|
|
110
|
+
| _Worker.DevUrl
|
|
111
|
+
? string
|
|
112
|
+
: T extends Self
|
|
113
|
+
? Service
|
|
114
|
+
: T extends Json<
|
|
115
|
+
infer T
|
|
111
116
|
>
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
+
? T
|
|
118
|
+
: T extends _Container<
|
|
119
|
+
infer Obj
|
|
120
|
+
>
|
|
121
|
+
? DurableObjectNamespace<
|
|
122
|
+
Obj &
|
|
123
|
+
Rpc.DurableObjectBranded
|
|
124
|
+
>
|
|
125
|
+
: T extends _VpcService
|
|
126
|
+
? Fetcher
|
|
127
|
+
: T extends undefined
|
|
128
|
+
? undefined
|
|
129
|
+
: Service;
|
|
@@ -1,30 +1,35 @@
|
|
|
1
1
|
import type { Context } from "../context.ts";
|
|
2
|
-
import {
|
|
2
|
+
import { DockerApi } from "../docker/api.ts";
|
|
3
|
+
import {
|
|
4
|
+
Image,
|
|
5
|
+
type DockerBuildOptions,
|
|
6
|
+
type ImageProps,
|
|
7
|
+
} from "../docker/image.ts";
|
|
8
|
+
import { pushImageToRegistry } from "../docker/registry.ts";
|
|
9
|
+
import type { RemoteImage } from "../docker/remote-image.ts";
|
|
3
10
|
import { Resource } from "../resource.ts";
|
|
4
11
|
import { Scope } from "../scope.ts";
|
|
5
|
-
import { secret } from "../secret.ts";
|
|
6
12
|
import {
|
|
13
|
+
createCloudflareApi,
|
|
7
14
|
type CloudflareApi,
|
|
8
15
|
type CloudflareApiOptions,
|
|
9
|
-
createCloudflareApi,
|
|
10
16
|
} from "./api.ts";
|
|
11
17
|
|
|
12
18
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* Extends ImageProps for Docker image configuration and CloudflareApiOptions
|
|
16
|
-
* for Cloudflare API authentication.
|
|
19
|
+
* Common properties shared between build and image container configurations
|
|
17
20
|
*/
|
|
18
|
-
|
|
19
|
-
extends
|
|
20
|
-
Omit<ImageProps, "registry" | "skipPush">,
|
|
21
|
-
Partial<CloudflareApiOptions> {
|
|
21
|
+
interface ContainerPropsBase extends Partial<CloudflareApiOptions> {
|
|
22
22
|
/**
|
|
23
23
|
* The class name for the container binding.
|
|
24
24
|
* This is used to identify the container class in Worker bindings.
|
|
25
25
|
*/
|
|
26
26
|
className: string;
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Tag for the image (e.g., "latest")
|
|
30
|
+
*/
|
|
31
|
+
tag?: string;
|
|
32
|
+
|
|
28
33
|
/**
|
|
29
34
|
* Maximum number of container instances that can be running.
|
|
30
35
|
* Controls horizontal scaling limits.
|
|
@@ -92,6 +97,75 @@ export interface ContainerProps
|
|
|
92
97
|
rollout?: ContainerApplicationRollout;
|
|
93
98
|
}
|
|
94
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Container configuration using an existing image reference
|
|
102
|
+
*/
|
|
103
|
+
interface ContainerPropsWithImage extends ContainerPropsBase {
|
|
104
|
+
/**
|
|
105
|
+
* Name for the container application.
|
|
106
|
+
*
|
|
107
|
+
* @default ${app}-${stage}-${id}
|
|
108
|
+
*/
|
|
109
|
+
name?: string;
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Image name or reference (e.g., "nginx:alpine")
|
|
113
|
+
*
|
|
114
|
+
* Use this when you want to deploy an existing image rather than building one.
|
|
115
|
+
* Cannot be used together with `build`.
|
|
116
|
+
*/
|
|
117
|
+
image: string | Image | RemoteImage;
|
|
118
|
+
|
|
119
|
+
build?: never;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Container configuration that builds a Docker image
|
|
124
|
+
*/
|
|
125
|
+
interface ContainerPropsWithBuild extends ContainerPropsBase {
|
|
126
|
+
/**
|
|
127
|
+
* Name for the container application and image repository.
|
|
128
|
+
*
|
|
129
|
+
* @default ${app}-${stage}-${id}
|
|
130
|
+
*/
|
|
131
|
+
name?: string;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Build configuration for the Docker image.
|
|
135
|
+
*
|
|
136
|
+
* Use this when you want to build an image from a Dockerfile.
|
|
137
|
+
* Cannot be used together with `image`.
|
|
138
|
+
*/
|
|
139
|
+
build: DockerBuildOptions;
|
|
140
|
+
|
|
141
|
+
image?: never;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Properties for creating a Container binding or ContainerApplication
|
|
146
|
+
*
|
|
147
|
+
* Either provide `image` to use an existing image, or `build` to build from a Dockerfile.
|
|
148
|
+
* These options are mutually exclusive.
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* // Using an existing image
|
|
152
|
+
* const container = await Container("my-container", {
|
|
153
|
+
* className: "MyContainer",
|
|
154
|
+
* image: "nginx:alpine"
|
|
155
|
+
* });
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* // Building from a Dockerfile
|
|
159
|
+
* const container = await Container("my-container", {
|
|
160
|
+
* className: "MyContainer",
|
|
161
|
+
* build: {
|
|
162
|
+
* context: "./app",
|
|
163
|
+
* dockerfile: "Dockerfile"
|
|
164
|
+
* }
|
|
165
|
+
* });
|
|
166
|
+
*/
|
|
167
|
+
export type ContainerProps = ContainerPropsWithImage | ContainerPropsWithBuild;
|
|
168
|
+
|
|
95
169
|
/**
|
|
96
170
|
* Instance types for Cloudflare Container deployments.
|
|
97
171
|
*
|
|
@@ -202,10 +276,60 @@ export type Container<T = any> = {
|
|
|
202
276
|
__phantom?: T;
|
|
203
277
|
};
|
|
204
278
|
|
|
279
|
+
/**
|
|
280
|
+
* Normalize an image reference for Cloudflare Container deployments.
|
|
281
|
+
*
|
|
282
|
+
* Follows wrangler's resolveImageName logic:
|
|
283
|
+
* - Short names like "myapp:v1" → "registry.cloudflare.com/{accountId}/myapp:v1"
|
|
284
|
+
* - CF registry without accountId like "registry.cloudflare.com/myapp:v1" → adds accountId
|
|
285
|
+
* - External registries like "docker.io/nginx:1.25" → pass through unchanged
|
|
286
|
+
*/
|
|
287
|
+
export function resolveImageName(accountId: string, image: string): string {
|
|
288
|
+
const cfRegistry = getCloudflareContainerRegistry();
|
|
289
|
+
|
|
290
|
+
// Check if image has a registry prefix (contains a dot in the first segment)
|
|
291
|
+
const segments = image.split("/");
|
|
292
|
+
const hasRegistryPrefix = segments.length > 1 && segments[0].includes(".");
|
|
293
|
+
|
|
294
|
+
if (!hasRegistryPrefix) {
|
|
295
|
+
// Short name like "myapp:v1" → add CF registry + accountId
|
|
296
|
+
return `${cfRegistry}/${accountId}/${image}`;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (image.startsWith(`${cfRegistry}/`)) {
|
|
300
|
+
// CF registry image - check if accountId is present
|
|
301
|
+
const afterRegistry = image.slice(`${cfRegistry}/`.length);
|
|
302
|
+
const segments = afterRegistry.split("/");
|
|
303
|
+
|
|
304
|
+
// If only one segment (e.g., "myapp:tag"), add accountId
|
|
305
|
+
// If first segment doesn't look like an accountId (32 hex chars), add it
|
|
306
|
+
if (segments.length === 1) {
|
|
307
|
+
return `${cfRegistry}/${accountId}/${afterRegistry}`;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Check if first segment is the accountId (32 hex chars)
|
|
311
|
+
const possibleAccountId = segments[0];
|
|
312
|
+
const isAccountId = /^[a-f0-9]{32}$/.test(possibleAccountId);
|
|
313
|
+
|
|
314
|
+
if (!isAccountId) {
|
|
315
|
+
// First segment is not an accountId, prepend it
|
|
316
|
+
return `${cfRegistry}/${accountId}/${afterRegistry}`;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// External registry or already fully-qualified CF registry → pass through
|
|
321
|
+
return image;
|
|
322
|
+
}
|
|
323
|
+
|
|
205
324
|
export async function Container<T>(
|
|
206
325
|
id: string,
|
|
207
326
|
props: ContainerProps,
|
|
208
327
|
): Promise<Container<T>> {
|
|
328
|
+
// Validate that build and image are mutually exclusive
|
|
329
|
+
if (props.build && props.image) {
|
|
330
|
+
throw new Error("Container: specify either `build` or `image`, not both.");
|
|
331
|
+
}
|
|
332
|
+
|
|
209
333
|
const scope = Scope.current;
|
|
210
334
|
const name = props.name ?? scope.createPhysicalName(id);
|
|
211
335
|
const tag =
|
|
@@ -230,6 +354,100 @@ export async function Container<T>(
|
|
|
230
354
|
};
|
|
231
355
|
|
|
232
356
|
const isDev = scope.local && !props.dev?.remote;
|
|
357
|
+
|
|
358
|
+
// Prebuilt image path: use as-is, no Docker pull/push
|
|
359
|
+
// This matches wrangler's behavior where registry URIs are passed directly
|
|
360
|
+
// to the Cloudflare API without any local Docker operations
|
|
361
|
+
if (props.image && !props.build) {
|
|
362
|
+
const rawImageRef =
|
|
363
|
+
typeof props.image === "string" ? props.image : props.image.imageRef;
|
|
364
|
+
|
|
365
|
+
if (isDev) {
|
|
366
|
+
// For local dev with prebuilt images, we need to pull and re-tag
|
|
367
|
+
const dockerApi = new DockerApi();
|
|
368
|
+
const devImageRef = `cloudflare-dev/${name}:${tag}`;
|
|
369
|
+
|
|
370
|
+
if (isCloudflareRegistryLink(rawImageRef)) {
|
|
371
|
+
// For CF registry images, authenticate before pulling
|
|
372
|
+
const api = await createCloudflareApi(props);
|
|
373
|
+
const credentials = await getContainerCredentials(api);
|
|
374
|
+
const cfRegistry = getCloudflareContainerRegistry();
|
|
375
|
+
|
|
376
|
+
await dockerApi.login(
|
|
377
|
+
cfRegistry,
|
|
378
|
+
credentials.username || credentials.user!,
|
|
379
|
+
credentials.password,
|
|
380
|
+
);
|
|
381
|
+
|
|
382
|
+
try {
|
|
383
|
+
// CF Containers run on linux/amd64, so we need to pull that platform
|
|
384
|
+
await dockerApi.pullImage(rawImageRef, { platform: "linux/amd64" });
|
|
385
|
+
await dockerApi.tagImage(rawImageRef, devImageRef);
|
|
386
|
+
} finally {
|
|
387
|
+
await dockerApi.logout(cfRegistry);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return {
|
|
391
|
+
...output,
|
|
392
|
+
image: {
|
|
393
|
+
kind: "Image",
|
|
394
|
+
name: `cloudflare-dev/${name}`,
|
|
395
|
+
imageRef: devImageRef,
|
|
396
|
+
tag,
|
|
397
|
+
builtAt: Date.now(),
|
|
398
|
+
build: undefined,
|
|
399
|
+
},
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// For external registry images in dev mode, pull and re-tag for Miniflare
|
|
404
|
+
const image = await Image(id, {
|
|
405
|
+
image: props.image,
|
|
406
|
+
tag,
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
await dockerApi.tagImage(image.imageRef, devImageRef);
|
|
410
|
+
|
|
411
|
+
return {
|
|
412
|
+
...output,
|
|
413
|
+
image: {
|
|
414
|
+
...image,
|
|
415
|
+
name: `cloudflare-dev/${name}`,
|
|
416
|
+
imageRef: devImageRef,
|
|
417
|
+
},
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Non-dev mode: normalize the image reference and use directly
|
|
422
|
+
const api = await createCloudflareApi(props);
|
|
423
|
+
const imageRef = resolveImageName(api.accountId, rawImageRef);
|
|
424
|
+
|
|
425
|
+
// Extract name and tag from the resolved reference
|
|
426
|
+
const [refWithoutDigest] = imageRef.split("@");
|
|
427
|
+
const lastColonIndex = refWithoutDigest.lastIndexOf(":");
|
|
428
|
+
const namePart =
|
|
429
|
+
lastColonIndex > -1
|
|
430
|
+
? refWithoutDigest.slice(0, lastColonIndex)
|
|
431
|
+
: refWithoutDigest;
|
|
432
|
+
const tagPart =
|
|
433
|
+
lastColonIndex > -1 ? refWithoutDigest.slice(lastColonIndex + 1) : tag;
|
|
434
|
+
|
|
435
|
+
const image: Image = {
|
|
436
|
+
kind: "Image",
|
|
437
|
+
name: namePart,
|
|
438
|
+
imageRef,
|
|
439
|
+
tag: tagPart,
|
|
440
|
+
builtAt: Date.now(),
|
|
441
|
+
build: undefined,
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
return {
|
|
445
|
+
...output,
|
|
446
|
+
image,
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// Build path: build locally and push to Cloudflare registry
|
|
233
451
|
if (isDev) {
|
|
234
452
|
const image = await Image(id, {
|
|
235
453
|
...props,
|
|
@@ -249,7 +467,6 @@ export async function Container<T>(
|
|
|
249
467
|
}
|
|
250
468
|
|
|
251
469
|
const api = await createCloudflareApi(props);
|
|
252
|
-
const credentials = await getContainerCredentials(api);
|
|
253
470
|
|
|
254
471
|
const image = await Image(id, {
|
|
255
472
|
name: `${api.accountId}/${name}`,
|
|
@@ -265,16 +482,23 @@ export async function Container<T>(
|
|
|
265
482
|
context: process.cwd(),
|
|
266
483
|
},
|
|
267
484
|
image: props.image,
|
|
268
|
-
|
|
269
|
-
server: "registry.cloudflare.com",
|
|
270
|
-
username: credentials.username || credentials.user!,
|
|
271
|
-
password: secret(credentials.password),
|
|
272
|
-
},
|
|
485
|
+
skipPush: true,
|
|
273
486
|
} as ImageProps);
|
|
274
487
|
|
|
488
|
+
const credentials = await getContainerCredentials(api);
|
|
489
|
+
const pushedImage = await pushImageToRegistry(image.imageRef, {
|
|
490
|
+
server: "registry.cloudflare.com",
|
|
491
|
+
username: credentials.username || credentials.user!,
|
|
492
|
+
password: credentials.password,
|
|
493
|
+
});
|
|
494
|
+
|
|
275
495
|
return {
|
|
276
496
|
...output,
|
|
277
|
-
image
|
|
497
|
+
image: {
|
|
498
|
+
...image,
|
|
499
|
+
imageRef: pushedImage.imageRef,
|
|
500
|
+
repoDigest: pushedImage.repoDigest,
|
|
501
|
+
},
|
|
278
502
|
};
|
|
279
503
|
}
|
|
280
504
|
|
|
@@ -44,6 +44,21 @@ export interface CustomDomainProps extends CloudflareApiOptions {
|
|
|
44
44
|
*/
|
|
45
45
|
adopt?: boolean;
|
|
46
46
|
|
|
47
|
+
/**
|
|
48
|
+
* If true, forcibly transfer the hostname when it is currently bound to a
|
|
49
|
+
* different Worker service. Without this flag, Cloudflare rejects the
|
|
50
|
+
* upsert with HTTP 409 / error code 100116 "Hostname '<host>' already in
|
|
51
|
+
* use by other custom domain".
|
|
52
|
+
*
|
|
53
|
+
* This is destructive to the previously bound Worker (its traffic on this
|
|
54
|
+
* hostname stops immediately), so it is opt-in. Only relevant when the
|
|
55
|
+
* existing binding points at a different `workerName` or `environment`.
|
|
56
|
+
*
|
|
57
|
+
* @see https://developers.cloudflare.com/api/resources/workers/subresources/domains/methods/update/
|
|
58
|
+
* @default false
|
|
59
|
+
*/
|
|
60
|
+
overrideExistingOrigin?: boolean;
|
|
61
|
+
|
|
47
62
|
/**
|
|
48
63
|
* Whether to delete the custom domain when removed from Alchemy.
|
|
49
64
|
* If set to false, the custom domain will remain but the resource will be removed from state.
|
|
@@ -269,12 +284,21 @@ async function ensureCustomDomain(
|
|
|
269
284
|
if (!bindingExists || needsUpdate) {
|
|
270
285
|
operationPerformed = bindingExists ? "update" : "create";
|
|
271
286
|
|
|
272
|
-
const putPayload
|
|
287
|
+
const putPayload: {
|
|
288
|
+
zone_id: string;
|
|
289
|
+
hostname: string;
|
|
290
|
+
service: string;
|
|
291
|
+
environment: string;
|
|
292
|
+
override_existing_origin?: boolean;
|
|
293
|
+
} = {
|
|
273
294
|
zone_id: props.zoneId,
|
|
274
295
|
hostname: domainHostname,
|
|
275
296
|
service: props.workerName,
|
|
276
297
|
environment: environment,
|
|
277
298
|
};
|
|
299
|
+
if (props.overrideExistingOrigin !== undefined) {
|
|
300
|
+
putPayload.override_existing_origin = props.overrideExistingOrigin;
|
|
301
|
+
}
|
|
278
302
|
|
|
279
303
|
const putResponse = await api.put(
|
|
280
304
|
`/accounts/${api.accountId}/workers/domains`,
|
package/src/cloudflare/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ export * from "./account-api-token.ts";
|
|
|
4
4
|
export * from "./account-id.ts";
|
|
5
5
|
export * from "./ai-crawler.ts";
|
|
6
6
|
export * from "./ai-gateway.ts";
|
|
7
|
+
export * from "./ai-search-namespace.ts";
|
|
7
8
|
export * from "./ai-search-token.ts";
|
|
8
9
|
export * from "./ai-search.ts";
|
|
9
10
|
export * from "./ai.ts";
|