devflare 1.0.0-next.1 → 1.0.0-next.11

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 (123) hide show
  1. package/LLM.md +1424 -610
  2. package/R2.md +200 -0
  3. package/README.md +302 -505
  4. package/bin/devflare.js +8 -8
  5. package/dist/{account-rvrj687w.js → account-8psavtg6.js} +27 -4
  6. package/dist/bridge/miniflare.d.ts +6 -0
  7. package/dist/bridge/miniflare.d.ts.map +1 -1
  8. package/dist/bridge/proxy.d.ts +5 -6
  9. package/dist/bridge/proxy.d.ts.map +1 -1
  10. package/dist/bridge/server.d.ts.map +1 -1
  11. package/dist/browser.d.ts +50 -0
  12. package/dist/browser.d.ts.map +1 -0
  13. package/dist/{build-mnf6v8gd.js → build-ezksv2dd.js} +26 -7
  14. package/dist/bundler/do-bundler.d.ts +7 -0
  15. package/dist/bundler/do-bundler.d.ts.map +1 -1
  16. package/dist/cli/commands/account.d.ts.map +1 -1
  17. package/dist/cli/commands/build.d.ts.map +1 -1
  18. package/dist/cli/commands/deploy.d.ts.map +1 -1
  19. package/dist/cli/commands/dev.d.ts.map +1 -1
  20. package/dist/cli/commands/doctor.d.ts.map +1 -1
  21. package/dist/cli/commands/init.d.ts.map +1 -1
  22. package/dist/cli/commands/types.d.ts.map +1 -1
  23. package/dist/cli/config-path.d.ts +5 -0
  24. package/dist/cli/config-path.d.ts.map +1 -0
  25. package/dist/cli/index.d.ts.map +1 -1
  26. package/dist/cli/package-metadata.d.ts +16 -0
  27. package/dist/cli/package-metadata.d.ts.map +1 -0
  28. package/dist/config/compiler.d.ts +7 -0
  29. package/dist/config/compiler.d.ts.map +1 -1
  30. package/dist/config/index.d.ts +1 -1
  31. package/dist/config/index.d.ts.map +1 -1
  32. package/dist/config/schema.d.ts +2594 -1234
  33. package/dist/config/schema.d.ts.map +1 -1
  34. package/dist/{deploy-nhceck39.js → deploy-jdpy21t6.js} +33 -15
  35. package/dist/{dev-qnxet3j9.js → dev-9mq7zhww.js} +900 -234
  36. package/dist/dev-server/miniflare-log.d.ts +12 -0
  37. package/dist/dev-server/miniflare-log.d.ts.map +1 -0
  38. package/dist/dev-server/runtime-stdio.d.ts +8 -0
  39. package/dist/dev-server/runtime-stdio.d.ts.map +1 -0
  40. package/dist/dev-server/server.d.ts +2 -0
  41. package/dist/dev-server/server.d.ts.map +1 -1
  42. package/dist/dev-server/vite-utils.d.ts +37 -0
  43. package/dist/dev-server/vite-utils.d.ts.map +1 -0
  44. package/dist/{doctor-e8fy6fj5.js → doctor-z4ffybce.js} +73 -50
  45. package/dist/{durable-object-t4kbb0yt.js → durable-object-yt8v1dyn.js} +1 -1
  46. package/dist/index-1p814k7s.js +227 -0
  47. package/dist/{index-tk6ej9dj.js → index-2q3pmzrx.js} +12 -16
  48. package/dist/{index-67qcae0f.js → index-51s1hkw4.js} +16 -1
  49. package/dist/{index-ep3445yc.js → index-53xcakh8.js} +414 -171
  50. package/dist/{index-pf5s73n9.js → index-59df49vn.js} +11 -281
  51. package/dist/index-5yxg30va.js +304 -0
  52. package/dist/index-62b3gt2g.js +12 -0
  53. package/dist/index-6h8xbs75.js +44 -0
  54. package/dist/index-8gtqgb3q.js +529 -0
  55. package/dist/{index-gz1gndna.js → index-9wt9x09k.js} +42 -62
  56. package/dist/index-dr6sbp8d.js +39 -0
  57. package/dist/index-fef08w43.js +231 -0
  58. package/dist/index-k7r18na8.js +0 -0
  59. package/dist/{index-m2q41jwa.js → index-n932ytmq.js} +9 -1
  60. package/dist/{index-07q6yxyc.js → index-v8vvsn9x.js} +1 -0
  61. package/dist/index-vky23txa.js +70 -0
  62. package/dist/{index-z14anrqp.js → index-wfbfz02q.js} +14 -15
  63. package/dist/index-ws68xvq2.js +311 -0
  64. package/dist/{index-hcex3rgh.js → index-wyf3s77s.js} +85 -8
  65. package/dist/index-xqfbd9fx.js +195 -0
  66. package/dist/index-xxwbb2nt.js +322 -0
  67. package/dist/index-y1d8za14.js +196 -0
  68. package/dist/{init-f9mgmew3.js → init-na2atvz2.js} +42 -55
  69. package/dist/router/types.d.ts +24 -0
  70. package/dist/router/types.d.ts.map +1 -0
  71. package/dist/runtime/context.d.ts +249 -8
  72. package/dist/runtime/context.d.ts.map +1 -1
  73. package/dist/runtime/exports.d.ts +50 -55
  74. package/dist/runtime/exports.d.ts.map +1 -1
  75. package/dist/runtime/index.d.ts +8 -1
  76. package/dist/runtime/index.d.ts.map +1 -1
  77. package/dist/runtime/middleware.d.ts +77 -60
  78. package/dist/runtime/middleware.d.ts.map +1 -1
  79. package/dist/runtime/router.d.ts +7 -0
  80. package/dist/runtime/router.d.ts.map +1 -0
  81. package/dist/runtime/validation.d.ts +1 -1
  82. package/dist/runtime/validation.d.ts.map +1 -1
  83. package/dist/src/browser.js +150 -0
  84. package/dist/src/cli/index.js +10 -0
  85. package/dist/{cloudflare → src/cloudflare}/index.js +3 -3
  86. package/dist/{decorators → src/decorators}/index.js +2 -2
  87. package/dist/src/index.js +132 -0
  88. package/dist/src/runtime/index.js +111 -0
  89. package/dist/{sveltekit → src/sveltekit}/index.js +14 -6
  90. package/dist/{test → src/test}/index.js +22 -13
  91. package/dist/{vite → src/vite}/index.js +128 -59
  92. package/dist/sveltekit/platform.d.ts.map +1 -1
  93. package/dist/test/bridge-context.d.ts +5 -2
  94. package/dist/test/bridge-context.d.ts.map +1 -1
  95. package/dist/test/cf.d.ts +25 -11
  96. package/dist/test/cf.d.ts.map +1 -1
  97. package/dist/test/email.d.ts +16 -7
  98. package/dist/test/email.d.ts.map +1 -1
  99. package/dist/test/queue.d.ts.map +1 -1
  100. package/dist/test/resolve-service-bindings.d.ts.map +1 -1
  101. package/dist/test/scheduled.d.ts.map +1 -1
  102. package/dist/test/simple-context.d.ts +1 -1
  103. package/dist/test/simple-context.d.ts.map +1 -1
  104. package/dist/test/tail.d.ts +2 -1
  105. package/dist/test/tail.d.ts.map +1 -1
  106. package/dist/test/worker.d.ts +6 -0
  107. package/dist/test/worker.d.ts.map +1 -1
  108. package/dist/transform/durable-object.d.ts.map +1 -1
  109. package/dist/transform/worker-entrypoint.d.ts.map +1 -1
  110. package/dist/{types-5nyrz1sz.js → types-nq5acrwh.js} +30 -16
  111. package/dist/utils/entrypoint-discovery.d.ts +6 -3
  112. package/dist/utils/entrypoint-discovery.d.ts.map +1 -1
  113. package/dist/utils/send-email.d.ts +15 -0
  114. package/dist/utils/send-email.d.ts.map +1 -0
  115. package/dist/vite/plugin.d.ts.map +1 -1
  116. package/dist/worker-entry/composed-worker.d.ts +13 -0
  117. package/dist/worker-entry/composed-worker.d.ts.map +1 -0
  118. package/dist/worker-entry/routes.d.ts +22 -0
  119. package/dist/worker-entry/routes.d.ts.map +1 -0
  120. package/dist/{worker-entrypoint-m9th0rg0.js → worker-entrypoint-c259fmfs.js} +1 -1
  121. package/package.json +21 -19
  122. package/dist/index.js +0 -298
  123. package/dist/runtime/index.js +0 -111
@@ -0,0 +1,322 @@
1
+ import {
2
+ normalizeDOBinding
3
+ } from "./index-wyf3s77s.js";
4
+ import {
5
+ __require
6
+ } from "./index-37x76zdn.js";
7
+
8
+ // src/bridge/miniflare.ts
9
+ function generateGatewayScript() {
10
+ return `
11
+ // Gateway Worker — Provides RPC access to all bindings
12
+ export default {
13
+ async fetch(request, env, ctx) {
14
+ const url = new URL(request.url)
15
+
16
+ // Health check
17
+ if (url.pathname === '/_devflare/health') {
18
+ return new Response(JSON.stringify({ status: 'ok', bindings: Object.keys(env) }), {
19
+ headers: { 'Content-Type': 'application/json' }
20
+ })
21
+ }
22
+
23
+ // RPC endpoint
24
+ if (url.pathname === '/_devflare/rpc' && request.method === 'POST') {
25
+ try {
26
+ const { method, params } = await request.json()
27
+ const result = await executeRpc(env, method, params)
28
+ return new Response(JSON.stringify({ ok: true, result }), {
29
+ headers: { 'Content-Type': 'application/json' }
30
+ })
31
+ } catch (error) {
32
+ return new Response(JSON.stringify({
33
+ ok: false,
34
+ error: { code: 'RPC_ERROR', message: error.message }
35
+ }), {
36
+ status: 500,
37
+ headers: { 'Content-Type': 'application/json' }
38
+ })
39
+ }
40
+ }
41
+
42
+ return new Response('Devflare Gateway', { status: 200 })
43
+ }
44
+ }
45
+
46
+ async function executeRpc(env, method, params) {
47
+ const [bindingName, ...methodPath] = method.split('.')
48
+ const binding = env[bindingName]
49
+ const RAW_EMAIL = 'EmailMessage::raw'
50
+
51
+ if (!binding) {
52
+ throw new Error(\`Binding "\${bindingName}" not found\`)
53
+ }
54
+
55
+ const methodName = methodPath.join('.')
56
+
57
+ // KV operations
58
+ if (methodName === 'get') return binding.get(params[0], params[1])
59
+ if (methodName === 'put') return binding.put(params[0], params[1], params[2])
60
+ if (methodName === 'delete') return binding.delete(params[0])
61
+ if (methodName === 'list') return binding.list(params[0])
62
+ if (methodName === 'getWithMetadata') return binding.getWithMetadata(params[0], params[1])
63
+
64
+ // R2 operations
65
+ if (methodName === 'head') return binding.head(params[0])
66
+ if (methodName === 'r2.get') return serializeR2Object(await binding.get(params[0], params[1]))
67
+ if (methodName === 'r2.put') return serializeR2Object(await binding.put(params[0], params[1], params[2]))
68
+ if (methodName === 'r2.delete') return binding.delete(params[0])
69
+ if (methodName === 'r2.list') return serializeR2Objects(await binding.list(params[0]))
70
+
71
+ // D1 operations
72
+ if (methodName === 'exec') return binding.exec(params[0])
73
+ if (methodName === 'batch') {
74
+ const statements = params[0].map(s => binding.prepare(s.sql).bind(...(s.bindings || [])))
75
+ return binding.batch(statements)
76
+ }
77
+ if (methodName.startsWith('stmt.')) {
78
+ const [, stmtMethod] = methodName.split('.')
79
+ const [sql, ...bindings] = params
80
+ const stmt = binding.prepare(sql).bind(...bindings.slice(0, -1))
81
+
82
+ if (stmtMethod === 'first') return stmt.first(bindings[bindings.length - 1])
83
+ if (stmtMethod === 'all') return stmt.all()
84
+ if (stmtMethod === 'run') return stmt.run()
85
+ if (stmtMethod === 'raw') return stmt.raw(bindings[bindings.length - 1])
86
+ }
87
+
88
+ // DO operations
89
+ if (methodName === 'idFromName') {
90
+ const id = binding.idFromName(params[0])
91
+ return { __type: 'DOId', hex: id.toString() }
92
+ }
93
+ if (methodName === 'idFromString') {
94
+ const id = binding.idFromString(params[0])
95
+ return { __type: 'DOId', hex: id.toString() }
96
+ }
97
+ if (methodName === 'newUniqueId') {
98
+ const id = binding.newUniqueId(params[0])
99
+ return { __type: 'DOId', hex: id.toString() }
100
+ }
101
+ if (methodName === 'stub.fetch') {
102
+ const [, doId, serializedReq] = params
103
+ const id = binding.idFromString(doId.hex)
104
+ const stub = binding.get(id)
105
+ const response = await stub.fetch(new Request(serializedReq.url, {
106
+ method: serializedReq.method,
107
+ headers: serializedReq.headers,
108
+ body: serializedReq.body?.type === 'bytes' ? atob(serializedReq.body.data) : undefined
109
+ }))
110
+ return serializeResponse(response)
111
+ }
112
+ if (methodName === 'stub.rpc') {
113
+ // DO RPC: Call a method on the DO instance
114
+ const [, doId, rpcMethod, rpcParams] = params
115
+ const id = binding.idFromString(doId.hex)
116
+ const stub = binding.get(id)
117
+
118
+ // Use fetch to call the RPC endpoint
119
+ const response = await stub.fetch(new Request('http://do/_rpc', {
120
+ method: 'POST',
121
+ headers: { 'Content-Type': 'application/json' },
122
+ body: JSON.stringify({ method: rpcMethod, params: rpcParams })
123
+ }))
124
+
125
+ const result = await response.json()
126
+ if (!result.ok) throw new Error(result.error?.message || 'RPC failed')
127
+ return result.result
128
+ }
129
+
130
+ // Queue operations
131
+ if (methodName === 'send') return binding.send(params[0], params[1])
132
+ if (methodName === 'sendBatch') return binding.sendBatch(params[0], params[1])
133
+
134
+ // Send Email operations
135
+ if (methodName === 'email.send') {
136
+ const message = params[0]
137
+ if (message && typeof message === 'object' && 'from' in message && 'to' in message && 'raw' in message) {
138
+ return binding.send({
139
+ from: message.from,
140
+ to: message.to,
141
+ [RAW_EMAIL]: createEmailMessageRaw(message.raw)
142
+ })
143
+ }
144
+ return binding.send(message)
145
+ }
146
+
147
+ // Generic fallback
148
+ if (typeof binding[methodName] === 'function') {
149
+ return binding[methodName](...params)
150
+ }
151
+
152
+ throw new Error(\`Unknown method: \${method}\`)
153
+ }
154
+
155
+ function createEmailMessageRaw(raw) {
156
+ if (typeof raw === 'string' || raw instanceof ReadableStream) {
157
+ return raw
158
+ }
159
+ if (raw instanceof ArrayBuffer || raw instanceof Uint8Array) {
160
+ return new Response(raw).body
161
+ }
162
+ throw new Error('Unsupported EmailMessage raw payload')
163
+ }
164
+
165
+ function serializeResponse(response) {
166
+ return {
167
+ status: response.status,
168
+ statusText: response.statusText,
169
+ headers: [...response.headers.entries()],
170
+ body: null // Will be streamed separately for large bodies
171
+ }
172
+ }
173
+
174
+ function serializeR2Object(obj) {
175
+ if (!obj) return null
176
+ return {
177
+ key: obj.key,
178
+ version: obj.version,
179
+ size: obj.size,
180
+ etag: obj.etag,
181
+ httpEtag: obj.httpEtag,
182
+ uploaded: obj.uploaded?.toISOString(),
183
+ httpMetadata: obj.httpMetadata,
184
+ customMetadata: obj.customMetadata
185
+ }
186
+ }
187
+
188
+ function serializeR2Objects(result) {
189
+ if (!result) return null
190
+ return {
191
+ objects: result.objects.map(serializeR2Object),
192
+ truncated: result.truncated,
193
+ cursor: result.cursor,
194
+ delimitedPrefixes: result.delimitedPrefixes
195
+ }
196
+ }
197
+ `;
198
+ }
199
+ async function startMiniflare(options = {}) {
200
+ const { Miniflare, Log, LogLevel } = await import("miniflare");
201
+ const port = options.port ?? 8787;
202
+ const persistPath = options.persistPath ?? ".devflare/data";
203
+ const mfConfig = {
204
+ modules: true,
205
+ script: generateGatewayScript(),
206
+ port,
207
+ host: "127.0.0.1",
208
+ log: options.verbose ? new Log(LogLevel.DEBUG) : new Log(LogLevel.WARN),
209
+ compatibilityDate: options.compatibilityDate ?? "2024-01-01",
210
+ compatibilityFlags: options.compatibilityFlags ?? []
211
+ };
212
+ const hasBindings = (val) => {
213
+ if (!val)
214
+ return false;
215
+ if (Array.isArray(val))
216
+ return val.length > 0;
217
+ return Object.keys(val).length > 0;
218
+ };
219
+ if (hasBindings(options.kvNamespaces)) {
220
+ mfConfig.kvNamespaces = options.kvNamespaces;
221
+ if (options.persist) {
222
+ mfConfig.kvPersist = `${persistPath}/kv`;
223
+ }
224
+ }
225
+ if (hasBindings(options.r2Buckets)) {
226
+ mfConfig.r2Buckets = options.r2Buckets;
227
+ if (options.persist) {
228
+ mfConfig.r2Persist = `${persistPath}/r2`;
229
+ }
230
+ }
231
+ if (hasBindings(options.d1Databases)) {
232
+ mfConfig.d1Databases = options.d1Databases;
233
+ if (options.persist) {
234
+ mfConfig.d1Persist = `${persistPath}/d1`;
235
+ }
236
+ }
237
+ if (options.durableObjects) {
238
+ mfConfig.durableObjects = options.durableObjects;
239
+ if (options.persist) {
240
+ mfConfig.durableObjectsPersist = `${persistPath}/do`;
241
+ }
242
+ }
243
+ if (options.sendEmail) {
244
+ mfConfig.email = {
245
+ send_email: Object.entries(options.sendEmail).map(([name, config]) => ({
246
+ name,
247
+ ...config.destinationAddress && {
248
+ destination_address: config.destinationAddress
249
+ },
250
+ ...config.allowedDestinationAddresses && {
251
+ allowed_destination_addresses: config.allowedDestinationAddresses
252
+ },
253
+ ...config.allowedSenderAddresses && {
254
+ allowed_sender_addresses: config.allowedSenderAddresses
255
+ }
256
+ }))
257
+ };
258
+ }
259
+ if (options.bindings && Object.keys(options.bindings).length > 0) {
260
+ mfConfig.bindings = options.bindings;
261
+ }
262
+ if (options.queues?.length) {
263
+ mfConfig.queueProducers = Object.fromEntries(options.queues.map((q) => [q, { queueName: q }]));
264
+ }
265
+ const mf = new Miniflare(mfConfig);
266
+ await mf.ready;
267
+ return {
268
+ ready: Promise.resolve(),
269
+ async dispose() {
270
+ await mf.dispose();
271
+ },
272
+ async getBindings() {
273
+ return mf.getBindings();
274
+ },
275
+ getKVNamespace: mf.getKVNamespace.bind(mf),
276
+ getR2Bucket: mf.getR2Bucket.bind(mf),
277
+ getD1Database: mf.getD1Database.bind(mf),
278
+ getDurableObjectNamespace: mf.getDurableObjectNamespace.bind(mf),
279
+ dispatchFetch: mf.dispatchFetch.bind(mf),
280
+ _mf: mf
281
+ };
282
+ }
283
+ async function startMiniflareFromConfig(config, options = {}) {
284
+ const bindings = config.bindings ?? {};
285
+ const mfOptions = {
286
+ ...options,
287
+ compatibilityDate: config.compatibilityDate,
288
+ compatibilityFlags: config.compatibilityFlags,
289
+ kvNamespaces: bindings.kv ? bindings.kv : undefined,
290
+ r2Buckets: bindings.r2 ? bindings.r2 : undefined,
291
+ d1Databases: bindings.d1 ? bindings.d1 : undefined,
292
+ queues: bindings.queues?.consumers?.map((c) => c.queue),
293
+ sendEmail: bindings.sendEmail ? bindings.sendEmail : undefined,
294
+ bindings: config.vars,
295
+ durableObjects: bindings.durableObjects ? Object.fromEntries(Object.entries(bindings.durableObjects).map(([bindingName, doConfig]) => {
296
+ const normalized = normalizeDOBinding(doConfig);
297
+ return [
298
+ bindingName,
299
+ {
300
+ className: normalized.className,
301
+ scriptPath: normalized.scriptName
302
+ }
303
+ ];
304
+ })) : undefined
305
+ };
306
+ return startMiniflare(mfOptions);
307
+ }
308
+ var globalMiniflare = null;
309
+ async function getMiniflare(options) {
310
+ if (!globalMiniflare) {
311
+ globalMiniflare = await startMiniflare(options);
312
+ }
313
+ return globalMiniflare;
314
+ }
315
+ async function stopMiniflare() {
316
+ if (globalMiniflare) {
317
+ await globalMiniflare.dispose();
318
+ globalMiniflare = null;
319
+ }
320
+ }
321
+
322
+ export { startMiniflare, startMiniflareFromConfig, getMiniflare, stopMiniflare };
@@ -0,0 +1,196 @@
1
+ import {
2
+ __require
3
+ } from "./index-37x76zdn.js";
4
+
5
+ // src/dev-server/vite-utils.ts
6
+ import { resolve } from "pathe";
7
+ import { spawn } from "node:child_process";
8
+ var VITE_CONFIG_FILES = [
9
+ "vite.config.ts",
10
+ "vite.config.js",
11
+ "vite.config.mts",
12
+ "vite.config.mjs",
13
+ "vite.config.cts",
14
+ "vite.config.cjs"
15
+ ];
16
+ var ANSI_REGEX = /\x1b\[[0-9;]*m/g;
17
+ var LOCAL_VITE_URL_REGEX = /https?:\/\/(?:localhost|127\.0\.0\.1|\[::1\])(?::\d+)?\/?/i;
18
+ async function getNodeFs() {
19
+ return await import("node:fs/promises");
20
+ }
21
+ function safeParsePackageJson(content) {
22
+ try {
23
+ return JSON.parse(content);
24
+ } catch {
25
+ return {};
26
+ }
27
+ }
28
+ function readDependencyFlag(pkg, name) {
29
+ const dependencies = pkg.dependencies;
30
+ const devDependencies = pkg.devDependencies;
31
+ return Boolean(dependencies?.[name] ?? devDependencies?.[name]);
32
+ }
33
+ async function detectViteProject(cwd, fs) {
34
+ const fileSystem = fs ?? await getNodeFs();
35
+ let viteConfigPath = null;
36
+ for (const configName of VITE_CONFIG_FILES) {
37
+ const absolutePath = resolve(cwd, configName);
38
+ try {
39
+ await fileSystem.access(absolutePath);
40
+ viteConfigPath = absolutePath;
41
+ break;
42
+ } catch {
43
+ continue;
44
+ }
45
+ }
46
+ let pkg = {};
47
+ try {
48
+ const packageJson = await fileSystem.readFile(resolve(cwd, "package.json"), "utf-8");
49
+ pkg = safeParsePackageJson(packageJson);
50
+ } catch {
51
+ pkg = {};
52
+ }
53
+ const hasLocalViteDependency = readDependencyFlag(pkg, "vite");
54
+ const hasLocalCloudflareVitePluginDependency = readDependencyFlag(pkg, "@cloudflare/vite-plugin");
55
+ const wantsViteIntegration = Boolean(viteConfigPath || hasLocalViteDependency || hasLocalCloudflareVitePluginDependency);
56
+ return {
57
+ viteConfigPath,
58
+ hasLocalViteDependency,
59
+ hasLocalCloudflareVitePluginDependency,
60
+ shouldStartVite: Boolean(viteConfigPath),
61
+ wantsViteIntegration
62
+ };
63
+ }
64
+ function stripAnsi(value) {
65
+ return value.replace(ANSI_REGEX, "");
66
+ }
67
+ function extractViteReadyUrl(output) {
68
+ const cleaned = stripAnsi(output);
69
+ const lines = cleaned.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
70
+ for (const line of lines) {
71
+ if (!line.toLowerCase().includes("local:")) {
72
+ continue;
73
+ }
74
+ const match = line.match(LOCAL_VITE_URL_REGEX);
75
+ if (match) {
76
+ return match[0];
77
+ }
78
+ }
79
+ const fallbackMatch = cleaned.match(LOCAL_VITE_URL_REGEX);
80
+ return fallbackMatch?.[0] ?? null;
81
+ }
82
+ async function waitForViteReady(process, options = {}) {
83
+ const {
84
+ timeoutMs = 15000,
85
+ onStdout,
86
+ onStderr
87
+ } = options;
88
+ let combinedOutput = "";
89
+ return await new Promise((resolvePromise, rejectPromise) => {
90
+ let settled = false;
91
+ let timeout;
92
+ const settle = (resolver) => {
93
+ if (settled) {
94
+ return;
95
+ }
96
+ settled = true;
97
+ clearTimeout(timeout);
98
+ resolver();
99
+ };
100
+ const inspectChunk = (chunk) => {
101
+ combinedOutput += typeof chunk === "string" ? chunk : chunk.toString("utf-8");
102
+ const readyUrl = extractViteReadyUrl(combinedOutput);
103
+ if (readyUrl) {
104
+ settle(() => resolvePromise(readyUrl));
105
+ }
106
+ };
107
+ process.stdout?.on("data", (chunk) => {
108
+ onStdout?.(chunk);
109
+ inspectChunk(chunk);
110
+ });
111
+ process.stderr?.on("data", (chunk) => {
112
+ onStderr?.(chunk);
113
+ inspectChunk(chunk);
114
+ });
115
+ process.on("error", (error) => {
116
+ settle(() => rejectPromise(error));
117
+ });
118
+ process.on("exit", (code, signal) => {
119
+ settle(() => {
120
+ const reason = signal ? `signal ${signal}` : `exit code ${code ?? "unknown"}`;
121
+ rejectPromise(new Error(`Vite exited before reporting a ready URL (${reason})`));
122
+ });
123
+ });
124
+ timeout = setTimeout(() => {
125
+ settle(() => resolvePromise(null));
126
+ }, timeoutMs);
127
+ });
128
+ }
129
+ async function defaultRunCommand(command, args) {
130
+ await new Promise((resolvePromise, rejectPromise) => {
131
+ const child = spawn(command, args, {
132
+ stdio: "ignore",
133
+ windowsHide: true
134
+ });
135
+ child.on("error", rejectPromise);
136
+ child.on("exit", () => resolvePromise());
137
+ });
138
+ }
139
+ function waitForProcessExit(process, timeoutMs) {
140
+ if (process.killed) {
141
+ return Promise.resolve(true);
142
+ }
143
+ return new Promise((resolvePromise) => {
144
+ let settled = false;
145
+ let timeout;
146
+ const settle = (value) => {
147
+ if (settled) {
148
+ return;
149
+ }
150
+ settled = true;
151
+ clearTimeout(timeout);
152
+ resolvePromise(value);
153
+ };
154
+ process.on("exit", () => settle(true));
155
+ timeout = setTimeout(() => {
156
+ settle(false);
157
+ }, timeoutMs);
158
+ });
159
+ }
160
+ async function stopSpawnedProcessTree(process, options = {}) {
161
+ const {
162
+ platform = globalThis.process?.platform ?? "linux",
163
+ timeoutMs = 3000,
164
+ runCommand = defaultRunCommand
165
+ } = options;
166
+ if (platform === "win32" && process.pid) {
167
+ try {
168
+ await runCommand("taskkill", ["/pid", String(process.pid), "/t", "/f"]);
169
+ } catch {
170
+ try {
171
+ process.kill("SIGTERM");
172
+ } catch {
173
+ return;
174
+ }
175
+ }
176
+ await waitForProcessExit(process, timeoutMs);
177
+ return;
178
+ }
179
+ try {
180
+ process.kill("SIGTERM");
181
+ } catch {
182
+ return;
183
+ }
184
+ const exited = await waitForProcessExit(process, timeoutMs);
185
+ if (exited) {
186
+ return;
187
+ }
188
+ try {
189
+ process.kill("SIGKILL");
190
+ } catch {
191
+ return;
192
+ }
193
+ await waitForProcessExit(process, timeoutMs);
194
+ }
195
+
196
+ export { detectViteProject, waitForViteReady, stopSpawnedProcessTree };
@@ -1,6 +1,9 @@
1
1
  import {
2
2
  getDependencies
3
3
  } from "./index-1xpj0m4r.js";
4
+ import {
5
+ getInitDependencyVersions
6
+ } from "./index-6h8xbs75.js";
4
7
  import"./index-37x76zdn.js";
5
8
 
6
9
  // src/cli/commands/init.ts
@@ -14,14 +17,20 @@ var MINIMAL_TEMPLATE = {
14
17
  export default defineConfig({
15
18
  name: '{{PROJECT_NAME}}',
16
19
  compatibilityDate: '${new Date().toISOString().split("T")[0]}',
20
+ files: {
21
+ fetch: 'src/fetch.ts'
22
+ }
17
23
  })
18
24
  `,
19
- "src/index.ts": `import { env, ctx, event, locals } from 'devflare/runtime'
25
+ "src/fetch.ts": `import type { FetchEvent } from 'devflare/runtime'
20
26
 
21
- export default {
22
- async fetch(request: Request): Promise<Response> {
23
- return new Response('Hello from devflare!')
24
- }
27
+ export async function fetch({ request }: FetchEvent): Promise<Response> {
28
+ const url = new URL(request.url)
29
+ return new Response(
30
+ url.pathname === '/'
31
+ ? 'Hello from Devflare'
32
+ : \`Hello from Devflare: \${url.pathname}\`
33
+ )
25
34
  }
26
35
  `,
27
36
  "package.json": `{
@@ -36,8 +45,10 @@ export default {
36
45
  "types": "devflare types"
37
46
  },
38
47
  "devDependencies": {
39
- "devflare": "^0.1.0",
40
- "typescript": "^5.7.0"
48
+ "@cloudflare/workers-types": "{{WORKERS_TYPES_VERSION}}",
49
+ "devflare": "{{DEVFLARE_VERSION}}",
50
+ "typescript": "{{TYPESCRIPT_VERSION}}",
51
+ "wrangler": "{{WRANGLER_VERSION}}"
41
52
  }
42
53
  }
43
54
  `,
@@ -50,91 +61,65 @@ export default {
50
61
  "skipLibCheck": true,
51
62
  "types": ["@cloudflare/workers-types"]
52
63
  },
53
- "include": ["src/**/*", "devflare.config.ts"]
64
+ "include": ["src/**/*", "env.d.ts", "devflare.config.ts"]
54
65
  }
55
66
  `
56
67
  }
57
68
  };
58
69
  var API_TEMPLATE = {
59
70
  name: "api",
60
- description: "API starter with middleware and routing",
71
+ description: "API starter with request-wide middleware",
61
72
  files: {
62
73
  "devflare.config.ts": `import { defineConfig } from 'devflare'
63
74
 
64
75
  export default defineConfig({
65
76
  name: '{{PROJECT_NAME}}',
66
77
  compatibilityDate: '${new Date().toISOString().split("T")[0]}',
67
- bindings: {
68
- // Add your bindings here
69
- // kv: [{ binding: 'CACHE' }],
70
- // d1: [{ binding: 'DB', database_name: 'my-db' }],
78
+ files: {
79
+ fetch: 'src/fetch.ts'
71
80
  }
72
81
  })
73
82
  `,
74
- "src/index.ts": `import { sequence, resolve } from 'devflare/runtime'
75
- import { corsMiddleware } from './middleware/cors'
76
- import { apiRouter } from './routes/api'
77
-
78
- const handler = sequence(
79
- corsMiddleware
80
- )(
81
- resolve(
82
- apiRouter,
83
- async () => new Response('Not Found', { status: 404 })
84
- )
85
- )
83
+ "src/fetch.ts": `import { sequence } from 'devflare/runtime'
84
+ import { corsHandle } from './middleware/cors'
85
+ import { appFetch } from './app'
86
86
 
87
- export default {
88
- async fetch(request: Request, env: unknown, ctx: ExecutionContext): Promise<Response> {
89
- return handler()
90
- }
91
- }
87
+ export const handle = sequence(corsHandle, appFetch)
92
88
  `,
93
- "src/middleware/cors.ts": `import type { Middleware } from 'devflare/runtime'
94
- import { event } from 'devflare/runtime'
89
+ "src/middleware/cors.ts": `import type { FetchEvent, ResolveFetch } from 'devflare/runtime'
95
90
 
96
- export const corsMiddleware: Middleware = async (next) => {
91
+ export async function corsHandle(event: FetchEvent, resolve: ResolveFetch): Promise<Response> {
97
92
  // Handle preflight
98
- if (event.request?.method === 'OPTIONS') {
93
+ if (event.request.method === 'OPTIONS') {
99
94
  return new Response(null, {
100
95
  headers: {
101
96
  'Access-Control-Allow-Origin': '*',
102
97
  'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
103
- 'Access-Control-Allow-Headers': 'Content-Type, Authorization',
98
+ 'Access-Control-Allow-Headers': 'Content-Type, Authorization'
104
99
  }
105
100
  })
106
101
  }
107
102
 
108
- const response = await next()
109
- const newResponse = new Response(response.body, response)
110
- newResponse.headers.set('Access-Control-Allow-Origin', '*')
111
- return newResponse
103
+ const response = await resolve(event)
104
+ const next = new Response(response.body, response)
105
+ next.headers.set('Access-Control-Allow-Origin', '*')
106
+ return next
112
107
  }
113
108
  `,
114
- "src/routes/api.ts": `import type { Handler } from 'devflare/runtime'
115
- import { event, env, locals } from 'devflare/runtime'
116
-
117
- export const apiRouter: Handler = async () => {
118
- const request = event.request
119
- if (!request) return null
109
+ "src/app.ts": `import type { FetchEvent } from 'devflare/runtime'
120
110
 
111
+ export async function appFetch({ request }: FetchEvent): Promise<Response> {
121
112
  const url = new URL(request.url)
122
113
 
123
114
  if (url.pathname === '/api/health') {
124
- return new Response(JSON.stringify({ status: 'ok' }), {
125
- headers: { 'Content-Type': 'application/json' }
126
- })
115
+ return Response.json({ status: 'ok' })
127
116
  }
128
117
 
129
118
  if (url.pathname.startsWith('/api/')) {
130
- return new Response(JSON.stringify({ error: 'Not found' }), {
131
- status: 404,
132
- headers: { 'Content-Type': 'application/json' }
133
- })
119
+ return Response.json({ error: 'Not found' }, { status: 404 })
134
120
  }
135
121
 
136
- // Not an API route, pass through
137
- return null
122
+ return new Response('Not Found', { status: 404 })
138
123
  }
139
124
  `,
140
125
  "package.json": MINIMAL_TEMPLATE.files["package.json"],
@@ -157,6 +142,7 @@ async function runInitCommand(parsed, logger, options) {
157
142
  return { exitCode: 1 };
158
143
  }
159
144
  const projectDir = resolve(cwd, projectName);
145
+ const dependencyVersions = await getInitDependencyVersions();
160
146
  const { fs } = await getDependencies();
161
147
  try {
162
148
  await fs.access(projectDir);
@@ -168,7 +154,7 @@ async function runInitCommand(parsed, logger, options) {
168
154
  const fullPath = join(projectDir, filePath);
169
155
  const dir = fullPath.substring(0, fullPath.lastIndexOf("/"));
170
156
  await fs.mkdir(dir, { recursive: true }).catch(() => {});
171
- const processedContent = content.replace(/\{\{PROJECT_NAME\}\}/g, projectName);
157
+ const processedContent = content.replace(/\{\{PROJECT_NAME\}\}/g, projectName).replace(/\{\{DEVFLARE_VERSION\}\}/g, dependencyVersions.devflare).replace(/\{\{TYPESCRIPT_VERSION\}\}/g, dependencyVersions.typescript).replace(/\{\{WRANGLER_VERSION\}\}/g, dependencyVersions.wrangler).replace(/\{\{WORKERS_TYPES_VERSION\}\}/g, dependencyVersions.workersTypes);
172
158
  await fs.writeFile(fullPath, processedContent, "utf-8");
173
159
  logger.info(` Created: ${filePath}`);
174
160
  }
@@ -178,6 +164,7 @@ Project created successfully!`);
178
164
  Next steps:`);
179
165
  logger.info(` cd ${projectName}`);
180
166
  logger.info(` bun install`);
167
+ logger.info(` bun run types`);
181
168
  logger.info(` bun run dev`);
182
169
  return { exitCode: 0 };
183
170
  }