devflare 0.0.0 → 1.0.0-next.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/LLM.md +976 -0
- package/README.md +737 -1
- package/bin/devflare.js +14 -0
- package/dist/account-rvrj687w.js +397 -0
- package/dist/ai-dx4fr9jh.js +107 -0
- package/dist/bridge/client.d.ts +82 -0
- package/dist/bridge/client.d.ts.map +1 -0
- package/dist/bridge/index.d.ts +7 -0
- package/dist/bridge/index.d.ts.map +1 -0
- package/dist/bridge/miniflare.d.ts +70 -0
- package/dist/bridge/miniflare.d.ts.map +1 -0
- package/dist/bridge/protocol.d.ts +146 -0
- package/dist/bridge/protocol.d.ts.map +1 -0
- package/dist/bridge/proxy.d.ts +49 -0
- package/dist/bridge/proxy.d.ts.map +1 -0
- package/dist/bridge/serialization.d.ts +83 -0
- package/dist/bridge/serialization.d.ts.map +1 -0
- package/dist/bridge/server.d.ts +8 -0
- package/dist/bridge/server.d.ts.map +1 -0
- package/dist/browser-shim/binding-worker.d.ts +7 -0
- package/dist/browser-shim/binding-worker.d.ts.map +1 -0
- package/dist/browser-shim/handler.d.ts +21 -0
- package/dist/browser-shim/handler.d.ts.map +1 -0
- package/dist/browser-shim/index.d.ts +3 -0
- package/dist/browser-shim/index.d.ts.map +1 -0
- package/dist/browser-shim/server.d.ts +25 -0
- package/dist/browser-shim/server.d.ts.map +1 -0
- package/dist/browser-shim/worker.d.ts +14 -0
- package/dist/browser-shim/worker.d.ts.map +1 -0
- package/dist/build-mnf6v8gd.js +53 -0
- package/dist/bundler/do-bundler.d.ts +42 -0
- package/dist/bundler/do-bundler.d.ts.map +1 -0
- package/dist/bundler/index.d.ts +2 -0
- package/dist/bundler/index.d.ts.map +1 -0
- package/dist/cli/bin.d.ts +3 -0
- package/dist/cli/bin.d.ts.map +1 -0
- package/dist/cli/colors.d.ts +11 -0
- package/dist/cli/colors.d.ts.map +1 -0
- package/dist/cli/commands/account.d.ts +4 -0
- package/dist/cli/commands/account.d.ts.map +1 -0
- package/dist/cli/commands/ai.d.ts +3 -0
- package/dist/cli/commands/ai.d.ts.map +1 -0
- package/dist/cli/commands/build.d.ts +4 -0
- package/dist/cli/commands/build.d.ts.map +1 -0
- package/dist/cli/commands/deploy.d.ts +4 -0
- package/dist/cli/commands/deploy.d.ts.map +1 -0
- package/dist/cli/commands/dev.d.ts +4 -0
- package/dist/cli/commands/dev.d.ts.map +1 -0
- package/dist/cli/commands/doctor.d.ts +4 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/init.d.ts +4 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/remote.d.ts +4 -0
- package/dist/cli/commands/remote.d.ts.map +1 -0
- package/dist/cli/commands/types.d.ts +4 -0
- package/dist/cli/commands/types.d.ts.map +1 -0
- package/dist/cli/dependencies.d.ts +90 -0
- package/dist/cli/dependencies.d.ts.map +1 -0
- package/dist/cli/index.d.ts +23 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/wrangler-auth.d.ts +36 -0
- package/dist/cli/wrangler-auth.d.ts.map +1 -0
- package/dist/cloudflare/account.d.ts +65 -0
- package/dist/cloudflare/account.d.ts.map +1 -0
- package/dist/cloudflare/api.d.ts +51 -0
- package/dist/cloudflare/api.d.ts.map +1 -0
- package/dist/cloudflare/auth.d.ts +35 -0
- package/dist/cloudflare/auth.d.ts.map +1 -0
- package/dist/cloudflare/index.d.ts +107 -0
- package/dist/cloudflare/index.d.ts.map +1 -0
- package/dist/cloudflare/index.js +13 -0
- package/dist/cloudflare/preferences.d.ts +46 -0
- package/dist/cloudflare/preferences.d.ts.map +1 -0
- package/dist/cloudflare/pricing.d.ts +15 -0
- package/dist/cloudflare/pricing.d.ts.map +1 -0
- package/dist/cloudflare/remote-config.d.ts +37 -0
- package/dist/cloudflare/remote-config.d.ts.map +1 -0
- package/dist/cloudflare/types.d.ts +161 -0
- package/dist/cloudflare/types.d.ts.map +1 -0
- package/dist/cloudflare/usage.d.ts +77 -0
- package/dist/cloudflare/usage.d.ts.map +1 -0
- package/dist/config/compiler.d.ts +146 -0
- package/dist/config/compiler.d.ts.map +1 -0
- package/dist/config/define.d.ts +44 -0
- package/dist/config/define.d.ts.map +1 -0
- package/dist/config/index.d.ts +6 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/loader.d.ts +52 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/ref.d.ts +160 -0
- package/dist/config/ref.d.ts.map +1 -0
- package/dist/config/schema.d.ts +3318 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/decorators/durable-object.d.ts +59 -0
- package/dist/decorators/durable-object.d.ts.map +1 -0
- package/dist/decorators/index.d.ts +3 -0
- package/dist/decorators/index.d.ts.map +1 -0
- package/dist/decorators/index.js +9 -0
- package/dist/deploy-nhceck39.js +70 -0
- package/dist/dev-qnxet3j9.js +2096 -0
- package/dist/dev-server/index.d.ts +2 -0
- package/dist/dev-server/index.d.ts.map +1 -0
- package/dist/dev-server/server.d.ts +30 -0
- package/dist/dev-server/server.d.ts.map +1 -0
- package/dist/doctor-e8fy6fj5.js +186 -0
- package/dist/durable-object-t4kbb0yt.js +13 -0
- package/dist/env.d.ts +48 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/index-07q6yxyc.js +168 -0
- package/dist/index-1xpj0m4r.js +57 -0
- package/dist/index-37x76zdn.js +4 -0
- package/dist/index-3t6rypgc.js +13 -0
- package/dist/index-67qcae0f.js +183 -0
- package/dist/index-a855bdsx.js +18 -0
- package/dist/index-d8bdkx2h.js +109 -0
- package/dist/index-ep3445yc.js +2225 -0
- package/dist/index-gz1gndna.js +307 -0
- package/dist/index-hcex3rgh.js +266 -0
- package/dist/index-m2q41jwa.js +462 -0
- package/dist/index-n7rs26ft.js +77 -0
- package/dist/index-pf5s73n9.js +1413 -0
- package/dist/index-rbht7m9r.js +36 -0
- package/dist/index-tfyxa77h.js +850 -0
- package/dist/index-tk6ej9dj.js +94 -0
- package/dist/index-z14anrqp.js +226 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +298 -0
- package/dist/init-f9mgmew3.js +186 -0
- package/dist/remote-q59qk463.js +97 -0
- package/dist/runtime/context.d.ts +46 -0
- package/dist/runtime/context.d.ts.map +1 -0
- package/dist/runtime/exports.d.ts +118 -0
- package/dist/runtime/exports.d.ts.map +1 -0
- package/dist/runtime/index.d.ts +4 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +111 -0
- package/dist/runtime/middleware.d.ts +82 -0
- package/dist/runtime/middleware.d.ts.map +1 -0
- package/dist/runtime/validation.d.ts +37 -0
- package/dist/runtime/validation.d.ts.map +1 -0
- package/dist/sveltekit/index.d.ts +2 -0
- package/dist/sveltekit/index.d.ts.map +1 -0
- package/dist/sveltekit/index.js +182 -0
- package/dist/sveltekit/platform.d.ts +141 -0
- package/dist/sveltekit/platform.d.ts.map +1 -0
- package/dist/test/bridge-context.d.ts +73 -0
- package/dist/test/bridge-context.d.ts.map +1 -0
- package/dist/test/cf.d.ts +130 -0
- package/dist/test/cf.d.ts.map +1 -0
- package/dist/test/email.d.ts +75 -0
- package/dist/test/email.d.ts.map +1 -0
- package/dist/test/index.d.ts +22 -0
- package/dist/test/index.d.ts.map +1 -0
- package/dist/test/index.js +71 -0
- package/dist/test/multi-worker-context.d.ts +114 -0
- package/dist/test/multi-worker-context.d.ts.map +1 -0
- package/dist/test/queue.d.ts +74 -0
- package/dist/test/queue.d.ts.map +1 -0
- package/dist/test/remote-ai.d.ts +6 -0
- package/dist/test/remote-ai.d.ts.map +1 -0
- package/dist/test/remote-vectorize.d.ts +6 -0
- package/dist/test/remote-vectorize.d.ts.map +1 -0
- package/dist/test/resolve-service-bindings.d.ts +68 -0
- package/dist/test/resolve-service-bindings.d.ts.map +1 -0
- package/dist/test/scheduled.d.ts +58 -0
- package/dist/test/scheduled.d.ts.map +1 -0
- package/dist/test/should-skip.d.ts +50 -0
- package/dist/test/should-skip.d.ts.map +1 -0
- package/dist/test/simple-context.d.ts +43 -0
- package/dist/test/simple-context.d.ts.map +1 -0
- package/dist/test/tail.d.ts +86 -0
- package/dist/test/tail.d.ts.map +1 -0
- package/dist/test/utilities.d.ts +99 -0
- package/dist/test/utilities.d.ts.map +1 -0
- package/dist/test/worker.d.ts +76 -0
- package/dist/test/worker.d.ts.map +1 -0
- package/dist/transform/durable-object.d.ts +46 -0
- package/dist/transform/durable-object.d.ts.map +1 -0
- package/dist/transform/index.d.ts +3 -0
- package/dist/transform/index.d.ts.map +1 -0
- package/dist/transform/worker-entrypoint.d.ts +66 -0
- package/dist/transform/worker-entrypoint.d.ts.map +1 -0
- package/dist/types-5nyrz1sz.js +454 -0
- package/dist/utils/entrypoint-discovery.d.ts +29 -0
- package/dist/utils/entrypoint-discovery.d.ts.map +1 -0
- package/dist/utils/glob.d.ts +33 -0
- package/dist/utils/glob.d.ts.map +1 -0
- package/dist/utils/resolve-package.d.ts +10 -0
- package/dist/utils/resolve-package.d.ts.map +1 -0
- package/dist/vite/index.d.ts +3 -0
- package/dist/vite/index.d.ts.map +1 -0
- package/dist/vite/index.js +339 -0
- package/dist/vite/plugin.d.ts +138 -0
- package/dist/vite/plugin.d.ts.map +1 -0
- package/dist/worker-entrypoint-m9th0rg0.js +13 -0
- package/dist/workerName.d.ts +17 -0
- package/dist/workerName.d.ts.map +1 -0
- package/package.json +112 -1
package/LLM.md
ADDED
|
@@ -0,0 +1,976 @@
|
|
|
1
|
+
# Devflare
|
|
2
|
+
|
|
3
|
+
This file is for LLMs and developers **using** the `devflare` library.
|
|
4
|
+
|
|
5
|
+
It explains the public mental model, the intended project structure, and the practical authoring patterns that make Devflare different from using raw Wrangler + Miniflare directly.
|
|
6
|
+
|
|
7
|
+
It does **not** document the internal repository layout or example cases. Treat it as package-facing guidance.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## What Devflare is
|
|
12
|
+
|
|
13
|
+
Devflare is a **developer platform for Cloudflare Workers built on top of Miniflare, Wrangler-compatible config, and framework-aware tooling**.
|
|
14
|
+
|
|
15
|
+
The short version:
|
|
16
|
+
|
|
17
|
+
> Miniflare gives you a local Workers runtime. Devflare turns that runtime into a coherent developer system.
|
|
18
|
+
|
|
19
|
+
Devflare does this by combining:
|
|
20
|
+
|
|
21
|
+
- typed config via `defineConfig()`
|
|
22
|
+
- convention-driven file discovery
|
|
23
|
+
- binding compilation to Wrangler-compatible config
|
|
24
|
+
- local Miniflare orchestration
|
|
25
|
+
- Node ↔ Worker bridging where needed
|
|
26
|
+
- dedicated test helpers for each handler type
|
|
27
|
+
- framework integration for Vite and SvelteKit
|
|
28
|
+
- local development features that Miniflare alone does not provide cleanly, such as **browser rendering support**, multi-surface orchestration, and a consistent file-structured app model
|
|
29
|
+
|
|
30
|
+
If you are building Cloudflare applications and want more structure than a single `worker.ts` plus manual plumbing, Devflare is the layer that supplies that structure.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## How Devflare extends Miniflare
|
|
35
|
+
|
|
36
|
+
This is the most important concept to understand.
|
|
37
|
+
|
|
38
|
+
Devflare is **not** trying to replace Miniflare. It **extends** Miniflare so developers can work with a higher-level application model.
|
|
39
|
+
|
|
40
|
+
### Raw Miniflare gives you a runtime
|
|
41
|
+
|
|
42
|
+
Miniflare is excellent at local execution and local emulation of many Cloudflare bindings.
|
|
43
|
+
|
|
44
|
+
But by itself, it does not define:
|
|
45
|
+
|
|
46
|
+
- how your project should be structured
|
|
47
|
+
- how `fetch`, `queue`, `scheduled`, `email`, routes, DOs, and entrypoints should relate
|
|
48
|
+
- how cross-worker references should stay typed
|
|
49
|
+
- how tests should address each handler surface consistently
|
|
50
|
+
- how framework output and auxiliary workers should be wired together
|
|
51
|
+
- how to simulate browser rendering locally in a coherent workflow
|
|
52
|
+
|
|
53
|
+
### Devflare adds the missing platform layer
|
|
54
|
+
|
|
55
|
+
Devflare adds those higher-level capabilities on top of Miniflare.
|
|
56
|
+
|
|
57
|
+
Concretely, it provides:
|
|
58
|
+
|
|
59
|
+
1. **Convention-first file structure**
|
|
60
|
+
- `src/fetch.ts`
|
|
61
|
+
- `src/queue.ts`
|
|
62
|
+
- `src/scheduled.ts`
|
|
63
|
+
- `src/email.ts`
|
|
64
|
+
- `src/routes/**`
|
|
65
|
+
- `do.*.ts`
|
|
66
|
+
- `ep.*.ts`
|
|
67
|
+
- `wf.*.ts`
|
|
68
|
+
- `src/transport.ts`
|
|
69
|
+
|
|
70
|
+
2. **Config compilation**
|
|
71
|
+
- Devflare compiles your config into Wrangler-compatible config rather than making you hand-maintain the low-level representation yourself.
|
|
72
|
+
|
|
73
|
+
3. **Responsibility isolation**
|
|
74
|
+
- Instead of one giant worker file containing every concern, Devflare encourages one surface per responsibility: fetch, queue, cron, email, routes, Durable Objects, WorkerEntrypoints, workflows, and transport.
|
|
75
|
+
|
|
76
|
+
4. **Unified testing surface**
|
|
77
|
+
- `cf.worker`
|
|
78
|
+
- `cf.queue`
|
|
79
|
+
- `cf.scheduled`
|
|
80
|
+
- `cf.email`
|
|
81
|
+
- `cf.tail`
|
|
82
|
+
|
|
83
|
+
5. **Bridge-backed local development**
|
|
84
|
+
- Devflare handles Node-side access, env access, transport decoding, and Miniflare orchestration so tests and tooling feel like one system.
|
|
85
|
+
|
|
86
|
+
6. **Capabilities beyond raw Miniflare**
|
|
87
|
+
- most notably **browser rendering support** through a local browser shim that lets browser-rendering workflows run locally in a way Miniflare does not provide directly
|
|
88
|
+
|
|
89
|
+
7. **Framework integration**
|
|
90
|
+
- Vite and SvelteKit support with config generation, worker-aware transforms, auxiliary Durable Object workers, and websocket-aware local dev workflows
|
|
91
|
+
|
|
92
|
+
So the design goal is not just “run a Worker locally”.
|
|
93
|
+
|
|
94
|
+
The design goal is:
|
|
95
|
+
|
|
96
|
+
> let developers build Cloudflare systems using coherent, isolated responsibilities while Devflare composes those responsibilities into a working local and testable whole
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## The authoring model Devflare wants you to use
|
|
101
|
+
|
|
102
|
+
Devflare is opinionated in a useful way.
|
|
103
|
+
|
|
104
|
+
It wants you to think in **surfaces** rather than in “one file that does everything”.
|
|
105
|
+
|
|
106
|
+
### HTTP surface
|
|
107
|
+
|
|
108
|
+
Use `src/fetch.ts` for normal request handling.
|
|
109
|
+
|
|
110
|
+
### Queue surface
|
|
111
|
+
|
|
112
|
+
Use `src/queue.ts` for queue consumers.
|
|
113
|
+
|
|
114
|
+
### Scheduled surface
|
|
115
|
+
|
|
116
|
+
Use `src/scheduled.ts` for cron triggers.
|
|
117
|
+
|
|
118
|
+
### Email surface
|
|
119
|
+
|
|
120
|
+
Use `src/email.ts` for incoming email handling. Use `sendEmail` bindings in config for outgoing email.
|
|
121
|
+
|
|
122
|
+
### Routing surface
|
|
123
|
+
|
|
124
|
+
Use `files.routes` with a directory like `src/routes` when you want file-based HTTP routing instead of one monolithic fetch file.
|
|
125
|
+
|
|
126
|
+
### Durable Object surface
|
|
127
|
+
|
|
128
|
+
Use `do.*.ts` files for Durable Objects.
|
|
129
|
+
|
|
130
|
+
### RPC / service-entry surface
|
|
131
|
+
|
|
132
|
+
Use `ep.*.ts` files for named WorkerEntrypoints.
|
|
133
|
+
|
|
134
|
+
### Workflow surface
|
|
135
|
+
|
|
136
|
+
Use `wf.*.ts` files for Workflow classes.
|
|
137
|
+
|
|
138
|
+
### Transport surface
|
|
139
|
+
|
|
140
|
+
Use `src/transport.ts` when values crossing boundaries need custom serialization.
|
|
141
|
+
|
|
142
|
+
This is one of Devflare’s biggest advantages: it turns Cloudflare development from “single worker blob” into **separated, discoverable, testable responsibilities**.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Public package entry points
|
|
147
|
+
|
|
148
|
+
Use the package subpaths intentionally.
|
|
149
|
+
|
|
150
|
+
| Import | Use for |
|
|
151
|
+
|---|---|
|
|
152
|
+
| `devflare` | config, `ref()`, `env`, bridge/test convenience exports |
|
|
153
|
+
| `devflare/runtime` | runtime-safe utilities for Worker code |
|
|
154
|
+
| `devflare/test` | test context, mocks, `cf.*` helpers |
|
|
155
|
+
| `devflare/vite` | Vite integration |
|
|
156
|
+
| `devflare/sveltekit` | SvelteKit integration |
|
|
157
|
+
| `devflare/cloudflare` | Cloudflare-specific helpers |
|
|
158
|
+
| `devflare/decorators` | decorators such as `durableObject()` |
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Core ideas you should internalize
|
|
163
|
+
|
|
164
|
+
### `defineConfig()` is the center of the project
|
|
165
|
+
|
|
166
|
+
Always define your worker through `defineConfig()`.
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
import { defineConfig } from 'devflare'
|
|
170
|
+
|
|
171
|
+
export default defineConfig({
|
|
172
|
+
name: 'my-worker'
|
|
173
|
+
})
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
This is the source of truth Devflare uses to:
|
|
177
|
+
|
|
178
|
+
- understand your bindings
|
|
179
|
+
- understand your file layout
|
|
180
|
+
- generate Wrangler-compatible config
|
|
181
|
+
- drive dev mode
|
|
182
|
+
- drive test mode
|
|
183
|
+
|
|
184
|
+
### Convention beats boilerplate
|
|
185
|
+
|
|
186
|
+
If your filenames follow the defaults, you do not need to spell everything out.
|
|
187
|
+
|
|
188
|
+
Defaults include:
|
|
189
|
+
|
|
190
|
+
- `src/fetch.ts`
|
|
191
|
+
- `src/queue.ts`
|
|
192
|
+
- `src/scheduled.ts`
|
|
193
|
+
- `src/email.ts`
|
|
194
|
+
- `src/transport.ts`
|
|
195
|
+
- `**/do.*.{ts,js}`
|
|
196
|
+
- `**/ep.*.{ts,js}`
|
|
197
|
+
- `**/wf.*.{ts,js}`
|
|
198
|
+
|
|
199
|
+
### `ref()` is how cross-worker systems stay clean
|
|
200
|
+
|
|
201
|
+
If one worker depends on another worker or on another worker’s Durable Objects, use `ref()` instead of manually duplicating names.
|
|
202
|
+
|
|
203
|
+
That keeps cross-worker relationships typed and centralized.
|
|
204
|
+
|
|
205
|
+
### `env` is the unified access point
|
|
206
|
+
|
|
207
|
+
Use:
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
import { env } from 'devflare'
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Devflare makes `env` work coherently across request handling, tests, and bridge-backed local flows.
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Why file structure matters so much in Devflare
|
|
218
|
+
|
|
219
|
+
In plain Worker setups, it is common to end up with a single file that mixes:
|
|
220
|
+
|
|
221
|
+
- HTTP routing
|
|
222
|
+
- queue handling
|
|
223
|
+
- scheduled jobs
|
|
224
|
+
- email handling
|
|
225
|
+
- Durable Object exports
|
|
226
|
+
- auxiliary RPC handlers
|
|
227
|
+
- test-specific wiring
|
|
228
|
+
|
|
229
|
+
Devflare pushes in the opposite direction.
|
|
230
|
+
|
|
231
|
+
It gives each responsibility a natural home and then composes them together.
|
|
232
|
+
|
|
233
|
+
That has several benefits:
|
|
234
|
+
|
|
235
|
+
- lower mental overhead
|
|
236
|
+
- easier code generation
|
|
237
|
+
- easier code review
|
|
238
|
+
- easier testing
|
|
239
|
+
- fewer accidental runtime coupling mistakes
|
|
240
|
+
- clearer ownership boundaries within a team
|
|
241
|
+
|
|
242
|
+
For LLM generation specifically, this matters a lot.
|
|
243
|
+
|
|
244
|
+
When generating Devflare code, prefer **separate files with clear responsibilities** over giant all-in-one worker files.
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## Config example
|
|
249
|
+
|
|
250
|
+
This is a good realistic starting point.
|
|
251
|
+
|
|
252
|
+
```ts
|
|
253
|
+
import { defineConfig } from 'devflare'
|
|
254
|
+
|
|
255
|
+
export default defineConfig({
|
|
256
|
+
name: 'my-app',
|
|
257
|
+
files: {
|
|
258
|
+
routes: {
|
|
259
|
+
dir: 'src/routes',
|
|
260
|
+
prefix: '/api'
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
bindings: {
|
|
264
|
+
kv: {
|
|
265
|
+
CACHE: 'cache-kv-id'
|
|
266
|
+
},
|
|
267
|
+
d1: {
|
|
268
|
+
DB: 'db-id'
|
|
269
|
+
},
|
|
270
|
+
r2: {
|
|
271
|
+
FILES: 'files-bucket'
|
|
272
|
+
},
|
|
273
|
+
durableObjects: {
|
|
274
|
+
COUNTER: 'Counter'
|
|
275
|
+
},
|
|
276
|
+
queues: {
|
|
277
|
+
producers: {
|
|
278
|
+
TASK_QUEUE: 'task-queue'
|
|
279
|
+
},
|
|
280
|
+
consumers: [
|
|
281
|
+
{
|
|
282
|
+
queue: 'task-queue',
|
|
283
|
+
maxBatchSize: 10,
|
|
284
|
+
maxRetries: 3
|
|
285
|
+
}
|
|
286
|
+
]
|
|
287
|
+
},
|
|
288
|
+
browser: {
|
|
289
|
+
binding: 'BROWSER'
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
triggers: {
|
|
293
|
+
crons: ['0 */6 * * *']
|
|
294
|
+
},
|
|
295
|
+
vars: {
|
|
296
|
+
LOG_LEVEL: 'info'
|
|
297
|
+
}
|
|
298
|
+
})
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## File-based routing
|
|
304
|
+
|
|
305
|
+
Devflare supports a file-structured routing model through `files.routes`.
|
|
306
|
+
|
|
307
|
+
That is important because it moves you away from hand-written request dispatch in one file and toward a more maintainable app structure.
|
|
308
|
+
|
|
309
|
+
Example:
|
|
310
|
+
|
|
311
|
+
```ts
|
|
312
|
+
import { defineConfig } from 'devflare'
|
|
313
|
+
|
|
314
|
+
export default defineConfig({
|
|
315
|
+
name: 'my-api',
|
|
316
|
+
files: {
|
|
317
|
+
routes: {
|
|
318
|
+
dir: 'src/routes',
|
|
319
|
+
prefix: '/api'
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
})
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
Conceptually, this means:
|
|
326
|
+
|
|
327
|
+
- route files map onto URL paths
|
|
328
|
+
- route handlers can stay focused on their specific endpoint logic
|
|
329
|
+
- your HTTP surface can scale without turning `fetch.ts` into a switch-statement graveyard
|
|
330
|
+
|
|
331
|
+
If you only need a small Worker, `src/fetch.ts` is perfect.
|
|
332
|
+
|
|
333
|
+
If your HTTP surface is growing, `files.routes` is usually the better model.
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## Queues: produce in one place, consume in another
|
|
338
|
+
|
|
339
|
+
Devflare wants queue production and queue consumption to live in obvious places.
|
|
340
|
+
|
|
341
|
+
- produce messages from `fetch` or another handler via the bound queue producer
|
|
342
|
+
- consume them in `src/queue.ts`
|
|
343
|
+
- test them with `cf.queue`
|
|
344
|
+
|
|
345
|
+
### Queue config example
|
|
346
|
+
|
|
347
|
+
```ts
|
|
348
|
+
import { defineConfig } from 'devflare'
|
|
349
|
+
|
|
350
|
+
export default defineConfig({
|
|
351
|
+
name: 'tasks-app',
|
|
352
|
+
bindings: {
|
|
353
|
+
queues: {
|
|
354
|
+
producers: {
|
|
355
|
+
TASK_QUEUE: 'task-queue'
|
|
356
|
+
},
|
|
357
|
+
consumers: [
|
|
358
|
+
{
|
|
359
|
+
queue: 'task-queue',
|
|
360
|
+
maxBatchSize: 10,
|
|
361
|
+
maxRetries: 3
|
|
362
|
+
}
|
|
363
|
+
]
|
|
364
|
+
},
|
|
365
|
+
kv: {
|
|
366
|
+
RESULTS: 'results-kv-id'
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
})
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### Producer example from `fetch`
|
|
373
|
+
|
|
374
|
+
```ts
|
|
375
|
+
import { env } from 'devflare'
|
|
376
|
+
|
|
377
|
+
export default async function fetch(request: Request): Promise<Response> {
|
|
378
|
+
const url = new URL(request.url)
|
|
379
|
+
|
|
380
|
+
if (url.pathname === '/tasks') {
|
|
381
|
+
const task = {
|
|
382
|
+
id: crypto.randomUUID(),
|
|
383
|
+
type: 'resize-image'
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
await env.TASK_QUEUE.send(task)
|
|
387
|
+
return Response.json({ queued: true, task })
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return new Response('Not found', { status: 404 })
|
|
391
|
+
}
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
### Consumer example in `src/queue.ts`
|
|
395
|
+
|
|
396
|
+
```ts
|
|
397
|
+
import type { MessageBatch } from '@cloudflare/workers-types'
|
|
398
|
+
|
|
399
|
+
type Task = {
|
|
400
|
+
id: string
|
|
401
|
+
type: string
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
export default async function queue(
|
|
405
|
+
batch: MessageBatch<Task>,
|
|
406
|
+
env: DevflareEnv
|
|
407
|
+
): Promise<void> {
|
|
408
|
+
for (const message of batch.messages) {
|
|
409
|
+
try {
|
|
410
|
+
await env.RESULTS.put(
|
|
411
|
+
`result:${message.body.id}`,
|
|
412
|
+
JSON.stringify({ status: 'completed', type: message.body.type })
|
|
413
|
+
)
|
|
414
|
+
message.ack()
|
|
415
|
+
} catch {
|
|
416
|
+
message.retry()
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
Authoring guidance:
|
|
423
|
+
|
|
424
|
+
- put queue consumers in `src/queue.ts` unless you have a strong reason not to
|
|
425
|
+
- keep message bodies plain and serializable
|
|
426
|
+
- use `message.ack()` on success and `message.retry()` on failure
|
|
427
|
+
- bind persistence or downstream systems in config rather than hiding them in globals
|
|
428
|
+
|
|
429
|
+
---
|
|
430
|
+
|
|
431
|
+
## Email: receiving and sending
|
|
432
|
+
|
|
433
|
+
Devflare supports two sides of email: **receiving** incoming messages and **sending** outgoing ones.
|
|
434
|
+
|
|
435
|
+
### Receiving email
|
|
436
|
+
|
|
437
|
+
Export an `email` function from `src/email.ts`. Devflare wires it as the worker's incoming email handler.
|
|
438
|
+
|
|
439
|
+
```ts
|
|
440
|
+
// src/email.ts
|
|
441
|
+
import { env } from 'devflare'
|
|
442
|
+
import type { ForwardableEmailMessage } from '@cloudflare/workers-types'
|
|
443
|
+
|
|
444
|
+
export async function email(message: ForwardableEmailMessage): Promise<void> {
|
|
445
|
+
const id = crypto.randomUUID()
|
|
446
|
+
|
|
447
|
+
// Log the email to KV
|
|
448
|
+
await env.EMAIL_LOG.put(
|
|
449
|
+
`email:${id}`,
|
|
450
|
+
JSON.stringify({
|
|
451
|
+
from: message.from,
|
|
452
|
+
to: message.to,
|
|
453
|
+
receivedAt: new Date().toISOString()
|
|
454
|
+
})
|
|
455
|
+
)
|
|
456
|
+
|
|
457
|
+
// Forward every incoming message to admin
|
|
458
|
+
await message.forward('admin@example.com')
|
|
459
|
+
}
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
The `ForwardableEmailMessage` object also supports `message.reply(replyMessage)` for auto-responses.
|
|
463
|
+
|
|
464
|
+
### Sending email (the `sendEmail` binding)
|
|
465
|
+
|
|
466
|
+
To send outgoing email, declare a `sendEmail` binding in config. Each key becomes a typed `env` property of type `SendEmail`.
|
|
467
|
+
|
|
468
|
+
The value object accepts an optional `destinationAddress`:
|
|
469
|
+
|
|
470
|
+
- **With `destinationAddress`** — every email sent through that binding is locked to that recipient.
|
|
471
|
+
- **Without it (`{}`)** — you choose the recipient at send time.
|
|
472
|
+
|
|
473
|
+
```ts
|
|
474
|
+
import { defineConfig } from 'devflare'
|
|
475
|
+
|
|
476
|
+
export default defineConfig({
|
|
477
|
+
name: 'support-mail',
|
|
478
|
+
bindings: {
|
|
479
|
+
kv: {
|
|
480
|
+
EMAIL_LOG: 'email-log-kv-id'
|
|
481
|
+
},
|
|
482
|
+
sendEmail: {
|
|
483
|
+
// Locked destination — always delivers to admin
|
|
484
|
+
ADMIN_EMAIL: { destinationAddress: 'admin@example.com' },
|
|
485
|
+
|
|
486
|
+
// Open destination — specify per message
|
|
487
|
+
EMAIL: {}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
})
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
This produces:
|
|
494
|
+
|
|
495
|
+
- `env.ADMIN_EMAIL: SendEmail` — hardcoded to `admin@example.com`
|
|
496
|
+
- `env.EMAIL: SendEmail` — recipient provided at send time
|
|
497
|
+
|
|
498
|
+
### Testing email
|
|
499
|
+
|
|
500
|
+
Use the `email` helper from `devflare/test`:
|
|
501
|
+
|
|
502
|
+
```ts
|
|
503
|
+
import { email } from 'devflare/test'
|
|
504
|
+
|
|
505
|
+
const response = await email.send({
|
|
506
|
+
from: 'sender@example.com',
|
|
507
|
+
to: 'recipient@example.com',
|
|
508
|
+
subject: 'Test message',
|
|
509
|
+
body: 'Hello from the test suite.'
|
|
510
|
+
})
|
|
511
|
+
|
|
512
|
+
expect(response.ok).toBe(true)
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
Authoring guidance:
|
|
516
|
+
|
|
517
|
+
- use `src/email.ts` for incoming mail handling
|
|
518
|
+
- use `message.forward(...)` for routing workflows
|
|
519
|
+
- use `message.reply(...)` when implementing auto-replies
|
|
520
|
+
- use `sendEmail` bindings with `destinationAddress` when the recipient is always the same
|
|
521
|
+
- use `sendEmail` bindings with `{}` when you need flexible per-message recipients
|
|
522
|
+
- log metadata to KV or D1 if you need observability or replay/debugging context
|
|
523
|
+
|
|
524
|
+
---
|
|
525
|
+
|
|
526
|
+
## Browser rendering is a real example of Devflare extending Miniflare
|
|
527
|
+
|
|
528
|
+
This is worth emphasizing.
|
|
529
|
+
|
|
530
|
+
Devflare supports **browser rendering** locally via a browser shim layer.
|
|
531
|
+
|
|
532
|
+
That matters because browser rendering is not something Miniflare gives you directly as a complete local developer workflow.
|
|
533
|
+
|
|
534
|
+
With Devflare, browser rendering becomes part of the same system as the rest of your bindings and local development story:
|
|
535
|
+
|
|
536
|
+
- declare the browser binding in config
|
|
537
|
+
- run the app through Devflare’s orchestration
|
|
538
|
+
- use the browser capability locally with the rest of your worker system
|
|
539
|
+
|
|
540
|
+
This is exactly the kind of feature that demonstrates the Devflare philosophy:
|
|
541
|
+
|
|
542
|
+
> take a low-level runtime foundation, then add the higher-level orchestration developers actually need
|
|
543
|
+
|
|
544
|
+
---
|
|
545
|
+
|
|
546
|
+
## Durable Objects and multi-worker systems
|
|
547
|
+
|
|
548
|
+
Devflare is designed to keep multi-worker and DO-heavy systems manageable.
|
|
549
|
+
|
|
550
|
+
### Local Durable Objects
|
|
551
|
+
|
|
552
|
+
Bind them in config:
|
|
553
|
+
|
|
554
|
+
```ts
|
|
555
|
+
bindings: {
|
|
556
|
+
durableObjects: {
|
|
557
|
+
COUNTER: 'Counter'
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
Put their classes in `do.*.ts` files.
|
|
563
|
+
|
|
564
|
+
### Complete local Durable Object example
|
|
565
|
+
|
|
566
|
+
If you need to generate a normal local Durable Object and call it from HTTP code, prefer this shape.
|
|
567
|
+
|
|
568
|
+
```ts
|
|
569
|
+
// devflare.config.ts
|
|
570
|
+
import { defineConfig } from 'devflare'
|
|
571
|
+
|
|
572
|
+
export default defineConfig({
|
|
573
|
+
name: 'sessions-app',
|
|
574
|
+
bindings: {
|
|
575
|
+
durableObjects: {
|
|
576
|
+
SESSION: 'SessionStore'
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
})
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
```ts
|
|
583
|
+
// src/do.session.ts
|
|
584
|
+
import { DurableObject } from 'cloudflare:workers'
|
|
585
|
+
|
|
586
|
+
export class SessionStore extends DurableObject<DevflareEnv> {
|
|
587
|
+
private data = new Map<string, string>()
|
|
588
|
+
|
|
589
|
+
getValue(key: string): string | null {
|
|
590
|
+
return this.data.get(key) ?? null
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
setValue(key: string, value: string): void {
|
|
594
|
+
this.data.set(key, value)
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
clearAll(): void {
|
|
598
|
+
this.data.clear()
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
```ts
|
|
604
|
+
// src/fetch.ts
|
|
605
|
+
import { env } from 'devflare'
|
|
606
|
+
|
|
607
|
+
export default async function fetch(request: Request): Promise<Response> {
|
|
608
|
+
const url = new URL(request.url)
|
|
609
|
+
const sessionId = url.searchParams.get('session') ?? 'default'
|
|
610
|
+
|
|
611
|
+
const id = env.SESSION.idFromName(sessionId)
|
|
612
|
+
const session = env.SESSION.get(id)
|
|
613
|
+
|
|
614
|
+
if (url.pathname === '/set') {
|
|
615
|
+
const key = url.searchParams.get('key') ?? 'name'
|
|
616
|
+
const value = url.searchParams.get('value') ?? 'Arthur'
|
|
617
|
+
await session.setValue(key, value)
|
|
618
|
+
return Response.json({ ok: true })
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
if (url.pathname === '/get') {
|
|
622
|
+
const key = url.searchParams.get('key') ?? 'name'
|
|
623
|
+
const value = await session.getValue(key)
|
|
624
|
+
return Response.json({ value })
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
if (url.pathname === '/clear') {
|
|
628
|
+
await session.clearAll()
|
|
629
|
+
return Response.json({ ok: true })
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
return new Response('Not found', { status: 404 })
|
|
633
|
+
}
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
Important authoring notes:
|
|
637
|
+
|
|
638
|
+
- extend `DurableObject` from `cloudflare:workers`
|
|
639
|
+
- bind the namespace in `bindings.durableObjects`
|
|
640
|
+
- put the class in a `do.*.ts` file so discovery works naturally
|
|
641
|
+
- create a stub with `env.SESSION.get(env.SESSION.idFromName(...))`
|
|
642
|
+
- keep RPC-style method inputs and outputs simple and serializable
|
|
643
|
+
|
|
644
|
+
### Transport layer example: Devflare encodes and decodes for you
|
|
645
|
+
|
|
646
|
+
Use `src/transport.ts` when a Worker or Durable Object returns custom classes that should survive supported RPC/test boundaries as real instances.
|
|
647
|
+
|
|
648
|
+
```ts
|
|
649
|
+
// src/DoubleableNumber.ts
|
|
650
|
+
export class DoubleableNumber {
|
|
651
|
+
value: number
|
|
652
|
+
|
|
653
|
+
constructor(n: number) {
|
|
654
|
+
this.value = n
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
get double() {
|
|
658
|
+
return this.value * 2
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
```ts
|
|
664
|
+
// src/transport.ts
|
|
665
|
+
import { DoubleableNumber } from './DoubleableNumber'
|
|
666
|
+
|
|
667
|
+
export const transport = {
|
|
668
|
+
DoubleableNumber: {
|
|
669
|
+
encode: (v: unknown) => v instanceof DoubleableNumber && v.value,
|
|
670
|
+
decode: (v: number) => new DoubleableNumber(v)
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
```ts
|
|
676
|
+
// src/do.counter.ts
|
|
677
|
+
import { DoubleableNumber } from './DoubleableNumber'
|
|
678
|
+
|
|
679
|
+
export class Counter {
|
|
680
|
+
private count = 0
|
|
681
|
+
|
|
682
|
+
getValue(): DoubleableNumber {
|
|
683
|
+
return new DoubleableNumber(this.count)
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
increment(n: number = 1): DoubleableNumber {
|
|
687
|
+
this.count += n
|
|
688
|
+
return new DoubleableNumber(this.count)
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
```ts
|
|
694
|
+
// tests/counter.test.ts
|
|
695
|
+
import { beforeAll, afterAll, describe, expect, test } from 'bun:test'
|
|
696
|
+
import { createTestContext } from 'devflare/test'
|
|
697
|
+
import { env } from 'devflare'
|
|
698
|
+
import { DoubleableNumber } from '../src/DoubleableNumber'
|
|
699
|
+
|
|
700
|
+
beforeAll(() => createTestContext())
|
|
701
|
+
afterAll(() => env.dispose())
|
|
702
|
+
|
|
703
|
+
describe('counter transport', () => {
|
|
704
|
+
test('result is decoded back into a class instance', async () => {
|
|
705
|
+
const id = env.COUNTER.idFromName('main')
|
|
706
|
+
const counter = env.COUNTER.get(id)
|
|
707
|
+
const result = await counter.increment(2)
|
|
708
|
+
|
|
709
|
+
expect(result).toBeInstanceOf(DoubleableNumber)
|
|
710
|
+
expect(result.double).toBe(4)
|
|
711
|
+
})
|
|
712
|
+
})
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
What Devflare is doing for the developer:
|
|
716
|
+
|
|
717
|
+
1. your DO returns `new DoubleableNumber(...)`
|
|
718
|
+
2. Devflare finds a matching encoder in `src/transport.ts`
|
|
719
|
+
3. the value is serialized while crossing the boundary
|
|
720
|
+
4. Devflare applies the decoder on the receiving side
|
|
721
|
+
5. your code gets a real `DoubleableNumber` instance back
|
|
722
|
+
|
|
723
|
+
This means the developer writes the codec once and then works with real domain objects rather than manual serialization code.
|
|
724
|
+
|
|
725
|
+
### Cross-worker references
|
|
726
|
+
|
|
727
|
+
Use `ref()` when one worker depends on another worker or another worker’s DO bindings.
|
|
728
|
+
|
|
729
|
+
```ts
|
|
730
|
+
import { defineConfig, ref } from 'devflare'
|
|
731
|
+
|
|
732
|
+
const authWorker = ref(() => import('../auth/devflare.config'))
|
|
733
|
+
|
|
734
|
+
export default defineConfig({
|
|
735
|
+
name: 'gateway',
|
|
736
|
+
bindings: {
|
|
737
|
+
services: {
|
|
738
|
+
AUTH: authWorker.worker
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
})
|
|
742
|
+
```
|
|
743
|
+
|
|
744
|
+
This is cleaner than scattering worker names and entrypoint names by hand throughout the codebase.
|
|
745
|
+
|
|
746
|
+
---
|
|
747
|
+
|
|
748
|
+
## Runtime-safe helpers
|
|
749
|
+
|
|
750
|
+
Import runtime-only utilities from `devflare/runtime` when the code runs inside Workers.
|
|
751
|
+
|
|
752
|
+
Important helpers include:
|
|
753
|
+
|
|
754
|
+
- `sequence()`
|
|
755
|
+
- `resolve()`
|
|
756
|
+
- `pipe()`
|
|
757
|
+
- context-related utilities
|
|
758
|
+
|
|
759
|
+
Use this path when you want runtime-safe middleware composition without pulling in Node-oriented orchestration code.
|
|
760
|
+
|
|
761
|
+
---
|
|
762
|
+
|
|
763
|
+
## Testing model
|
|
764
|
+
|
|
765
|
+
Devflare testing is one of the library’s biggest strengths.
|
|
766
|
+
|
|
767
|
+
### Integration-style local tests
|
|
768
|
+
|
|
769
|
+
Use `createTestContext()` when you want a real local Devflare environment backed by Miniflare orchestration.
|
|
770
|
+
|
|
771
|
+
```ts
|
|
772
|
+
import { beforeAll, afterAll, describe, expect, test } from 'bun:test'
|
|
773
|
+
import { createTestContext, cf } from 'devflare/test'
|
|
774
|
+
import { env } from 'devflare'
|
|
775
|
+
|
|
776
|
+
beforeAll(() => createTestContext())
|
|
777
|
+
afterAll(() => env.dispose())
|
|
778
|
+
|
|
779
|
+
describe('worker', () => {
|
|
780
|
+
test('GET /health', async () => {
|
|
781
|
+
const response = await cf.worker.get('/health')
|
|
782
|
+
expect(response.status).toBe(200)
|
|
783
|
+
})
|
|
784
|
+
})
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
### Mock/unit-style tests
|
|
788
|
+
|
|
789
|
+
Use `createMockTestContext()`, `withTestContext()`, and the mock binding helpers when you want pure logic tests.
|
|
790
|
+
|
|
791
|
+
### Why this is better than ad hoc Miniflare setup
|
|
792
|
+
|
|
793
|
+
Devflare gives you a single mental model for testing each handler surface:
|
|
794
|
+
|
|
795
|
+
- HTTP via `cf.worker`
|
|
796
|
+
- queues via `cf.queue`
|
|
797
|
+
- cron via `cf.scheduled`
|
|
798
|
+
- email via `cf.email`
|
|
799
|
+
- tail events via `cf.tail`
|
|
800
|
+
|
|
801
|
+
That consistency is part of the value. Instead of every project inventing its own test harness, Devflare gives you one coherent interface.
|
|
802
|
+
|
|
803
|
+
---
|
|
804
|
+
|
|
805
|
+
## Vite and SvelteKit integration
|
|
806
|
+
|
|
807
|
+
Devflare includes dedicated framework-aware entry points.
|
|
808
|
+
|
|
809
|
+
### `devflare/vite`
|
|
810
|
+
|
|
811
|
+
Use this when you want:
|
|
812
|
+
|
|
813
|
+
- config compilation during dev and build
|
|
814
|
+
- worker-aware transforms
|
|
815
|
+
- auxiliary Durable Object worker generation
|
|
816
|
+
- websocket-aware proxying for local development
|
|
817
|
+
|
|
818
|
+
### `devflare/sveltekit`
|
|
819
|
+
|
|
820
|
+
Use this when your app needs:
|
|
821
|
+
|
|
822
|
+
- a Devflare-aware platform object
|
|
823
|
+
- integration between SvelteKit and Worker bindings
|
|
824
|
+
- local dev behavior that still fits the Devflare model
|
|
825
|
+
|
|
826
|
+
This is another example of Devflare going beyond raw local runtime emulation. It helps compose framework tooling, worker bindings, auxiliary workers, and local dev behavior into one predictable setup.
|
|
827
|
+
|
|
828
|
+
---
|
|
829
|
+
|
|
830
|
+
## Remote-only services
|
|
831
|
+
|
|
832
|
+
Some Cloudflare services are not meaningfully local.
|
|
833
|
+
|
|
834
|
+
Most importantly:
|
|
835
|
+
|
|
836
|
+
- Workers AI
|
|
837
|
+
- Vectorize
|
|
838
|
+
|
|
839
|
+
Devflare treats those as explicit remote-mode concerns rather than pretending they are fully local.
|
|
840
|
+
|
|
841
|
+
That is a good thing.
|
|
842
|
+
|
|
843
|
+
It keeps the local story honest while still giving you an intentional path to test remote capabilities when needed.
|
|
844
|
+
|
|
845
|
+
Use the `devflare remote` workflow and guard remote-only tests appropriately.
|
|
846
|
+
|
|
847
|
+
---
|
|
848
|
+
|
|
849
|
+
## Generated artifacts
|
|
850
|
+
|
|
851
|
+
Devflare uses `.devflare/` for generated local artifacts.
|
|
852
|
+
|
|
853
|
+
Treat that directory as generated output rather than hand-authored source.
|
|
854
|
+
|
|
855
|
+
---
|
|
856
|
+
|
|
857
|
+
## Recommended project structure
|
|
858
|
+
|
|
859
|
+
Here is a strong default layout for most projects.
|
|
860
|
+
|
|
861
|
+
```text
|
|
862
|
+
.
|
|
863
|
+
├── devflare.config.ts
|
|
864
|
+
├── env.d.ts
|
|
865
|
+
├── src/
|
|
866
|
+
│ ├── fetch.ts
|
|
867
|
+
│ ├── queue.ts
|
|
868
|
+
│ ├── scheduled.ts
|
|
869
|
+
│ ├── email.ts
|
|
870
|
+
│ ├── transport.ts
|
|
871
|
+
│ ├── routes/
|
|
872
|
+
│ ├── do.counter.ts
|
|
873
|
+
│ ├── ep.admin.ts
|
|
874
|
+
│ └── wf.sync.ts
|
|
875
|
+
└── tests/
|
|
876
|
+
└── worker.test.ts
|
|
877
|
+
```
|
|
878
|
+
|
|
879
|
+
You will not always need every file.
|
|
880
|
+
|
|
881
|
+
But this shape captures the Devflare philosophy well:
|
|
882
|
+
|
|
883
|
+
- each capability gets an isolated home
|
|
884
|
+
- config stays declarative
|
|
885
|
+
- tests target named surfaces
|
|
886
|
+
- local orchestration is handled by Devflare instead of bespoke glue
|
|
887
|
+
|
|
888
|
+
---
|
|
889
|
+
|
|
890
|
+
## Guidance for LLMs generating Devflare code
|
|
891
|
+
|
|
892
|
+
When generating Devflare projects or edits, follow these rules.
|
|
893
|
+
|
|
894
|
+
### Prefer conventions first
|
|
895
|
+
|
|
896
|
+
Only override paths when there is a real reason.
|
|
897
|
+
|
|
898
|
+
### Prefer separation of responsibilities
|
|
899
|
+
|
|
900
|
+
Do **not** collapse fetch, queue, scheduled, email, DOs, and entrypoints into a single file unless the task explicitly demands that shape.
|
|
901
|
+
|
|
902
|
+
### Prefer `defineConfig()`
|
|
903
|
+
|
|
904
|
+
Never emit untyped ad hoc config objects when a normal Devflare config is appropriate.
|
|
905
|
+
|
|
906
|
+
### Prefer `ref()` for cross-worker relationships
|
|
907
|
+
|
|
908
|
+
Avoid manually duplicating worker names and binding metadata.
|
|
909
|
+
|
|
910
|
+
### Prefer `devflare/test` for tests
|
|
911
|
+
|
|
912
|
+
Use `createTestContext()` for integration-style tests and mock helpers for unit tests.
|
|
913
|
+
|
|
914
|
+
### Prefer `env` for ergonomic binding access
|
|
915
|
+
|
|
916
|
+
Use `import { env } from 'devflare'` when it improves clarity.
|
|
917
|
+
|
|
918
|
+
### Respect remote-only boundaries
|
|
919
|
+
|
|
920
|
+
Do not fake fully local AI or Vectorize support.
|
|
921
|
+
|
|
922
|
+
### Use routing when HTTP complexity grows
|
|
923
|
+
|
|
924
|
+
If the app has many endpoints, prefer `files.routes` and route files over a giant `fetch.ts` dispatcher.
|
|
925
|
+
|
|
926
|
+
---
|
|
927
|
+
|
|
928
|
+
## Minimal example
|
|
929
|
+
|
|
930
|
+
```ts
|
|
931
|
+
// devflare.config.ts
|
|
932
|
+
import { defineConfig } from 'devflare'
|
|
933
|
+
|
|
934
|
+
export default defineConfig({
|
|
935
|
+
name: 'hello-worker'
|
|
936
|
+
})
|
|
937
|
+
```
|
|
938
|
+
|
|
939
|
+
```ts
|
|
940
|
+
// src/fetch.ts
|
|
941
|
+
export default async function fetch(): Promise<Response> {
|
|
942
|
+
return new Response('Hello from Devflare')
|
|
943
|
+
}
|
|
944
|
+
```
|
|
945
|
+
|
|
946
|
+
```ts
|
|
947
|
+
// tests/worker.test.ts
|
|
948
|
+
import { beforeAll, afterAll, describe, expect, test } from 'bun:test'
|
|
949
|
+
import { createTestContext, cf } from 'devflare/test'
|
|
950
|
+
import { env } from 'devflare'
|
|
951
|
+
|
|
952
|
+
beforeAll(() => createTestContext())
|
|
953
|
+
afterAll(() => env.dispose())
|
|
954
|
+
|
|
955
|
+
describe('hello-worker', () => {
|
|
956
|
+
test('GET / returns text', async () => {
|
|
957
|
+
const response = await cf.worker.get('/')
|
|
958
|
+
expect(response.status).toBe(200)
|
|
959
|
+
expect(await response.text()).toBe('Hello from Devflare')
|
|
960
|
+
})
|
|
961
|
+
})
|
|
962
|
+
```
|
|
963
|
+
|
|
964
|
+
---
|
|
965
|
+
|
|
966
|
+
## What to remember
|
|
967
|
+
|
|
968
|
+
If you remember only seven things, remember these:
|
|
969
|
+
|
|
970
|
+
1. `defineConfig()` is the source of truth
|
|
971
|
+
2. Devflare extends Miniflare into a full developer workflow
|
|
972
|
+
3. file structure is a feature, not a side detail
|
|
973
|
+
4. responsibilities should live in separate surfaces and files
|
|
974
|
+
5. browser rendering is a concrete example of Devflare adding capabilities above raw Miniflare
|
|
975
|
+
6. `devflare/test` gives you a coherent test API across handler types
|
|
976
|
+
7. use routing, refs, and framework integrations when the app grows beyond a tiny single-worker shape
|