devflare 1.0.0-next.6 → 1.0.0-next.8
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 +219 -23
- package/README.md +36 -5
- package/dist/{build-nz5yrj7f.js → build-9myaxf07.js} +19 -2
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/deploy.d.ts.map +1 -1
- package/dist/cli/commands/types.d.ts.map +1 -1
- package/dist/{deploy-a5pcxd5w.js → deploy-h1wz5p7m.js} +26 -10
- package/dist/{dev-7agn9g5s.js → dev-074w858d.js} +467 -126
- 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/server.d.ts.map +1 -1
- package/dist/index-a0fjkq68.js +198 -0
- package/dist/{index-rprrn24p.js → index-f8qh2tyh.js} +9 -10
- package/dist/index.js +5 -5
- package/dist/test/index.js +1 -1
- package/dist/test/simple-context.d.ts +1 -1
- package/dist/test/simple-context.d.ts.map +1 -1
- package/dist/{types-vss6vrz7.js → types-wdcpnfvy.js} +39 -7
- package/dist/vite/index.js +118 -50
- package/dist/vite/plugin.d.ts.map +1 -1
- package/dist/worker-entry/composed-worker.d.ts +10 -0
- package/dist/worker-entry/composed-worker.d.ts.map +1 -0
- package/package.json +2 -2
|
@@ -10,7 +10,8 @@ import {
|
|
|
10
10
|
findDurableObjectClasses
|
|
11
11
|
} from "./index-gz1gndna.js";
|
|
12
12
|
import {
|
|
13
|
-
loadConfig
|
|
13
|
+
loadConfig,
|
|
14
|
+
resolveConfigPath
|
|
14
15
|
} from "./index-hcex3rgh.js";
|
|
15
16
|
import {
|
|
16
17
|
__require
|
|
@@ -21,7 +22,7 @@ import { createConsola } from "consola";
|
|
|
21
22
|
import { resolve as resolve3 } from "pathe";
|
|
22
23
|
|
|
23
24
|
// src/dev-server/server.ts
|
|
24
|
-
import { dirname as dirname2, resolve as resolve2 } from "pathe";
|
|
25
|
+
import { dirname as dirname2, relative as relative2, resolve as resolve2 } from "pathe";
|
|
25
26
|
|
|
26
27
|
// src/bundler/do-bundler.ts
|
|
27
28
|
import { resolve, dirname, relative } from "pathe";
|
|
@@ -1092,6 +1093,40 @@ async function checkRemoteBindingRequirements(config) {
|
|
|
1092
1093
|
};
|
|
1093
1094
|
}
|
|
1094
1095
|
|
|
1096
|
+
// src/dev-server/miniflare-log.ts
|
|
1097
|
+
var ANSI_ESCAPE_REGEX = /\u001B\[[0-9;]*m/g;
|
|
1098
|
+
var COMPATIBILITY_DATE_FALLBACK_REGEX = /^The latest compatibility date supported by the installed Cloudflare Workers Runtime is "([^"]+)", but you've requested "([^"]+)"\. Falling back to "([^"]+)"\.\.\.$/;
|
|
1099
|
+
function normalizeMiniflareMessage(message) {
|
|
1100
|
+
return message.replace(ANSI_ESCAPE_REGEX, "").replace(/\s+/g, " ").trim();
|
|
1101
|
+
}
|
|
1102
|
+
function formatCompatibilityDateFallbackNotice(message) {
|
|
1103
|
+
const normalizedMessage = normalizeMiniflareMessage(message);
|
|
1104
|
+
const match = COMPATIBILITY_DATE_FALLBACK_REGEX.exec(normalizedMessage);
|
|
1105
|
+
if (!match) {
|
|
1106
|
+
return null;
|
|
1107
|
+
}
|
|
1108
|
+
const [, _supportedDate, requestedDate, fallbackDate] = match;
|
|
1109
|
+
return `Using latest supported Cloudflare Workers Runtime compatibility date ${fallbackDate} (requested ${requestedDate})`;
|
|
1110
|
+
}
|
|
1111
|
+
function createCompatibilityAwareMiniflareLog(BaseLog, level, logger) {
|
|
1112
|
+
const log = new BaseLog(level);
|
|
1113
|
+
const originalWarn = log.warn.bind(log);
|
|
1114
|
+
const originalInfo = log.info.bind(log);
|
|
1115
|
+
log.warn = (message) => {
|
|
1116
|
+
const notice = formatCompatibilityDateFallbackNotice(message);
|
|
1117
|
+
if (!notice) {
|
|
1118
|
+
originalWarn(message);
|
|
1119
|
+
return;
|
|
1120
|
+
}
|
|
1121
|
+
if (logger) {
|
|
1122
|
+
logger.info(notice);
|
|
1123
|
+
return;
|
|
1124
|
+
}
|
|
1125
|
+
originalInfo(notice);
|
|
1126
|
+
};
|
|
1127
|
+
return log;
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1095
1130
|
// src/dev-server/server.ts
|
|
1096
1131
|
var DEFAULT_FETCH_ENTRY_FILES = [
|
|
1097
1132
|
"src/fetch.ts",
|
|
@@ -1099,17 +1134,38 @@ var DEFAULT_FETCH_ENTRY_FILES = [
|
|
|
1099
1134
|
"src/fetch.mts",
|
|
1100
1135
|
"src/fetch.mjs"
|
|
1101
1136
|
];
|
|
1137
|
+
var DEFAULT_QUEUE_ENTRY_FILES = [
|
|
1138
|
+
"src/queue.ts",
|
|
1139
|
+
"src/queue.js",
|
|
1140
|
+
"src/queue.mts",
|
|
1141
|
+
"src/queue.mjs"
|
|
1142
|
+
];
|
|
1143
|
+
var DEFAULT_SCHEDULED_ENTRY_FILES = [
|
|
1144
|
+
"src/scheduled.ts",
|
|
1145
|
+
"src/scheduled.js",
|
|
1146
|
+
"src/scheduled.mts",
|
|
1147
|
+
"src/scheduled.mjs"
|
|
1148
|
+
];
|
|
1149
|
+
var DEFAULT_EMAIL_ENTRY_FILES = [
|
|
1150
|
+
"src/email.ts",
|
|
1151
|
+
"src/email.js",
|
|
1152
|
+
"src/email.mts",
|
|
1153
|
+
"src/email.mjs"
|
|
1154
|
+
];
|
|
1102
1155
|
var INTERNAL_APP_SERVICE_BINDING = "__DEVFLARE_APP";
|
|
1103
|
-
|
|
1104
|
-
|
|
1156
|
+
function formatErrorMessage(error) {
|
|
1157
|
+
return error instanceof Error ? error.message : String(error);
|
|
1158
|
+
}
|
|
1159
|
+
async function resolveWorkerHandlerPath(cwd, configuredPath, defaultEntries) {
|
|
1160
|
+
if (configuredPath === false) {
|
|
1105
1161
|
return null;
|
|
1106
1162
|
}
|
|
1107
1163
|
const fs = await import("node:fs/promises");
|
|
1108
1164
|
const candidates = new Set;
|
|
1109
|
-
if (typeof
|
|
1110
|
-
candidates.add(
|
|
1165
|
+
if (typeof configuredPath === "string" && configuredPath) {
|
|
1166
|
+
candidates.add(configuredPath);
|
|
1111
1167
|
}
|
|
1112
|
-
for (const defaultEntry of
|
|
1168
|
+
for (const defaultEntry of defaultEntries) {
|
|
1113
1169
|
candidates.add(defaultEntry);
|
|
1114
1170
|
}
|
|
1115
1171
|
for (const candidate of candidates) {
|
|
@@ -1123,7 +1179,214 @@ async function resolveMainWorkerScriptPath(cwd, config) {
|
|
|
1123
1179
|
}
|
|
1124
1180
|
return null;
|
|
1125
1181
|
}
|
|
1126
|
-
function
|
|
1182
|
+
async function resolveMainWorkerSurfacePaths(cwd, config) {
|
|
1183
|
+
return {
|
|
1184
|
+
fetch: await resolveWorkerHandlerPath(cwd, config.files?.fetch, DEFAULT_FETCH_ENTRY_FILES),
|
|
1185
|
+
queue: await resolveWorkerHandlerPath(cwd, config.files?.queue, DEFAULT_QUEUE_ENTRY_FILES),
|
|
1186
|
+
scheduled: await resolveWorkerHandlerPath(cwd, config.files?.scheduled, DEFAULT_SCHEDULED_ENTRY_FILES),
|
|
1187
|
+
email: await resolveWorkerHandlerPath(cwd, config.files?.email, DEFAULT_EMAIL_ENTRY_FILES)
|
|
1188
|
+
};
|
|
1189
|
+
}
|
|
1190
|
+
function getFirstWorkerSurfacePath(surfacePaths) {
|
|
1191
|
+
return surfacePaths.fetch ?? surfacePaths.queue ?? surfacePaths.scheduled ?? surfacePaths.email;
|
|
1192
|
+
}
|
|
1193
|
+
function hasWorkerSurfacePaths(surfacePaths) {
|
|
1194
|
+
return Object.values(surfacePaths).some((surfacePath) => typeof surfacePath === "string" && surfacePath.length > 0);
|
|
1195
|
+
}
|
|
1196
|
+
function toImportSpecifier(fromFilePath, toFilePath) {
|
|
1197
|
+
const specifier = relative2(dirname2(fromFilePath), toFilePath).replace(/\\/g, "/");
|
|
1198
|
+
return specifier.startsWith(".") ? specifier : `./${specifier}`;
|
|
1199
|
+
}
|
|
1200
|
+
function getMainWorkerEntryScript(surfaceImportPaths) {
|
|
1201
|
+
const importLines = [`import { runWithContext } from 'devflare/runtime'`];
|
|
1202
|
+
const moduleFallbackLines = [];
|
|
1203
|
+
const registerSurfaceModule = (identifier, importPath) => {
|
|
1204
|
+
if (importPath) {
|
|
1205
|
+
importLines.push(`import * as ${identifier} from '${importPath}'`);
|
|
1206
|
+
return;
|
|
1207
|
+
}
|
|
1208
|
+
moduleFallbackLines.push(`const ${identifier} = {}`);
|
|
1209
|
+
};
|
|
1210
|
+
registerSurfaceModule("__devflareFetchModule", surfaceImportPaths.fetch);
|
|
1211
|
+
registerSurfaceModule("__devflareQueueModule", surfaceImportPaths.queue);
|
|
1212
|
+
registerSurfaceModule("__devflareScheduledModule", surfaceImportPaths.scheduled);
|
|
1213
|
+
registerSurfaceModule("__devflareEmailModule", surfaceImportPaths.email);
|
|
1214
|
+
return `
|
|
1215
|
+
${importLines.join(`
|
|
1216
|
+
`)}
|
|
1217
|
+
${moduleFallbackLines.join(`
|
|
1218
|
+
`)}
|
|
1219
|
+
|
|
1220
|
+
const __devflareResolveHandler = (module, namedExport) => {
|
|
1221
|
+
const defaultExport = module.default
|
|
1222
|
+
|
|
1223
|
+
if (typeof defaultExport === 'function') {
|
|
1224
|
+
return defaultExport
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
if (defaultExport && typeof defaultExport[namedExport] === 'function') {
|
|
1228
|
+
return defaultExport[namedExport].bind(defaultExport)
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
if (typeof module[namedExport] === 'function') {
|
|
1232
|
+
return module[namedExport]
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
return null
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
const __devflareFetchHandler = __devflareResolveHandler(__devflareFetchModule, 'fetch')
|
|
1239
|
+
const __devflareQueueHandler = __devflareResolveHandler(__devflareQueueModule, 'queue')
|
|
1240
|
+
const __devflareScheduledHandler = __devflareResolveHandler(__devflareScheduledModule, 'scheduled')
|
|
1241
|
+
const __devflareEmailHandler = __devflareResolveHandler(__devflareEmailModule, 'email')
|
|
1242
|
+
|
|
1243
|
+
function __devflareCreateEmailHeaders(rawBody) {
|
|
1244
|
+
const headers = new Headers()
|
|
1245
|
+
const lines = rawBody.split(/\\r?\\n/)
|
|
1246
|
+
|
|
1247
|
+
for (const line of lines) {
|
|
1248
|
+
if (line.trim() === '') {
|
|
1249
|
+
break
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
const colonIndex = line.indexOf(':')
|
|
1253
|
+
if (colonIndex <= 0) {
|
|
1254
|
+
continue
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
headers.append(line.slice(0, colonIndex).trim(), line.slice(colonIndex + 1).trim())
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
return headers
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
function __devflareCreateEmailRawStream(rawBody) {
|
|
1264
|
+
return new ReadableStream({
|
|
1265
|
+
start(controller) {
|
|
1266
|
+
controller.enqueue(new TextEncoder().encode(rawBody))
|
|
1267
|
+
controller.close()
|
|
1268
|
+
}
|
|
1269
|
+
})
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
async function __devflareHandleInternalEmail(request, env, ctx) {
|
|
1273
|
+
if (!__devflareEmailHandler) {
|
|
1274
|
+
return new Response('Email handler not configured', { status: 501 })
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
const from = request.headers.get('x-devflare-email-from') || 'unknown@example.com'
|
|
1278
|
+
const to = request.headers.get('x-devflare-email-to') || 'worker@example.com'
|
|
1279
|
+
const rawBody = await request.text()
|
|
1280
|
+
const emailMessage = {
|
|
1281
|
+
from,
|
|
1282
|
+
to,
|
|
1283
|
+
headers: __devflareCreateEmailHeaders(rawBody),
|
|
1284
|
+
raw: __devflareCreateEmailRawStream(rawBody),
|
|
1285
|
+
rawSize: rawBody.length,
|
|
1286
|
+
setReject(reason) {
|
|
1287
|
+
console.warn('[Devflare email rejected]', reason)
|
|
1288
|
+
},
|
|
1289
|
+
async forward(rcptTo) {
|
|
1290
|
+
console.log('[Devflare email forwarded]', rcptTo)
|
|
1291
|
+
return Promise.resolve()
|
|
1292
|
+
},
|
|
1293
|
+
async reply(message) {
|
|
1294
|
+
console.log('[Devflare email reply sent]', message?.from)
|
|
1295
|
+
return Promise.resolve()
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
await runWithContext(
|
|
1300
|
+
env,
|
|
1301
|
+
ctx,
|
|
1302
|
+
null,
|
|
1303
|
+
() => __devflareEmailHandler(emailMessage, env, ctx),
|
|
1304
|
+
'email'
|
|
1305
|
+
)
|
|
1306
|
+
|
|
1307
|
+
return new Response(JSON.stringify({ ok: true, from, to }), {
|
|
1308
|
+
headers: { 'Content-Type': 'application/json' }
|
|
1309
|
+
})
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
export default {
|
|
1313
|
+
async fetch(request, env, ctx) {
|
|
1314
|
+
const url = new URL(request.url)
|
|
1315
|
+
|
|
1316
|
+
if (
|
|
1317
|
+
request.headers.get('x-devflare-event') === 'email' &&
|
|
1318
|
+
url.pathname === '/_devflare/internal/email'
|
|
1319
|
+
) {
|
|
1320
|
+
return __devflareHandleInternalEmail(request, env, ctx)
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
if (!__devflareFetchHandler) {
|
|
1324
|
+
return new Response('Fetch handler not configured', { status: 404 })
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
return runWithContext(
|
|
1328
|
+
env,
|
|
1329
|
+
ctx,
|
|
1330
|
+
request,
|
|
1331
|
+
() => __devflareFetchHandler(request, env, ctx),
|
|
1332
|
+
'fetch'
|
|
1333
|
+
)
|
|
1334
|
+
},
|
|
1335
|
+
...(__devflareQueueHandler
|
|
1336
|
+
? {
|
|
1337
|
+
async queue(batch, env, ctx) {
|
|
1338
|
+
return runWithContext(
|
|
1339
|
+
env,
|
|
1340
|
+
ctx,
|
|
1341
|
+
null,
|
|
1342
|
+
() => __devflareQueueHandler(batch, env, ctx),
|
|
1343
|
+
'queue'
|
|
1344
|
+
)
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
: {}),
|
|
1348
|
+
...(__devflareScheduledHandler
|
|
1349
|
+
? {
|
|
1350
|
+
async scheduled(controller, env, ctx) {
|
|
1351
|
+
return runWithContext(
|
|
1352
|
+
env,
|
|
1353
|
+
ctx,
|
|
1354
|
+
null,
|
|
1355
|
+
() => __devflareScheduledHandler(controller, env, ctx),
|
|
1356
|
+
'scheduled'
|
|
1357
|
+
)
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
: {}),
|
|
1361
|
+
...(__devflareEmailHandler
|
|
1362
|
+
? {
|
|
1363
|
+
async email(message, env, ctx) {
|
|
1364
|
+
return runWithContext(
|
|
1365
|
+
env,
|
|
1366
|
+
ctx,
|
|
1367
|
+
null,
|
|
1368
|
+
() => __devflareEmailHandler(message, env, ctx),
|
|
1369
|
+
'email'
|
|
1370
|
+
)
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
: {})
|
|
1374
|
+
}
|
|
1375
|
+
`;
|
|
1376
|
+
}
|
|
1377
|
+
function addWorkerWatchRoots(roots, cwd, configuredPath, defaultEntries) {
|
|
1378
|
+
if (configuredPath === false) {
|
|
1379
|
+
return;
|
|
1380
|
+
}
|
|
1381
|
+
if (typeof configuredPath === "string" && configuredPath) {
|
|
1382
|
+
roots.add(dirname2(resolve2(cwd, configuredPath)));
|
|
1383
|
+
return;
|
|
1384
|
+
}
|
|
1385
|
+
for (const defaultEntry of defaultEntries) {
|
|
1386
|
+
roots.add(dirname2(resolve2(cwd, defaultEntry)));
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
function collectWorkerWatchRoots(cwd, config, mainWorkerSurfacePaths) {
|
|
1127
1390
|
const roots = new Set;
|
|
1128
1391
|
const addFileParent = (filePath) => {
|
|
1129
1392
|
if (typeof filePath !== "string" || !filePath) {
|
|
@@ -1131,13 +1394,15 @@ function collectWorkerWatchRoots(cwd, config, mainWorkerScriptPath) {
|
|
|
1131
1394
|
}
|
|
1132
1395
|
roots.add(dirname2(resolve2(cwd, filePath)));
|
|
1133
1396
|
};
|
|
1134
|
-
|
|
1135
|
-
|
|
1397
|
+
for (const surfacePath of Object.values(mainWorkerSurfacePaths)) {
|
|
1398
|
+
if (surfacePath) {
|
|
1399
|
+
roots.add(dirname2(surfacePath));
|
|
1400
|
+
}
|
|
1136
1401
|
}
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1402
|
+
addWorkerWatchRoots(roots, cwd, config.files?.fetch, DEFAULT_FETCH_ENTRY_FILES);
|
|
1403
|
+
addWorkerWatchRoots(roots, cwd, config.files?.queue, DEFAULT_QUEUE_ENTRY_FILES);
|
|
1404
|
+
addWorkerWatchRoots(roots, cwd, config.files?.scheduled, DEFAULT_SCHEDULED_ENTRY_FILES);
|
|
1405
|
+
addWorkerWatchRoots(roots, cwd, config.files?.email, DEFAULT_EMAIL_ENTRY_FILES);
|
|
1141
1406
|
addFileParent(config.files?.transport);
|
|
1142
1407
|
if (config.files?.routes && typeof config.files.routes === "object") {
|
|
1143
1408
|
roots.add(resolve2(cwd, config.files.routes.dir));
|
|
@@ -1263,69 +1528,27 @@ async function handleEmailIncoming(request, env, ctx, url) {
|
|
|
1263
1528
|
const rawBody = await request.text()
|
|
1264
1529
|
|
|
1265
1530
|
log('Email incoming:', { from, to, bodyLength: rawBody.length })
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
const value = line.slice(colonIdx + 1).trim()
|
|
1285
|
-
headers.append(key, value)
|
|
1286
|
-
}
|
|
1287
|
-
}
|
|
1288
|
-
|
|
1289
|
-
// Create ReadableStream from raw email
|
|
1290
|
-
const rawStream = new ReadableStream({
|
|
1291
|
-
start(controller) {
|
|
1292
|
-
controller.enqueue(new TextEncoder().encode(rawBody))
|
|
1293
|
-
controller.close()
|
|
1294
|
-
}
|
|
1295
|
-
})
|
|
1296
|
-
|
|
1297
|
-
// Create ForwardableEmailMessage-like object
|
|
1298
|
-
const emailMessage = {
|
|
1299
|
-
from,
|
|
1300
|
-
to,
|
|
1301
|
-
headers,
|
|
1302
|
-
raw: rawStream,
|
|
1303
|
-
rawSize: rawBody.length,
|
|
1304
|
-
|
|
1305
|
-
setReject(reason) {
|
|
1306
|
-
log('Email rejected:', reason)
|
|
1307
|
-
},
|
|
1308
|
-
|
|
1309
|
-
async forward(rcptTo, extraHeaders) {
|
|
1310
|
-
log('Email forwarded to:', rcptTo)
|
|
1311
|
-
return Promise.resolve()
|
|
1312
|
-
},
|
|
1313
|
-
|
|
1314
|
-
async reply(message) {
|
|
1315
|
-
log('Email reply sent to:', message.from)
|
|
1316
|
-
return Promise.resolve()
|
|
1531
|
+
|
|
1532
|
+
if (APP_SERVICE_BINDING) {
|
|
1533
|
+
const appWorker = env[APP_SERVICE_BINDING]
|
|
1534
|
+
if (appWorker && typeof appWorker.fetch === 'function') {
|
|
1535
|
+
const response = await appWorker.fetch(new Request('http://devflare.internal/_devflare/internal/email', {
|
|
1536
|
+
method: 'POST',
|
|
1537
|
+
headers: {
|
|
1538
|
+
'x-devflare-event': 'email',
|
|
1539
|
+
'x-devflare-email-from': from,
|
|
1540
|
+
'x-devflare-email-to': to,
|
|
1541
|
+
'content-type': request.headers.get('content-type') || 'text/plain'
|
|
1542
|
+
},
|
|
1543
|
+
body: rawBody
|
|
1544
|
+
}))
|
|
1545
|
+
|
|
1546
|
+
if (!response.ok) {
|
|
1547
|
+
return response
|
|
1548
|
+
}
|
|
1317
1549
|
}
|
|
1318
1550
|
}
|
|
1319
1551
|
|
|
1320
|
-
// Look for email handler in the worker module
|
|
1321
|
-
// For now, we call via a special RPC method that DO workers can implement
|
|
1322
|
-
// The email binding should be configured in the worker
|
|
1323
|
-
|
|
1324
|
-
// Check if there's an EMAIL_HANDLER binding (special DO for email handling)
|
|
1325
|
-
if (env.__emailHandler && typeof env.__emailHandler.email === 'function') {
|
|
1326
|
-
await env.__emailHandler.email(emailMessage, env, ctx)
|
|
1327
|
-
}
|
|
1328
|
-
|
|
1329
1552
|
return new Response(JSON.stringify({ ok: true, from, to }), {
|
|
1330
1553
|
headers: { 'Content-Type': 'application/json' }
|
|
1331
1554
|
})
|
|
@@ -1737,16 +1960,24 @@ function createDevServer(options) {
|
|
|
1737
1960
|
let miniflare = null;
|
|
1738
1961
|
let doBundler = null;
|
|
1739
1962
|
let workerSourceWatcher = null;
|
|
1963
|
+
let workerWatchTargets = [];
|
|
1740
1964
|
let viteProcess = null;
|
|
1741
1965
|
let config = null;
|
|
1742
1966
|
let browserShim = null;
|
|
1743
1967
|
let browserShimPort = 8788;
|
|
1968
|
+
let mainWorkerSurfacePaths = {
|
|
1969
|
+
fetch: null,
|
|
1970
|
+
queue: null,
|
|
1971
|
+
scheduled: null,
|
|
1972
|
+
email: null
|
|
1973
|
+
};
|
|
1974
|
+
let resolvedWorkerConfigPath = null;
|
|
1744
1975
|
let mainWorkerScriptPath = null;
|
|
1745
1976
|
let bundledMainWorkerScriptPath = null;
|
|
1746
1977
|
let currentDoResult = null;
|
|
1747
1978
|
let reloadChain = Promise.resolve();
|
|
1748
1979
|
async function bundleMainWorker() {
|
|
1749
|
-
if (!
|
|
1980
|
+
if (!hasWorkerSurfacePaths(mainWorkerSurfacePaths)) {
|
|
1750
1981
|
bundledMainWorkerScriptPath = null;
|
|
1751
1982
|
return;
|
|
1752
1983
|
}
|
|
@@ -1755,17 +1986,27 @@ function createDevServer(options) {
|
|
|
1755
1986
|
}
|
|
1756
1987
|
const fs = await import("node:fs/promises");
|
|
1757
1988
|
const outDir = resolve2(cwd, ".devflare", "worker-bundles");
|
|
1989
|
+
const entryDir = resolve2(cwd, ".devflare", "worker-entrypoints");
|
|
1758
1990
|
await fs.rm(outDir, { recursive: true, force: true });
|
|
1759
1991
|
await fs.mkdir(outDir, { recursive: true });
|
|
1992
|
+
await fs.mkdir(entryDir, { recursive: true });
|
|
1993
|
+
const entryPath = resolve2(entryDir, "fetch-entry.ts");
|
|
1994
|
+
const surfaceImportPaths = {
|
|
1995
|
+
fetch: mainWorkerSurfacePaths.fetch ? toImportSpecifier(entryPath, mainWorkerSurfacePaths.fetch) : null,
|
|
1996
|
+
queue: mainWorkerSurfacePaths.queue ? toImportSpecifier(entryPath, mainWorkerSurfacePaths.queue) : null,
|
|
1997
|
+
scheduled: mainWorkerSurfacePaths.scheduled ? toImportSpecifier(entryPath, mainWorkerSurfacePaths.scheduled) : null,
|
|
1998
|
+
email: mainWorkerSurfacePaths.email ? toImportSpecifier(entryPath, mainWorkerSurfacePaths.email) : null
|
|
1999
|
+
};
|
|
2000
|
+
await fs.writeFile(entryPath, getMainWorkerEntryScript(surfaceImportPaths));
|
|
1760
2001
|
const result = await Bun.build({
|
|
1761
|
-
entrypoints: [
|
|
2002
|
+
entrypoints: [entryPath],
|
|
1762
2003
|
outdir: outDir,
|
|
1763
2004
|
target: "browser",
|
|
1764
2005
|
conditions: ["browser"],
|
|
1765
2006
|
format: "esm",
|
|
1766
2007
|
minify: false,
|
|
1767
2008
|
splitting: false,
|
|
1768
|
-
external: ["cloudflare:workers", "cloudflare:*"]
|
|
2009
|
+
external: ["cloudflare:workers", "cloudflare:*", "node:*"]
|
|
1769
2010
|
});
|
|
1770
2011
|
if (!result.success || result.outputs.length === 0) {
|
|
1771
2012
|
const logs = result.logs.map((log) => ("message" in log) ? log.message : String(log)).join(`
|
|
@@ -1783,7 +2024,7 @@ ${logs}`);
|
|
|
1783
2024
|
const bindings = loadedConfig.bindings ?? {};
|
|
1784
2025
|
const persistPath = resolve2(cwd, ".devflare/data");
|
|
1785
2026
|
const appWorkerName = loadedConfig.name;
|
|
1786
|
-
const shouldRunMainWorker = !enableVite &&
|
|
2027
|
+
const shouldRunMainWorker = !enableVite && hasWorkerSurfacePaths(mainWorkerSurfacePaths);
|
|
1787
2028
|
const queueProducers = (() => {
|
|
1788
2029
|
if (!bindings.queues?.producers) {
|
|
1789
2030
|
return;
|
|
@@ -1794,6 +2035,23 @@ ${logs}`);
|
|
|
1794
2035
|
}
|
|
1795
2036
|
return producers;
|
|
1796
2037
|
})();
|
|
2038
|
+
const queueConsumers = (() => {
|
|
2039
|
+
if (!bindings.queues?.consumers || bindings.queues.consumers.length === 0) {
|
|
2040
|
+
return;
|
|
2041
|
+
}
|
|
2042
|
+
const consumers = {};
|
|
2043
|
+
for (const consumer of bindings.queues.consumers) {
|
|
2044
|
+
consumers[consumer.queue] = {
|
|
2045
|
+
...consumer.maxBatchSize !== undefined && { maxBatchSize: consumer.maxBatchSize },
|
|
2046
|
+
...consumer.maxBatchTimeout !== undefined && { maxBatchTimeout: consumer.maxBatchTimeout },
|
|
2047
|
+
...consumer.maxRetries !== undefined && { maxRetries: consumer.maxRetries },
|
|
2048
|
+
...consumer.deadLetterQueue && { deadLetterQueue: consumer.deadLetterQueue },
|
|
2049
|
+
...consumer.maxConcurrency !== undefined && { maxConcurrency: consumer.maxConcurrency },
|
|
2050
|
+
...consumer.retryDelay !== undefined && { retryDelay: consumer.retryDelay }
|
|
2051
|
+
};
|
|
2052
|
+
}
|
|
2053
|
+
return consumers;
|
|
2054
|
+
})();
|
|
1797
2055
|
const sharedOptions = {
|
|
1798
2056
|
port: miniflarePort,
|
|
1799
2057
|
host: "127.0.0.1",
|
|
@@ -1829,7 +2087,9 @@ ${logs}`);
|
|
|
1829
2087
|
...bindings.r2 && { r2Buckets: bindings.r2 },
|
|
1830
2088
|
...bindings.d1 && { d1Databases: bindings.d1 },
|
|
1831
2089
|
...loadedConfig.vars && Object.keys(loadedConfig.vars).length > 0 && { bindings: loadedConfig.vars },
|
|
1832
|
-
...queueProducers && { queueProducers }
|
|
2090
|
+
...queueProducers && { queueProducers },
|
|
2091
|
+
...options2.queueConsumers && { queueConsumers: options2.queueConsumers },
|
|
2092
|
+
...options2.triggers && { triggers: options2.triggers }
|
|
1833
2093
|
};
|
|
1834
2094
|
if (options2.scriptPath) {
|
|
1835
2095
|
workerConfig.scriptPath = options2.scriptPath;
|
|
@@ -1879,7 +2139,9 @@ ${logs}`);
|
|
|
1879
2139
|
const mainWorkerConfig = createWorkerConfig({
|
|
1880
2140
|
name: appWorkerName,
|
|
1881
2141
|
scriptPath: bundledMainWorkerScriptPath ?? mainWorkerScriptPath,
|
|
1882
|
-
serviceBindings: mainWorkerServiceBindings
|
|
2142
|
+
serviceBindings: mainWorkerServiceBindings,
|
|
2143
|
+
queueConsumers,
|
|
2144
|
+
triggers: loadedConfig.triggers?.crons?.length ? { crons: loadedConfig.triggers.crons } : undefined
|
|
1883
2145
|
});
|
|
1884
2146
|
workers.push(mainWorkerConfig);
|
|
1885
2147
|
}
|
|
@@ -1935,48 +2197,53 @@ ${logs}`);
|
|
|
1935
2197
|
async function startMiniflare(doResult) {
|
|
1936
2198
|
const { Miniflare, Log, LogLevel } = await import("miniflare");
|
|
1937
2199
|
const mfConfig = buildMiniflareConfig(doResult);
|
|
1938
|
-
mfConfig.log =
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
logger?.info(
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
2200
|
+
mfConfig.log = createCompatibilityAwareMiniflareLog(Log, LogLevel.DEBUG, logger);
|
|
2201
|
+
const shouldLogMiniflareDiagnostics = verbose || debug;
|
|
2202
|
+
if (shouldLogMiniflareDiagnostics) {
|
|
2203
|
+
logger?.info("=== MINIFLARE CONFIG DEBUG ===");
|
|
2204
|
+
logger?.info("Full config:", JSON.stringify(mfConfig, (key, value) => {
|
|
2205
|
+
if (key === "script" && typeof value === "string" && value.length > 200) {
|
|
2206
|
+
return value.substring(0, 200) + "...[truncated]";
|
|
2207
|
+
}
|
|
2208
|
+
return value;
|
|
2209
|
+
}, 2));
|
|
2210
|
+
if (mfConfig.workers) {
|
|
2211
|
+
logger?.info("Workers order:");
|
|
2212
|
+
for (const w of mfConfig.workers) {
|
|
2213
|
+
logger?.info(` → ${w.name}:`);
|
|
2214
|
+
logger?.info(` script: ${w.script ? "inline" : w.scriptPath}`);
|
|
2215
|
+
logger?.info(` browserRendering: ${JSON.stringify(w.browserRendering)}`);
|
|
2216
|
+
logger?.info(` durableObjects: ${JSON.stringify(w.durableObjects)}`);
|
|
2217
|
+
}
|
|
1953
2218
|
}
|
|
1954
2219
|
}
|
|
1955
2220
|
miniflare = new Miniflare(mfConfig);
|
|
1956
2221
|
await miniflare.ready;
|
|
1957
2222
|
logger?.success(`Miniflare ready on http://localhost:${miniflarePort}`);
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
2223
|
+
if (shouldLogMiniflareDiagnostics) {
|
|
2224
|
+
try {
|
|
2225
|
+
const gatewayBindings = await miniflare.getBindings("gateway");
|
|
2226
|
+
logger?.info("Gateway worker bindings:", Object.keys(gatewayBindings));
|
|
2227
|
+
if (mfConfig.workers) {
|
|
2228
|
+
for (const w of mfConfig.workers) {
|
|
2229
|
+
if (w.name !== "gateway") {
|
|
2230
|
+
try {
|
|
2231
|
+
const doBindings = await miniflare.getBindings(w.name);
|
|
2232
|
+
logger?.info(`${w.name} worker bindings:`, Object.keys(doBindings));
|
|
2233
|
+
if ("BROWSER" in doBindings) {
|
|
2234
|
+
logger?.success(`${w.name} has BROWSER binding!`);
|
|
2235
|
+
} else {
|
|
2236
|
+
logger?.warn(`${w.name} is MISSING BROWSER binding`);
|
|
2237
|
+
}
|
|
2238
|
+
} catch (error) {
|
|
2239
|
+
logger?.debug(`Skipping binding diagnostics for ${w.name}: ${formatErrorMessage(error)}`);
|
|
1971
2240
|
}
|
|
1972
|
-
} catch (e) {
|
|
1973
|
-
logger?.warn(`Could not get bindings for ${w.name}:`, e);
|
|
1974
2241
|
}
|
|
1975
2242
|
}
|
|
1976
2243
|
}
|
|
2244
|
+
} catch (error) {
|
|
2245
|
+
logger?.debug(`Skipping Miniflare binding diagnostics: ${formatErrorMessage(error)}`);
|
|
1977
2246
|
}
|
|
1978
|
-
} catch (e) {
|
|
1979
|
-
logger?.warn("Error getting bindings:", e);
|
|
1980
2247
|
}
|
|
1981
2248
|
}
|
|
1982
2249
|
async function reloadMiniflare(doResult) {
|
|
@@ -1986,7 +2253,7 @@ ${logs}`);
|
|
|
1986
2253
|
return;
|
|
1987
2254
|
const { Log, LogLevel } = await import("miniflare");
|
|
1988
2255
|
const mfConfig = buildMiniflareConfig(currentDoResult);
|
|
1989
|
-
mfConfig.log =
|
|
2256
|
+
mfConfig.log = createCompatibilityAwareMiniflareLog(Log, LogLevel.DEBUG, logger);
|
|
1990
2257
|
logger?.info("Reloading Miniflare...");
|
|
1991
2258
|
await miniflare.setOptions(mfConfig);
|
|
1992
2259
|
logger?.success("Miniflare reloaded");
|
|
@@ -1994,12 +2261,68 @@ ${logs}`);
|
|
|
1994
2261
|
reloadChain = queuedReload.catch(() => {});
|
|
1995
2262
|
await queuedReload;
|
|
1996
2263
|
}
|
|
2264
|
+
async function resolveWorkerConfigWatchPath() {
|
|
2265
|
+
if (configPath) {
|
|
2266
|
+
const explicitPath = resolve2(cwd, configPath);
|
|
2267
|
+
const fs = await import("node:fs/promises");
|
|
2268
|
+
try {
|
|
2269
|
+
await fs.access(explicitPath);
|
|
2270
|
+
return explicitPath;
|
|
2271
|
+
} catch {}
|
|
2272
|
+
}
|
|
2273
|
+
return await resolveConfigPath(cwd) ?? null;
|
|
2274
|
+
}
|
|
2275
|
+
async function refreshWorkerOnlySurfaceState() {
|
|
2276
|
+
if (!config) {
|
|
2277
|
+
return;
|
|
2278
|
+
}
|
|
2279
|
+
mainWorkerSurfacePaths = await resolveMainWorkerSurfacePaths(cwd, config);
|
|
2280
|
+
mainWorkerScriptPath = getFirstWorkerSurfacePath(mainWorkerSurfacePaths);
|
|
2281
|
+
if (hasWorkerSurfacePaths(mainWorkerSurfacePaths)) {
|
|
2282
|
+
await bundleMainWorker();
|
|
2283
|
+
} else {
|
|
2284
|
+
bundledMainWorkerScriptPath = null;
|
|
2285
|
+
}
|
|
2286
|
+
await syncWorkerWatchTargets();
|
|
2287
|
+
}
|
|
2288
|
+
function getWorkerWatchTargets() {
|
|
2289
|
+
if (enableVite || !config) {
|
|
2290
|
+
return [];
|
|
2291
|
+
}
|
|
2292
|
+
const targets = collectWorkerWatchRoots(cwd, config, mainWorkerSurfacePaths);
|
|
2293
|
+
if (resolvedWorkerConfigPath) {
|
|
2294
|
+
targets.push(resolvedWorkerConfigPath);
|
|
2295
|
+
}
|
|
2296
|
+
return [...new Set(targets)];
|
|
2297
|
+
}
|
|
2298
|
+
async function syncWorkerWatchTargets() {
|
|
2299
|
+
if (!workerSourceWatcher) {
|
|
2300
|
+
return;
|
|
2301
|
+
}
|
|
2302
|
+
const nextWatchTargets = getWorkerWatchTargets();
|
|
2303
|
+
const nextWatchTargetSet = new Set(nextWatchTargets);
|
|
2304
|
+
const targetsToRemove = workerWatchTargets.filter((target) => !nextWatchTargetSet.has(target));
|
|
2305
|
+
const targetsToAdd = nextWatchTargets.filter((target) => !workerWatchTargets.includes(target));
|
|
2306
|
+
if (targetsToRemove.length > 0) {
|
|
2307
|
+
await workerSourceWatcher.unwatch(targetsToRemove);
|
|
2308
|
+
}
|
|
2309
|
+
if (targetsToAdd.length > 0) {
|
|
2310
|
+
workerSourceWatcher.add(targetsToAdd);
|
|
2311
|
+
}
|
|
2312
|
+
workerWatchTargets = nextWatchTargets;
|
|
2313
|
+
}
|
|
2314
|
+
async function reloadWorkerOnlyConfig() {
|
|
2315
|
+
config = await loadConfig({ cwd, configFile: configPath });
|
|
2316
|
+
resolvedWorkerConfigPath = await resolveWorkerConfigWatchPath();
|
|
2317
|
+
await refreshWorkerOnlySurfaceState();
|
|
2318
|
+
await reloadMiniflare(currentDoResult);
|
|
2319
|
+
}
|
|
1997
2320
|
async function startWorkerSourceWatcher() {
|
|
1998
|
-
if (enableVite || !config
|
|
2321
|
+
if (enableVite || !config) {
|
|
1999
2322
|
return;
|
|
2000
2323
|
}
|
|
2001
|
-
const
|
|
2002
|
-
if (
|
|
2324
|
+
const watchTargets = getWorkerWatchTargets();
|
|
2325
|
+
if (watchTargets.length === 0) {
|
|
2003
2326
|
return;
|
|
2004
2327
|
}
|
|
2005
2328
|
const chokidar = await import("chokidar");
|
|
@@ -2020,8 +2343,14 @@ ${logs}`);
|
|
|
2020
2343
|
}
|
|
2021
2344
|
reloadInProgress = true;
|
|
2022
2345
|
try {
|
|
2346
|
+
const normalizedConfigPath = resolvedWorkerConfigPath ? normalizePath(resolvedWorkerConfigPath) : null;
|
|
2347
|
+
if (normalizedConfigPath && normalizePath(filePath) === normalizedConfigPath) {
|
|
2348
|
+
logger?.info(`Devflare config changed: ${filePath}`);
|
|
2349
|
+
await reloadWorkerOnlyConfig();
|
|
2350
|
+
return;
|
|
2351
|
+
}
|
|
2023
2352
|
logger?.info(`Worker source changed: ${filePath}`);
|
|
2024
|
-
await
|
|
2353
|
+
await refreshWorkerOnlySurfaceState();
|
|
2025
2354
|
await reloadMiniflare(currentDoResult);
|
|
2026
2355
|
} catch (error) {
|
|
2027
2356
|
logger?.error("Worker source reload failed:", error);
|
|
@@ -2042,7 +2371,8 @@ ${logs}`);
|
|
|
2042
2371
|
triggerReload(filePath);
|
|
2043
2372
|
}, 150);
|
|
2044
2373
|
};
|
|
2045
|
-
|
|
2374
|
+
workerWatchTargets = watchTargets;
|
|
2375
|
+
workerSourceWatcher = chokidar.watch(watchTargets, {
|
|
2046
2376
|
ignoreInitial: true,
|
|
2047
2377
|
usePolling: isWindows,
|
|
2048
2378
|
interval: isWindows ? 300 : undefined,
|
|
@@ -2061,12 +2391,22 @@ ${logs}`);
|
|
|
2061
2391
|
workerSourceWatcher.on("change", onFileEvent);
|
|
2062
2392
|
workerSourceWatcher.on("add", onFileEvent);
|
|
2063
2393
|
workerSourceWatcher.on("unlink", onFileEvent);
|
|
2064
|
-
workerSourceWatcher.on("ready", () => {
|
|
2065
|
-
logger?.info(`Worker source watcher ready (${watchRoots.length} root(s))`);
|
|
2066
|
-
});
|
|
2067
2394
|
workerSourceWatcher.on("error", (error) => {
|
|
2068
2395
|
logger?.error("Worker source watcher error:", error);
|
|
2069
2396
|
});
|
|
2397
|
+
await new Promise((resolvePromise, rejectPromise) => {
|
|
2398
|
+
const handleReady = () => {
|
|
2399
|
+
workerSourceWatcher?.off("error", handleInitialError);
|
|
2400
|
+
logger?.info(`Worker source watcher ready (${watchTargets.length} target(s))`);
|
|
2401
|
+
resolvePromise();
|
|
2402
|
+
};
|
|
2403
|
+
const handleInitialError = (error) => {
|
|
2404
|
+
workerSourceWatcher?.off("ready", handleReady);
|
|
2405
|
+
rejectPromise(error instanceof Error ? error : new Error(String(error)));
|
|
2406
|
+
};
|
|
2407
|
+
workerSourceWatcher?.once("ready", handleReady);
|
|
2408
|
+
workerSourceWatcher?.once("error", handleInitialError);
|
|
2409
|
+
});
|
|
2070
2410
|
}
|
|
2071
2411
|
async function runD1Migrations() {
|
|
2072
2412
|
if (!miniflare || !config?.bindings?.d1)
|
|
@@ -2152,13 +2492,14 @@ ${logs}`);
|
|
|
2152
2492
|
async function start() {
|
|
2153
2493
|
logger?.info("Starting unified dev server...");
|
|
2154
2494
|
config = await loadConfig({ cwd, configFile: configPath });
|
|
2495
|
+
resolvedWorkerConfigPath = await resolveWorkerConfigWatchPath();
|
|
2155
2496
|
logger?.debug("Loaded config:", config.name);
|
|
2156
|
-
|
|
2157
|
-
if (!enableVite &&
|
|
2158
|
-
|
|
2159
|
-
|
|
2497
|
+
await refreshWorkerOnlySurfaceState();
|
|
2498
|
+
if (!enableVite && hasWorkerSurfacePaths(mainWorkerSurfacePaths)) {
|
|
2499
|
+
const detectedWorkerHandlers = Object.entries(mainWorkerSurfacePaths).filter(([, surfacePath]) => !!surfacePath).map(([surfaceName, surfacePath]) => `${surfaceName}=${surfacePath}`).join(", ");
|
|
2500
|
+
logger?.info(`Worker handlers detected: ${detectedWorkerHandlers}`);
|
|
2160
2501
|
} else if (!enableVite) {
|
|
2161
|
-
logger?.warn("No local
|
|
2502
|
+
logger?.warn("No local worker handler entry was found for worker-only mode");
|
|
2162
2503
|
}
|
|
2163
2504
|
const remoteCheck = await checkRemoteBindingRequirements(config);
|
|
2164
2505
|
if (remoteCheck.hasRemoteBindings) {
|