libSQL
快速开始
使用 libSQL 蓝图为现有 Flue 项目添加持久化 libSQL 存储。在终端或你选择的编码智能体中运行以下命令:
flue add database libsql
概述
libSQL 蓝图会安装 @flue/libsql 和 @libsql/client,在源代码根目录创建 db.ts,并在项目已有环境文档时更新它。生成的适配器将客户端结果集映射为普通对象,并序列化操作,确保本地 SQLite 文件不会接收到来自同一进程的重叠写入:
import { libsql } from '@flue/libsql';
import { createClient, type ResultSet } from '@libsql/client';
const client = createClient({ url: process.env.LIBSQL_URL! });
export default libsql({
query: (text, params = []) => {
// await client.execute({ sql: text, args: params }))),
// ...
}
transaction: (fn) => {
// const tx = await client.transaction('write');
// ...
}
close: () => client.close(),
});Flue 在构建时发现适配器并将其连接到生成的 Node 服务器。启动时,它会创建或验证所需的 flue_* 表。规范的智能体对话、不可变附件和已接受的提交随后会根据 LIBSQL_URL 持久化到本地 SQLite 文件或自托管 libSQL 服务器中;应用业务数据仍由应用自行管理。嵌入式副本需要额外的 syncUrl 客户端配置。此蓝图仅适用于 Node 目标,因为 Cloudflare 部署使用 Durable Object SQLite。
配置
| 变量 | 用途 |
|---|---|
LIBSQL_URL | 必需 — 本地文件(file:./data/flue.db)或 libSQL 服务器(http://host:8080)。 |
createClient 在运行时读取此变量 — 它不会被烘焙到构建中。对于本地开发,vite dev 会加载项目 .env 文件,而 flue run --env <file> 会加载任何 .env 格式的文件。在生产环境中,请从你平台的密钥存储中提供它。
蓝图会安装 @flue/libsql 和官方的 @libsql/client,并在源代码根目录创建一个包装客户端的 db.ts。Flue 在构建时发现 db.ts 并将其连接到生成的 Node 服务器。对于托管的 Turso,请改用 flue add database turso — 它是同一个适配器,但使用 Turso 客户端配置。
@flue/libsql 是一个 Node.js 适配器。Cloudflare 目标会自动使用 Durable Object SQLite,并在构建时拒绝 db.ts 文件,因此本指南适用于 Node 部署。有关每个目标如何存储状态的完整信息,请参阅数据库。
使用自定义驱动
@flue/libsql 不会选择或捆绑数据库驱动。它运行在你围绕已配置的
@libsql/client 包装的小型运行器上,
因此你拥有客户端及其连接选项。运行器包含三个函数:query(带 ? 占位符的 SQL 字符串加上位置参数,返回结果行)、transaction(在一个 write 事务中运行其回调)和 close。
@libsql/client 返回一个 ResultSet,因此需要将其 rows/columns 映射为普通对象:
import { libsql } from '@flue/libsql';
import { createClient, type ResultSet } from '@libsql/client';
const client = createClient({ url: process.env.LIBSQL_URL! });
const toRows = (rs: ResultSet) =>
rs.rows.map((row) => Object.fromEntries(rs.columns.map((column) => [column, row[column]])));
let tail: Promise<unknown> = Promise.resolve();
const serialize = <T>(operation: () => Promise<T>): Promise<T> => {
const result = tail.then(operation, operation);
tail = result.then(
() => undefined,
() => undefined,
);
return result;
};
export default libsql({
query: (text, params = []) =>
serialize(async () => toRows(await client.execute({ sql: text, args: params }))),
transaction: (fn) =>
serialize(async () => {
const tx = await client.transaction('write');
try {
const result = await fn({
query: async (text, params = []) => toRows(await tx.execute({ sql: text, args: params })),
});
await tx.commit();
return result;
} catch (error) {
await tx.rollback();
throw error;
} finally {
tx.close();
}
}),
close: () => client.close(),
});连接目标
createClient 决定状态存储位置 — 适配器在所有目标中都是相同的:
| 目标 | createClient(...) |
|---|---|
| 本地 SQLite 文件 | `{ url: ‘file:./data/flue.db’ }’ |
自托管 libSQL 服务器(sqld) | `{ url: ‘http://127.0.0.1:8080’ }’ |
| 嵌入式副本(本地文件同步到远程) | `{ url: ‘file:local.db’, syncUrl, authToken }’ |
| 托管的 Turso | 请参阅 Turso 指南 |
嵌入式文件并发
当 LIBSQL_URL 是本地 file: 数据库时,异步写入可能会重叠并导致 SQLITE_BUSY 错误。上面的运行器会序列化来自同一进程的所有操作,使其事务不会与顶层查询冲突。Flue 不保证对单个嵌入式文件的多进程或多租户写入。自托管 libSQL 服务器和托管的 Turso 会在服务器端序列化写入。
迁移
适配器的 migrate() 钩子会在生成的 Node 服务器启动时自动运行。它会幂等地创建 Flue 的 flue_* 表并标记格式版本,因此首次启动时会配置新数据库,重启时会重用现有数据库。没有单独的迁移命令需要运行,由较新 Flue 版本写入的数据库会拒绝启动,而不是损坏状态。
存储内容
Flue 数据库存储运行时状态,而不是整个应用。
由 Flue 存储:
- 规范的智能体对话流和压缩记录;
- 不可变附件负载;
- 已接受的直接提示和
dispatch(...)提交; - 持久化提交声明、租约和结算记录。
不由 Flue 存储:
- 沙盒文件和已安装的依赖项;
- 外部 API 副作用;
- 应用拥有的业务数据,除非你自己的工具存储它;
- 提供商凭证或密钥。
有关恢复如何使用提交状态的信息,请参阅持久性, 有关确切适配器契约的信息,请参阅数据持久化 API。
何时选择 libSQL
| 用例 | 适配器 |
|---|---|
| 本地开发 | 来自 @flue/runtime/node 的 sqlite(),或 libSQL 针对 file: 数据库 |
| 单主机 Node 部署 | 文件支持的 sqlite() 或 libSQL file: |
| 通过网络的自托管 SQLite,或嵌入式副本 | @flue/libsql |
| 托管、复制的 SQLite | @flue/libsql 针对 Turso |
| 基于 Postgres 的多副本 Node 部署 | @flue/postgres |
当你想要 SQLite 的模型但需要通过网络访问或作为嵌入式副本保持在应用附近时,libSQL 是正确的选择。 对于完全托管的复制部署,请将同一适配器指向 Turso。