devflare 1.0.0-next.34 → 1.0.0-next.36
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/LICENSE +21 -0
- package/LLM.md +2 -0
- package/README.md +5 -4
- package/bin/devflare.js +1 -1
- package/dist/{account-p9eywryw.js → account-4tgh03a8.js} +4 -6
- package/dist/bridge/client.d.ts +1 -1
- package/dist/bridge/v2/control-messages.d.ts +1 -6
- package/dist/bridge/v2/control-messages.d.ts.map +1 -1
- package/dist/bridge/v2/wire.d.ts +4 -4
- package/dist/bridge/v2/wire.d.ts.map +1 -1
- package/dist/browser.js +2340 -19
- package/dist/{build-8bn7frg8.js → build-rh7ghrgw.js} +7 -10
- package/dist/cli/index.js +1377 -6
- package/dist/cloudflare/index.js +2 -3
- package/dist/config-entry.js +1 -9
- package/dist/{config-gphd6wx7.js → config-ghk2bxj3.js} +3 -6
- package/dist/{deploy-vpcp41fw.js → deploy-6hzv76sh.js} +13 -17
- package/dist/{dev-9scy9gap.js → dev-mkhjdwh7.js} +971 -73
- package/dist/{doctor-swfeeqny.js → doctor-0ng5qrr1.js} +2 -3
- package/dist/{index-e7xn30bh.js → index-0cc05hzv.js} +1 -1
- package/dist/{index-t0r7evpw.js → index-293aqfy4.js} +7 -9
- package/dist/{index-3t0e51a0.js → index-2yv7w548.js} +1 -1
- package/dist/{index-ksqth51q.js → index-a8pnvg3r.js} +1 -1
- package/dist/{index-bhxj7ff6.js → index-e251zhns.js} +943 -117
- package/dist/{index-2xz4etye.js → index-hvtd936d.js} +3 -0
- package/dist/{index-13sfensw.js → index-kcp7y951.js} +2 -13
- package/dist/{index-w8c8tjc5.js → index-mek9msfv.js} +1 -4
- package/dist/{index-dem45895.js → index-mjvybn39.js} +1 -1
- package/dist/{index-x0wpeb3x.js → index-phcrvs6d.js} +112 -2
- package/dist/{index-b20xx2vk.js → index-se4kw8tm.js} +4 -6
- package/dist/{index-pd97hk2j.js → index-x8s9rwfh.js} +466 -19
- package/dist/index.js +1 -42
- package/dist/{login-7vyzeqy4.js → login-4h1sfsed.js} +4 -6
- package/dist/{previews-jfbectpc.js → previews-qr7c5j8h.js} +10 -14
- package/dist/{productions-49ct4kjm.js → productions-xpmd6bw8.js} +6 -9
- package/dist/{remote-6876k6mz.js → remote-kqcvhnfx.js} +100 -5
- package/dist/runtime/index.js +0 -63
- package/dist/{secrets-9ne7armr.js → secrets-6wbdsdek.js} +3 -5
- package/dist/sveltekit/index.js +0 -263
- package/dist/test/index.js +2 -4626
- package/dist/{types-zekmbebk.js → types-ahnnpphq.js} +6 -8
- package/dist/vite/index.js +7 -9
- package/dist/{worker-706g8qeh.js → worker-1yg72jxg.js} +6 -9
- package/package.json +5 -3
- package/dist/index-1trss579.js +0 -2299
- package/dist/index-34dejneh.js +0 -214
- package/dist/index-3z20d3kd.js +0 -1380
- package/dist/index-4xmtkg9g.js +0 -109
- package/dist/index-62b3gt2g.js +0 -12
- package/dist/index-97rfy7n2.js +0 -413
- package/dist/index-c4zd39tc.js +0 -155
- package/dist/index-dgx495pv.js +0 -1036
- package/dist/index-dm9q84c7.js +0 -360
- package/dist/index-pmnb7eke.js +0 -109
- package/dist/index-t23wq5js.js +0 -111
- package/dist/index-wxdcrzcp.js +0 -475
- package/dist/index-x2fd1361.js +0 -133
- package/dist/index-za0r01bx.js +0 -588
|
@@ -1,9 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildLocalSecretWrappedBindingConfig
|
|
3
|
+
} from "./index-kcp7y951.js";
|
|
4
|
+
import {
|
|
5
|
+
bundleWorkerEntry,
|
|
6
|
+
createDOBundler
|
|
7
|
+
} from "./index-mjvybn39.js";
|
|
1
8
|
import {
|
|
2
9
|
detectViteProject,
|
|
3
10
|
resolveViteMode,
|
|
4
11
|
stopSpawnedProcessTree,
|
|
5
12
|
waitForViteReady
|
|
6
13
|
} from "./index-zdd0d9qa.js";
|
|
14
|
+
import {
|
|
15
|
+
clearLocalSendEmailBindings,
|
|
16
|
+
setLocalSendEmailBindings
|
|
17
|
+
} from "./index-n4xkaymm.js";
|
|
7
18
|
import {
|
|
8
19
|
createCliTheme,
|
|
9
20
|
cyanBold,
|
|
@@ -12,25 +23,11 @@ import {
|
|
|
12
23
|
yellow
|
|
13
24
|
} from "./index-maw0jjnn.js";
|
|
14
25
|
import"./index-3t6rypgc.js";
|
|
15
|
-
import {
|
|
16
|
-
bundleWorkflowEntrypointScript
|
|
17
|
-
} from "./index-pmnb7eke.js";
|
|
18
|
-
import {
|
|
19
|
-
bundleWorkerEntry,
|
|
20
|
-
createDOBundler
|
|
21
|
-
} from "./index-dem45895.js";
|
|
22
26
|
import {
|
|
23
27
|
DEFAULT_EMAIL_ENTRY_FILES,
|
|
24
28
|
DEFAULT_FETCH_ENTRY_FILES,
|
|
25
29
|
DEFAULT_QUEUE_ENTRY_FILES,
|
|
26
30
|
DEFAULT_SCHEDULED_ENTRY_FILES,
|
|
27
|
-
hasWorkerSurfacePaths,
|
|
28
|
-
prepareComposedWorkerEntrypoint,
|
|
29
|
-
resolveEffectiveViteProject,
|
|
30
|
-
resolveWorkerSurfacePaths,
|
|
31
|
-
writeGeneratedViteConfig
|
|
32
|
-
} from "./index-bhxj7ff6.js";
|
|
33
|
-
import {
|
|
34
31
|
buildAiSearchInstancesConfig,
|
|
35
32
|
buildAiSearchNamespacesConfig,
|
|
36
33
|
buildArtifactsConfig,
|
|
@@ -50,55 +47,46 @@ import {
|
|
|
50
47
|
buildWorkflowsConfig,
|
|
51
48
|
discoverRoutes,
|
|
52
49
|
getRouteDirectoryCandidate,
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
hasWorkerSurfacePaths,
|
|
51
|
+
prepareComposedWorkerEntrypoint,
|
|
52
|
+
resolveEffectiveViteProject,
|
|
53
|
+
resolveServiceBindings,
|
|
54
|
+
resolveWorkerSurfacePaths,
|
|
55
|
+
writeGeneratedViteConfig
|
|
56
|
+
} from "./index-e251zhns.js";
|
|
55
57
|
import"./index-by27mzra.js";
|
|
56
|
-
import"./index-5dkjffqz.js";
|
|
57
|
-
import"./index-7m557715.js";
|
|
58
|
-
import"./index-p93cngdp.js";
|
|
59
58
|
import {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
} from "./index-za0r01bx.js";
|
|
64
|
-
import {
|
|
65
|
-
buildLocalBindingShimServiceConfig
|
|
66
|
-
} from "./index-dm9q84c7.js";
|
|
67
|
-
import {
|
|
68
|
-
buildLocalSecretWrappedBindingConfig
|
|
69
|
-
} from "./index-13sfensw.js";
|
|
70
|
-
import {
|
|
71
|
-
clearLocalSendEmailBindings,
|
|
72
|
-
setLocalSendEmailBindings
|
|
73
|
-
} from "./index-n4xkaymm.js";
|
|
59
|
+
DEFAULT_WORKFLOW_PATTERN,
|
|
60
|
+
findFiles
|
|
61
|
+
} from "./index-5dkjffqz.js";
|
|
74
62
|
import {
|
|
75
63
|
applyLocalDevVarsToConfig
|
|
76
|
-
} from "./index-
|
|
64
|
+
} from "./index-mek9msfv.js";
|
|
77
65
|
import {
|
|
66
|
+
EnvVarResolutionError,
|
|
67
|
+
getDevflareDotenvPaths,
|
|
78
68
|
getLocalD1DatabaseIdentifier,
|
|
79
69
|
getLocalKVNamespaceIdentifier,
|
|
80
70
|
getSingleBrowserBindingName,
|
|
81
71
|
loadConfig,
|
|
72
|
+
normalizeWorkflowBinding,
|
|
73
|
+
resolveConfigEnvVars,
|
|
82
74
|
resolveConfigPath
|
|
83
|
-
} from "./index-
|
|
75
|
+
} from "./index-x8s9rwfh.js";
|
|
84
76
|
import"./index-h7r11y4a.js";
|
|
85
77
|
import"./index-04wh9bxx.js";
|
|
86
|
-
import
|
|
87
|
-
|
|
88
|
-
getDevflareDotenvPaths,
|
|
89
|
-
resolveConfigEnvVars
|
|
90
|
-
} from "./index-wxdcrzcp.js";
|
|
91
|
-
import"./index-34dejneh.js";
|
|
78
|
+
import"./index-7m557715.js";
|
|
79
|
+
import"./index-p93cngdp.js";
|
|
92
80
|
import {
|
|
93
81
|
__require
|
|
94
82
|
} from "./index-37x76zdn.js";
|
|
95
83
|
|
|
96
84
|
// src/cli/commands/dev.ts
|
|
97
85
|
import { createConsola } from "consola";
|
|
98
|
-
import { relative, resolve as
|
|
86
|
+
import { relative as relative2, resolve as resolve8 } from "pathe";
|
|
99
87
|
|
|
100
88
|
// src/dev-server/server.ts
|
|
101
|
-
import { dirname as
|
|
89
|
+
import { dirname as dirname3, resolve as resolve7 } from "pathe";
|
|
102
90
|
|
|
103
91
|
// src/cli/wrangler-auth.ts
|
|
104
92
|
import { exec } from "node:child_process";
|
|
@@ -173,9 +161,106 @@ async function checkRemoteBindingRequirements(config) {
|
|
|
173
161
|
};
|
|
174
162
|
}
|
|
175
163
|
|
|
164
|
+
// src/workflows/local-workflow-entrypoints.ts
|
|
165
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
166
|
+
import { join, relative, resolve } from "pathe";
|
|
167
|
+
function findExportedClasses(code) {
|
|
168
|
+
const classes = [];
|
|
169
|
+
const classPattern = /export\s+class\s+(\w+)/g;
|
|
170
|
+
let match;
|
|
171
|
+
while ((match = classPattern.exec(code)) !== null) {
|
|
172
|
+
classes.push(match[1]);
|
|
173
|
+
}
|
|
174
|
+
return classes;
|
|
175
|
+
}
|
|
176
|
+
function toImportSpecifier(fromDir, filePath) {
|
|
177
|
+
const relativePath = relative(fromDir, filePath).replace(/\\/g, "/");
|
|
178
|
+
return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
|
|
179
|
+
}
|
|
180
|
+
async function discoverWorkflowClasses(config, configDir) {
|
|
181
|
+
const classToFilePath = new Map;
|
|
182
|
+
const workflowPatternConfig = config.files?.workflows;
|
|
183
|
+
const workflowPattern = typeof workflowPatternConfig === "string" ? workflowPatternConfig : DEFAULT_WORKFLOW_PATTERN;
|
|
184
|
+
if (workflowPatternConfig === false) {
|
|
185
|
+
return classToFilePath;
|
|
186
|
+
}
|
|
187
|
+
const files = await findFiles(workflowPattern, { cwd: configDir });
|
|
188
|
+
for (const filePath of files) {
|
|
189
|
+
try {
|
|
190
|
+
const code = await readFile(filePath, "utf-8");
|
|
191
|
+
for (const className of findExportedClasses(code)) {
|
|
192
|
+
classToFilePath.set(className, filePath);
|
|
193
|
+
}
|
|
194
|
+
} catch {}
|
|
195
|
+
}
|
|
196
|
+
return classToFilePath;
|
|
197
|
+
}
|
|
198
|
+
async function resolveLocalWorkflowEntrypoints(config, configDir) {
|
|
199
|
+
const workflows = config.bindings?.workflows;
|
|
200
|
+
if (!workflows || Object.keys(workflows).length === 0) {
|
|
201
|
+
return [];
|
|
202
|
+
}
|
|
203
|
+
const classToFilePath = await discoverWorkflowClasses(config, configDir);
|
|
204
|
+
const entrypoints = [];
|
|
205
|
+
for (const [bindingName, binding] of Object.entries(workflows)) {
|
|
206
|
+
const normalized = normalizeWorkflowBinding(binding);
|
|
207
|
+
if (normalized.scriptName) {
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
const scriptPath = classToFilePath.get(normalized.className);
|
|
211
|
+
if (!scriptPath) {
|
|
212
|
+
throw new Error(`Workflow binding ${bindingName} (className: '${normalized.className}') not found.
|
|
213
|
+
` + `Either set files.workflows to match the workflow source file, or set scriptName when the workflow lives in another worker.`);
|
|
214
|
+
}
|
|
215
|
+
entrypoints.push({
|
|
216
|
+
bindingName,
|
|
217
|
+
className: normalized.className,
|
|
218
|
+
scriptPath
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
return entrypoints;
|
|
222
|
+
}
|
|
223
|
+
function buildWorkflowVirtualEntry(entrypoints, entryDir) {
|
|
224
|
+
const imports = entrypoints.map((entrypoint, index) => {
|
|
225
|
+
const importName = `__DevflareWorkflow${index}`;
|
|
226
|
+
const importPath = toImportSpecifier(entryDir, entrypoint.scriptPath);
|
|
227
|
+
return {
|
|
228
|
+
importName,
|
|
229
|
+
className: entrypoint.className,
|
|
230
|
+
line: `import { ${entrypoint.className} as ${importName} } from '${importPath}'`
|
|
231
|
+
};
|
|
232
|
+
});
|
|
233
|
+
const exports = imports.map((entrypoint) => {
|
|
234
|
+
return `export { ${entrypoint.importName} as ${entrypoint.className} }`;
|
|
235
|
+
});
|
|
236
|
+
return [...imports.map((entrypoint) => entrypoint.line), "", ...exports].join(`
|
|
237
|
+
`);
|
|
238
|
+
}
|
|
239
|
+
async function bundleWorkflowEntrypointScript(config, configDir, options = {}) {
|
|
240
|
+
const entrypoints = await resolveLocalWorkflowEntrypoints(config, configDir);
|
|
241
|
+
if (entrypoints.length === 0) {
|
|
242
|
+
return "";
|
|
243
|
+
}
|
|
244
|
+
const entryDir = resolve(configDir, ".devflare", "workflow-entrypoints");
|
|
245
|
+
const entryPath = join(entryDir, "__entry.ts");
|
|
246
|
+
const outFile = join(entryDir, "index.js");
|
|
247
|
+
await mkdir(entryDir, { recursive: true });
|
|
248
|
+
await writeFile(entryPath, buildWorkflowVirtualEntry(entrypoints, entryDir));
|
|
249
|
+
await bundleWorkerEntry({
|
|
250
|
+
cwd: configDir,
|
|
251
|
+
inputFile: entryPath,
|
|
252
|
+
outFile,
|
|
253
|
+
rolldownOptions: config.rolldown?.options,
|
|
254
|
+
sourcemap: config.rolldown?.sourcemap,
|
|
255
|
+
minify: config.rolldown?.minify,
|
|
256
|
+
logger: options.logger
|
|
257
|
+
});
|
|
258
|
+
return await readFile(outFile, "utf-8");
|
|
259
|
+
}
|
|
260
|
+
|
|
176
261
|
// src/dev-server/d1-migrations.ts
|
|
177
262
|
import { createHash } from "node:crypto";
|
|
178
|
-
import { resolve } from "pathe";
|
|
263
|
+
import { resolve as resolve2 } from "pathe";
|
|
179
264
|
var MIGRATION_RETRY_DELAYS_MS = [500, 1000, 1500, 2000];
|
|
180
265
|
function collectMigrationStatements(sql) {
|
|
181
266
|
const cleanedSql = sql.split(`
|
|
@@ -238,7 +323,7 @@ async function runD1Migrations(options) {
|
|
|
238
323
|
return;
|
|
239
324
|
}
|
|
240
325
|
const { existsSync, readdirSync, readFileSync, statSync } = await import("node:fs");
|
|
241
|
-
const migrationsDir =
|
|
326
|
+
const migrationsDir = resolve2(cwd, "migrations");
|
|
242
327
|
if (!existsSync(migrationsDir)) {
|
|
243
328
|
logger?.debug("No migrations/ directory found, skipping D1 migrations");
|
|
244
329
|
return;
|
|
@@ -248,7 +333,7 @@ async function runD1Migrations(options) {
|
|
|
248
333
|
if (sharedFiles.length > 0) {
|
|
249
334
|
sharedFileEntries = [];
|
|
250
335
|
for (const file of sharedFiles) {
|
|
251
|
-
const sql = readFileSync(
|
|
336
|
+
const sql = readFileSync(resolve2(migrationsDir, file), "utf-8");
|
|
252
337
|
const fileStatements = collectMigrationStatements(sql);
|
|
253
338
|
sharedFileEntries.push({
|
|
254
339
|
filename: file,
|
|
@@ -259,7 +344,7 @@ async function runD1Migrations(options) {
|
|
|
259
344
|
}
|
|
260
345
|
}
|
|
261
346
|
for (const [bindingName] of Object.entries(config.bindings.d1)) {
|
|
262
|
-
const perBindingDir =
|
|
347
|
+
const perBindingDir = resolve2(migrationsDir, bindingName);
|
|
263
348
|
const hasPerBindingDir = existsSync(perBindingDir) && statSync(perBindingDir).isDirectory();
|
|
264
349
|
let files = [];
|
|
265
350
|
let sourceLabel = "";
|
|
@@ -270,7 +355,7 @@ async function runD1Migrations(options) {
|
|
|
270
355
|
continue;
|
|
271
356
|
}
|
|
272
357
|
for (const file of perBindingFiles) {
|
|
273
|
-
const sql = readFileSync(
|
|
358
|
+
const sql = readFileSync(resolve2(perBindingDir, file), "utf-8");
|
|
274
359
|
const fileStatements = collectMigrationStatements(sql);
|
|
275
360
|
files.push({
|
|
276
361
|
filename: file,
|
|
@@ -303,6 +388,819 @@ async function runD1Migrations(options) {
|
|
|
303
388
|
}
|
|
304
389
|
}
|
|
305
390
|
|
|
391
|
+
// src/dev-server/miniflare-log.ts
|
|
392
|
+
var ANSI_ESCAPE_REGEX = /\u001B\[[0-9;]*m/g;
|
|
393
|
+
var COMPATIBILITY_DATE_FALLBACK_REGEX = /^The latest compatibility date supported by the installed Cloudflare Workers Runtime is "([^"]+)", but you've requested "([^"]+)"\. Falling back to "([^"]+)"\.\.\.$/;
|
|
394
|
+
var MINIFLARE_LOG_LEVEL_FALLBACKS = {
|
|
395
|
+
WARN: 2,
|
|
396
|
+
DEBUG: 4
|
|
397
|
+
};
|
|
398
|
+
function normalizeMiniflareMessage(message) {
|
|
399
|
+
return message.replace(ANSI_ESCAPE_REGEX, "").replace(/\s+/g, " ").trim();
|
|
400
|
+
}
|
|
401
|
+
function formatCompatibilityDateFallbackNotice(message) {
|
|
402
|
+
const normalizedMessage = normalizeMiniflareMessage(message);
|
|
403
|
+
const match = COMPATIBILITY_DATE_FALLBACK_REGEX.exec(normalizedMessage);
|
|
404
|
+
if (!match) {
|
|
405
|
+
return null;
|
|
406
|
+
}
|
|
407
|
+
const [, _supportedDate, requestedDate, fallbackDate] = match;
|
|
408
|
+
return `Using latest supported Cloudflare Workers Runtime compatibility date ${fallbackDate} (requested ${requestedDate})`;
|
|
409
|
+
}
|
|
410
|
+
function resolveMiniflareLogLevel(logLevel, levelName) {
|
|
411
|
+
return logLevel?.[levelName] ?? MINIFLARE_LOG_LEVEL_FALLBACKS[levelName];
|
|
412
|
+
}
|
|
413
|
+
function createMiniflareLog(BaseLog, logLevel, levelName, logger) {
|
|
414
|
+
if (!BaseLog) {
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
return createCompatibilityAwareMiniflareLog(BaseLog, resolveMiniflareLogLevel(logLevel, levelName), logger);
|
|
418
|
+
}
|
|
419
|
+
function createCompatibilityAwareMiniflareLog(BaseLog, level, logger) {
|
|
420
|
+
const log = new BaseLog(level);
|
|
421
|
+
const originalWarn = log.warn.bind(log);
|
|
422
|
+
const originalInfo = log.info.bind(log);
|
|
423
|
+
log.warn = (message) => {
|
|
424
|
+
const notice = formatCompatibilityDateFallbackNotice(message);
|
|
425
|
+
if (!notice) {
|
|
426
|
+
originalWarn(message);
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
if (logger) {
|
|
430
|
+
logger.info(notice);
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
originalInfo(notice);
|
|
434
|
+
};
|
|
435
|
+
return log;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// src/shims/local-media-bindings.ts
|
|
439
|
+
var LOCAL_MEDIA_BINDING_SCRIPT = `
|
|
440
|
+
import { RpcTarget, WorkerEntrypoint } from 'cloudflare:workers'
|
|
441
|
+
|
|
442
|
+
function emptyStream() {
|
|
443
|
+
return new ReadableStream({
|
|
444
|
+
start(controller) {
|
|
445
|
+
controller.close()
|
|
446
|
+
}
|
|
447
|
+
})
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
function normalizeContentType(value, fallback) {
|
|
451
|
+
if (typeof value !== 'string' || value.length === 0) return fallback
|
|
452
|
+
if (value.includes('/')) return value
|
|
453
|
+
if (value === 'jpg') return 'image/jpeg'
|
|
454
|
+
if (value === 'jpeg') return 'image/jpeg'
|
|
455
|
+
if (value === 'png') return 'image/png'
|
|
456
|
+
if (value === 'webp') return 'image/webp'
|
|
457
|
+
if (value === 'avif') return 'image/avif'
|
|
458
|
+
if (value === 'mp4') return 'video/mp4'
|
|
459
|
+
return value
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
async function readBytes(stream) {
|
|
463
|
+
return await new Response(stream || emptyStream()).arrayBuffer()
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function streamFromBytes(bytes) {
|
|
467
|
+
return new Response(bytes.slice(0)).body || emptyStream()
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function createBodyReader(bytes) {
|
|
471
|
+
return function takeBody() {
|
|
472
|
+
return streamFromBytes(bytes)
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
class LocalImageResult extends RpcTarget {
|
|
477
|
+
constructor(takeBody, contentType) {
|
|
478
|
+
super()
|
|
479
|
+
this.takeBody = takeBody
|
|
480
|
+
this.contentTypeValue = contentType
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
response() {
|
|
484
|
+
return new Response(this.takeBody(), {
|
|
485
|
+
headers: { 'Content-Type': this.contentTypeValue }
|
|
486
|
+
})
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
contentType() {
|
|
490
|
+
return this.contentTypeValue
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
image() {
|
|
494
|
+
return this.takeBody()
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
class LocalImageTransformer extends RpcTarget {
|
|
499
|
+
constructor(bytes) {
|
|
500
|
+
super()
|
|
501
|
+
this.takeBody = createBodyReader(bytes)
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
transform() {
|
|
505
|
+
return this
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
draw() {
|
|
509
|
+
return this
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
async output(options = {}) {
|
|
513
|
+
return new LocalImageResult(
|
|
514
|
+
this.takeBody,
|
|
515
|
+
normalizeContentType(options.format, 'image/png')
|
|
516
|
+
)
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
function createHostedImagesBinding() {
|
|
521
|
+
const unsupported = () => {
|
|
522
|
+
throw new Error('Devflare local Images hosted API is not implemented. Use the transform/info APIs locally, or connect to Cloudflare for hosted image storage.')
|
|
523
|
+
}
|
|
524
|
+
return {
|
|
525
|
+
image() {
|
|
526
|
+
return {
|
|
527
|
+
details: unsupported,
|
|
528
|
+
bytes: unsupported,
|
|
529
|
+
update: unsupported,
|
|
530
|
+
delete: unsupported
|
|
531
|
+
}
|
|
532
|
+
},
|
|
533
|
+
upload: unsupported,
|
|
534
|
+
list: unsupported
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
export class LocalImagesBinding extends WorkerEntrypoint {
|
|
539
|
+
async info(stream) {
|
|
540
|
+
const bytes = await readBytes(stream)
|
|
541
|
+
return {
|
|
542
|
+
format: 'image/png',
|
|
543
|
+
fileSize: bytes.byteLength,
|
|
544
|
+
width: 1,
|
|
545
|
+
height: 1
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
async input(stream) {
|
|
550
|
+
return new LocalImageTransformer(await readBytes(stream))
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
get hosted() {
|
|
554
|
+
return createHostedImagesBinding()
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
class LocalMediaResult extends RpcTarget {
|
|
559
|
+
constructor(takeBody, contentType) {
|
|
560
|
+
super()
|
|
561
|
+
this.takeBody = takeBody
|
|
562
|
+
this.contentTypeValue = contentType
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
async media() {
|
|
566
|
+
return this.takeBody()
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
async response() {
|
|
570
|
+
return new Response(this.takeBody(), {
|
|
571
|
+
headers: { 'Content-Type': this.contentTypeValue }
|
|
572
|
+
})
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
async contentType() {
|
|
576
|
+
return this.contentTypeValue
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
class LocalMediaTransformationGenerator extends RpcTarget {
|
|
581
|
+
constructor(takeBody) {
|
|
582
|
+
super()
|
|
583
|
+
this.takeBody = takeBody
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
output(options = {}) {
|
|
587
|
+
return new LocalMediaResult(
|
|
588
|
+
this.takeBody,
|
|
589
|
+
normalizeContentType(options.format, 'video/mp4')
|
|
590
|
+
)
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
class LocalMediaTransformer extends RpcTarget {
|
|
595
|
+
constructor(bytes) {
|
|
596
|
+
super()
|
|
597
|
+
this.takeBody = createBodyReader(bytes)
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
transform() {
|
|
601
|
+
return new LocalMediaTransformationGenerator(this.takeBody)
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
output(options = {}) {
|
|
605
|
+
return new LocalMediaResult(
|
|
606
|
+
this.takeBody,
|
|
607
|
+
normalizeContentType(options.format, 'video/mp4')
|
|
608
|
+
)
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
export class LocalMediaBinding extends WorkerEntrypoint {
|
|
613
|
+
async input(stream) {
|
|
614
|
+
return new LocalMediaTransformer(await readBytes(stream))
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
export default {
|
|
619
|
+
fetch() {
|
|
620
|
+
return new Response('Devflare local Images/Media binding')
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
`;
|
|
624
|
+
function toLocalShimWorkerName(kind, bindingName, index) {
|
|
625
|
+
const slug = bindingName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || kind;
|
|
626
|
+
return `devflare-local-${kind}-${index}-${slug}`;
|
|
627
|
+
}
|
|
628
|
+
function getEntrypoint(kind) {
|
|
629
|
+
return kind === "images" ? "LocalImagesBinding" : "LocalMediaBinding";
|
|
630
|
+
}
|
|
631
|
+
function buildLocalBindingShimServiceConfig(config) {
|
|
632
|
+
const entries = [
|
|
633
|
+
...Object.keys(config.bindings?.images ?? {}).map((bindingName) => ({
|
|
634
|
+
bindingName,
|
|
635
|
+
kind: "images"
|
|
636
|
+
})),
|
|
637
|
+
...Object.keys(config.bindings?.media ?? {}).map((bindingName) => ({
|
|
638
|
+
bindingName,
|
|
639
|
+
kind: "media"
|
|
640
|
+
}))
|
|
641
|
+
];
|
|
642
|
+
return {
|
|
643
|
+
localBindingNames: entries.map((entry) => entry.bindingName),
|
|
644
|
+
serviceBindings: Object.fromEntries(entries.map((entry, index) => {
|
|
645
|
+
const workerName = toLocalShimWorkerName(entry.kind, entry.bindingName, index);
|
|
646
|
+
return [
|
|
647
|
+
entry.bindingName,
|
|
648
|
+
{
|
|
649
|
+
name: workerName,
|
|
650
|
+
entrypoint: getEntrypoint(entry.kind)
|
|
651
|
+
}
|
|
652
|
+
];
|
|
653
|
+
})),
|
|
654
|
+
workers: entries.map((entry, index) => ({
|
|
655
|
+
name: toLocalShimWorkerName(entry.kind, entry.bindingName, index),
|
|
656
|
+
modules: true,
|
|
657
|
+
script: LOCAL_MEDIA_BINDING_SCRIPT,
|
|
658
|
+
compatibilityDate: config.compatibilityDate ?? "2025-01-01",
|
|
659
|
+
...config.compatibilityFlags && { compatibilityFlags: config.compatibilityFlags }
|
|
660
|
+
}))
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// src/bridge/gateway-runtime.ts
|
|
665
|
+
var GATEWAY_RUNTIME_JS = `
|
|
666
|
+
const RAW_EMAIL = 'EmailMessage::raw'
|
|
667
|
+
|
|
668
|
+
function arrayBufferToBase64(buffer) {
|
|
669
|
+
const bytes = new Uint8Array(buffer)
|
|
670
|
+
let binary = ''
|
|
671
|
+
for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i])
|
|
672
|
+
return btoa(binary)
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
function base64ToArrayBuffer(base64) {
|
|
676
|
+
const binary = atob(base64)
|
|
677
|
+
const bytes = new Uint8Array(binary.length)
|
|
678
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i)
|
|
679
|
+
return bytes.buffer
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
function serializeR2Object(obj) {
|
|
683
|
+
if (!obj) return null
|
|
684
|
+
return {
|
|
685
|
+
__type: 'R2Object',
|
|
686
|
+
key: obj.key,
|
|
687
|
+
version: obj.version,
|
|
688
|
+
size: obj.size,
|
|
689
|
+
etag: obj.etag,
|
|
690
|
+
httpEtag: obj.httpEtag,
|
|
691
|
+
checksums: obj.checksums,
|
|
692
|
+
uploaded: obj.uploaded?.toISOString(),
|
|
693
|
+
httpMetadata: obj.httpMetadata,
|
|
694
|
+
customMetadata: obj.customMetadata,
|
|
695
|
+
range: obj.range,
|
|
696
|
+
storageClass: obj.storageClass
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
function serializeR2ObjectBody(obj, bodyData) {
|
|
701
|
+
if (!obj) return null
|
|
702
|
+
return {
|
|
703
|
+
__type: 'R2ObjectBody',
|
|
704
|
+
key: obj.key,
|
|
705
|
+
version: obj.version,
|
|
706
|
+
size: obj.size,
|
|
707
|
+
etag: obj.etag,
|
|
708
|
+
httpEtag: obj.httpEtag,
|
|
709
|
+
checksums: obj.checksums,
|
|
710
|
+
uploaded: obj.uploaded?.toISOString(),
|
|
711
|
+
httpMetadata: obj.httpMetadata,
|
|
712
|
+
customMetadata: obj.customMetadata,
|
|
713
|
+
range: obj.range,
|
|
714
|
+
storageClass: obj.storageClass,
|
|
715
|
+
bodyData
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
function serializeR2Objects(result) {
|
|
720
|
+
if (!result) return null
|
|
721
|
+
return {
|
|
722
|
+
objects: result.objects.map(serializeR2Object),
|
|
723
|
+
truncated: result.truncated,
|
|
724
|
+
cursor: result.cursor,
|
|
725
|
+
delimitedPrefixes: result.delimitedPrefixes
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
async function serializeResponse(response) {
|
|
730
|
+
let body = null
|
|
731
|
+
if (response.body) {
|
|
732
|
+
const bytes = await response.arrayBuffer()
|
|
733
|
+
if (bytes.byteLength > 0) {
|
|
734
|
+
body = { type: 'bytes', data: arrayBufferToBase64(bytes) }
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
return {
|
|
738
|
+
status: response.status,
|
|
739
|
+
statusText: response.statusText,
|
|
740
|
+
headers: [...response.headers.entries()],
|
|
741
|
+
body
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
function deserializeRequest(serializedReq) {
|
|
746
|
+
return new Request(serializedReq.url, {
|
|
747
|
+
method: serializedReq.method,
|
|
748
|
+
headers: serializedReq.headers,
|
|
749
|
+
body: serializedReq.body?.type === 'bytes'
|
|
750
|
+
? base64ToArrayBuffer(serializedReq.body.data)
|
|
751
|
+
: undefined,
|
|
752
|
+
redirect: serializedReq.redirect
|
|
753
|
+
})
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
function createEmailMessageRaw(raw) {
|
|
757
|
+
if (typeof raw === 'string' || raw instanceof ReadableStream) {
|
|
758
|
+
return raw
|
|
759
|
+
}
|
|
760
|
+
if (raw instanceof ArrayBuffer || raw instanceof Uint8Array) {
|
|
761
|
+
return new Response(raw).body
|
|
762
|
+
}
|
|
763
|
+
throw new Error('Unsupported EmailMessage raw payload')
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
function isDurableObjectNamespace(binding) {
|
|
767
|
+
return !!binding
|
|
768
|
+
&& typeof binding.idFromName === 'function'
|
|
769
|
+
&& typeof binding.idFromString === 'function'
|
|
770
|
+
&& typeof binding.newUniqueId === 'function'
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/**
|
|
774
|
+
* Resolve a (possibly jurisdiction-scoped) DurableObjectNamespace.
|
|
775
|
+
* Forwards the jurisdiction faithfully when the binding supports it
|
|
776
|
+
* (real Cloudflare), and degrades to the plain binding otherwise
|
|
777
|
+
* (miniflare/workerd local emulation, which does not pin jurisdictions).
|
|
778
|
+
*/
|
|
779
|
+
function resolveDoNamespace(binding, jurisdiction) {
|
|
780
|
+
if (jurisdiction && typeof binding.jurisdiction === 'function') {
|
|
781
|
+
return binding.jurisdiction(jurisdiction)
|
|
782
|
+
}
|
|
783
|
+
return binding
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
/**
|
|
787
|
+
* Execute an RPC method against the gateway's bindings.
|
|
788
|
+
*
|
|
789
|
+
* Method format: "binding.operation". Operations must be namespaced by
|
|
790
|
+
* binding kind (e.g. "kv.get", "r2.head", "d1.stmt.first", "do.fetch",
|
|
791
|
+
* "service.fetch", "queue.send", "email.send", "ai.run"). Bare verbs and the legacy
|
|
792
|
+
* "stmt.*" / "stub.*" sub-prefixes were removed in B3-final and now throw.
|
|
793
|
+
* Method vocabulary must stay in sync with the canonical server in
|
|
794
|
+
* src/bridge/server.ts.
|
|
795
|
+
*/
|
|
796
|
+
async function executeRpcMethod(method, params, env, _ctx) {
|
|
797
|
+
const parts = method.split('.')
|
|
798
|
+
if (parts.length < 2) throw new Error('Invalid method format: ' + method)
|
|
799
|
+
|
|
800
|
+
const bindingName = parts[0]
|
|
801
|
+
const operation = parts.slice(1).join('.')
|
|
802
|
+
const binding = env[bindingName]
|
|
803
|
+
|
|
804
|
+
if (!binding) throw new Error('Binding not found: ' + bindingName)
|
|
805
|
+
|
|
806
|
+
const isNamespaced =
|
|
807
|
+
operation.indexOf('kv.') === 0 ||
|
|
808
|
+
operation.indexOf('r2.') === 0 ||
|
|
809
|
+
operation.indexOf('d1.') === 0 ||
|
|
810
|
+
operation.indexOf('do.') === 0 ||
|
|
811
|
+
operation.indexOf('service.') === 0 ||
|
|
812
|
+
operation.indexOf('queue.') === 0 ||
|
|
813
|
+
operation.indexOf('email.') === 0 ||
|
|
814
|
+
operation.indexOf('ai.') === 0 ||
|
|
815
|
+
operation.indexOf('workflow.') === 0 ||
|
|
816
|
+
operation.indexOf('var.') === 0
|
|
817
|
+
if (!isNamespaced) {
|
|
818
|
+
throw new Error(createUnsupportedBridgeOperationErrorMessage(bindingName, operation))
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// KV
|
|
822
|
+
if (operation === 'kv.get') return binding.get(params[0], params[1])
|
|
823
|
+
if (operation === 'kv.put') return binding.put(params[0], params[1], params[2])
|
|
824
|
+
if (operation === 'kv.delete') return binding.delete(params[0])
|
|
825
|
+
if (operation === 'kv.list') return binding.list(params[0])
|
|
826
|
+
if (operation === 'kv.getWithMetadata') return binding.getWithMetadata(params[0], params[1])
|
|
827
|
+
|
|
828
|
+
// DO get (returns DOStub reference)
|
|
829
|
+
if (operation === 'do.get') {
|
|
830
|
+
return { __type: 'DOStub', binding: bindingName, id: params[0] }
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
// R2
|
|
834
|
+
if (operation === 'r2.head') return serializeR2Object(await binding.head(params[0]))
|
|
835
|
+
if (operation === 'r2.get') {
|
|
836
|
+
const obj = await binding.get(params[0], params[1])
|
|
837
|
+
if (!obj) return null
|
|
838
|
+
const body = await obj.arrayBuffer()
|
|
839
|
+
return serializeR2ObjectBody(obj, arrayBufferToBase64(body))
|
|
840
|
+
}
|
|
841
|
+
if (operation === 'r2.put') {
|
|
842
|
+
let value = params[1]
|
|
843
|
+
if (value && typeof value === 'object') {
|
|
844
|
+
if (value.__type === 'ArrayBuffer' || value.__type === 'Uint8Array') {
|
|
845
|
+
value = base64ToArrayBuffer(value.data)
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
return serializeR2Object(await binding.put(params[0], value, params[2]))
|
|
849
|
+
}
|
|
850
|
+
if (operation === 'r2.delete') return binding.delete(params[0])
|
|
851
|
+
if (operation === 'r2.list') return serializeR2Objects(await binding.list(params[0]))
|
|
852
|
+
|
|
853
|
+
// D1
|
|
854
|
+
if (operation === 'd1.exec') return binding.exec(params[0])
|
|
855
|
+
if (operation === 'd1.batch') {
|
|
856
|
+
const statements = params[0].map((s) => binding.prepare(s.sql).bind(...(s.bindings || [])))
|
|
857
|
+
return binding.batch(statements)
|
|
858
|
+
}
|
|
859
|
+
if (operation.indexOf('d1.stmt.') === 0) {
|
|
860
|
+
const mode = operation.split('.')[2]
|
|
861
|
+
const [sql, ...rest] = params
|
|
862
|
+
let bindings = rest
|
|
863
|
+
let extraParam
|
|
864
|
+
if (mode === 'first' || mode === 'raw') {
|
|
865
|
+
extraParam = rest[rest.length - 1]
|
|
866
|
+
bindings = rest.slice(0, -1)
|
|
867
|
+
}
|
|
868
|
+
let stmt = binding.prepare(sql)
|
|
869
|
+
if (bindings.length > 0) stmt = stmt.bind(...bindings)
|
|
870
|
+
if (mode === 'first') {
|
|
871
|
+
if (typeof extraParam === 'string' && extraParam.length > 0) return stmt.first(extraParam)
|
|
872
|
+
return stmt.first()
|
|
873
|
+
}
|
|
874
|
+
if (mode === 'all') return stmt.all()
|
|
875
|
+
if (mode === 'run') return stmt.run()
|
|
876
|
+
if (mode === 'raw') return stmt.raw(extraParam)
|
|
877
|
+
throw new Error('Unknown stmt mode: ' + mode)
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
// Durable Objects
|
|
881
|
+
if (operation === 'do.idFromName') {
|
|
882
|
+
const ns = resolveDoNamespace(binding, params[1])
|
|
883
|
+
const id = ns.idFromName(params[0])
|
|
884
|
+
return { __type: 'DOId', hex: id.toString() }
|
|
885
|
+
}
|
|
886
|
+
if (operation === 'do.idFromString') {
|
|
887
|
+
const id = binding.idFromString(params[0])
|
|
888
|
+
return { __type: 'DOId', hex: id.toString() }
|
|
889
|
+
}
|
|
890
|
+
if (operation === 'do.newUniqueId') {
|
|
891
|
+
const ns = resolveDoNamespace(binding, params[1])
|
|
892
|
+
const id = ns.newUniqueId(params[0])
|
|
893
|
+
return { __type: 'DOId', hex: id.toString() }
|
|
894
|
+
}
|
|
895
|
+
if (operation === 'do.fetch') {
|
|
896
|
+
const [, serializedId, serializedReq] = params
|
|
897
|
+
const id = binding.idFromString(serializedId.hex)
|
|
898
|
+
const stub = binding.get(id)
|
|
899
|
+
const response = await stub.fetch(new Request(serializedReq.url, {
|
|
900
|
+
method: serializedReq.method,
|
|
901
|
+
headers: serializedReq.headers,
|
|
902
|
+
body: serializedReq.body?.type === 'bytes'
|
|
903
|
+
? base64ToArrayBuffer(serializedReq.body.data)
|
|
904
|
+
: undefined
|
|
905
|
+
}))
|
|
906
|
+
return serializeResponse(response)
|
|
907
|
+
}
|
|
908
|
+
if (operation === 'do.rpc') {
|
|
909
|
+
const [, serializedId, methodName, args] = params
|
|
910
|
+
const id = binding.idFromString(serializedId.hex)
|
|
911
|
+
const stub = binding.get(id)
|
|
912
|
+
const response = await stub.fetch(new Request('http://do/_rpc', {
|
|
913
|
+
method: 'POST',
|
|
914
|
+
headers: { 'Content-Type': 'application/json' },
|
|
915
|
+
body: JSON.stringify({ method: methodName, params: args })
|
|
916
|
+
}))
|
|
917
|
+
const result = await response.json()
|
|
918
|
+
if (!result.ok) throw new Error(result.error?.message || 'RPC failed')
|
|
919
|
+
return result.result
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
// Service Bindings
|
|
923
|
+
if (operation === 'service.fetch') {
|
|
924
|
+
if (!binding || typeof binding.fetch !== 'function') {
|
|
925
|
+
throw new Error('Service binding ' + bindingName + ' does not support fetch()')
|
|
926
|
+
}
|
|
927
|
+
const response = await binding.fetch(deserializeRequest(params[0]))
|
|
928
|
+
return serializeResponse(response)
|
|
929
|
+
}
|
|
930
|
+
if (operation === 'service.rpc') {
|
|
931
|
+
const methodName = params[0]
|
|
932
|
+
if (typeof methodName !== 'string') {
|
|
933
|
+
throw new Error('Service binding ' + bindingName + ' RPC method name must be a string')
|
|
934
|
+
}
|
|
935
|
+
const args = Array.isArray(params[1]) ? params[1] : []
|
|
936
|
+
const method = binding && binding[methodName]
|
|
937
|
+
if (typeof method !== 'function') {
|
|
938
|
+
throw new Error('Service binding ' + bindingName + ' does not support ' + methodName + '()')
|
|
939
|
+
}
|
|
940
|
+
return method.apply(binding, args)
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
// Queues
|
|
944
|
+
if (operation === 'queue.send') return binding.send(params[0], params[1])
|
|
945
|
+
if (operation === 'queue.sendBatch') return binding.sendBatch(params[0], params[1])
|
|
946
|
+
|
|
947
|
+
// Send Email
|
|
948
|
+
if (operation === 'email.send') {
|
|
949
|
+
if (binding && typeof binding.send === 'function') {
|
|
950
|
+
const message = params[0]
|
|
951
|
+
if (message && typeof message === 'object' && 'from' in message && 'to' in message && 'raw' in message) {
|
|
952
|
+
return binding.send({
|
|
953
|
+
from: message.from,
|
|
954
|
+
to: message.to,
|
|
955
|
+
[RAW_EMAIL]: createEmailMessageRaw(message.raw)
|
|
956
|
+
})
|
|
957
|
+
}
|
|
958
|
+
return binding.send(message)
|
|
959
|
+
}
|
|
960
|
+
return { ok: true, simulated: true }
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
// Workflows
|
|
964
|
+
if (operation === 'workflow.create') {
|
|
965
|
+
return serializeWorkflowInstance(await binding.create(params[0]))
|
|
966
|
+
}
|
|
967
|
+
if (operation === 'workflow.get') {
|
|
968
|
+
return serializeWorkflowInstance(await binding.get(params[0]))
|
|
969
|
+
}
|
|
970
|
+
if (operation === 'workflow.status') {
|
|
971
|
+
return (await binding.get(params[0])).status()
|
|
972
|
+
}
|
|
973
|
+
if (operation === 'workflow.pause') {
|
|
974
|
+
return (await binding.get(params[0])).pause()
|
|
975
|
+
}
|
|
976
|
+
if (operation === 'workflow.resume') {
|
|
977
|
+
return (await binding.get(params[0])).resume()
|
|
978
|
+
}
|
|
979
|
+
if (operation === 'workflow.terminate') {
|
|
980
|
+
return (await binding.get(params[0])).terminate()
|
|
981
|
+
}
|
|
982
|
+
if (operation === 'workflow.restart') {
|
|
983
|
+
return (await binding.get(params[0])).restart()
|
|
984
|
+
}
|
|
985
|
+
if (operation === 'workflow.sendEvent') {
|
|
986
|
+
return (await binding.get(params[0])).sendEvent(params[1])
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
// AI / generic run()
|
|
990
|
+
if (operation === 'ai.run') {
|
|
991
|
+
if (typeof binding.run !== 'function') {
|
|
992
|
+
throw new Error('Binding ' + bindingName + ' does not support run(): ' + method)
|
|
993
|
+
}
|
|
994
|
+
return binding.run(params[0], params[1])
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
throw new Error('Unknown operation: ' + method)
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
function createUnsupportedBridgeOperationErrorMessage(bindingName, operation) {
|
|
1001
|
+
const base = "[devflare][bridge] Unsupported bridge operation '" + operation + "' for binding '" + bindingName + "'."
|
|
1002
|
+
if (operation === 'fetch') {
|
|
1003
|
+
return base + ' Devflare could not dispatch fetch() for this binding through the local bridge. '
|
|
1004
|
+
+ 'Expected Cloudflare API: env.' + bindingName + '.fetch(request). '
|
|
1005
|
+
+ 'If this came from SvelteKit platform.env, make sure the binding is declared as a service binding; '
|
|
1006
|
+
+ 'this is a Devflare local bridge issue when service bindings fall back to a bare fetch operation.'
|
|
1007
|
+
}
|
|
1008
|
+
if (operation === 'toString') {
|
|
1009
|
+
return base + ' A platform.env value was coerced to a string through the bridge. '
|
|
1010
|
+
+ 'For SvelteKit local dev, declared vars should be plain string values and missing env names should read as undefined.'
|
|
1011
|
+
}
|
|
1012
|
+
return base + ' Bare verbs and the legacy stmt.*/stub.* sub-prefixes are not supported; '
|
|
1013
|
+
+ 'use the namespaced form (e.g. kv.get, r2.put, d1.stmt.first, do.fetch, service.fetch).'
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
function serializeWorkflowInstance(instance) {
|
|
1017
|
+
return {
|
|
1018
|
+
__type: 'WorkflowInstance',
|
|
1019
|
+
id: instance.id
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
// ---------------------------------------------------------------------------
|
|
1024
|
+
// WebSocket bridge (shared with src/bridge/server.ts in shape)
|
|
1025
|
+
// ---------------------------------------------------------------------------
|
|
1026
|
+
// NOTE: wsProxies is intentionally created per handleBridgeWebSocket call so
|
|
1027
|
+
// state never leaks across connections or across gateway-script regenerations.
|
|
1028
|
+
|
|
1029
|
+
async function handleBridgeRpcCall(msg, ws, env, ctx) {
|
|
1030
|
+
try {
|
|
1031
|
+
const result = await executeRpcMethod(msg.method, msg.params, env, ctx)
|
|
1032
|
+
ws.send(JSON.stringify({ t: 'rpc.ok', id: msg.id, result }))
|
|
1033
|
+
} catch (error) {
|
|
1034
|
+
ws.send(JSON.stringify({
|
|
1035
|
+
t: 'rpc.err',
|
|
1036
|
+
id: msg.id,
|
|
1037
|
+
error: {
|
|
1038
|
+
code: error?.code || 'INTERNAL_ERROR',
|
|
1039
|
+
message: error?.message || String(error)
|
|
1040
|
+
}
|
|
1041
|
+
}))
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
async function handleBridgeWsOpen(msg, ws, env, wsProxies) {
|
|
1046
|
+
try {
|
|
1047
|
+
const binding = env[msg.target.binding]
|
|
1048
|
+
const id = binding.idFromString(msg.target.id)
|
|
1049
|
+
const stub = binding.get(id)
|
|
1050
|
+
|
|
1051
|
+
const headers = new Headers(msg.target.headers || [])
|
|
1052
|
+
headers.set('Upgrade', 'websocket')
|
|
1053
|
+
|
|
1054
|
+
const response = await stub.fetch(new Request(msg.target.url, { method: 'GET', headers }))
|
|
1055
|
+
const doWs = response.webSocket
|
|
1056
|
+
|
|
1057
|
+
if (!doWs) {
|
|
1058
|
+
ws.send(JSON.stringify({
|
|
1059
|
+
t: 'rpc.err',
|
|
1060
|
+
id: 'ws_' + msg.wid,
|
|
1061
|
+
error: { code: 'WS_FAILED', message: 'No WebSocket returned' }
|
|
1062
|
+
}))
|
|
1063
|
+
return
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
doWs.accept()
|
|
1067
|
+
wsProxies.set(msg.wid, { doWs })
|
|
1068
|
+
|
|
1069
|
+
doWs.addEventListener('message', (event) => {
|
|
1070
|
+
const isText = typeof event.data === 'string'
|
|
1071
|
+
const data = isText ? event.data : arrayBufferToBase64(event.data)
|
|
1072
|
+
ws.send(JSON.stringify({ t: 'ws.data', wid: msg.wid, data, isText }))
|
|
1073
|
+
})
|
|
1074
|
+
|
|
1075
|
+
doWs.addEventListener('close', (event) => {
|
|
1076
|
+
ws.send(JSON.stringify({ t: 'ws.close', wid: msg.wid, code: event.code, reason: event.reason }))
|
|
1077
|
+
wsProxies.delete(msg.wid)
|
|
1078
|
+
})
|
|
1079
|
+
|
|
1080
|
+
ws.send(JSON.stringify({ t: 'ws.opened', wid: msg.wid }))
|
|
1081
|
+
} catch (error) {
|
|
1082
|
+
ws.send(JSON.stringify({
|
|
1083
|
+
t: 'rpc.err',
|
|
1084
|
+
id: 'ws_' + msg.wid,
|
|
1085
|
+
error: { code: 'WS_FAILED', message: error.message }
|
|
1086
|
+
}))
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
function handleBridgeWsClose(msg, wsProxies) {
|
|
1091
|
+
const proxy = wsProxies.get(msg.wid)
|
|
1092
|
+
if (proxy) {
|
|
1093
|
+
proxy.doWs.close(msg.code, msg.reason)
|
|
1094
|
+
wsProxies.delete(msg.wid)
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
async function handleBridgeJsonMessage(data, ws, env, ctx, wsProxies) {
|
|
1099
|
+
const msg = JSON.parse(data)
|
|
1100
|
+
switch (msg.t) {
|
|
1101
|
+
case 'hello':
|
|
1102
|
+
// v2 handshake — acknowledge with welcome echoing the negotiated
|
|
1103
|
+
// capability intersection. Capabilities advertised by the gateway
|
|
1104
|
+
// are kept in sync with src/bridge/client.ts (BRIDGE_CLIENT_CAPABILITIES).
|
|
1105
|
+
ws.send(JSON.stringify({
|
|
1106
|
+
t: 'welcome',
|
|
1107
|
+
protocolVersion: 2,
|
|
1108
|
+
capabilities: ['streams', 'ws-relay', 'http-transfer']
|
|
1109
|
+
.filter((c) => Array.isArray(msg.capabilities) && msg.capabilities.includes(c))
|
|
1110
|
+
.sort()
|
|
1111
|
+
}))
|
|
1112
|
+
break
|
|
1113
|
+
case 'rpc.call':
|
|
1114
|
+
await handleBridgeRpcCall(msg, ws, env, ctx)
|
|
1115
|
+
break
|
|
1116
|
+
case 'ws.open':
|
|
1117
|
+
await handleBridgeWsOpen(msg, ws, env, wsProxies)
|
|
1118
|
+
break
|
|
1119
|
+
case 'ws.close':
|
|
1120
|
+
handleBridgeWsClose(msg, wsProxies)
|
|
1121
|
+
break
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
function handleBridgeWebSocket(request, env, ctx) {
|
|
1126
|
+
const { 0: client, 1: server } = new WebSocketPair()
|
|
1127
|
+
server.accept()
|
|
1128
|
+
|
|
1129
|
+
// Per-connection state: recreated for every bridge client so reloads and
|
|
1130
|
+
// concurrent clients never share WS proxy entries.
|
|
1131
|
+
const wsProxies = new Map()
|
|
1132
|
+
|
|
1133
|
+
server.addEventListener('message', async (event) => {
|
|
1134
|
+
try {
|
|
1135
|
+
if (typeof event.data === 'string') {
|
|
1136
|
+
await handleBridgeJsonMessage(event.data, server, env, ctx, wsProxies)
|
|
1137
|
+
}
|
|
1138
|
+
} catch (error) {
|
|
1139
|
+
console.error('[Gateway] Error:', error)
|
|
1140
|
+
}
|
|
1141
|
+
})
|
|
1142
|
+
|
|
1143
|
+
server.addEventListener('close', () => {
|
|
1144
|
+
for (const proxy of wsProxies.values()) {
|
|
1145
|
+
// Best-effort cleanup: the DO-side WS may already be closed or in an
|
|
1146
|
+
// invalid state; any throw here would abort sibling closes. Surface
|
|
1147
|
+
// the swallowed error when DEVFLARE_DEBUG_BRIDGE is enabled.
|
|
1148
|
+
try { proxy.doWs.close() } catch (error) {
|
|
1149
|
+
if (globalThis.DEVFLARE_DEBUG_BRIDGE) {
|
|
1150
|
+
console.warn('[devflare:bridge] proxy.doWs.close() failed', error)
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
wsProxies.clear()
|
|
1155
|
+
})
|
|
1156
|
+
|
|
1157
|
+
return new Response(null, { status: 101, webSocket: client })
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
// ---------------------------------------------------------------------------
|
|
1161
|
+
// HTTP transfer for R2 bodies (shared with src/bridge/server.ts in shape)
|
|
1162
|
+
// ---------------------------------------------------------------------------
|
|
1163
|
+
|
|
1164
|
+
async function handleHttpTransfer(request, env, url) {
|
|
1165
|
+
const transferIdEncoded = url.pathname.split('/').pop()
|
|
1166
|
+
const transferId = decodeURIComponent(transferIdEncoded || '')
|
|
1167
|
+
const [binding, ...keyParts] = transferId.split(':')
|
|
1168
|
+
const key = keyParts.join(':')
|
|
1169
|
+
const bucket = env[binding]
|
|
1170
|
+
|
|
1171
|
+
if (!bucket) return new Response('Bucket not found: ' + binding, { status: 404 })
|
|
1172
|
+
|
|
1173
|
+
if (request.method === 'PUT' || request.method === 'POST') {
|
|
1174
|
+
const result = await bucket.put(key, request.body)
|
|
1175
|
+
return new Response(JSON.stringify(serializeR2Object(result)), {
|
|
1176
|
+
headers: { 'Content-Type': 'application/json' }
|
|
1177
|
+
})
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
if (request.method === 'GET') {
|
|
1181
|
+
const object = await bucket.get(key)
|
|
1182
|
+
if (!object) return new Response('Not found', { status: 404 })
|
|
1183
|
+
return new Response(object.body, {
|
|
1184
|
+
headers: {
|
|
1185
|
+
'Content-Type': object.httpMetadata?.contentType || 'application/octet-stream',
|
|
1186
|
+
'Content-Length': String(object.size)
|
|
1187
|
+
}
|
|
1188
|
+
})
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
return new Response('Method not allowed', { status: 405 })
|
|
1192
|
+
}
|
|
1193
|
+
`;
|
|
1194
|
+
|
|
1195
|
+
// src/bridge/miniflare.ts
|
|
1196
|
+
function isIgnorableMiniflareDisposeError(error) {
|
|
1197
|
+
if (!(error instanceof Error)) {
|
|
1198
|
+
return false;
|
|
1199
|
+
}
|
|
1200
|
+
const details = error;
|
|
1201
|
+
return details.code === "EBADF" && details.syscall === "kill";
|
|
1202
|
+
}
|
|
1203
|
+
|
|
306
1204
|
// src/dev-server/dev-server-state.ts
|
|
307
1205
|
function createDevServerState(initial) {
|
|
308
1206
|
return {
|
|
@@ -363,7 +1261,7 @@ async function disposeDevServerState(state) {
|
|
|
363
1261
|
}
|
|
364
1262
|
|
|
365
1263
|
// src/dev-server/miniflare-dev-config.ts
|
|
366
|
-
import { resolve as
|
|
1264
|
+
import { resolve as resolve5 } from "pathe";
|
|
367
1265
|
|
|
368
1266
|
// src/browser-shim/binding-worker.ts
|
|
369
1267
|
var MAX_CHUNK_SIZE = 1048575;
|
|
@@ -928,7 +1826,7 @@ async function handleDoWebSocket(request, env, url, route) {
|
|
|
928
1826
|
}
|
|
929
1827
|
|
|
930
1828
|
// src/dev-server/miniflare-worker-config.ts
|
|
931
|
-
import { resolve as
|
|
1829
|
+
import { resolve as resolve3 } from "pathe";
|
|
932
1830
|
var DEFAULT_MODULE_RULES = [
|
|
933
1831
|
{ type: "ESModule", include: ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.mjs"] },
|
|
934
1832
|
{ type: "CommonJS", include: ["**/*.js", "**/*.cjs"] },
|
|
@@ -1024,7 +1922,7 @@ function makeMiniflareWorker(context, options) {
|
|
|
1024
1922
|
};
|
|
1025
1923
|
if (options.scriptPath) {
|
|
1026
1924
|
workerConfig.scriptPath = options.scriptPath;
|
|
1027
|
-
workerConfig.modulesRoot = loadedConfig.baseDir ?
|
|
1925
|
+
workerConfig.modulesRoot = loadedConfig.baseDir ? resolve3(cwd, loadedConfig.baseDir) : cwd;
|
|
1028
1926
|
workerConfig.modulesRules = [
|
|
1029
1927
|
...loadedConfig.rules?.map(toMiniflareModuleRule) ?? [],
|
|
1030
1928
|
...DEFAULT_MODULE_RULES
|
|
@@ -1043,7 +1941,7 @@ function makeMiniflareWorker(context, options) {
|
|
|
1043
1941
|
}
|
|
1044
1942
|
|
|
1045
1943
|
// src/dev-server/worker-surface-paths.ts
|
|
1046
|
-
import { dirname, resolve as
|
|
1944
|
+
import { dirname as dirname2, resolve as resolve4 } from "pathe";
|
|
1047
1945
|
var DEFAULT_TRANSPORT_ENTRY_FILES = [
|
|
1048
1946
|
"src/transport.ts",
|
|
1049
1947
|
"src/transport.js",
|
|
@@ -1056,18 +1954,18 @@ function addWorkerWatchRoots(roots, cwd, configuredPath, defaultEntries) {
|
|
|
1056
1954
|
return;
|
|
1057
1955
|
}
|
|
1058
1956
|
if (typeof configuredPath === "string" && configuredPath) {
|
|
1059
|
-
roots.add(
|
|
1957
|
+
roots.add(dirname2(resolve4(cwd, configuredPath)));
|
|
1060
1958
|
return;
|
|
1061
1959
|
}
|
|
1062
1960
|
for (const defaultEntry of defaultEntries) {
|
|
1063
|
-
roots.add(
|
|
1961
|
+
roots.add(dirname2(resolve4(cwd, defaultEntry)));
|
|
1064
1962
|
}
|
|
1065
1963
|
}
|
|
1066
1964
|
function collectWorkerWatchRoots(cwd, config, mainWorkerSurfacePaths) {
|
|
1067
1965
|
const roots = new Set;
|
|
1068
1966
|
for (const surfacePath of Object.values(mainWorkerSurfacePaths)) {
|
|
1069
1967
|
if (surfacePath) {
|
|
1070
|
-
roots.add(
|
|
1968
|
+
roots.add(dirname2(surfacePath));
|
|
1071
1969
|
}
|
|
1072
1970
|
}
|
|
1073
1971
|
addWorkerWatchRoots(roots, cwd, config.files?.fetch, DEFAULT_FETCH_ENTRY_FILES);
|
|
@@ -1104,7 +2002,7 @@ function buildMiniflareDevConfig(input) {
|
|
|
1104
2002
|
logger
|
|
1105
2003
|
} = input;
|
|
1106
2004
|
const bindings = loadedConfig.bindings ?? {};
|
|
1107
|
-
const persistPath =
|
|
2005
|
+
const persistPath = resolve5(cwd, ".devflare/data");
|
|
1108
2006
|
const appWorkerName = loadedConfig.name;
|
|
1109
2007
|
const shouldRunMainWorker = !enableVite && (hasWorkerSurfacePaths(mainWorkerSurfacePaths) || Boolean(mainWorkerRoutes?.routes.length));
|
|
1110
2008
|
const queueProducers = buildQueueProducers(bindings);
|
|
@@ -1328,7 +2226,7 @@ function createRuntimeStdioForwarder(logger) {
|
|
|
1328
2226
|
}
|
|
1329
2227
|
|
|
1330
2228
|
// src/dev-server/server-startup-helpers.ts
|
|
1331
|
-
import { resolve as
|
|
2229
|
+
import { resolve as resolve6 } from "pathe";
|
|
1332
2230
|
|
|
1333
2231
|
// src/browser-shim/server.ts
|
|
1334
2232
|
import { existsSync } from "node:fs";
|
|
@@ -1336,7 +2234,7 @@ import {
|
|
|
1336
2234
|
createServer
|
|
1337
2235
|
} from "node:http";
|
|
1338
2236
|
import { homedir } from "node:os";
|
|
1339
|
-
import { join } from "node:path";
|
|
2237
|
+
import { join as join2 } from "node:path";
|
|
1340
2238
|
import {
|
|
1341
2239
|
Browser as BrowserType,
|
|
1342
2240
|
detectBrowserPlatform,
|
|
@@ -1437,7 +2335,7 @@ function createBrowserShim(options = {}) {
|
|
|
1437
2335
|
logger,
|
|
1438
2336
|
verbose = false,
|
|
1439
2337
|
keepAlive = 60000,
|
|
1440
|
-
cacheDir =
|
|
2338
|
+
cacheDir = join2(homedir(), ".devflare", "chrome"),
|
|
1441
2339
|
allowNoSandbox = false
|
|
1442
2340
|
} = options;
|
|
1443
2341
|
const chromeLaunchArgs = resolveChromeFlags({ allowNoSandbox });
|
|
@@ -1635,7 +2533,7 @@ function createBrowserShim(options = {}) {
|
|
|
1635
2533
|
res.end("Not found");
|
|
1636
2534
|
}
|
|
1637
2535
|
function readBody(req) {
|
|
1638
|
-
return new Promise((
|
|
2536
|
+
return new Promise((resolve6, reject) => {
|
|
1639
2537
|
const chunks = [];
|
|
1640
2538
|
let totalBytes = 0;
|
|
1641
2539
|
req.on("data", (chunk) => {
|
|
@@ -1647,7 +2545,7 @@ function createBrowserShim(options = {}) {
|
|
|
1647
2545
|
}
|
|
1648
2546
|
chunks.push(chunk);
|
|
1649
2547
|
});
|
|
1650
|
-
req.on("end", () =>
|
|
2548
|
+
req.on("end", () => resolve6(Buffer.concat(chunks).toString()));
|
|
1651
2549
|
req.on("error", reject);
|
|
1652
2550
|
});
|
|
1653
2551
|
}
|
|
@@ -1796,10 +2694,10 @@ function createBrowserShim(options = {}) {
|
|
|
1796
2694
|
});
|
|
1797
2695
|
});
|
|
1798
2696
|
}
|
|
1799
|
-
await new Promise((
|
|
2697
|
+
await new Promise((resolve6, reject) => {
|
|
1800
2698
|
server.on("error", reject);
|
|
1801
2699
|
server.listen(port, host, () => {
|
|
1802
|
-
|
|
2700
|
+
resolve6();
|
|
1803
2701
|
});
|
|
1804
2702
|
});
|
|
1805
2703
|
logger?.success(`Browser shim server ready on http://${host}:${port}`);
|
|
@@ -1809,8 +2707,8 @@ function createBrowserShim(options = {}) {
|
|
|
1809
2707
|
await closeSession(sessionId, 3, "ServerShutdown");
|
|
1810
2708
|
}
|
|
1811
2709
|
if (server) {
|
|
1812
|
-
await new Promise((
|
|
1813
|
-
server.close(() =>
|
|
2710
|
+
await new Promise((resolve6) => {
|
|
2711
|
+
server.close(() => resolve6());
|
|
1814
2712
|
});
|
|
1815
2713
|
server = null;
|
|
1816
2714
|
}
|
|
@@ -1868,7 +2766,7 @@ function formatErrorMessage(error) {
|
|
|
1868
2766
|
}
|
|
1869
2767
|
async function resolveWorkerConfigWatchPath(cwd, configPath) {
|
|
1870
2768
|
if (configPath) {
|
|
1871
|
-
const explicitPath =
|
|
2769
|
+
const explicitPath = resolve6(cwd, configPath);
|
|
1872
2770
|
const fs = await import("node:fs/promises");
|
|
1873
2771
|
try {
|
|
1874
2772
|
await fs.access(explicitPath);
|
|
@@ -1939,7 +2837,7 @@ async function maybeStartDOBundler(config, options) {
|
|
|
1939
2837
|
if (typeof doPattern !== "string" || !doPattern) {
|
|
1940
2838
|
return { bundler: null, result: null };
|
|
1941
2839
|
}
|
|
1942
|
-
const outDir =
|
|
2840
|
+
const outDir = resolve6(options.cwd, ".devflare/do-bundles");
|
|
1943
2841
|
const bundler = createDOBundler({
|
|
1944
2842
|
cwd: options.cwd,
|
|
1945
2843
|
pattern: doPattern,
|
|
@@ -2155,7 +3053,7 @@ function createDevServer(options) {
|
|
|
2155
3053
|
state.bundledMainWorkerScriptPath = await bundleWorkerEntry({
|
|
2156
3054
|
cwd,
|
|
2157
3055
|
inputFile: state.mainWorkerScriptPath,
|
|
2158
|
-
outFile:
|
|
3056
|
+
outFile: resolve7(cwd, ".devflare", "worker-entrypoints", "main.js"),
|
|
2159
3057
|
rolldownOptions: state.config.rolldown?.options,
|
|
2160
3058
|
sourcemap: state.config.rolldown?.sourcemap,
|
|
2161
3059
|
minify: state.config.rolldown?.minify,
|
|
@@ -2273,11 +3171,11 @@ function createDevServer(options) {
|
|
|
2273
3171
|
cwd,
|
|
2274
3172
|
configPath: state.resolvedWorkerConfigPath ?? undefined
|
|
2275
3173
|
});
|
|
2276
|
-
state.serviceBindingResolution = state.config.bindings?.services ? await resolveServiceBindings(state.config, state.resolvedWorkerConfigPath ?
|
|
3174
|
+
state.serviceBindingResolution = state.config.bindings?.services ? await resolveServiceBindings(state.config, state.resolvedWorkerConfigPath ? dirname3(state.resolvedWorkerConfigPath) : cwd) : null;
|
|
2277
3175
|
}
|
|
2278
3176
|
async function waitForDotenvChange() {
|
|
2279
3177
|
const configWatchPath = state.resolvedWorkerConfigPath ?? await resolveWorkerConfigWatchPath(cwd, configPath);
|
|
2280
|
-
const startDir = configWatchPath ?
|
|
3178
|
+
const startDir = configWatchPath ? dirname3(configWatchPath) : cwd;
|
|
2281
3179
|
const watchPaths = getDevflareDotenvPaths(startDir);
|
|
2282
3180
|
const { watch } = await import("chokidar");
|
|
2283
3181
|
await new Promise((resolveWait, rejectWait) => {
|
|
@@ -2434,11 +3332,11 @@ async function createLogWriter(cwd, options) {
|
|
|
2434
3332
|
const fs = await import("node:fs");
|
|
2435
3333
|
let logPath;
|
|
2436
3334
|
if (options.logTemp) {
|
|
2437
|
-
logPath =
|
|
3335
|
+
logPath = resolve8(cwd, ".log");
|
|
2438
3336
|
} else {
|
|
2439
3337
|
const now = new Date;
|
|
2440
3338
|
const timestamp = now.toISOString().replace(/[:.]/g, "-").replace("T", "_").slice(0, 19);
|
|
2441
|
-
logPath =
|
|
3339
|
+
logPath = resolve8(cwd, `.log-${timestamp}`);
|
|
2442
3340
|
}
|
|
2443
3341
|
const fileStream = fs.createWriteStream(logPath, { flags: "w" });
|
|
2444
3342
|
const ansiRegex = /\x1b\[[0-9;]*m/g;
|
|
@@ -2486,7 +3384,7 @@ async function runDevCommand(parsed, logger, options) {
|
|
|
2486
3384
|
logTemp: logTempEnabled
|
|
2487
3385
|
});
|
|
2488
3386
|
if (logWriter) {
|
|
2489
|
-
const logFile =
|
|
3387
|
+
const logFile = relative2(cwd, logWriter.path) || ".log";
|
|
2490
3388
|
logLine(logger, `${dim("logging", theme)} ${logFile}`);
|
|
2491
3389
|
}
|
|
2492
3390
|
const devLogger = createConsola({
|