配置
Flue 项目配置有两个编写接口和一个编程模块:
flue.config.ts— 项目根目录的可选文件,描述项目:构建目标、入口模块路径和'use agent'扫描范围。flue()的内联选项 — Vite 插件接受相同字段并按字段合并覆盖发现的文件。@flue/runtime/config— 实现发现、验证和解析的模块,供宿主和工具使用。
两个消费者读取配置:flue() Vite 插件(vite dev、vite build、vite preview)和 flue run。按消费者的行为在按消费者解析中指定。
flue.config.ts
// flue.config.ts
import { defineConfig } from '@flue/runtime/config';
export default defineConfig({
target: 'node',
});
该文件是可选的,每个字段都是可选的,配置必须是模块的默认导出。没有对象默认导出的模块会失败并提示 [flue] <file> must export a config object as the default export.
defineConfig()
function defineConfig(config: FlueConfig): FlueConfig;
原样返回配置;用于类型检查和编辑器补全。普通的对象默认导出同样有效。
文件发现
在项目根目录搜索配置文件——flue() 插件使用 Vite 根目录,flue run 使用工作目录。按以下优先顺序尝试基名(第一个匹配者胜出):
flue.config.tsflue.config.mtsflue.config.mjsflue.config.jsflue.config.cjsflue.config.cts
当同一目录中存在多个文件时,flue() 插件会记录警告并指出胜出者([flue] Multiple Flue config files found (…); using <basename>.);flue run 按相同优先级静默选择。没有面向用户的选项可以将插件或 CLI 指向不同名称的配置文件;显式路径仅存在于编程 API 上。
模块评估
配置模块使用 Node 的原生动态 import() 评估,每次加载时缓存失效——不通过 Vite。Vite 别名、插件和转换不适用;配置文件中的每个导入必须能被 Node 本身解析。
TypeScript 配置文件依赖 Node 的类型剥离加载器:
- 需要 Node >= 22.19 或 >= 23.6。在较旧的 Node 上,加载
.ts配置会失败并提示[flue] Cannot load <file>: this Node (v…) does not support TypeScript natively. - 只接受可擦除的 TypeScript 语法。
enum、带运行时代码的namespace、参数属性和装饰器会失败并提示[flue] <file> uses TypeScript syntax that Node's type-stripping loader doesn't support …。
验证
默认导出会针对以下字段集严格验证:
- 未知字段是错误。(
flue run是唯一例外:它丢弃未知键而不是拒绝;参见flue run。) - 非对象值会失败并提示
[flue] <source> must be a config object. - 字段级失败(无效的
target值、空路径字符串)会一起报告为[flue] Invalid config in <source>:后跟每个字段一行。
入口路径解析
两个消费者使用相同的规则对配置的字段进行文件系统解析:
- 源根目录。 当
<root>/.flue作为目录存在时,从它发现编写的模块,否则从<root>/src,否则从项目根目录本身。参见项目布局。 - 默认入口查找。 未设置的
app/db/cloudflare字段回退到<sourceRoot>/<field>.<ext>,按ts、mts、js、mjs的顺序尝试扩展名。缺失的默认入口在解析时不是错误;是否需要是消费者的决定(app是vite dev/vite build必需的;flue run或vite preview不需要任何)。 - 显式路径。 已设置的字段从配置文件目录解析(当值仅来自内联
flue()选项时从项目根目录)且必须存在;缺失的显式入口抛出[flue] Configured `<field>` entry not found: <path>。
flue() Vite 插件
import { flue } from '@flue/vite';
function flue(config?: FlueConfig): Plugin[];
将 Vite 项目变成 Flue 应用。返回插件数组;核心插件名为 flue。在同一 Vite 配置中多次添加 flue() 是错误。参见部署了解各目标的构建和部署流程。
// vite.config.ts
import { defineConfig } from 'vite';
import { flue } from '@flue/vite';
export default defineConfig({
plugins: [flue({ target: 'node' })],
});
插件选项是 FlueConfig。在 Vite 配置解析期间,插件:
- 验证内联选项(失败时指出
inline flue() options为来源)。 - 从 Vite 根目录发现并加载
flue.config.*。 - 按字段将内联选项合并覆盖文件:已定义的内联字段优先;
undefined内联字段回退到文件值。所有字段都是标量;没有深度合并。 - 解析项目布局,并在 preview 之外运行
'use agent'扫描。
项目相对于 Vite 根目录解析(Vite 配置中的 root,否则为工作目录)。如果另一个插件在 flue() 解析后更改了 root,配置解析会失败并提示你在 Vite 配置本身中设置 root。
目标检测
有效目标按以下顺序决定:
- 合并的
target字段(当设置时)。 - 当
@cloudflare/vite-plugin存在于解析的 Vite 插件数组中时为'cloudflare'。 - 否则为
'node'。
Cloudflare 布线在配置解析时验证;每个失败是不同的错误:
target: 'cloudflare'但插件数组中没有@cloudflare/vite-plugin。cloudflare()列在flue()之前——flue()必须在它之前,因为同级的配置解析调用 Flue 的 worker-config 自定义器,需要完整的项目解析和智能体扫描。- Cloudflare 插件存在但不作为普通
plugins条目对flue()可见(包装在 Promise 中,或由另一个插件注入)。 - 调用
cloudflare()时没有config: flueWorkerConfig(),使同级缺少 Flue 的 Worker 入口和 Durable Object 绑定。
插件设置的 Vite 配置
在每个目标和模式上,插件设置 appType: 'custom' 并将 @flue/runtime 和 hono 去重为每个模块图一个副本。
在 Node 目标上,vite build 额外强制:build.ssr: true、build.target: 'node22'、双入口 rolldown 输入(自启动的 server.mjs 加上它导入的非监听 app.mjs 块)、.mjs 入口和块文件名,以及 ES 模块输出。Node 内置模块、项目的 package.json 依赖(及其子路径)和 Flue 的可选原生依赖保留在包外部。用户在这些强制路径上的设置值会被覆盖并附带警告([flue] The following Vite config options are overridden by flue(): …),而非错误。用户保留:
build.outDir— 默认'dist'。解析为(或包含)项目根目录或源根目录的输出目录——包括通过符号链接或连接——会被拒绝,因为构建会清空它。build.sourcemap— 默认true。build.emptyOutDir— 留给 Vite 自己的默认值和防护。
在 vite dev 和 vite preview(两个目标)中,server.cors / preview.cors 默认为仅 localhost 的凭据策略,还暴露持久流协调头(Stream-Next-Offset、Stream-Up-To-Date、Location);显式的用户值替换默认值。已部署的服务器不应用 CORS——参见路由。
在 Node 目标上,vite dev 还将项目的 .env 文件集(Vite 标准的 .env、.env.local、.[mode]、.env.[mode].local)以 shell 优先语义加载到 process.env 中,与 flue run 匹配。Cloudflare 开发时变量(.dev.vars)属于 Cloudflare 插件。
在 Cloudflare 目标上,插件不强制任何构建配置;@cloudflare/vite-plugin 拥有 Worker 构建、开发服务器和预览。
虚拟模块
插件提供六个虚拟模块;它们只能在插件拥有的模块图中解析:
virtual:flue/app— 解析的app入口(必需)。virtual:flue/db— 解析的db入口,或导出undefined的存根(然后使用内置默认适配器)。virtual:flue/agents— 扫描的'use agent'模块集。virtual:flue/providers— 从providers生成的 Provider 注册(或全部内置默认值)。virtual:flue/server— Node 服务器引导。virtual:flue/worker— 生成的 Cloudflare Worker 入口(wranglermain;每个智能体一个 Durable Object 类)。
FlueVitePluginApi
interface FlueVitePluginApi {
readonly resolved: FlueResolvedProjectInfo | undefined;
}
interface FlueResolvedProjectInfo {
readonly config: FlueConfig;
readonly configPath: string | undefined;
readonly project: ResolvedFlueProject;
readonly target: 'node' | 'cloudflare';
readonly agents: readonly AgentScanResult[];
}
从 @flue/vite 导出。核心 flue 插件在其 api 字段上暴露 FlueVitePluginApi 作为其他工具的只读表面。resolved 在 Vite 配置完成之前为 undefined。
config— 合并后的 Flue 配置(文件 + 内联选项)。configPath— 发现的flue.config.*的绝对路径(如果有)。project— 解析后的文件系统布局;参见ResolvedFlueProject。target— 检测后的有效目标。agents— 扫描的智能体集;开发中实时(反映最新的重新扫描)。
flueWorkerConfig()
import { flue, flueWorkerConfig } from '@flue/vite';
function flueWorkerConfig(): FlueWorkerConfigCustomizer;
type FlueWorkerConfigCustomizer = (config: object) => void;
为 Cloudflare 目标创建 worker-config 自定义器,传递给同级插件的 config 选项:
// vite.config.ts
import { cloudflare } from '@cloudflare/vite-plugin';
import { flue, flueWorkerConfig } from '@flue/vite';
export default defineConfig({
plugins: [flue(), cloudflare({ config: flueWorkerConfig() })],
});
flueWorkerConfig() 必须在同一 Vite 配置评估中在 flue() 之后调用;先调用它会抛出 [flue] flueWorkerConfig() was called before flue(). …。自定义器在 Cloudflare 插件的配置解析中运行,针对活动 Cloudflare 环境(CLOUDFLARE_ENV)的解析后配置,恰好贡献四样东西:
main— 设为virtual:flue/worker,生成的 Worker 入口,除非用户的 wrangler 配置设置了自己的main(它可以重新导出生成的模块:export * from 'virtual:flue/worker')。- 每个扫描的智能体一个 Durable Object 绑定。占用 Flue 保留绑定名称的用户绑定必须匹配 Flue 会生成的绑定(相同的
class_name,没有script_name/environment);冲突的绑定抛出错误。 nodejs_compat兼容标志,合并到compatibility_flags中。- 验证用户设置的
compatibility_date:必须是YYYY-MM-DD且至少2026-04-01。更早的日期是错误,不是静默提升。未设置的日期留给 Cloudflare 插件自己的默认值。
wrangler 配置中的其他一切——name、用户 Durable Object、容器、R2 存储桶、迁移——原样传递。Flue 从不读取、合并或写入 wrangler 配置文件;参见 wrangler.jsonc。在 vite preview 下,自定义器是空操作(preview 服务于已构建的 Worker 输出)。
按消费者解析
vite dev
配置解析在插件的 config 钩子中运行:在 Vite 根目录中发现 flue.config.*,合并内联选项,解析入口,要求 app,扫描智能体。开发服务器随后保持解析实时:
- 编辑发现的
flue.config.*——或在启动时不存在时创建任何flue.config.*候选——会重启开发服务器,重新运行完整解析。 - 扫描的智能体集变化(文件添加/删除、指令或身份变化)会重新生成
virtual:flue/agents并在 Node 上重新加载应用;在 Cloudflare 上会重启开发服务器,因此 Worker 入口和 Durable Object 绑定会被重新生成。 - 在 Cloudflare 上,根目录出现或消失的
wrangler.jsonc/wrangler.json/wrangler.toml会重启开发服务器;对现有文件的编辑由 Cloudflare 插件本身处理。 - 监视期间的扫描失败(编辑中的语法错误、重复身份)会被记录,最后一个良好的智能体集保持不变。
vite build
与 vite dev 相同的解析,一次。app 是必需的;智能体扫描必须成功;在 Node 上应用强制构建配置和 build.outDir 安全检查;在 Cloudflare 上应用排序和布线验证,合并的 wrangler 配置由 Cloudflare 插件输出到构建结果中。
vite preview
Preview 基于产物:配置文件仍被发现、加载和验证,入口被解析,但不需要任何内容,不运行智能体扫描,不生成任何内容。Node preview 服务于已构建的 dist/ 输出;Cloudflare preview 完全由 Cloudflare 插件拥有(workerd 在已构建的 Worker 上)。
flue run
flue run 直接解析配置,无需 Vite 配置或插件:
flue.config.*从工作目录发现(它也是项目根目录);从不读取vite.config.ts。- 未知配置键在验证前被丢弃,而不是被拒绝。
target和agents被忽略:运行始终是 Node 本地的,智能体模块是显式的<path>参数,不是扫描结果。db被尊重;没有时,运行使用项目根目录下node_modules/.cache/flue/run.db的 SQLite 数据库。app和cloudflare被解析但未使用。显式路径存在检查仍然适用:不存在的已配置入口会使运行失败。
@flue/runtime/config
编程配置模块。它是宿主端工具——它接触文件系统——必须从构建或 CLI 代码导入,永远不要从智能体模块导入。defineConfig() 和 FlueConfig 在上面有文档。
parseFlueConfig()
function parseFlueConfig(value: unknown, source?: string): FlueConfig;
对原始配置值(配置模块的默认导出或内联插件选项)针对严格字段集进行验证。抛出带有字段级诊断的异常,指出 source(默认 'flue config')。返回验证后的 FlueConfig。
mergeFlueConfig()
function mergeFlueConfig(file: FlueConfig, inline: FlueConfig): FlueConfig;
将宿主提供的配置按字段合并覆盖发现的文件配置:已定义的 inline 字段优先,undefined 字段回退到 file。
resolveFlueConfigPath()
interface ResolveFlueConfigPathOptions {
cwd: string;
configFile?: string;
}
function resolveFlueConfigPath(opts: ResolveFlueConfigPathOptions): string | undefined;
解析项目的 flue.config.* 的绝对路径,不存在时返回 undefined。
cwd— 搜索配置基名的目录,以及configFile的基准。configFile— 显式配置路径(相对于cwd,或绝对路径)。不存在的显式路径抛出[flue] Config file not found: <path>而不是返回undefined。
loadFlueConfig()
interface LoadedFlueConfig {
configPath: string | undefined;
config: FlueConfig;
}
function loadFlueConfig(opts: ResolveFlueConfigPathOptions): Promise<LoadedFlueConfig>;
在模块评估规则下,一步发现、评估和验证项目配置。不存在配置文件时返回 { configPath: undefined, config: {} }。缺失的显式 configFile、非对象默认导出或验证失败时抛出。
loadFlueConfigModule()
function loadFlueConfigModule(absConfigPath: string): Promise<Record<string, unknown>>;
通过原生动态 import()(每次调用缓存失效)评估配置文件并返回模块命名空间,未验证。类型剥离失败和不支持的 Node 失败会以模块评估下列出的诊断消息重新抛出。
resolveSourceRoot()
function resolveSourceRoot(root: string): string;
返回发现编写模块的目录:当 <root>/.flue 作为目录存在时,否则 <root>/src,否则 root。
discoverProjectEntry()
function discoverProjectEntry(sourceRoot: string, basename: string): string | undefined;
使用扩展名优先级 ts、mts、js、mjs 定位 <sourceRoot>/<basename>.<ext>。不存在候选项时返回 undefined。
resolveFlueProject()
interface ResolveFlueProjectOptions {
root: string;
config?: FlueConfig;
configPath?: string;
}
function resolveFlueProject(opts: ResolveFlueProjectOptions): ResolvedFlueProject;
将验证后的配置针对文件系统解析,应用入口路径解析中的规则。缺失的显式入口抛出;缺失的默认入口解析为 undefined(是否是错误由调用者决定)。
root— 项目根目录(绝对,或从工作目录解析)。config— 要解析的合并配置。默认{}。configPath— 值来源的配置文件路径;相对入口路径从其目录解析。默认从root解析。
ResolvedFlueProject
interface ResolvedFlueProject {
root: string;
sourceRoot: string;
target: 'node' | 'cloudflare' | undefined;
app: string | undefined;
db: string | undefined;
cloudflare: string | undefined;
agents: string | undefined;
}
项目完全解析的文件系统布局。root、sourceRoot 和入口字段是绝对路径;target 从配置传递(检测是 Vite 插件的工作);agents 是编写的扫描 glob 原文。
常量
const FLUE_CONFIG_BASENAMES: readonly string[];
const PROJECT_ENTRY_EXTENSIONS: readonly string[];
FLUE_CONFIG_BASENAMES— 配置基名,按优先级顺序。PROJECT_ENTRY_EXTENSIONS— 入口扩展名优先级:['ts', 'mts', 'js', 'mjs']。