devflare 1.0.0-next.1 → 1.0.0-next.10
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/LLM.md +775 -637
- package/R2.md +200 -0
- package/README.md +285 -514
- package/bin/devflare.js +8 -8
- package/dist/{account-rvrj687w.js → account-8psavtg6.js} +27 -4
- package/dist/bridge/miniflare.d.ts +6 -0
- package/dist/bridge/miniflare.d.ts.map +1 -1
- package/dist/bridge/proxy.d.ts +5 -6
- package/dist/bridge/proxy.d.ts.map +1 -1
- package/dist/bridge/server.d.ts.map +1 -1
- package/dist/browser.d.ts +50 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/{build-mnf6v8gd.js → build-k36xrzvy.js} +26 -7
- package/dist/bundler/do-bundler.d.ts +7 -0
- package/dist/bundler/do-bundler.d.ts.map +1 -1
- package/dist/cli/commands/account.d.ts.map +1 -1
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/deploy.d.ts.map +1 -1
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/types.d.ts.map +1 -1
- package/dist/cli/config-path.d.ts +5 -0
- package/dist/cli/config-path.d.ts.map +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/package-metadata.d.ts +16 -0
- package/dist/cli/package-metadata.d.ts.map +1 -0
- package/dist/config/compiler.d.ts +7 -0
- package/dist/config/compiler.d.ts.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/schema.d.ts +2575 -1221
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/{deploy-nhceck39.js → deploy-dbvfq8vq.js} +33 -15
- package/dist/{dev-qnxet3j9.js → dev-rk8p6pse.js} +900 -234
- package/dist/dev-server/miniflare-log.d.ts +12 -0
- package/dist/dev-server/miniflare-log.d.ts.map +1 -0
- package/dist/dev-server/runtime-stdio.d.ts +8 -0
- package/dist/dev-server/runtime-stdio.d.ts.map +1 -0
- package/dist/dev-server/server.d.ts +2 -0
- package/dist/dev-server/server.d.ts.map +1 -1
- package/dist/dev-server/vite-utils.d.ts +37 -0
- package/dist/dev-server/vite-utils.d.ts.map +1 -0
- package/dist/{doctor-e8fy6fj5.js → doctor-06y8nxd4.js} +73 -50
- package/dist/{durable-object-t4kbb0yt.js → durable-object-yt8v1dyn.js} +1 -1
- package/dist/index-05fyzwne.js +195 -0
- package/dist/index-1p814k7s.js +227 -0
- package/dist/{index-hcex3rgh.js → index-1phx14av.js} +84 -7
- package/dist/{index-tk6ej9dj.js → index-2q3pmzrx.js} +12 -16
- package/dist/{index-pf5s73n9.js → index-59df49vn.js} +11 -281
- package/dist/index-5yxg30va.js +304 -0
- package/dist/index-62b3gt2g.js +12 -0
- package/dist/index-6h8xbs75.js +44 -0
- package/dist/{index-67qcae0f.js → index-6v3wjg1r.js} +16 -1
- package/dist/index-8gtqgb3q.js +529 -0
- package/dist/{index-gz1gndna.js → index-9wt9x09k.js} +42 -62
- package/dist/index-fef08w43.js +231 -0
- package/dist/{index-ep3445yc.js → index-jht2j546.js} +393 -170
- package/dist/index-k7r18na8.js +0 -0
- package/dist/{index-m2q41jwa.js → index-n932ytmq.js} +9 -1
- package/dist/index-pwgyy2q9.js +39 -0
- package/dist/{index-07q6yxyc.js → index-v8vvsn9x.js} +1 -0
- package/dist/index-vky23txa.js +70 -0
- package/dist/index-vs49yxn4.js +322 -0
- package/dist/{index-z14anrqp.js → index-wfbfz02q.js} +14 -15
- package/dist/index-ws68xvq2.js +311 -0
- package/dist/index-y1d8za14.js +196 -0
- package/dist/{init-f9mgmew3.js → init-na2atvz2.js} +42 -55
- package/dist/router/types.d.ts +24 -0
- package/dist/router/types.d.ts.map +1 -0
- package/dist/runtime/context.d.ts +249 -8
- package/dist/runtime/context.d.ts.map +1 -1
- package/dist/runtime/exports.d.ts +50 -55
- package/dist/runtime/exports.d.ts.map +1 -1
- package/dist/runtime/index.d.ts +8 -1
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/middleware.d.ts +77 -60
- package/dist/runtime/middleware.d.ts.map +1 -1
- package/dist/runtime/router.d.ts +7 -0
- package/dist/runtime/router.d.ts.map +1 -0
- package/dist/runtime/validation.d.ts +1 -1
- package/dist/runtime/validation.d.ts.map +1 -1
- package/dist/src/browser.js +150 -0
- package/dist/src/cli/index.js +10 -0
- package/dist/{cloudflare → src/cloudflare}/index.js +3 -3
- package/dist/{decorators → src/decorators}/index.js +2 -2
- package/dist/src/index.js +132 -0
- package/dist/src/runtime/index.js +111 -0
- package/dist/{sveltekit → src/sveltekit}/index.js +14 -6
- package/dist/{test → src/test}/index.js +22 -13
- package/dist/{vite → src/vite}/index.js +128 -59
- package/dist/sveltekit/platform.d.ts.map +1 -1
- package/dist/test/bridge-context.d.ts +5 -2
- package/dist/test/bridge-context.d.ts.map +1 -1
- package/dist/test/cf.d.ts +25 -11
- package/dist/test/cf.d.ts.map +1 -1
- package/dist/test/email.d.ts +16 -7
- package/dist/test/email.d.ts.map +1 -1
- package/dist/test/queue.d.ts.map +1 -1
- package/dist/test/resolve-service-bindings.d.ts.map +1 -1
- package/dist/test/scheduled.d.ts.map +1 -1
- package/dist/test/simple-context.d.ts +1 -1
- package/dist/test/simple-context.d.ts.map +1 -1
- package/dist/test/tail.d.ts +2 -1
- package/dist/test/tail.d.ts.map +1 -1
- package/dist/test/worker.d.ts +6 -0
- package/dist/test/worker.d.ts.map +1 -1
- package/dist/transform/durable-object.d.ts.map +1 -1
- package/dist/transform/worker-entrypoint.d.ts.map +1 -1
- package/dist/{types-5nyrz1sz.js → types-x9q7t491.js} +30 -16
- package/dist/utils/entrypoint-discovery.d.ts +6 -3
- package/dist/utils/entrypoint-discovery.d.ts.map +1 -1
- package/dist/utils/send-email.d.ts +15 -0
- package/dist/utils/send-email.d.ts.map +1 -0
- package/dist/vite/plugin.d.ts.map +1 -1
- package/dist/worker-entry/composed-worker.d.ts +13 -0
- package/dist/worker-entry/composed-worker.d.ts.map +1 -0
- package/dist/worker-entry/routes.d.ts +22 -0
- package/dist/worker-entry/routes.d.ts.map +1 -0
- package/dist/{worker-entrypoint-m9th0rg0.js → worker-entrypoint-c259fmfs.js} +1 -1
- package/package.json +21 -19
- package/dist/index.js +0 -298
- package/dist/runtime/index.js +0 -111
package/README.md
CHANGED
|
@@ -2,101 +2,42 @@
|
|
|
2
2
|
|
|
3
3
|
**Build Cloudflare Workers like an application, not a pile of glue.**
|
|
4
4
|
|
|
5
|
-
Devflare is a developer-first
|
|
5
|
+
Devflare is a developer-first layer on top of Cloudflare Workers, Miniflare, Bun, Vite, and Wrangler-compatible config.
|
|
6
6
|
|
|
7
7
|
It gives you:
|
|
8
8
|
|
|
9
|
-
- a clean file structure
|
|
10
9
|
- typed config with `defineConfig()`
|
|
11
|
-
-
|
|
12
|
-
-
|
|
10
|
+
- convention-friendly worker surfaces
|
|
11
|
+
- local orchestration for multi-surface Worker apps
|
|
13
12
|
- first-class Durable Object and multi-worker workflows
|
|
14
|
-
-
|
|
15
|
-
-
|
|
13
|
+
- test helpers that mirror real handler surfaces
|
|
14
|
+
- worker-safe runtime helpers under `devflare/runtime`
|
|
15
|
+
- optional Vite and SvelteKit integration when your package actually uses them
|
|
16
16
|
|
|
17
17
|
Miniflare gives you a local runtime.
|
|
18
18
|
|
|
19
19
|
Devflare turns that runtime into a **coherent development system**.
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
## Why Devflare?
|
|
24
|
-
|
|
25
|
-
Cloudflare development gets messy fast.
|
|
26
|
-
|
|
27
|
-
You start with one Worker, then suddenly you have:
|
|
28
|
-
|
|
29
|
-
- HTTP routes
|
|
30
|
-
- queues
|
|
31
|
-
- scheduled jobs
|
|
32
|
-
- email handlers
|
|
33
|
-
- Durable Objects
|
|
34
|
-
- service bindings
|
|
35
|
-
- framework output
|
|
36
|
-
- test setup that no longer resembles runtime
|
|
37
|
-
|
|
38
|
-
Devflare keeps those responsibilities isolated and composable.
|
|
39
|
-
|
|
40
|
-
Instead of one giant worker file, you get a structure like this:
|
|
41
|
-
|
|
42
|
-
```text
|
|
43
|
-
.
|
|
44
|
-
├── devflare.config.ts
|
|
45
|
-
├── env.d.ts
|
|
46
|
-
├── src/
|
|
47
|
-
│ ├── fetch.ts
|
|
48
|
-
│ ├── queue.ts
|
|
49
|
-
│ ├── scheduled.ts
|
|
50
|
-
│ ├── email.ts
|
|
51
|
-
│ ├── routes/
|
|
52
|
-
│ ├── do.counter.ts
|
|
53
|
-
│ ├── ep.admin.ts
|
|
54
|
-
│ └── transport.ts
|
|
55
|
-
└── tests/
|
|
56
|
-
└── worker.test.ts
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
That structure is one of Devflare’s biggest strengths: **separate responsibilities, fewer surprises**.
|
|
60
|
-
|
|
61
|
-
---
|
|
62
|
-
|
|
63
|
-
## What makes it different from plain Miniflare?
|
|
64
|
-
|
|
65
|
-
Devflare does not replace Miniflare.
|
|
66
|
-
|
|
67
|
-
It **extends** it.
|
|
68
|
-
|
|
69
|
-
Miniflare gives you local execution.
|
|
70
|
-
Devflare adds the developer workflow around it:
|
|
71
|
-
|
|
72
|
-
- convention-first file discovery
|
|
73
|
-
- config compilation to Wrangler-compatible output
|
|
74
|
-
- unified test helpers for `fetch`, `queue`, `scheduled`, `email`, and `tail`
|
|
75
|
-
- typed cross-worker references with `ref()`
|
|
76
|
-
- local orchestration for multi-surface Worker apps
|
|
77
|
-
- browser rendering support in local development
|
|
78
|
-
- framework-aware integration for Vite and SvelteKit
|
|
79
|
-
|
|
80
|
-
In short:
|
|
81
|
-
|
|
82
|
-
> Devflare helps you build Cloudflare systems with clear boundaries and smooth local development.
|
|
21
|
+
For the deeper public contract, caveats, and current feature boundaries, see [`LLM.md`](./LLM.md).
|
|
83
22
|
|
|
84
23
|
---
|
|
85
24
|
|
|
86
25
|
## Install
|
|
87
26
|
|
|
88
|
-
For a
|
|
27
|
+
For a worker-only project, Devflare works fine with just the Worker toolchain:
|
|
89
28
|
|
|
90
29
|
```bash
|
|
91
|
-
bun add -d devflare wrangler
|
|
30
|
+
bun add -d devflare wrangler @cloudflare/workers-types
|
|
92
31
|
```
|
|
93
32
|
|
|
94
|
-
If
|
|
33
|
+
If the current package also uses Vite, add Vite and the Cloudflare Vite plugin too:
|
|
95
34
|
|
|
96
35
|
```bash
|
|
97
|
-
bun add -d vite @cloudflare/vite-plugin
|
|
36
|
+
bun add -d devflare wrangler @cloudflare/workers-types vite @cloudflare/vite-plugin
|
|
98
37
|
```
|
|
99
38
|
|
|
39
|
+
A local `vite.config.*` opts that package into Vite-backed flows. Without one, Devflare stays in worker-only mode.
|
|
40
|
+
|
|
100
41
|
---
|
|
101
42
|
|
|
102
43
|
## Quick start
|
|
@@ -108,7 +49,11 @@ bun add -d vite @cloudflare/vite-plugin
|
|
|
108
49
|
import { defineConfig } from 'devflare'
|
|
109
50
|
|
|
110
51
|
export default defineConfig({
|
|
111
|
-
name: 'hello-worker'
|
|
52
|
+
name: 'hello-worker',
|
|
53
|
+
compatibilityDate: '2026-03-17',
|
|
54
|
+
files: {
|
|
55
|
+
fetch: 'src/fetch.ts'
|
|
56
|
+
}
|
|
112
57
|
})
|
|
113
58
|
```
|
|
114
59
|
|
|
@@ -116,8 +61,15 @@ export default defineConfig({
|
|
|
116
61
|
|
|
117
62
|
```ts
|
|
118
63
|
// src/fetch.ts
|
|
119
|
-
|
|
120
|
-
|
|
64
|
+
import type { FetchEvent } from 'devflare/runtime'
|
|
65
|
+
|
|
66
|
+
export async function fetch({ request }: FetchEvent): Promise<Response> {
|
|
67
|
+
const url = new URL(request.url)
|
|
68
|
+
return new Response(
|
|
69
|
+
url.pathname === '/'
|
|
70
|
+
? 'Hello from Devflare'
|
|
71
|
+
: `Hello from Devflare: ${url.pathname}`
|
|
72
|
+
)
|
|
121
73
|
}
|
|
122
74
|
```
|
|
123
75
|
|
|
@@ -127,7 +79,7 @@ export default async function fetch(): Promise<Response> {
|
|
|
127
79
|
bunx --bun devflare types
|
|
128
80
|
```
|
|
129
81
|
|
|
130
|
-
This
|
|
82
|
+
This generates `env.d.ts` so bindings, secrets, and discovered entrypoints stay typed.
|
|
131
83
|
|
|
132
84
|
### 4. Start development
|
|
133
85
|
|
|
@@ -157,477 +109,322 @@ describe('hello-worker', () => {
|
|
|
157
109
|
|
|
158
110
|
---
|
|
159
111
|
|
|
160
|
-
##
|
|
112
|
+
## Package entrypoints
|
|
161
113
|
|
|
162
|
-
|
|
114
|
+
Use subpaths intentionally.
|
|
163
115
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
| File | Purpose |
|
|
116
|
+
| Import | Use for |
|
|
167
117
|
|---|---|
|
|
168
|
-
| `
|
|
169
|
-
| `
|
|
170
|
-
| `
|
|
171
|
-
| `
|
|
172
|
-
| `
|
|
173
|
-
| `
|
|
174
|
-
| `
|
|
175
|
-
| `wf.*.ts` | workflows |
|
|
176
|
-
| `src/transport.ts` | custom serialization across boundaries |
|
|
177
|
-
|
|
178
|
-
This is more than style.
|
|
179
|
-
|
|
180
|
-
It is how Devflare helps you keep local development, testing, and runtime behavior aligned.
|
|
118
|
+
| `devflare` | main package entrypoint: config helpers, `ref()`, unified `env`, bridge helpers, CLI helpers, decorators |
|
|
119
|
+
| `devflare/runtime` | worker-safe runtime helpers like `env`, `ctx`, `event`, `locals`, event types/getters, middleware helpers |
|
|
120
|
+
| `devflare/test` | `createTestContext`, `cf.*`, mock helpers, bridge test context, skip helpers |
|
|
121
|
+
| `devflare/vite` | Vite integration |
|
|
122
|
+
| `devflare/sveltekit` | SvelteKit integration |
|
|
123
|
+
| `devflare/cloudflare` | Cloudflare account/auth/usage/limits/preferences helpers |
|
|
124
|
+
| `devflare/decorators` | decorators only |
|
|
181
125
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
## Example config
|
|
126
|
+
### Runtime import rule of thumb
|
|
185
127
|
|
|
186
|
-
|
|
187
|
-
import {
|
|
128
|
+
- use `import { env } from 'devflare/runtime'` for **strict request-scoped runtime access**
|
|
129
|
+
- use `import { env } from 'devflare'` when you want the **unified proxy** that can fall back to test or bridge context outside a live request
|
|
188
130
|
|
|
189
|
-
export
|
|
190
|
-
name: 'my-app',
|
|
191
|
-
files: {
|
|
192
|
-
routes: {
|
|
193
|
-
dir: 'src/routes',
|
|
194
|
-
prefix: '/api'
|
|
195
|
-
}
|
|
196
|
-
},
|
|
197
|
-
bindings: {
|
|
198
|
-
kv: {
|
|
199
|
-
CACHE: 'cache-kv-id'
|
|
200
|
-
},
|
|
201
|
-
d1: {
|
|
202
|
-
DB: 'db-id'
|
|
203
|
-
},
|
|
204
|
-
durableObjects: {
|
|
205
|
-
COUNTER: 'Counter'
|
|
206
|
-
},
|
|
207
|
-
queues: {
|
|
208
|
-
producers: {
|
|
209
|
-
TASK_QUEUE: 'task-queue'
|
|
210
|
-
}
|
|
211
|
-
},
|
|
212
|
-
browser: {
|
|
213
|
-
binding: 'BROWSER'
|
|
214
|
-
}
|
|
215
|
-
},
|
|
216
|
-
triggers: {
|
|
217
|
-
crons: ['0 */6 * * *']
|
|
218
|
-
}
|
|
219
|
-
})
|
|
220
|
-
```
|
|
131
|
+
The reduced worker-safe main entry for `devflare` is selected through the package `browser` export condition. `devflare/runtime` is the direct worker-safe runtime subpath.
|
|
221
132
|
|
|
222
133
|
---
|
|
223
134
|
|
|
224
|
-
##
|
|
135
|
+
## Event-first handlers
|
|
225
136
|
|
|
226
|
-
|
|
137
|
+
Fresh Devflare code should be event-first:
|
|
227
138
|
|
|
228
|
-
|
|
139
|
+
- `fetch(event: FetchEvent)`
|
|
140
|
+
- `queue(event: QueueEvent)`
|
|
141
|
+
- `scheduled(event: ScheduledEvent)`
|
|
142
|
+
- `email(event: EmailEvent)`
|
|
143
|
+
- Durable Object lifecycle handlers with their matching event types
|
|
229
144
|
|
|
230
|
-
|
|
231
|
-
import { defineConfig } from 'devflare'
|
|
145
|
+
Devflare stores the active event in `AsyncLocalStorage`, and Devflare-managed entrypoints establish that context for you before your handler runs. That includes generated worker wrappers, the local dev server, and `createTestContext()` helper surfaces.
|
|
232
146
|
|
|
233
|
-
|
|
234
|
-
name: 'my-api',
|
|
235
|
-
files: {
|
|
236
|
-
routes: {
|
|
237
|
-
dir: 'src/routes',
|
|
238
|
-
prefix: '/api'
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
})
|
|
242
|
-
```
|
|
147
|
+
Helpers deeper in the same call trail can recover the current surface with getters like:
|
|
243
148
|
|
|
244
|
-
|
|
149
|
+
- `getFetchEvent()`
|
|
150
|
+
- `getQueueEvent()`
|
|
151
|
+
- `getScheduledEvent()`
|
|
152
|
+
- `getEmailEvent()`
|
|
153
|
+
- `getDurableObjectFetchEvent()`
|
|
245
154
|
|
|
246
|
-
|
|
155
|
+
Every getter also exposes `.safe()`, which returns `null` instead of throwing.
|
|
247
156
|
|
|
248
|
-
|
|
157
|
+
In normal app code you should not need to call `runWithEventContext()` or `runWithContext()` yourself.
|
|
249
158
|
|
|
250
|
-
|
|
159
|
+
---
|
|
251
160
|
|
|
252
|
-
|
|
253
|
-
- consume them in `src/queue.ts`
|
|
254
|
-
- test them with `cf.queue`
|
|
161
|
+
## HTTP structure that matches the runtime today
|
|
255
162
|
|
|
256
|
-
|
|
257
|
-
// devflare.config.ts
|
|
258
|
-
import { defineConfig } from 'devflare'
|
|
163
|
+
The built-in HTTP story now has **two cooperating layers**:
|
|
259
164
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
bindings: {
|
|
263
|
-
queues: {
|
|
264
|
-
producers: {
|
|
265
|
-
TASK_QUEUE: 'task-queue'
|
|
266
|
-
},
|
|
267
|
-
consumers: [
|
|
268
|
-
{
|
|
269
|
-
queue: 'task-queue',
|
|
270
|
-
maxBatchSize: 10,
|
|
271
|
-
maxRetries: 3
|
|
272
|
-
}
|
|
273
|
-
]
|
|
274
|
-
},
|
|
275
|
-
kv: {
|
|
276
|
-
RESULTS: 'results-kv-id'
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
})
|
|
280
|
-
```
|
|
165
|
+
1. an optional global fetch module at `src/fetch.ts`
|
|
166
|
+
2. a built-in file router rooted at `src/routes/**`
|
|
281
167
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
import { env } from 'devflare'
|
|
168
|
+
Use `src/fetch.ts` for request-wide middleware and whole-app HTTP concerns.
|
|
169
|
+
Use `src/routes/**` for leaf handlers.
|
|
285
170
|
|
|
286
|
-
|
|
287
|
-
const url = new URL(request.url)
|
|
171
|
+
### Request-wide middleware
|
|
288
172
|
|
|
289
|
-
|
|
290
|
-
const task = {
|
|
291
|
-
id: crypto.randomUUID(),
|
|
292
|
-
type: 'resize-image'
|
|
293
|
-
}
|
|
173
|
+
Use `sequence(...)` with a single exported primary fetch entry:
|
|
294
174
|
|
|
295
|
-
|
|
296
|
-
|
|
175
|
+
```ts
|
|
176
|
+
import { sequence } from 'devflare/runtime'
|
|
177
|
+
import type { FetchEvent, ResolveFetch } from 'devflare/runtime'
|
|
178
|
+
|
|
179
|
+
async function corsHandle(event: FetchEvent, resolve: ResolveFetch): Promise<Response> {
|
|
180
|
+
if (event.request.method === 'OPTIONS') {
|
|
181
|
+
return new Response(null, {
|
|
182
|
+
headers: {
|
|
183
|
+
'Access-Control-Allow-Origin': '*',
|
|
184
|
+
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
|
|
185
|
+
'Access-Control-Allow-Headers': 'Content-Type, Authorization'
|
|
186
|
+
}
|
|
187
|
+
})
|
|
297
188
|
}
|
|
298
189
|
|
|
299
|
-
|
|
190
|
+
const response = await resolve(event)
|
|
191
|
+
const next = new Response(response.body, response)
|
|
192
|
+
next.headers.set('Access-Control-Allow-Origin', '*')
|
|
193
|
+
return next
|
|
300
194
|
}
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
```ts
|
|
304
|
-
// src/queue.ts
|
|
305
|
-
import type { MessageBatch } from '@cloudflare/workers-types'
|
|
306
195
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
type: string
|
|
196
|
+
async function appFetch({ request }: FetchEvent): Promise<Response> {
|
|
197
|
+
return Response.json({ path: new URL(request.url).pathname })
|
|
310
198
|
}
|
|
311
199
|
|
|
312
|
-
export
|
|
313
|
-
batch: MessageBatch<Task>,
|
|
314
|
-
env: DevflareEnv
|
|
315
|
-
): Promise<void> {
|
|
316
|
-
for (const message of batch.messages) {
|
|
317
|
-
try {
|
|
318
|
-
await env.RESULTS.put(
|
|
319
|
-
`result:${message.body.id}`,
|
|
320
|
-
JSON.stringify({ status: 'completed', type: message.body.type })
|
|
321
|
-
)
|
|
322
|
-
message.ack()
|
|
323
|
-
} catch {
|
|
324
|
-
message.retry()
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
}
|
|
200
|
+
export const handle = sequence(corsHandle, appFetch)
|
|
328
201
|
```
|
|
329
202
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
## Email
|
|
333
|
-
|
|
334
|
-
Devflare supports two sides of email: **receiving** incoming messages and **sending** outgoing ones.
|
|
335
|
-
|
|
336
|
-
### Receiving email
|
|
337
|
-
|
|
338
|
-
Export an `email` function from `src/email.ts`. Devflare wires it as the worker's incoming email handler automatically.
|
|
203
|
+
Important rules:
|
|
339
204
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
export async function email(message: ForwardableEmailMessage): Promise<void> {
|
|
346
|
-
const id = crypto.randomUUID()
|
|
347
|
-
|
|
348
|
-
// Log the email to KV for later inspection
|
|
349
|
-
await env.EMAIL_LOG.put(
|
|
350
|
-
`email:${id}`,
|
|
351
|
-
JSON.stringify({
|
|
352
|
-
from: message.from,
|
|
353
|
-
to: message.to,
|
|
354
|
-
receivedAt: new Date().toISOString()
|
|
355
|
-
})
|
|
356
|
-
)
|
|
357
|
-
|
|
358
|
-
// Forward to an admin address
|
|
359
|
-
await message.forward(env.FORWARD_ADDRESS)
|
|
360
|
-
}
|
|
361
|
-
```
|
|
205
|
+
- `fetch` and `handle` are two names for the same primary HTTP entry
|
|
206
|
+
- export **one** of them from a given module, never both
|
|
207
|
+
- if `src/fetch.ts` exports same-module `GET()` / `POST()` handlers, those run before file routes for matching methods
|
|
208
|
+
- for route-tree apps, keep `src/fetch.ts` focused on request-wide middleware and put leaf handlers in `src/routes/**`
|
|
362
209
|
|
|
363
|
-
|
|
210
|
+
### File router
|
|
364
211
|
|
|
365
|
-
|
|
212
|
+
`src/routes/**` is now a real built-in router.
|
|
366
213
|
|
|
367
|
-
|
|
214
|
+
By default, Devflare discovers `src/routes/**` automatically when that directory exists.
|
|
368
215
|
|
|
369
|
-
|
|
216
|
+
You can customize or disable it with `files.routes`:
|
|
370
217
|
|
|
371
218
|
```ts
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
EMAIL_LOG: 'email-log-kv-id'
|
|
380
|
-
},
|
|
381
|
-
sendEmail: {
|
|
382
|
-
// Locked to a specific recipient — every send goes to admin
|
|
383
|
-
ADMIN_EMAIL: { destinationAddress: 'admin@example.com' },
|
|
384
|
-
|
|
385
|
-
// Open — you choose the recipient per message
|
|
386
|
-
EMAIL: {}
|
|
219
|
+
export default {
|
|
220
|
+
name: 'api-worker',
|
|
221
|
+
files: {
|
|
222
|
+
fetch: 'src/fetch.ts',
|
|
223
|
+
routes: {
|
|
224
|
+
dir: 'src/routes',
|
|
225
|
+
prefix: '/api'
|
|
387
226
|
}
|
|
388
227
|
}
|
|
389
|
-
}
|
|
228
|
+
}
|
|
390
229
|
```
|
|
391
230
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
- `
|
|
395
|
-
- `env.EMAIL` — you provide the destination when composing the message
|
|
396
|
-
|
|
397
|
-
### Testing email
|
|
398
|
-
|
|
399
|
-
Import the `email` helper from `devflare/test` to send test messages to your running dev server:
|
|
400
|
-
|
|
401
|
-
```ts
|
|
402
|
-
import { email } from 'devflare/test'
|
|
403
|
-
|
|
404
|
-
const response = await email.send({
|
|
405
|
-
from: 'sender@example.com',
|
|
406
|
-
to: 'recipient@example.com',
|
|
407
|
-
subject: 'Hello from devflare',
|
|
408
|
-
body: 'This is a test email.'
|
|
409
|
-
})
|
|
231
|
+
- `files.routes.dir` changes the route root
|
|
232
|
+
- `files.routes.prefix` mounts the route tree under a fixed prefix
|
|
233
|
+
- `files.routes: false` disables automatic route discovery entirely
|
|
410
234
|
|
|
411
|
-
|
|
412
|
-
```
|
|
235
|
+
Route conventions:
|
|
413
236
|
|
|
414
|
-
|
|
237
|
+
- `src/routes/index.ts` → `/`
|
|
238
|
+
- `src/routes/users/index.ts` → `/users`
|
|
239
|
+
- `src/routes/users/[id].ts` → `/users/:id`
|
|
240
|
+
- `src/routes/blog/[...slug].ts` → `/blog/*` with `params.slug = 'a/b'`
|
|
241
|
+
- `src/routes/docs/[[...slug]].ts` → optional catch-all, including `/docs`
|
|
242
|
+
- files or directories that start with `_` are ignored so you can keep route-local helpers nearby
|
|
415
243
|
|
|
416
|
-
|
|
244
|
+
Route module exports use the same fetch-module rules as `src/fetch.ts`:
|
|
417
245
|
|
|
418
|
-
|
|
246
|
+
- `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `OPTIONS`, `ALL`
|
|
247
|
+
- route-local `fetch(event)` / `handle(event, resolve)` if you want per-route middleware
|
|
248
|
+
- `HEAD` falls back to `GET` when no explicit `HEAD` export exists
|
|
419
249
|
|
|
420
|
-
|
|
250
|
+
Route params are available on `event.params`.
|
|
421
251
|
|
|
422
252
|
```ts
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
}
|
|
253
|
+
// src/routes/users/[id].ts
|
|
254
|
+
export async function GET(event): Promise<Response> {
|
|
255
|
+
return Response.json({ id: event.params.id })
|
|
427
256
|
}
|
|
428
257
|
```
|
|
429
258
|
|
|
430
|
-
|
|
259
|
+
### Resolution order
|
|
431
260
|
|
|
432
|
-
|
|
261
|
+
When a request comes in, Devflare resolves HTTP in this order:
|
|
433
262
|
|
|
434
|
-
|
|
263
|
+
1. create the initial fetch event and populate `event.params` from the matched file route, if any
|
|
264
|
+
2. run the primary `src/fetch.ts` `fetch` / `handle` export if present
|
|
265
|
+
3. inside `resolve(event)`, try same-module method handlers from `src/fetch.ts`
|
|
266
|
+
4. if no same-module handler matched, dispatch to the matched route module from `src/routes/**`
|
|
267
|
+
5. return `404 Not Found` if nothing matched
|
|
268
|
+
|
|
269
|
+
That means global middleware can see `event.params`, while route files remain the main leaf-handler story.
|
|
270
|
+
|
|
271
|
+
For example:
|
|
435
272
|
|
|
436
273
|
```ts
|
|
437
|
-
//
|
|
438
|
-
import {
|
|
274
|
+
// src/fetch.ts
|
|
275
|
+
import { sequence } from 'devflare/runtime'
|
|
439
276
|
|
|
440
|
-
export
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
}
|
|
446
|
-
}
|
|
277
|
+
export const handle = sequence(async (event, resolve) => {
|
|
278
|
+
const response = await resolve(event)
|
|
279
|
+
const next = new Response(response.body, response)
|
|
280
|
+
next.headers.set('x-user-id', event.params.id ?? 'none')
|
|
281
|
+
return next
|
|
447
282
|
})
|
|
448
283
|
```
|
|
449
284
|
|
|
450
|
-
|
|
451
|
-
// src/do.session.ts
|
|
452
|
-
import { DurableObject } from 'cloudflare:workers'
|
|
285
|
+
---
|
|
453
286
|
|
|
454
|
-
|
|
455
|
-
private data = new Map<string, string>()
|
|
287
|
+
## Config highlights
|
|
456
288
|
|
|
457
|
-
|
|
458
|
-
return this.data.get(key) ?? null
|
|
459
|
-
}
|
|
289
|
+
Devflare looks for these config filenames:
|
|
460
290
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
291
|
+
- `devflare.config.ts`
|
|
292
|
+
- `devflare.config.mts`
|
|
293
|
+
- `devflare.config.js`
|
|
294
|
+
- `devflare.config.mjs`
|
|
464
295
|
|
|
465
|
-
|
|
466
|
-
this.data.clear()
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
```
|
|
296
|
+
The most important top-level keys are:
|
|
470
297
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
298
|
+
- `name`
|
|
299
|
+
- `accountId`
|
|
300
|
+
- `compatibilityDate`
|
|
301
|
+
- `compatibilityFlags`
|
|
302
|
+
- `files`
|
|
303
|
+
- `bindings`
|
|
304
|
+
- `triggers`
|
|
305
|
+
- `vars`
|
|
306
|
+
- `secrets`
|
|
307
|
+
- `routes`
|
|
308
|
+
- `wsRoutes`
|
|
309
|
+
- `assets`
|
|
310
|
+
- `limits`
|
|
311
|
+
- `observability`
|
|
312
|
+
- `migrations`
|
|
313
|
+
- `rolldown`
|
|
314
|
+
- `vite`
|
|
315
|
+
- `env`
|
|
316
|
+
- `wrangler.passthrough`
|
|
474
317
|
|
|
475
|
-
|
|
476
|
-
const url = new URL(request.url)
|
|
477
|
-
const sessionId = url.searchParams.get('session') ?? 'default'
|
|
318
|
+
### `vars` vs `secrets`
|
|
478
319
|
|
|
479
|
-
|
|
480
|
-
const session = env.SESSION.get(id)
|
|
320
|
+
Keep these separate:
|
|
481
321
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
const value = url.searchParams.get('value') ?? 'Arthur'
|
|
485
|
-
await session.setValue(key, value)
|
|
486
|
-
return Response.json({ ok: true })
|
|
487
|
-
}
|
|
322
|
+
- `vars` are **string-valued config bindings** that compile into generated Wrangler config
|
|
323
|
+
- `secrets` are **declarations of expected runtime secret bindings**
|
|
488
324
|
|
|
489
|
-
|
|
490
|
-
const key = url.searchParams.get('key') ?? 'name'
|
|
491
|
-
const value = await session.getValue(key)
|
|
492
|
-
return Response.json({ value })
|
|
493
|
-
}
|
|
325
|
+
`loadConfig()` does **not** do dotenv loading by itself.
|
|
494
326
|
|
|
495
|
-
|
|
496
|
-
await session.clearAll()
|
|
497
|
-
return Response.json({ ok: true })
|
|
498
|
-
}
|
|
327
|
+
When you run the CLI under Bun, Bun may already have loaded `.env` values into `process.env` before your config executes.
|
|
499
328
|
|
|
500
|
-
|
|
501
|
-
}
|
|
502
|
-
```
|
|
329
|
+
### `config.env`
|
|
503
330
|
|
|
504
|
-
|
|
331
|
+
`config.env` is a Devflare merge layer, not a raw Wrangler env mirror.
|
|
505
332
|
|
|
506
|
-
|
|
507
|
-
2. implement the class in a `do.*.ts` file
|
|
508
|
-
3. get a stub with `env.SESSION.get(env.SESSION.idFromName(...))`
|
|
509
|
-
4. call your methods from `fetch`
|
|
333
|
+
When you select `--env name`, Devflare merges `config.env[name]` into the base config before compiling.
|
|
510
334
|
|
|
511
|
-
###
|
|
335
|
+
### Native config vs Wrangler coverage
|
|
512
336
|
|
|
513
|
-
|
|
337
|
+
Devflare natively models the Worker config it actively composes around:
|
|
514
338
|
|
|
515
|
-
|
|
516
|
-
|
|
339
|
+
- handler/file surfaces
|
|
340
|
+
- core bindings and service composition
|
|
341
|
+
- routes, assets, migrations, observability, and limits
|
|
342
|
+
- Vite and Rolldown metadata
|
|
343
|
+
- environment overlays
|
|
344
|
+
|
|
345
|
+
It does **not** try to re-model every Wrangler key as a first-class Devflare schema field.
|
|
346
|
+
For unsupported Wrangler options, use `wrangler.passthrough`.
|
|
347
|
+
|
|
348
|
+
Current compile order is:
|
|
517
349
|
|
|
518
|
-
|
|
350
|
+
1. Devflare compiles native config into Wrangler-compatible output
|
|
351
|
+
2. `wrangler.passthrough` is shallow-merged on top
|
|
352
|
+
3. if the same key exists in both places, the passthrough value wins
|
|
519
353
|
|
|
354
|
+
```ts
|
|
520
355
|
export default defineConfig({
|
|
521
|
-
name: '
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
356
|
+
name: 'advanced-worker',
|
|
357
|
+
files: {
|
|
358
|
+
fetch: 'src/fetch.ts'
|
|
359
|
+
},
|
|
360
|
+
wrangler: {
|
|
361
|
+
passthrough: {
|
|
362
|
+
placement: {
|
|
363
|
+
mode: 'smart'
|
|
364
|
+
}
|
|
525
365
|
}
|
|
526
366
|
}
|
|
527
367
|
})
|
|
528
368
|
```
|
|
529
369
|
|
|
530
|
-
|
|
370
|
+
Special case: `wrangler.passthrough.main` tells higher-level `build`, `deploy`, and `devflare/vite` flows to stop generating a composed `.devflare/worker-entrypoints/main.ts` and use your explicit main entry instead.
|
|
531
371
|
|
|
532
372
|
---
|
|
533
373
|
|
|
534
|
-
##
|
|
535
|
-
|
|
536
|
-
Sometimes your Worker or Durable Object wants to return a real class instance, not just plain JSON.
|
|
537
|
-
|
|
538
|
-
That is what `src/transport.ts` is for.
|
|
374
|
+
## Bindings
|
|
539
375
|
|
|
540
|
-
|
|
376
|
+
Devflare natively models:
|
|
541
377
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
}
|
|
555
|
-
```
|
|
556
|
-
|
|
557
|
-
```ts
|
|
558
|
-
// src/transport.ts
|
|
559
|
-
import { DoubleableNumber } from './DoubleableNumber'
|
|
378
|
+
- KV
|
|
379
|
+
- D1
|
|
380
|
+
- R2
|
|
381
|
+
- Durable Objects
|
|
382
|
+
- Queues
|
|
383
|
+
- Services
|
|
384
|
+
- AI
|
|
385
|
+
- Vectorize
|
|
386
|
+
- Hyperdrive
|
|
387
|
+
- Browser Rendering
|
|
388
|
+
- Analytics Engine
|
|
389
|
+
- `sendEmail`
|
|
560
390
|
|
|
561
|
-
|
|
562
|
-
DoubleableNumber: {
|
|
563
|
-
encode: (v: unknown) => v instanceof DoubleableNumber && v.value,
|
|
564
|
-
decode: (v: number) => new DoubleableNumber(v)
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
```
|
|
391
|
+
### Caveats worth knowing up front
|
|
568
392
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
393
|
+
- KV, D1, R2, Durable Objects, queues, and the core test/runtime flow are the strongest surfaces
|
|
394
|
+
- AI and Vectorize are remote-oriented bindings
|
|
395
|
+
- named service entrypoints are modeled at the Devflare layer, but validate generated deployment output if they are critical to your app
|
|
396
|
+
- `sendEmail` is modeled through config compilation, generated env types, and local runtime/test flows
|
|
397
|
+
- R2 bindings are real in local dev/test/runtime flows, but Devflare does **not** publish a stable browser-facing local bucket URL contract; browser-visible local asset flows should go through your Worker routes
|
|
572
398
|
|
|
573
|
-
|
|
574
|
-
private count = 0
|
|
399
|
+
For R2 delivery strategy guidance, see [`R2.md`](./R2.md).
|
|
575
400
|
|
|
576
|
-
|
|
577
|
-
this.count += n
|
|
578
|
-
return new DoubleableNumber(this.count)
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
```
|
|
401
|
+
---
|
|
582
402
|
|
|
583
|
-
|
|
584
|
-
// tests/counter.test.ts
|
|
585
|
-
import { beforeAll, afterAll, expect, test } from 'bun:test'
|
|
586
|
-
import { createTestContext } from 'devflare/test'
|
|
587
|
-
import { env } from 'devflare'
|
|
588
|
-
import { DoubleableNumber } from '../src/DoubleableNumber'
|
|
403
|
+
## Dev, build, deploy, Vite, and Rolldown
|
|
589
404
|
|
|
590
|
-
|
|
591
|
-
afterAll(() => env.dispose())
|
|
405
|
+
Devflare is worker-only first.
|
|
592
406
|
|
|
593
|
-
|
|
594
|
-
const id = env.COUNTER.idFromName('main')
|
|
595
|
-
const counter = env.COUNTER.get(id)
|
|
596
|
-
const result = await counter.increment(2)
|
|
407
|
+
### Mode selection
|
|
597
408
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
```
|
|
409
|
+
- a local `vite.config.*` opts the current package into **Vite-backed** flows
|
|
410
|
+
- Vite-related dependencies without a local config do **not** switch the package into Vite mode
|
|
411
|
+
- without a local `vite.config.*`, `dev`, `build`, and `deploy` stay in **worker-only** mode
|
|
602
412
|
|
|
603
|
-
|
|
413
|
+
### Current behavior that matters
|
|
604
414
|
|
|
605
|
-
|
|
415
|
+
- worker-only `dev` is a real first-class path
|
|
416
|
+
- `build` and `deploy` skip Vite when the current package has no local `vite.config.*`
|
|
417
|
+
- higher-level `build`, `deploy`, and `devflare/vite` flows currently synthesize `.devflare/worker-entrypoints/main.ts` whenever a fetch, route tree, queue, scheduled, or email surface is discovered
|
|
418
|
+
- `wrangler.passthrough.main` disables that composed-entry generation path
|
|
419
|
+
- Rolldown currently matters most in the worker-only Durable Object bundler path
|
|
606
420
|
|
|
607
421
|
---
|
|
608
422
|
|
|
609
|
-
## Testing
|
|
423
|
+
## Testing
|
|
610
424
|
|
|
611
|
-
|
|
425
|
+
Use `devflare/test`.
|
|
612
426
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
Use `createTestContext()` for real local behavior backed by Devflare’s Miniflare orchestration.
|
|
616
|
-
|
|
617
|
-
### Mock-style tests
|
|
618
|
-
|
|
619
|
-
Use:
|
|
620
|
-
|
|
621
|
-
- `createMockTestContext()`
|
|
622
|
-
- `withTestContext()`
|
|
623
|
-
- `createMockKV()`
|
|
624
|
-
- `createMockD1()`
|
|
625
|
-
- `createMockR2()`
|
|
626
|
-
- `createMockQueue()`
|
|
627
|
-
|
|
628
|
-
### Unified handler helpers
|
|
629
|
-
|
|
630
|
-
You can test each surface directly:
|
|
427
|
+
The high-level entrypoint is `createTestContext()`, and the unified helper surface is `cf`:
|
|
631
428
|
|
|
632
429
|
- `cf.worker`
|
|
633
430
|
- `cf.queue`
|
|
@@ -635,56 +432,52 @@ You can test each surface directly:
|
|
|
635
432
|
- `cf.email`
|
|
636
433
|
- `cf.tail`
|
|
637
434
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
---
|
|
641
|
-
|
|
642
|
-
## Browser rendering, locally
|
|
643
|
-
|
|
644
|
-
Browser rendering is a perfect example of Devflare adding capability above raw Miniflare.
|
|
645
|
-
|
|
646
|
-
With Devflare, browser rendering becomes part of the same local development story as the rest of your Worker bindings.
|
|
647
|
-
|
|
648
|
-
That means you can build richer Worker applications without stitching together a separate custom local browser setup by hand.
|
|
435
|
+
### `createTestContext()` autodiscovery
|
|
649
436
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
## Framework support
|
|
653
|
-
|
|
654
|
-
### `devflare/vite`
|
|
437
|
+
If you omit the config path, `createTestContext()` walks upward from the calling test file and looks for the nearest supported config filename:
|
|
655
438
|
|
|
656
|
-
|
|
439
|
+
- `devflare.config.ts`
|
|
440
|
+
- `devflare.config.mts`
|
|
441
|
+
- `devflare.config.js`
|
|
442
|
+
- `devflare.config.mjs`
|
|
657
443
|
|
|
658
|
-
-
|
|
659
|
-
- worker-aware transforms
|
|
660
|
-
- auxiliary Durable Object worker generation
|
|
661
|
-
- websocket-aware local development
|
|
444
|
+
It also auto-detects conventional `src/fetch.ts`, `src/queue.ts`, `src/scheduled.ts`, `src/email.ts`, built-in `src/routes/**`, and `src/tail.ts` when they are present.
|
|
662
445
|
|
|
663
|
-
###
|
|
446
|
+
### Important current testing truth
|
|
664
447
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
-
|
|
668
|
-
-
|
|
669
|
-
- local
|
|
448
|
+
- `cf.worker.fetch()` returns when the handler resolves and does **not** eagerly wait for all `waitUntil()` work
|
|
449
|
+
- `cf.worker.fetch()` and the shorthand helpers dispatch through both `src/fetch.ts` and built-in `src/routes/**` file routes when present
|
|
450
|
+
- `cf.queue.trigger()` and `cf.scheduled.trigger()` do wait for queued background work before they return
|
|
451
|
+
- `cf.tail.trigger()` is exported, and `createTestContext()` auto-detects `src/tail.ts` when present; there is still no public `files.tail` config key
|
|
452
|
+
- `cf.email.send()` invokes the configured email handler in `createTestContext()`-backed tests and otherwise falls back to the local email endpoint; for ingress-fidelity-sensitive flows, validate with a higher-level integration test
|
|
453
|
+
- remote mode is mainly about AI and Vectorize, not “make every binding remote”
|
|
670
454
|
|
|
671
455
|
---
|
|
672
456
|
|
|
673
457
|
## CLI
|
|
674
458
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
devflare
|
|
679
|
-
devflare
|
|
680
|
-
devflare build
|
|
681
|
-
devflare
|
|
682
|
-
devflare
|
|
683
|
-
devflare
|
|
684
|
-
devflare
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
459
|
+
| Command | What it does |
|
|
460
|
+
|---|---|
|
|
461
|
+
| `devflare init` | scaffold a project using `src/fetch.ts` and explicit `files.fetch` |
|
|
462
|
+
| `devflare dev` | start the worker-only dev server, enabling Vite only when the current package has a local `vite.config.*` |
|
|
463
|
+
| `devflare build` | resolve config, generate `wrangler.jsonc`, and run `vite build` only for Vite-backed packages |
|
|
464
|
+
| `devflare deploy` | build and deploy with Wrangler |
|
|
465
|
+
| `devflare types` | generate `env.d.ts` |
|
|
466
|
+
| `devflare doctor` | check project configuration and generated artifacts |
|
|
467
|
+
| `devflare account` | inspect accounts, resources, usage, and limits |
|
|
468
|
+
| `devflare ai` | show Workers AI model pricing info |
|
|
469
|
+
| `devflare remote` | manage remote test mode |
|
|
470
|
+
|
|
471
|
+
Useful flags:
|
|
472
|
+
|
|
473
|
+
- `build --env <name>`
|
|
474
|
+
- `deploy --env <name>`
|
|
475
|
+
- `deploy --dry-run`
|
|
476
|
+
- `types --output <path>`
|
|
477
|
+
- `doctor --config <path>`
|
|
478
|
+
- `account --account <id>`
|
|
479
|
+
|
|
480
|
+
Recommended invocation style:
|
|
688
481
|
|
|
689
482
|
```bash
|
|
690
483
|
bunx --bun devflare dev
|
|
@@ -694,44 +487,22 @@ bunx --bun devflare build
|
|
|
694
487
|
|
|
695
488
|
---
|
|
696
489
|
|
|
697
|
-
##
|
|
698
|
-
|
|
699
|
-
```text
|
|
700
|
-
.
|
|
701
|
-
├── devflare.config.ts
|
|
702
|
-
├── env.d.ts
|
|
703
|
-
├── src/
|
|
704
|
-
│ ├── fetch.ts
|
|
705
|
-
│ ├── routes/
|
|
706
|
-
│ ├── queue.ts
|
|
707
|
-
│ ├── scheduled.ts
|
|
708
|
-
│ ├── email.ts
|
|
709
|
-
│ ├── do.counter.ts
|
|
710
|
-
│ ├── ep.admin.ts
|
|
711
|
-
│ └── transport.ts
|
|
712
|
-
└── tests/
|
|
713
|
-
└── worker.test.ts
|
|
714
|
-
```
|
|
715
|
-
|
|
716
|
-
Not every project needs every file.
|
|
717
|
-
|
|
718
|
-
But if you follow this shape, Devflare will feel very natural.
|
|
490
|
+
## Generated artifacts
|
|
719
491
|
|
|
720
|
-
|
|
492
|
+
Treat these as generated output, not source of truth:
|
|
721
493
|
|
|
722
|
-
|
|
494
|
+
- `.devflare/`
|
|
495
|
+
- `env.d.ts`
|
|
496
|
+
- generated `wrangler.jsonc`
|
|
723
497
|
|
|
724
|
-
|
|
498
|
+
The source of truth is still:
|
|
725
499
|
|
|
726
|
-
-
|
|
727
|
-
-
|
|
728
|
-
-
|
|
729
|
-
- local-first development with realistic tests
|
|
730
|
-
- browser rendering in your local workflow
|
|
731
|
-
- Vite or SvelteKit apps that still want a clean Worker model
|
|
500
|
+
- `devflare.config.ts`
|
|
501
|
+
- your source files under `src/`
|
|
502
|
+
- your tests
|
|
732
503
|
|
|
733
504
|
---
|
|
734
505
|
|
|
735
506
|
## In one sentence
|
|
736
507
|
|
|
737
|
-
**Devflare helps you build Cloudflare Workers with clearer structure, better local tooling, and a development
|
|
508
|
+
**Devflare helps you build Cloudflare Workers with clearer structure, better local tooling, and a development workflow that stays coherent as the app grows.**
|