alchemy 2.0.0-beta.2 → 2.0.0-beta.3
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-effect.js +2 -2
- package/bin/alchemy-effect.js.map +1 -1
- package/package.json +18 -9
- package/src/Cloudflare/Container/Container.ts +122 -0
- package/src/Cloudflare/Container/ContainerApplication.ts +3 -0
- package/src/Cloudflare/Container/ContainerBinding.ts +1 -1
- package/src/Cloudflare/Container/StartContainer.ts +1 -1
- package/src/Cloudflare/D1/D1Database.ts +23 -4
- package/src/Cloudflare/KV/KVNamespace.ts +25 -0
- package/src/Cloudflare/R2/R2Bucket.ts +43 -0
- package/src/Cloudflare/Website/StaticSite.ts +57 -0
- package/src/Cloudflare/Website/Vite.ts +52 -8
- package/src/Cloudflare/Workers/{DurableObject.ts → DurableObjectNamespace.ts} +242 -0
- package/src/Cloudflare/Workers/DynamicWorkerLoader.ts +66 -7
- package/src/Cloudflare/Workers/WebSocket.ts +1 -1
- package/src/Cloudflare/Workers/Worker.ts +312 -7
- package/src/Cloudflare/Workers/Workflow.ts +53 -11
- package/src/Cloudflare/Workers/index.ts +1 -1
- package/src/GitHub/Comment.ts +224 -0
- package/src/GitHub/Secret.ts +212 -0
- package/src/GitHub/Variable.ts +123 -0
- package/src/GitHub/index.ts +3 -0
- package/src/Provider.ts +1 -1
- package/src/Util/dedent.ts +56 -0
- package/src/Util/index.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "alchemy",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.3",
|
|
4
4
|
"homepage": "https://alchemy.run",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Sam Goodwin <sam@alchemy.run>",
|
|
@@ -20,6 +20,14 @@
|
|
|
20
20
|
],
|
|
21
21
|
"type": "module",
|
|
22
22
|
"sideEffects": false,
|
|
23
|
+
"scripts": {
|
|
24
|
+
"dev": "tsdown --watch",
|
|
25
|
+
"build": "bun bundle && bun pm pack",
|
|
26
|
+
"bundle": "tsdown",
|
|
27
|
+
"bundle:watch": "tsdown --watch",
|
|
28
|
+
"publish:npm": "cp ../README.md . && bun publish && rm README.md",
|
|
29
|
+
"test:benchmark": "tsx --tsconfig tsconfig.test.json test/types.benchmark.ts"
|
|
30
|
+
},
|
|
23
31
|
"exports": {
|
|
24
32
|
".": {
|
|
25
33
|
"types": "./src/index.d.ts",
|
|
@@ -123,6 +131,12 @@
|
|
|
123
131
|
"worker": "./src/Cloudflare/*/index.ts",
|
|
124
132
|
"import": "./lib/Cloudflare/*/index.js"
|
|
125
133
|
},
|
|
134
|
+
"./GitHub": {
|
|
135
|
+
"types": "./src/GitHub/index.d.ts",
|
|
136
|
+
"bun": "./src/GitHub/index.ts",
|
|
137
|
+
"worker": "./src/GitHub/index.ts",
|
|
138
|
+
"import": "./lib/GitHub/index.js"
|
|
139
|
+
},
|
|
126
140
|
"./Endpoint": {
|
|
127
141
|
"types": "./src/Endpoint/index.d.ts",
|
|
128
142
|
"bun": "./src/Endpoint/index.ts",
|
|
@@ -187,14 +201,6 @@
|
|
|
187
201
|
"publishConfig": {
|
|
188
202
|
"access": "public"
|
|
189
203
|
},
|
|
190
|
-
"scripts": {
|
|
191
|
-
"dev": "tsdown --watch",
|
|
192
|
-
"build": "bun bundle && bun pm pack",
|
|
193
|
-
"bundle": "tsdown",
|
|
194
|
-
"bundle:watch": "tsdown --watch",
|
|
195
|
-
"publish:npm": "cp ../README.md . && bun publish && rm README.md",
|
|
196
|
-
"test:benchmark": "tsx --tsconfig tsconfig.test.json test/types.benchmark.ts"
|
|
197
|
-
},
|
|
198
204
|
"dependencies": {
|
|
199
205
|
"@ai-sdk/anthropic": "^3.0.31",
|
|
200
206
|
"@ai-sdk/openai": "^3.0.23",
|
|
@@ -207,10 +213,12 @@
|
|
|
207
213
|
"@distilled.cloud/core": "^0.10.0",
|
|
208
214
|
"@effect/vitest": "4.0.0-beta.48",
|
|
209
215
|
"@libsql/client": "^0.17.0",
|
|
216
|
+
"@octokit/rest": "^22.0.1",
|
|
210
217
|
"@smithy/node-config-provider": "^4.0.0",
|
|
211
218
|
"@smithy/shared-ini-file-loader": "^4.3.4",
|
|
212
219
|
"@smithy/types": "^4.8.1",
|
|
213
220
|
"@types/aws-lambda": "^8.10.152",
|
|
221
|
+
"@types/libsodium-wrappers": "^0.8.2",
|
|
214
222
|
"ai": "^6.0.62",
|
|
215
223
|
"aws4fetch": "^1.0.20",
|
|
216
224
|
"capnp-es": "^0.0.14",
|
|
@@ -219,6 +227,7 @@
|
|
|
219
227
|
"fast-xml-parser": "^5.3.4",
|
|
220
228
|
"ignore": "^7.0.5",
|
|
221
229
|
"jszip": "^3.10.1",
|
|
230
|
+
"libsodium-wrappers": "^0.8.3",
|
|
222
231
|
"rolldown": "1.0.0-rc.13",
|
|
223
232
|
"solid-js": "latest",
|
|
224
233
|
"sonda": "^0.11.1",
|
|
@@ -48,6 +48,128 @@ export type Container = {
|
|
|
48
48
|
interceptAllOutboundHttp(binding: Fetcher): Effect.Effect<void>;
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
+
/**
|
|
52
|
+
* A Cloudflare Container that runs a long-lived process alongside a
|
|
53
|
+
* Durable Object.
|
|
54
|
+
*
|
|
55
|
+
* Containers always use the **Container Layer** pattern — the class
|
|
56
|
+
* and `.make()` must live in separate files. A Container must be
|
|
57
|
+
* bound to a Durable Object, and the DO imports the class to get a
|
|
58
|
+
* typed handle. If the class and `.make()` lived in the same file,
|
|
59
|
+
* the DO's bundle would pull in all of the container's runtime
|
|
60
|
+
* dependencies (process spawners, Node APIs, SDKs, etc.), which
|
|
61
|
+
* would bloat the bundle and likely break the Cloudflare Workers
|
|
62
|
+
* runtime. Keeping them separate ensures the bundler only includes
|
|
63
|
+
* the tiny class in the DO's output.
|
|
64
|
+
*
|
|
65
|
+
* See the {@link https://alchemy.run/concepts/platform | Platform
|
|
66
|
+
* concept} page for how this fits into the async / effect / layer
|
|
67
|
+
* progression.
|
|
68
|
+
*
|
|
69
|
+
* @section Container Layer
|
|
70
|
+
* Define the class and `.make()` in separate files. The class
|
|
71
|
+
* declares the container's identity, configuration, and typed
|
|
72
|
+
* shape. `.make()` provides the runtime implementation as a
|
|
73
|
+
* default export. Use `Container.of` to construct the typed
|
|
74
|
+
* shape — it ensures your implementation matches the methods
|
|
75
|
+
* declared on the class.
|
|
76
|
+
*
|
|
77
|
+
* @example Container class
|
|
78
|
+
* ```typescript
|
|
79
|
+
* // src/Sandbox.ts
|
|
80
|
+
* export class Sandbox extends Cloudflare.Container<
|
|
81
|
+
* Sandbox,
|
|
82
|
+
* {
|
|
83
|
+
* exec: (cmd: string) => Effect.Effect<{
|
|
84
|
+
* exitCode: number;
|
|
85
|
+
* stdout: string;
|
|
86
|
+
* stderr: string;
|
|
87
|
+
* }>;
|
|
88
|
+
* }
|
|
89
|
+
* >()(
|
|
90
|
+
* "Sandbox",
|
|
91
|
+
* { main: import.meta.filename },
|
|
92
|
+
* ) {}
|
|
93
|
+
* ```
|
|
94
|
+
*
|
|
95
|
+
* @example Container .make()
|
|
96
|
+
* ```typescript
|
|
97
|
+
* // src/Sandbox.runtime.ts
|
|
98
|
+
* export default Sandbox.make(
|
|
99
|
+
* Effect.gen(function* () {
|
|
100
|
+
* const cp = yield* ChildProcessSpawner;
|
|
101
|
+
*
|
|
102
|
+
* return Sandbox.of({
|
|
103
|
+
* exec: (cmd) =>
|
|
104
|
+
* cp.spawn(ChildProcess.make(cmd, { shell: true })).pipe(
|
|
105
|
+
* Effect.map(({ exitCode, stdout, stderr }) => ({
|
|
106
|
+
* exitCode, stdout, stderr,
|
|
107
|
+
* })),
|
|
108
|
+
* Effect.scoped,
|
|
109
|
+
* ),
|
|
110
|
+
* fetch: Effect.succeed(
|
|
111
|
+
* HttpServerResponse.text("Hello from container!"),
|
|
112
|
+
* ),
|
|
113
|
+
* });
|
|
114
|
+
* }),
|
|
115
|
+
* );
|
|
116
|
+
* ```
|
|
117
|
+
*
|
|
118
|
+
* @section Configuration
|
|
119
|
+
* The props object accepts `main` (entrypoint file), `instanceType`
|
|
120
|
+
* (compute size), `runtime` (`"bun"` or `"node"`), and
|
|
121
|
+
* `observability` settings. Use `Stack.useSync` to vary config by
|
|
122
|
+
* stage.
|
|
123
|
+
*
|
|
124
|
+
* @example Stage-dependent configuration
|
|
125
|
+
* ```typescript
|
|
126
|
+
* export class Sandbox extends Cloudflare.Container<Sandbox>()(
|
|
127
|
+
* "Sandbox",
|
|
128
|
+
* Stack.useSync((stack) => ({
|
|
129
|
+
* main: import.meta.filename,
|
|
130
|
+
* instanceType: stack.stage === "prod" ? "standard-1" : "dev",
|
|
131
|
+
* observability: { logs: { enabled: true } },
|
|
132
|
+
* })),
|
|
133
|
+
* ) {}
|
|
134
|
+
* ```
|
|
135
|
+
*
|
|
136
|
+
* @section Starting from a Durable Object
|
|
137
|
+
* Use `Cloudflare.Container.bind` in the outer init phase to bind
|
|
138
|
+
* the container class, then `Cloudflare.start` in the inner
|
|
139
|
+
* per-instance phase to start it. Because the DO only imports the
|
|
140
|
+
* class, the runtime implementation is completely excluded from the
|
|
141
|
+
* DO's bundle.
|
|
142
|
+
*
|
|
143
|
+
* @example Binding and starting a container
|
|
144
|
+
* ```typescript
|
|
145
|
+
* // init (outer Effect) — only imports the class
|
|
146
|
+
* const sandbox = yield* Cloudflare.Container.bind(Sandbox);
|
|
147
|
+
*
|
|
148
|
+
* // per-instance (inner Effect)
|
|
149
|
+
* return Effect.gen(function* () {
|
|
150
|
+
* const container = yield* Cloudflare.start(sandbox);
|
|
151
|
+
*
|
|
152
|
+
* return {
|
|
153
|
+
* exec: (cmd: string) => container.exec(cmd),
|
|
154
|
+
* };
|
|
155
|
+
* });
|
|
156
|
+
* ```
|
|
157
|
+
*
|
|
158
|
+
* @section HTTP Requests to Container Ports
|
|
159
|
+
* Use `getTcpPort` to get a `fetch` handle for a specific port on
|
|
160
|
+
* the running container. This lets you make HTTP requests to
|
|
161
|
+
* servers running inside the container process.
|
|
162
|
+
*
|
|
163
|
+
* @example Fetching from a container port
|
|
164
|
+
* ```typescript
|
|
165
|
+
* const container = yield* Cloudflare.start(sandbox);
|
|
166
|
+
* const { fetch } = yield* container.getTcpPort(3000);
|
|
167
|
+
*
|
|
168
|
+
* const response = yield* fetch(
|
|
169
|
+
* HttpClientRequest.get("http://container/health"),
|
|
170
|
+
* );
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
51
173
|
export const Container: Platform<
|
|
52
174
|
ContainerApplication,
|
|
53
175
|
ContainerServices,
|
|
@@ -223,6 +223,9 @@ export type ContainerServices =
|
|
|
223
223
|
|
|
224
224
|
export type ContainerShape = Main<ContainerServices>;
|
|
225
225
|
|
|
226
|
+
/**
|
|
227
|
+
* @internal
|
|
228
|
+
*/
|
|
226
229
|
export interface ContainerApplication<Shape = unknown> extends Resource<
|
|
227
230
|
ContainerTypeId,
|
|
228
231
|
ContainerApplicationProps,
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
import {
|
|
9
9
|
DurableObjectNamespace,
|
|
10
10
|
DurableObjectState,
|
|
11
|
-
} from "../Workers/
|
|
11
|
+
} from "../Workers/DurableObjectNamespace.ts";
|
|
12
12
|
import { Worker } from "../Workers/Worker.ts";
|
|
13
13
|
import type { Container } from "./Container.ts";
|
|
14
14
|
import type { ContainerApplication } from "./ContainerApplication.ts";
|
|
@@ -6,7 +6,7 @@ import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse";
|
|
|
6
6
|
import * as HttpServerRequest from "effect/unstable/http/HttpServerRequest";
|
|
7
7
|
import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse";
|
|
8
8
|
import { type Fetcher } from "../Fetcher.ts";
|
|
9
|
-
import { DurableObjectState } from "../Workers/
|
|
9
|
+
import { DurableObjectState } from "../Workers/DurableObjectNamespace.ts";
|
|
10
10
|
import { type Container, ContainerError } from "./Container.ts";
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -57,18 +57,37 @@ export type D1Database = Resource<
|
|
|
57
57
|
/**
|
|
58
58
|
* A Cloudflare D1 serverless SQL database built on SQLite.
|
|
59
59
|
*
|
|
60
|
+
* D1 is a serverless relational database that runs at the edge. Create a
|
|
61
|
+
* database as a resource, then bind it to a Worker to run SQL queries.
|
|
62
|
+
*
|
|
60
63
|
* @section Creating a Database
|
|
61
|
-
* @example Basic
|
|
64
|
+
* @example Basic database
|
|
62
65
|
* ```typescript
|
|
63
|
-
* const db = yield*
|
|
66
|
+
* const db = yield* Cloudflare.D1Database("my-db");
|
|
64
67
|
* ```
|
|
65
68
|
*
|
|
66
|
-
* @example Database with
|
|
69
|
+
* @example Database with location hint
|
|
67
70
|
* ```typescript
|
|
68
|
-
* const db = yield*
|
|
71
|
+
* const db = yield* Cloudflare.D1Database("my-db", {
|
|
69
72
|
* primaryLocationHint: "wnam",
|
|
70
73
|
* });
|
|
71
74
|
* ```
|
|
75
|
+
*
|
|
76
|
+
* @section Binding to a Worker
|
|
77
|
+
* @example Using D1 inside a Worker
|
|
78
|
+
* ```typescript
|
|
79
|
+
* const db = yield* Cloudflare.D1Connection.bind(MyDB);
|
|
80
|
+
*
|
|
81
|
+
* // Run a query
|
|
82
|
+
* const results = yield* db.prepare("SELECT * FROM users WHERE id = ?")
|
|
83
|
+
* .bind(userId)
|
|
84
|
+
* .all();
|
|
85
|
+
*
|
|
86
|
+
* // Execute a mutation
|
|
87
|
+
* yield* db.prepare("INSERT INTO users (id, name) VALUES (?, ?)")
|
|
88
|
+
* .bind(newId, name)
|
|
89
|
+
* .run();
|
|
90
|
+
* ```
|
|
72
91
|
*/
|
|
73
92
|
export const D1Database = Resource<D1Database>("Cloudflare.D1Database");
|
|
74
93
|
|
|
@@ -30,6 +30,31 @@ export type KVNamespace = Resource<
|
|
|
30
30
|
Providers
|
|
31
31
|
>;
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* A Cloudflare Workers KV namespace for key-value storage at the edge.
|
|
35
|
+
*
|
|
36
|
+
* KV provides eventually-consistent, low-latency reads with global
|
|
37
|
+
* replication. Create a namespace as a resource, then bind it to a Worker
|
|
38
|
+
* to get/put values at runtime.
|
|
39
|
+
*
|
|
40
|
+
* @section Creating a Namespace
|
|
41
|
+
* @example Basic KV namespace
|
|
42
|
+
* ```typescript
|
|
43
|
+
* const kv = yield* Cloudflare.KVNamespace("MyKV");
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* @section Binding to a Worker
|
|
47
|
+
* @example Using KV inside a Worker
|
|
48
|
+
* ```typescript
|
|
49
|
+
* const kv = yield* Cloudflare.KVNamespace.bind(MyKV);
|
|
50
|
+
*
|
|
51
|
+
* // Read a value
|
|
52
|
+
* const value = yield* kv.get("my-key");
|
|
53
|
+
*
|
|
54
|
+
* // Write a value
|
|
55
|
+
* yield* kv.put("my-key", "hello world");
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
33
58
|
export const KVNamespace = Resource<KVNamespace>("Cloudflare.KVNamespace")({
|
|
34
59
|
bind: KVNamespaceBinding.bind,
|
|
35
60
|
});
|
|
@@ -47,6 +47,49 @@ export type R2Bucket = Resource<
|
|
|
47
47
|
Providers
|
|
48
48
|
>;
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* A Cloudflare R2 object storage bucket with S3-compatible API.
|
|
52
|
+
*
|
|
53
|
+
* R2 provides zero-egress-fee object storage. Create a bucket as a resource,
|
|
54
|
+
* then bind it to a Worker to read and write objects at runtime.
|
|
55
|
+
*
|
|
56
|
+
* @section Creating a Bucket
|
|
57
|
+
* @example Basic R2 bucket
|
|
58
|
+
* ```typescript
|
|
59
|
+
* const bucket = yield* Cloudflare.R2Bucket("MyBucket");
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* @example Bucket with location hint
|
|
63
|
+
* ```typescript
|
|
64
|
+
* const bucket = yield* Cloudflare.R2Bucket("MyBucket", {
|
|
65
|
+
* locationHint: "wnam",
|
|
66
|
+
* });
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
69
|
+
* @section Binding to a Worker
|
|
70
|
+
* @example Reading and writing objects
|
|
71
|
+
* ```typescript
|
|
72
|
+
* const bucket = yield* Cloudflare.R2Bucket.bind(MyBucket);
|
|
73
|
+
*
|
|
74
|
+
* // Write an object
|
|
75
|
+
* yield* bucket.put("hello.txt", "Hello, World!");
|
|
76
|
+
*
|
|
77
|
+
* // Read an object
|
|
78
|
+
* const object = yield* bucket.get("hello.txt");
|
|
79
|
+
* if (object) {
|
|
80
|
+
* const text = yield* object.text();
|
|
81
|
+
* }
|
|
82
|
+
* ```
|
|
83
|
+
*
|
|
84
|
+
* @example Streaming upload with content length
|
|
85
|
+
* ```typescript
|
|
86
|
+
* const bucket = yield* Cloudflare.R2Bucket.bind(MyBucket);
|
|
87
|
+
*
|
|
88
|
+
* yield* bucket.put("upload.bin", request.stream, {
|
|
89
|
+
* contentLength: Number(request.headers["content-length"] ?? 0),
|
|
90
|
+
* });
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
50
93
|
export const R2Bucket = Resource<R2Bucket>("Cloudflare.R2Bucket")({
|
|
51
94
|
bind: R2BucketBinding.bind,
|
|
52
95
|
});
|
|
@@ -19,6 +19,63 @@ export interface StaticSiteProps
|
|
|
19
19
|
|
|
20
20
|
export type StaticSite = ReturnType<typeof StaticSite>;
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* A Cloudflare Worker that serves static assets built by a shell command.
|
|
24
|
+
*
|
|
25
|
+
* `StaticSite` runs a build command (e.g. `npm run build`), content-hashes
|
|
26
|
+
* the output directory, and deploys the result as a Cloudflare Worker with
|
|
27
|
+
* static assets. Use this when your site has its own build step that
|
|
28
|
+
* produces a directory of files — Hugo, Zola, Eleventy, or any custom
|
|
29
|
+
* pipeline.
|
|
30
|
+
*
|
|
31
|
+
* For Vite-based projects, prefer `Cloudflare.Vite` which handles
|
|
32
|
+
* building automatically.
|
|
33
|
+
*
|
|
34
|
+
* @resource
|
|
35
|
+
*
|
|
36
|
+
* @section Basic Usage
|
|
37
|
+
* Point `command` at your build script and `outdir` at where it writes
|
|
38
|
+
* output. Alchemy runs the command, hashes the output, and deploys it.
|
|
39
|
+
*
|
|
40
|
+
* @example Deploying a Hugo site
|
|
41
|
+
* ```typescript
|
|
42
|
+
* const site = yield* Cloudflare.StaticSite("Blog", {
|
|
43
|
+
* command: "hugo --minify",
|
|
44
|
+
* outdir: "public",
|
|
45
|
+
* });
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* @section Asset Configuration
|
|
49
|
+
* Use `assetsConfig` to control how Cloudflare handles routing for
|
|
50
|
+
* your static files — HTML handling, not-found behavior, etc.
|
|
51
|
+
*
|
|
52
|
+
* @example SPA-style routing
|
|
53
|
+
* ```typescript
|
|
54
|
+
* const site = yield* Cloudflare.StaticSite("App", {
|
|
55
|
+
* command: "npm run build",
|
|
56
|
+
* outdir: "dist",
|
|
57
|
+
* assetsConfig: {
|
|
58
|
+
* htmlHandling: "auto-trailing-slash",
|
|
59
|
+
* notFoundHandling: "single-page-application",
|
|
60
|
+
* },
|
|
61
|
+
* });
|
|
62
|
+
* ```
|
|
63
|
+
*
|
|
64
|
+
* @section Custom Rebuild Scope
|
|
65
|
+
* By default, all non-gitignored files are hashed to decide whether
|
|
66
|
+
* the build should re-run. Use `memo` to narrow the scope.
|
|
67
|
+
*
|
|
68
|
+
* @example Narrowing the memo scope
|
|
69
|
+
* ```typescript
|
|
70
|
+
* const site = yield* Cloudflare.StaticSite("Docs", {
|
|
71
|
+
* command: "npm run build",
|
|
72
|
+
* outdir: "dist",
|
|
73
|
+
* memo: {
|
|
74
|
+
* include: ["content/**", "templates/**", "config.toml"],
|
|
75
|
+
* },
|
|
76
|
+
* });
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
22
79
|
export const StaticSite = (id: string, props: InputProps<StaticSiteProps>) =>
|
|
23
80
|
Effect.gen(function* () {
|
|
24
81
|
// TODO(sam): local dev/hmr support?
|
|
@@ -21,22 +21,31 @@ export interface ViteProps extends Omit<WorkerProps, "vite" | "main"> {
|
|
|
21
21
|
/**
|
|
22
22
|
* A Cloudflare Worker deployed from a Vite project.
|
|
23
23
|
*
|
|
24
|
-
* `Vite` uses the Cloudflare Vite plugin to build both the server bundle
|
|
25
|
-
* client assets in a single `vite build` invocation — no manual
|
|
26
|
-
* entrypoint, build command, output directory, or Wrangler
|
|
27
|
-
* required.
|
|
24
|
+
* `Vite` uses the Cloudflare Vite plugin to build both the server bundle
|
|
25
|
+
* and client assets in a single `vite build` invocation — no manual
|
|
26
|
+
* `main` entrypoint, build command, output directory, or Wrangler
|
|
27
|
+
* configuration required.
|
|
28
28
|
*
|
|
29
29
|
* Input files are content-hashed (respecting `.gitignore` by default) so
|
|
30
30
|
* unchanged projects skip the build and deploy entirely.
|
|
31
31
|
*
|
|
32
|
+
* @resource
|
|
33
|
+
*
|
|
32
34
|
* @section Deploying a Static Site
|
|
33
|
-
*
|
|
35
|
+
* For a pure static site (no SSR), a single call is all you need.
|
|
36
|
+
* Vite builds the project and Alchemy deploys the output as a
|
|
37
|
+
* Cloudflare Worker with static assets.
|
|
38
|
+
*
|
|
39
|
+
* @example Static Vite site
|
|
34
40
|
* ```typescript
|
|
35
41
|
* const site = yield* Cloudflare.Vite("Website");
|
|
36
42
|
* ```
|
|
37
43
|
*
|
|
38
|
-
* @section
|
|
39
|
-
*
|
|
44
|
+
* @section SSR Frameworks
|
|
45
|
+
* For SSR frameworks like TanStack Start, SolidStart, or Nuxt, enable
|
|
46
|
+
* `nodejs_compat` so the server bundle can use Node.js APIs.
|
|
47
|
+
*
|
|
48
|
+
* @example TanStack Start
|
|
40
49
|
* ```typescript
|
|
41
50
|
* const app = yield* Cloudflare.Vite("TanStackStart", {
|
|
42
51
|
* compatibility: {
|
|
@@ -45,8 +54,43 @@ export interface ViteProps extends Omit<WorkerProps, "vite" | "main"> {
|
|
|
45
54
|
* });
|
|
46
55
|
* ```
|
|
47
56
|
*
|
|
57
|
+
* @example SolidStart with worker-first routing
|
|
58
|
+
* ```typescript
|
|
59
|
+
* const app = yield* Cloudflare.Vite("SolidStart", {
|
|
60
|
+
* compatibility: {
|
|
61
|
+
* flags: ["nodejs_compat"],
|
|
62
|
+
* },
|
|
63
|
+
* assets: {
|
|
64
|
+
* config: { runWorkerFirst: true },
|
|
65
|
+
* },
|
|
66
|
+
* });
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
69
|
+
* @section Single-Page Applications
|
|
70
|
+
* For SPAs (React, Vue, etc.), configure asset handling so all
|
|
71
|
+
* routes fall back to `index.html`.
|
|
72
|
+
*
|
|
73
|
+
* @example Vue SPA
|
|
74
|
+
* ```typescript
|
|
75
|
+
* const app = yield* Cloudflare.Vite("Vue", {
|
|
76
|
+
* compatibility: {
|
|
77
|
+
* flags: ["nodejs_compat"],
|
|
78
|
+
* },
|
|
79
|
+
* assets: {
|
|
80
|
+
* config: {
|
|
81
|
+
* htmlHandling: "auto-trailing-slash",
|
|
82
|
+
* notFoundHandling: "single-page-application",
|
|
83
|
+
* },
|
|
84
|
+
* },
|
|
85
|
+
* });
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
48
88
|
* @section Custom Rebuild Scope
|
|
49
|
-
*
|
|
89
|
+
* By default, every non-gitignored file is hashed to decide whether
|
|
90
|
+
* a rebuild is needed. Use `memo` to narrow the scope when your
|
|
91
|
+
* project has large directories that don't affect the build output.
|
|
92
|
+
*
|
|
93
|
+
* @example Narrowing the memo scope
|
|
50
94
|
* ```typescript
|
|
51
95
|
* const site = yield* Cloudflare.Vite("Docs", {
|
|
52
96
|
* memo: {
|