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.
- package/LLM.md +1424 -610
- package/R2.md +200 -0
- package/README.md +302 -505
- package/bin/devflare.js +8 -8
- package/dist/{account-rvrj687w.js → account-8psavtg6.js} +27 -4
- package/dist/bridge/miniflare.d.ts +6 -0
- package/dist/bridge/miniflare.d.ts.map +1 -1
- package/dist/bridge/proxy.d.ts +5 -6
- package/dist/bridge/proxy.d.ts.map +1 -1
- package/dist/bridge/server.d.ts.map +1 -1
- package/dist/browser.d.ts +50 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/{build-mnf6v8gd.js → build-ezksv2dd.js} +26 -7
- package/dist/bundler/do-bundler.d.ts +7 -0
- package/dist/bundler/do-bundler.d.ts.map +1 -1
- package/dist/cli/commands/account.d.ts.map +1 -1
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/deploy.d.ts.map +1 -1
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/types.d.ts.map +1 -1
- package/dist/cli/config-path.d.ts +5 -0
- package/dist/cli/config-path.d.ts.map +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/package-metadata.d.ts +16 -0
- package/dist/cli/package-metadata.d.ts.map +1 -0
- package/dist/config/compiler.d.ts +7 -0
- package/dist/config/compiler.d.ts.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/schema.d.ts +2594 -1234
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/{deploy-nhceck39.js → deploy-jdpy21t6.js} +33 -15
- package/dist/{dev-qnxet3j9.js → dev-9mq7zhww.js} +900 -234
- package/dist/dev-server/miniflare-log.d.ts +12 -0
- package/dist/dev-server/miniflare-log.d.ts.map +1 -0
- package/dist/dev-server/runtime-stdio.d.ts +8 -0
- package/dist/dev-server/runtime-stdio.d.ts.map +1 -0
- package/dist/dev-server/server.d.ts +2 -0
- package/dist/dev-server/server.d.ts.map +1 -1
- package/dist/dev-server/vite-utils.d.ts +37 -0
- package/dist/dev-server/vite-utils.d.ts.map +1 -0
- package/dist/{doctor-e8fy6fj5.js → doctor-z4ffybce.js} +73 -50
- package/dist/{durable-object-t4kbb0yt.js → durable-object-yt8v1dyn.js} +1 -1
- package/dist/index-1p814k7s.js +227 -0
- package/dist/{index-tk6ej9dj.js → index-2q3pmzrx.js} +12 -16
- package/dist/{index-67qcae0f.js → index-51s1hkw4.js} +16 -1
- package/dist/{index-ep3445yc.js → index-53xcakh8.js} +414 -171
- package/dist/{index-pf5s73n9.js → index-59df49vn.js} +11 -281
- package/dist/index-5yxg30va.js +304 -0
- package/dist/index-62b3gt2g.js +12 -0
- package/dist/index-6h8xbs75.js +44 -0
- package/dist/index-8gtqgb3q.js +529 -0
- package/dist/{index-gz1gndna.js → index-9wt9x09k.js} +42 -62
- package/dist/index-dr6sbp8d.js +39 -0
- package/dist/index-fef08w43.js +231 -0
- package/dist/index-k7r18na8.js +0 -0
- package/dist/{index-m2q41jwa.js → index-n932ytmq.js} +9 -1
- package/dist/{index-07q6yxyc.js → index-v8vvsn9x.js} +1 -0
- package/dist/index-vky23txa.js +70 -0
- package/dist/{index-z14anrqp.js → index-wfbfz02q.js} +14 -15
- package/dist/index-ws68xvq2.js +311 -0
- package/dist/{index-hcex3rgh.js → index-wyf3s77s.js} +85 -8
- package/dist/index-xqfbd9fx.js +195 -0
- package/dist/index-xxwbb2nt.js +322 -0
- package/dist/index-y1d8za14.js +196 -0
- package/dist/{init-f9mgmew3.js → init-na2atvz2.js} +42 -55
- package/dist/router/types.d.ts +24 -0
- package/dist/router/types.d.ts.map +1 -0
- package/dist/runtime/context.d.ts +249 -8
- package/dist/runtime/context.d.ts.map +1 -1
- package/dist/runtime/exports.d.ts +50 -55
- package/dist/runtime/exports.d.ts.map +1 -1
- package/dist/runtime/index.d.ts +8 -1
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/middleware.d.ts +77 -60
- package/dist/runtime/middleware.d.ts.map +1 -1
- package/dist/runtime/router.d.ts +7 -0
- package/dist/runtime/router.d.ts.map +1 -0
- package/dist/runtime/validation.d.ts +1 -1
- package/dist/runtime/validation.d.ts.map +1 -1
- package/dist/src/browser.js +150 -0
- package/dist/src/cli/index.js +10 -0
- package/dist/{cloudflare → src/cloudflare}/index.js +3 -3
- package/dist/{decorators → src/decorators}/index.js +2 -2
- package/dist/src/index.js +132 -0
- package/dist/src/runtime/index.js +111 -0
- package/dist/{sveltekit → src/sveltekit}/index.js +14 -6
- package/dist/{test → src/test}/index.js +22 -13
- package/dist/{vite → src/vite}/index.js +128 -59
- package/dist/sveltekit/platform.d.ts.map +1 -1
- package/dist/test/bridge-context.d.ts +5 -2
- package/dist/test/bridge-context.d.ts.map +1 -1
- package/dist/test/cf.d.ts +25 -11
- package/dist/test/cf.d.ts.map +1 -1
- package/dist/test/email.d.ts +16 -7
- package/dist/test/email.d.ts.map +1 -1
- package/dist/test/queue.d.ts.map +1 -1
- package/dist/test/resolve-service-bindings.d.ts.map +1 -1
- package/dist/test/scheduled.d.ts.map +1 -1
- package/dist/test/simple-context.d.ts +1 -1
- package/dist/test/simple-context.d.ts.map +1 -1
- package/dist/test/tail.d.ts +2 -1
- package/dist/test/tail.d.ts.map +1 -1
- package/dist/test/worker.d.ts +6 -0
- package/dist/test/worker.d.ts.map +1 -1
- package/dist/transform/durable-object.d.ts.map +1 -1
- package/dist/transform/worker-entrypoint.d.ts.map +1 -1
- package/dist/{types-5nyrz1sz.js → types-nq5acrwh.js} +30 -16
- package/dist/utils/entrypoint-discovery.d.ts +6 -3
- package/dist/utils/entrypoint-discovery.d.ts.map +1 -1
- package/dist/utils/send-email.d.ts +15 -0
- package/dist/utils/send-email.d.ts.map +1 -0
- package/dist/vite/plugin.d.ts.map +1 -1
- package/dist/worker-entry/composed-worker.d.ts +13 -0
- package/dist/worker-entry/composed-worker.d.ts.map +1 -0
- package/dist/worker-entry/routes.d.ts +22 -0
- package/dist/worker-entry/routes.d.ts.map +1 -0
- package/dist/{worker-entrypoint-m9th0rg0.js → worker-entrypoint-c259fmfs.js} +1 -1
- package/package.json +21 -19
- package/dist/index.js +0 -298
- package/dist/runtime/index.js +0 -111
|
@@ -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/
|
|
25
|
+
"src/fetch.ts": `import type { FetchEvent } from 'devflare/runtime'
|
|
20
26
|
|
|
21
|
-
export
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
"
|
|
40
|
-
"
|
|
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
|
|
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
|
-
|
|
68
|
-
|
|
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/
|
|
75
|
-
import {
|
|
76
|
-
import {
|
|
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
|
|
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 {
|
|
94
|
-
import { event } from 'devflare/runtime'
|
|
89
|
+
"src/middleware/cors.ts": `import type { FetchEvent, ResolveFetch } from 'devflare/runtime'
|
|
95
90
|
|
|
96
|
-
export
|
|
91
|
+
export async function corsHandle(event: FetchEvent, resolve: ResolveFetch): Promise<Response> {
|
|
97
92
|
// Handle preflight
|
|
98
|
-
if (event.request
|
|
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
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
return
|
|
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/
|
|
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
|
|
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
|
|
131
|
-
status: 404,
|
|
132
|
-
headers: { 'Content-Type': 'application/json' }
|
|
133
|
-
})
|
|
119
|
+
return Response.json({ error: 'Not found' }, { status: 404 })
|
|
134
120
|
}
|
|
135
121
|
|
|
136
|
-
|
|
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
|
}
|