跳转到内容

输入关键词开始搜索文档。

libSQL

最后更新 查看 Markdown 源文件@flue/libsql

快速开始

使用 libSQL 蓝图为现有 Flue 项目添加持久化 libSQL 存储。在终端或你选择的编码智能体中运行以下命令:

flue add database libsql

概述

libSQL 蓝图会安装 @flue/libsql 和 @libsql/client,在源代码根目录创建 db.ts,并在项目已有环境文档时更新它。生成的适配器将客户端结果集映射为普通对象,并序列化操作,确保本地 SQLite 文件不会接收到来自同一进程的重叠写入:

src/db.ts (简化版)
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 映射为普通对象:

src/db.ts
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。