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.
Files changed (199) hide show
  1. package/LLM.md +976 -0
  2. package/README.md +737 -1
  3. package/bin/devflare.js +14 -0
  4. package/dist/account-rvrj687w.js +397 -0
  5. package/dist/ai-dx4fr9jh.js +107 -0
  6. package/dist/bridge/client.d.ts +82 -0
  7. package/dist/bridge/client.d.ts.map +1 -0
  8. package/dist/bridge/index.d.ts +7 -0
  9. package/dist/bridge/index.d.ts.map +1 -0
  10. package/dist/bridge/miniflare.d.ts +70 -0
  11. package/dist/bridge/miniflare.d.ts.map +1 -0
  12. package/dist/bridge/protocol.d.ts +146 -0
  13. package/dist/bridge/protocol.d.ts.map +1 -0
  14. package/dist/bridge/proxy.d.ts +49 -0
  15. package/dist/bridge/proxy.d.ts.map +1 -0
  16. package/dist/bridge/serialization.d.ts +83 -0
  17. package/dist/bridge/serialization.d.ts.map +1 -0
  18. package/dist/bridge/server.d.ts +8 -0
  19. package/dist/bridge/server.d.ts.map +1 -0
  20. package/dist/browser-shim/binding-worker.d.ts +7 -0
  21. package/dist/browser-shim/binding-worker.d.ts.map +1 -0
  22. package/dist/browser-shim/handler.d.ts +21 -0
  23. package/dist/browser-shim/handler.d.ts.map +1 -0
  24. package/dist/browser-shim/index.d.ts +3 -0
  25. package/dist/browser-shim/index.d.ts.map +1 -0
  26. package/dist/browser-shim/server.d.ts +25 -0
  27. package/dist/browser-shim/server.d.ts.map +1 -0
  28. package/dist/browser-shim/worker.d.ts +14 -0
  29. package/dist/browser-shim/worker.d.ts.map +1 -0
  30. package/dist/build-mnf6v8gd.js +53 -0
  31. package/dist/bundler/do-bundler.d.ts +42 -0
  32. package/dist/bundler/do-bundler.d.ts.map +1 -0
  33. package/dist/bundler/index.d.ts +2 -0
  34. package/dist/bundler/index.d.ts.map +1 -0
  35. package/dist/cli/bin.d.ts +3 -0
  36. package/dist/cli/bin.d.ts.map +1 -0
  37. package/dist/cli/colors.d.ts +11 -0
  38. package/dist/cli/colors.d.ts.map +1 -0
  39. package/dist/cli/commands/account.d.ts +4 -0
  40. package/dist/cli/commands/account.d.ts.map +1 -0
  41. package/dist/cli/commands/ai.d.ts +3 -0
  42. package/dist/cli/commands/ai.d.ts.map +1 -0
  43. package/dist/cli/commands/build.d.ts +4 -0
  44. package/dist/cli/commands/build.d.ts.map +1 -0
  45. package/dist/cli/commands/deploy.d.ts +4 -0
  46. package/dist/cli/commands/deploy.d.ts.map +1 -0
  47. package/dist/cli/commands/dev.d.ts +4 -0
  48. package/dist/cli/commands/dev.d.ts.map +1 -0
  49. package/dist/cli/commands/doctor.d.ts +4 -0
  50. package/dist/cli/commands/doctor.d.ts.map +1 -0
  51. package/dist/cli/commands/init.d.ts +4 -0
  52. package/dist/cli/commands/init.d.ts.map +1 -0
  53. package/dist/cli/commands/remote.d.ts +4 -0
  54. package/dist/cli/commands/remote.d.ts.map +1 -0
  55. package/dist/cli/commands/types.d.ts +4 -0
  56. package/dist/cli/commands/types.d.ts.map +1 -0
  57. package/dist/cli/dependencies.d.ts +90 -0
  58. package/dist/cli/dependencies.d.ts.map +1 -0
  59. package/dist/cli/index.d.ts +23 -0
  60. package/dist/cli/index.d.ts.map +1 -0
  61. package/dist/cli/wrangler-auth.d.ts +36 -0
  62. package/dist/cli/wrangler-auth.d.ts.map +1 -0
  63. package/dist/cloudflare/account.d.ts +65 -0
  64. package/dist/cloudflare/account.d.ts.map +1 -0
  65. package/dist/cloudflare/api.d.ts +51 -0
  66. package/dist/cloudflare/api.d.ts.map +1 -0
  67. package/dist/cloudflare/auth.d.ts +35 -0
  68. package/dist/cloudflare/auth.d.ts.map +1 -0
  69. package/dist/cloudflare/index.d.ts +107 -0
  70. package/dist/cloudflare/index.d.ts.map +1 -0
  71. package/dist/cloudflare/index.js +13 -0
  72. package/dist/cloudflare/preferences.d.ts +46 -0
  73. package/dist/cloudflare/preferences.d.ts.map +1 -0
  74. package/dist/cloudflare/pricing.d.ts +15 -0
  75. package/dist/cloudflare/pricing.d.ts.map +1 -0
  76. package/dist/cloudflare/remote-config.d.ts +37 -0
  77. package/dist/cloudflare/remote-config.d.ts.map +1 -0
  78. package/dist/cloudflare/types.d.ts +161 -0
  79. package/dist/cloudflare/types.d.ts.map +1 -0
  80. package/dist/cloudflare/usage.d.ts +77 -0
  81. package/dist/cloudflare/usage.d.ts.map +1 -0
  82. package/dist/config/compiler.d.ts +146 -0
  83. package/dist/config/compiler.d.ts.map +1 -0
  84. package/dist/config/define.d.ts +44 -0
  85. package/dist/config/define.d.ts.map +1 -0
  86. package/dist/config/index.d.ts +6 -0
  87. package/dist/config/index.d.ts.map +1 -0
  88. package/dist/config/loader.d.ts +52 -0
  89. package/dist/config/loader.d.ts.map +1 -0
  90. package/dist/config/ref.d.ts +160 -0
  91. package/dist/config/ref.d.ts.map +1 -0
  92. package/dist/config/schema.d.ts +3318 -0
  93. package/dist/config/schema.d.ts.map +1 -0
  94. package/dist/decorators/durable-object.d.ts +59 -0
  95. package/dist/decorators/durable-object.d.ts.map +1 -0
  96. package/dist/decorators/index.d.ts +3 -0
  97. package/dist/decorators/index.d.ts.map +1 -0
  98. package/dist/decorators/index.js +9 -0
  99. package/dist/deploy-nhceck39.js +70 -0
  100. package/dist/dev-qnxet3j9.js +2096 -0
  101. package/dist/dev-server/index.d.ts +2 -0
  102. package/dist/dev-server/index.d.ts.map +1 -0
  103. package/dist/dev-server/server.d.ts +30 -0
  104. package/dist/dev-server/server.d.ts.map +1 -0
  105. package/dist/doctor-e8fy6fj5.js +186 -0
  106. package/dist/durable-object-t4kbb0yt.js +13 -0
  107. package/dist/env.d.ts +48 -0
  108. package/dist/env.d.ts.map +1 -0
  109. package/dist/index-07q6yxyc.js +168 -0
  110. package/dist/index-1xpj0m4r.js +57 -0
  111. package/dist/index-37x76zdn.js +4 -0
  112. package/dist/index-3t6rypgc.js +13 -0
  113. package/dist/index-67qcae0f.js +183 -0
  114. package/dist/index-a855bdsx.js +18 -0
  115. package/dist/index-d8bdkx2h.js +109 -0
  116. package/dist/index-ep3445yc.js +2225 -0
  117. package/dist/index-gz1gndna.js +307 -0
  118. package/dist/index-hcex3rgh.js +266 -0
  119. package/dist/index-m2q41jwa.js +462 -0
  120. package/dist/index-n7rs26ft.js +77 -0
  121. package/dist/index-pf5s73n9.js +1413 -0
  122. package/dist/index-rbht7m9r.js +36 -0
  123. package/dist/index-tfyxa77h.js +850 -0
  124. package/dist/index-tk6ej9dj.js +94 -0
  125. package/dist/index-z14anrqp.js +226 -0
  126. package/dist/index.d.ts +13 -0
  127. package/dist/index.d.ts.map +1 -0
  128. package/dist/index.js +298 -0
  129. package/dist/init-f9mgmew3.js +186 -0
  130. package/dist/remote-q59qk463.js +97 -0
  131. package/dist/runtime/context.d.ts +46 -0
  132. package/dist/runtime/context.d.ts.map +1 -0
  133. package/dist/runtime/exports.d.ts +118 -0
  134. package/dist/runtime/exports.d.ts.map +1 -0
  135. package/dist/runtime/index.d.ts +4 -0
  136. package/dist/runtime/index.d.ts.map +1 -0
  137. package/dist/runtime/index.js +111 -0
  138. package/dist/runtime/middleware.d.ts +82 -0
  139. package/dist/runtime/middleware.d.ts.map +1 -0
  140. package/dist/runtime/validation.d.ts +37 -0
  141. package/dist/runtime/validation.d.ts.map +1 -0
  142. package/dist/sveltekit/index.d.ts +2 -0
  143. package/dist/sveltekit/index.d.ts.map +1 -0
  144. package/dist/sveltekit/index.js +182 -0
  145. package/dist/sveltekit/platform.d.ts +141 -0
  146. package/dist/sveltekit/platform.d.ts.map +1 -0
  147. package/dist/test/bridge-context.d.ts +73 -0
  148. package/dist/test/bridge-context.d.ts.map +1 -0
  149. package/dist/test/cf.d.ts +130 -0
  150. package/dist/test/cf.d.ts.map +1 -0
  151. package/dist/test/email.d.ts +75 -0
  152. package/dist/test/email.d.ts.map +1 -0
  153. package/dist/test/index.d.ts +22 -0
  154. package/dist/test/index.d.ts.map +1 -0
  155. package/dist/test/index.js +71 -0
  156. package/dist/test/multi-worker-context.d.ts +114 -0
  157. package/dist/test/multi-worker-context.d.ts.map +1 -0
  158. package/dist/test/queue.d.ts +74 -0
  159. package/dist/test/queue.d.ts.map +1 -0
  160. package/dist/test/remote-ai.d.ts +6 -0
  161. package/dist/test/remote-ai.d.ts.map +1 -0
  162. package/dist/test/remote-vectorize.d.ts +6 -0
  163. package/dist/test/remote-vectorize.d.ts.map +1 -0
  164. package/dist/test/resolve-service-bindings.d.ts +68 -0
  165. package/dist/test/resolve-service-bindings.d.ts.map +1 -0
  166. package/dist/test/scheduled.d.ts +58 -0
  167. package/dist/test/scheduled.d.ts.map +1 -0
  168. package/dist/test/should-skip.d.ts +50 -0
  169. package/dist/test/should-skip.d.ts.map +1 -0
  170. package/dist/test/simple-context.d.ts +43 -0
  171. package/dist/test/simple-context.d.ts.map +1 -0
  172. package/dist/test/tail.d.ts +86 -0
  173. package/dist/test/tail.d.ts.map +1 -0
  174. package/dist/test/utilities.d.ts +99 -0
  175. package/dist/test/utilities.d.ts.map +1 -0
  176. package/dist/test/worker.d.ts +76 -0
  177. package/dist/test/worker.d.ts.map +1 -0
  178. package/dist/transform/durable-object.d.ts +46 -0
  179. package/dist/transform/durable-object.d.ts.map +1 -0
  180. package/dist/transform/index.d.ts +3 -0
  181. package/dist/transform/index.d.ts.map +1 -0
  182. package/dist/transform/worker-entrypoint.d.ts +66 -0
  183. package/dist/transform/worker-entrypoint.d.ts.map +1 -0
  184. package/dist/types-5nyrz1sz.js +454 -0
  185. package/dist/utils/entrypoint-discovery.d.ts +29 -0
  186. package/dist/utils/entrypoint-discovery.d.ts.map +1 -0
  187. package/dist/utils/glob.d.ts +33 -0
  188. package/dist/utils/glob.d.ts.map +1 -0
  189. package/dist/utils/resolve-package.d.ts +10 -0
  190. package/dist/utils/resolve-package.d.ts.map +1 -0
  191. package/dist/vite/index.d.ts +3 -0
  192. package/dist/vite/index.d.ts.map +1 -0
  193. package/dist/vite/index.js +339 -0
  194. package/dist/vite/plugin.d.ts +138 -0
  195. package/dist/vite/plugin.d.ts.map +1 -0
  196. package/dist/worker-entrypoint-m9th0rg0.js +13 -0
  197. package/dist/workerName.d.ts +17 -0
  198. package/dist/workerName.d.ts.map +1 -0
  199. 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