@lovrabet/sdk
v1.6.1
Published
Keywords
Readme
Lovrabet SDK
强大且灵活的 Lovrabet 数据平台 SDK,支持多种认证模式,适用于 Node.js 和浏览器环境。
✨ 特性
- 🔐 三种认证模式 - OpenAPI、Client AK、WebAPI(Cookie)
- 🎯 环境自适应 - 根据运行环境自动选择最佳认证方式
- 🔑 Token 生成 - 内置 Token 生成器,支持批量生成
- 📦 TypeScript - 完整的类型定义和智能提示
- 🚀 简洁 API - 基于
createClient()的现代化设计 - 🌍 多环境支持 - 轻松切换生产、测试环境
- 🔄 向后兼容 - 保持对旧版 API 的兼容
🆕 What's New
v1.6.0 (2026-10-23)
- ✨ 业务域命名空间 - 新增权限、Artifact、审批、应用、数据集元信息、脚本管理能力
await client.permit.getRoleList({ currentPage: 1, pageSize: 20 }); const artifacts = await client.artifact.list({ artifactType: "page" }); const todo = await client.approval.listTodo(); - ✨ 区分
/api与/client- 双模式能力自动按authMode选择前缀;单模式能力在不支持的模式下 fail-closed - ✨ 数据集可恢复导入 -
ClientAkModel新增importContract/importBatch/importReadback/importImage/importImageReadback - ✨ SQL 与 BFF 扩展 - 新增
client.sql.query()/client.sql.listSqls()与client.bff.getEndpoint()/listEndpoints()/getScript() - ✨ 其余 Client 接口补齐 -
datasetMeta.queryByCode()、artifact.updateFavorite()、permit.resolveIdentity()、permit.appInviteCallback() - ✨ Personal BFF CRUD -
client.personal.bff新增list/get/create/update
v1.3.7 (2026-03-16)
- ✨ 批量操作支持 -
update()和delete()现在支持批量操作,最多 1000 条记录// 批量更新 await client.models.users.update([1001, 1002, 1003], { status: "active" }); // 批量删除 await client.models.users.delete([1001, 1002, 1003]); - ✨ 聚合查询接口 - 新增
aggregate()方法,支持分组统计、求和、平均值等操作const result = await client.models.orders.aggregate({ groupBy: ["status"], aggregate: [ { column: "id", type: "COUNT", alias: "orderCount" }, { column: "amount", type: "SUM", alias: "totalAmount" } ] }); - 📖 文档完善 - 新增完整的使用指南文档,详见
help/lovrabet-sdk-guide.md
v1.1.14 (2026-03-06)
- ✨ Node.js 环境 Cookie 支持 - 新增
cookie配置参数,支持在 Node.js 环境显式传递 Cookie 字符串 - 🎯 统一认证接口 - Cookie 认证现在在浏览器和 Node.js 环境中都可以使用,只是传递方式不同
- 📖 文档更新 - 新增 Node.js 环境使用 Cookie 认证的示例
v1.1.13 (2025-10-11)
- 🐛 修复 isTokenExpiring 逻辑 - 正确计算 token 剩余时间
📦 安装
npm install @lovrabet/sdk
# 或
bun add @lovrabet/sdk🚀 快速开始
服务端使用(Node.js)
import { createClient } from "@lovrabet/sdk";
// 使用 accessKey 认证(推荐)
const client = createClient({
appCode: "your-app-code",
authMode: "openapi", // 使用 accessKey 时必须显式声明;缺省 authMode 一律走 cookie,accessKey 会被忽略
accessKey: process.env.LOVRABET_ACCESS_KEY,
models: [
{ tableName: "users", datasetCode: "abc123def456", alias: "users" },
{ tableName: "posts", datasetCode: "ghi789jkl012" },
],
});
// CRUD 操作(通过 alias 或 datasetCode 访问)
const users = await client.models.users.getList(); // 通过 alias
const posts = await client.models["dataset_ghi789jkl012"].getList(); // 通过 datasetCode
const user = await client.models.users.getOne("user-id");
const newUser = await client.models.users.create({ name: "John" });独立审批 Flow OpenAPI
Flow 仅支持 openapi 认证,并绑定当前应用与操作人。Node 客户端已经绑定 appCode,因此通过 client.flow(operatorUserId) 创建审批客户端:
import { createClient } from "@lovrabet/sdk";
const client = createClient({
appCode: "your-app-code",
authMode: "openapi",
accessKey: process.env.LOVRABET_ACCESS_KEY!,
// Runtime dev profile 本地联调:runtimeDomain: "http://127.0.0.1:8081"
});
const flow = client.flow("1001");
const started = await flow.start({
flowCode: "leave",
formData: { days: 2, reason: "年假" },
variables: { businessId: "LEAVE-20260821-001" },
idempotencyKey: "leave-request-001",
});
const todo = await flow.listTodo({
variables: { businessId: "LEAVE-20260821-001" },
currentPage: 1,
pageSize: 20,
});
await flow.approve({
taskId: todo.records[0].taskId,
comment: "同意",
variables: { auditSource: "node-sdk" },
});常用入口包括 listTodo、listDone、listSubmitted、getTaskDetail、getProcessDetail、updateVariables、approve、reject、complete、transfer、withdraw、resubmit、returnTask、ccProcess 和 listCc。应用管理员可使用 listAppTodo、listAppDone、listAppSubmitted 查询当前应用全量数据;服务端仍会校验操作人是否为应用管理员。
浏览器不能保存 accessKey。服务端可使用 generateFlowOpenApiToken() 为指定操作人生成短期 Token,浏览器必须用同一个 appCode、operatorUserId 和 timestamp 发起请求:
import { generateFlowOpenApiToken } from "@lovrabet/sdk";
const auth = await generateFlowOpenApiToken({
appCode: "your-app-code",
operatorUserId: "1001",
accessKey: process.env.LOVRABET_ACCESS_KEY!,
});浏览器拿到 { token, timestamp } 后:
const client = createClient({
appCode: "your-app-code",
authMode: "openapi",
token,
timestamp,
});
const flow = client.flow("1001"); // 必须与生成 Token 时的 operatorUserId 一致
const todo = await flow.listTodo();浏览器端使用
方式 1:使用预生成 Token(推荐)
服务端生成 Token:
// app/api/token/route.ts (Next.js)
import { generateOpenApiToken } from "@lovrabet/sdk";
export async function GET() {
const result = await generateOpenApiToken({
appCode: process.env.LOVRABET_APP_CODE!,
datasetCode: process.env.DATASET_ID!,
accessKey: process.env.LOVRABET_ACCESS_KEY!,
});
return Response.json(result);
}浏览器使用 Token:
import { createClient } from "@lovrabet/sdk";
// 获取 token
const { token, timestamp } = await fetch("/api/token").then((r) => r.json());
// 创建客户端
const client = createClient({
appCode: "your-app-code",
authMode: "openapi", // 使用 token 时必须显式声明;缺省 authMode 一律走 cookie,token 会被忽略
token: token,
timestamp: timestamp,
models: [
{ tableName: "users", datasetCode: "abc123def456", alias: "users" },
],
});
// 使用
const users = await client.models.users.getList();方式 2:使用 Cookie(已登录用户)
浏览器环境:
import { createClient } from "@lovrabet/sdk";
// 不提供认证信息,自动使用浏览器 Cookie
const client = createClient({
appCode: "your-app-code",
models: [
{ tableName: "users", datasetCode: "abc123def456", alias: "users" },
],
});
// 请求会自动带上 Cookie (credentials: 'include')
const users = await client.models.users.getList();Node.js 环境(显式传递 Cookie):
import { createClient } from "@lovrabet/sdk";
// 显式传递 cookie 字符串(用于服务端调用)
const client = createClient({
appCode: "your-app-code",
cookie: "session_token=xxx; user_id=yyy", // 从浏览器复制的完整 cookie 字符串
models: [
{ tableName: "users", datasetCode: "abc123def456", alias: "users" },
],
});
// Cookie 会自动添加到请求头
const users = await client.models.users.getList();运行态服务:OCR
OCR 通过 client.services.ocr 提供统一入口。SDK 会根据认证模式选择真实服务端路由:
cookie:调用 WebAPIGET /api/ocr/recognize-textclient-ak:调用 Client APIPOST /client/ocropenapi:当前没有对应 OCR 契约,会在发送请求前抛出OCR_AUTH_MODE_UNSUPPORTED
浏览器或显式 Cookie 场景:
import { createClient, OCR_TYPES, OcrType } from "@lovrabet/sdk";
const client = createClient({
appCode: "your-app-code",
authMode: "cookie",
// 浏览器会自动携带 Cookie;Node.js 可在这里传入 cookie
});
const result = await client.services.ocr.recognize({
url: "https://example.com/invoice.png",
type: OcrType.Invoice,
});
console.log(result.text, result.kvData);
console.log(OCR_TYPES); // 可直接用于下拉框或能力发现Client AK 场景:
import { createClient, OcrType } from "@lovrabet/sdk";
const client = createClient({
appCode: "your-app-code",
authMode: "client-ak",
accessKey: process.env.LOVRABET_ACCESS_KEY,
});
const result = await client.services.ocr.recognize({
url: "https://example.com/license.png",
type: OcrType.BusinessLicense,
});当前支持的 OCR 类型为 General、Invoice、Advanced、AdvancedCoordinate、
AdvancedGeneral、IdCard、BankCard、BusinessLicense、DrivingLicense、
CarNumber 和 Table。当前 SDK OCR 接口只接收 URL,不负责本地文件上传。
运行态服务:文件
文件上传与访问 URL 查询通过 client.services.file 提供。SDK 根据认证模式选择路由:
cookie:在 Runtime 域名调用 WebAPI/api/common/uploadFile、/api/common/queryFileUrlclient-ak:调用 Client API/client/uploadFile、/client/queryFileUrlopenapi:当前没有对应文件接口契约
浏览器上传:
const client = createClient({
appCode: "your-app-code",
authMode: "cookie",
});
const uploaded = await client.services.file.upload({ file: input.files[0] });
const access = await client.services.file.queryUrl({
filePath: uploaded.filePath!,
});Cookie 与 Client AK 模式都使用 runtimeDomain。认证模式只决定路由与请求头:
Cookie 使用 WebAPI 路由并携带 Cookie,Client AK 使用 /client/* 路由并携带
X-User-AK。
Node.js 可传入标准 Blob,并显式提供文件名:
const uploaded = await client.services.file.upload({
file: new Blob([buffer], { type: "application/pdf" }),
fileName: "invoice.pdf",
});filePath 是适合业务字段长期保存的稳定引用。fileUrl 默认用于临时预览、OCR
或短时间消费;只有 URL-only 内容需要长期展示时才传 longTerm: true。
🔑 Token 管理
生成单个 Token
import { generateOpenApiToken } from "@lovrabet/sdk";
const result = await generateOpenApiToken({
appCode: "your-app-code",
datasetCode: "your-dataset-id",
accessKey: process.env.LOVRABET_ACCESS_KEY!,
});
console.log(result.token); // 生成的 token
console.log(result.timestamp); // 时间戳
console.log(result.expiresAt); // 过期时间批量生成 Token
import { TokenGenerator } from "@lovrabet/sdk";
const generator = new TokenGenerator(process.env.LOVRABET_ACCESS_KEY!);
const tokens = await generator.generateBatch({
appCode: "your-app-code",
datasets: [
{ name: "users", code: "dataset-001" },
{ name: "posts", code: "dataset-002" },
],
});
// 使用生成的 tokens
console.log(tokens.users.token);
console.log(tokens.posts.token);Token 有效期管理
import { isTokenExpiring, getTokenRemainingTime } from "@lovrabet/sdk";
// 检查 token 是否即将过期
if (isTokenExpiring(timestamp)) {
// 刷新 token
const newToken = await fetchNewToken();
client.setToken(newToken.token, newToken.timestamp);
}
// 获取剩余时间
const remaining = getTokenRemainingTime(timestamp);
console.log(`Token 剩余 ${remaining / 1000} 秒`);🔧 高级用法
预注册配置
import { registerModels, createClient } from "@lovrabet/sdk";
// 注册配置(数组格式)
registerModels({
appCode: "your-app-code",
models: [
{ tableName: "users", datasetCode: "abc123def456", alias: "users", name: "用户" },
{ tableName: "posts", datasetCode: "ghi789jkl012", alias: "posts", name: "文章" },
],
});
// 创建客户端(无参数)
const client = createClient();
// 使用(通过 alias 访问)
const users = await client.models.users.getList();
const posts = await client.models.posts.getList();多环境管理
import { registerModels, createClient } from "@lovrabet/sdk";
// 注册不同环境的配置
registerModels(
{
appCode: "prod-app",
models: [
{ tableName: "users", datasetCode: "prod_abc123", alias: "users" },
],
},
"prod"
);
registerModels(
{
appCode: "dev-app",
models: [
{ tableName: "users", datasetCode: "dev_abc123", alias: "users" },
],
},
"dev"
);
// 创建不同环境的客户端
const prodClient = createClient("prod");
const devClient = createClient("dev");动态更新 Token
// 创建客户端
const client = createClient({
appCode: "your-app-code",
models: [
{ tableName: "users", datasetCode: "abc123def456", alias: "users" },
],
});
// 后续更新 token
client.setToken(newToken, newTimestamp);
// 切换环境
client.setEnvironment("daily");执行自定义 SQL
SDK 提供了 client.api 命名空间,用于执行自定义 SQL 查询和其他通用 API 调用。
基础查询
// 执行 SQL 查询,返回 SqlExecuteResult 对象
const data = await client.api.executeSql("fc8e7777-06e3847d");
// 检查执行结果
if (data.execSuccess && data.execResult) {
console.log(data.execResult);
// [
// { creation_date: '2025-08-13', page_count: 2 },
// { creation_date: '2025-08-19', page_count: 3 },
// ...
// ]
}参数化查询
// 传递参数到 SQL
const data = await client.api.executeSql("fc8e7777-xxxxx", {
userId: "123",
startDate: "2025-01-01",
});
if (data.execSuccess && data.execResult) {
// 处理结果
}带类型提示
// 定义结果类型
interface PageStat {
creation_date: string;
page_count: number;
}
// 使用泛型获得类型安全
const data = await client.api.executeSql<PageStat>("fc8e7777-06e3847d");
if (data.execSuccess && data.execResult) {
data.execResult.forEach((stat) => {
console.log(stat.creation_date); // TypeScript 自动补全
console.log(stat.page_count);
});
}处理查询结果
// 获取查询结果
const data = await client.api.executeSql<PageStat>("fc8e7777-xxxxx");
if (data.execSuccess && data.execResult) {
const results = data.execResult;
const firstResult = results[0];
if (firstResult) {
console.log(
`日期: ${firstResult.creation_date}, 数量: ${firstResult.page_count}`
);
} else {
console.log("无结果");
}
// 过滤结果
const activeItems = results.filter((item) => item.status === "active");
// 查找特定结果
const targetItem = results.find((item) => item.id === "123");
}错误处理
try {
const results = await client.api.executeSql("fc8e7777-xxxxx");
} catch (error) {
if (error instanceof LovrabetError) {
console.error("SQL执行失败:", error.message);
console.error("错误代码:", error.code);
}
}业务域能力(v1.6.0 新增)
SDK 按后端实际注册的路径把能力划分为多个顶级命名空间。双模式能力自动按 authMode 选择 /api 或 /client 前缀;单模式能力在不支持的模式下会直接抛出错误,不会静默请求不存在的路径。
模式支持对照
| 命名空间 | cookie(/api) | client-ak(/client) | openapi |
| --- | --- | --- | --- |
| client.permit | 支持 | 支持 | 抛 API_AUTH_MODE_UNSUPPORTED |
| client.personal.bff | 支持 | 支持 | 抛 PERSONAL_BFF_AUTH_MODE_UNSUPPORTED |
| client.artifact | 抛 ARTIFACT_AUTH_MODE_UNSUPPORTED | 支持 | 抛 ARTIFACT_AUTH_MODE_UNSUPPORTED |
| client.approval | 抛 APPROVAL_AUTH_MODE_UNSUPPORTED | 支持 | 抛 APPROVAL_AUTH_MODE_UNSUPPORTED |
| client.app | 抛 APP_AUTH_MODE_UNSUPPORTED | 支持 | 抛 APP_AUTH_MODE_UNSUPPORTED |
| client.datasetMeta | 抛 DATASET_META_AUTH_MODE_UNSUPPORTED | 支持 | 抛 DATASET_META_AUTH_MODE_UNSUPPORTED |
| client.management | 支持 | 抛 MANAGEMENT_AUTH_MODE_UNSUPPORTED | 抛 MANAGEMENT_AUTH_MODE_UNSUPPORTED |
appCode 会由 SDK 自动注入请求体或查询参数,调用时无需重复传递。
例外:
client.permit.resolveIdentity()后端只在/client注册,cookie 模式会抛IDENTITY_PERMISSION_AUTH_MODE_UNSUPPORTED。
const web = createClient({ appCode: "demo" }); // 默认 cookie
const ak = createClient({
appCode: "demo",
authMode: "client-ak",
accessKey: "ak_xxx",
secretKey: "sk_xxx",
});权限、角色与成员
适用于 cookie 与 client-ak 两种模式,后端同时提供 /api/permit/* 与 /client/permit/* 镜像。
// 角色
const roles = await client.permit.getRoleList({ currentPage: 1, pageSize: 20 });
await client.permit.createRole({
roleType: "CUSTOM",
roleName: "店长",
roleCode: "store_manager",
permits: { page: ["order-list"], dataset: ["orders"] },
});
await client.permit.updateRole({ id: 12, roleName: "新名称" });
await client.permit.deleteRole(12);
await client.permit.getRolePermitList({ needSelf: true, needPublic: false });
// 角色成员
await client.permit.getRoleUserList({ roleId: 12 });
await client.permit.updateRoleUserList({
roleList: [{ id: 12, userList: [{ userId: "u1" }] }],
});
// 应用成员
const employees = await client.permit.queryAppUsers();
// 页面权限
await client.permit.getPagePermit(menuId);
await client.permit.savePagePermit({ menuId, dataCreate: { /* ResourcePermitDTO */ } });
// 资源权限
await client.permit.getResourceMenuList({ roleId: 12 });
await client.permit.saveMenuPermits({ permitList: [] });
await client.permit.saveDatasetPermits({ permitList: [] });
// 数据集与菜单权限
await client.permit.getDatasetPermitList(12);
await client.permit.getDatasetPermitMeta();
await client.permit.getMenuPermitList();
// 当前用户运行态权限快照(仅 client-ak;cookie 模式抛 IDENTITY_PERMISSION_AUTH_MODE_UNSUPPORTED)
const identity = await client.permit.resolveIdentity();
// 应用邀请审批回调(双模式)
const approved = await client.permit.appInviteCallback({ userId: "u1", approve: "agree" });Artifact(仅 client-ak)
const list = await client.artifact.list({ artifactType: "page", favorite: true });
const one = await client.artifact.get(3); // 不存在时为 null
const created = await client.artifact.create({
artifactType: "page",
name: "订单页",
content: "...",
metadata: { version: 1 },
});
await client.artifact.update(created.id!, { content: "新内容" });
await client.artifact.updateFavorite(created.id!, true);审批任务(仅 client-ak)
const todo = await client.approval.listTodo({ currentPage: 1, pageSize: 20 });
const detail = await client.approval.getTask("task-id");
const formDataVersion = detail.formView?.formDataVersion;
// 修改表单时需要详情中的有效版本号;0 是有效初始版本。
if (typeof formDataVersion !== "number" || !Number.isSafeInteger(formDataVersion) || formDataVersion < 0) {
throw new Error("Task detail does not contain a valid form version.");
}
await client.approval.approve({
taskId: "task-id",
approved: true,
comment: "同意",
formPatch: { amount: 100 }, // 只允许当前节点可编辑的顶层字段
formDataVersion, // 原样回传 formView 中的版本,不自行生成或递增
});
await client.approval.transfer({
taskId: "task-id",
targetUserId: "u2",
comment: "转办",
});应用、用户、配置与通知(仅 client-ak)
const apps = await client.app.myApps();
const me = await client.app.loginUserInfo();
await client.app.bindAccount({ provider: "dingtalk", accountId: "acc_1" });
const flag = await client.app.getConfigByKey<boolean>("feature.newImport");
const channels = await client.app.listNotificationChannels("DINGTALK");数据集元信息(仅 client-ak)
const datasets = await client.datasetMeta.list({ code: "orders" });
// 按 code 查单个数据集(后端已标记 @Deprecated,优先用 getDrivenData)
const dataset = await client.datasetMeta.queryByCode("orders");
// id 与 code 至少传一个;返回结构随 doVersion 为 v1 / v2
const driven = await client.datasetMeta.getDrivenData<DoProtocol>({ code: "orders" });脚本缓存与日志(仅 cookie)
await client.management.clearScriptCache();
const logs = await client.management.searchScriptLogs({
startTime: Date.now() - 3600_000,
endTime: Date.now(),
level: "ERROR",
keyword: "timeout",
limit: 200,
});自定义 SQL 列表查询(仅 client-ak)
client.sql 是联合类型,query() 只由 ClientAkSqlClient 提供,需要先用 instanceof 收窄。
query() 必须传入已有 SQL 的 sqlCode;仅传 sqlName 或 appCode 不足以完成鉴权。查询应用下全部 SQL 使用 listSqls()。
import { ClientAkSqlClient } from "@lovrabet/sdk";
if (client.sql instanceof ClientAkSqlClient) {
const page = await client.sql.query({
sqlCode: "2305f915-dd48cd4c", // 替换为实际存在的 SQL 编码
sqlName: "order",
currentPage: 1,
pageSize: 20,
});
// page.tableData / page.paging
// 不分页,返回应用下全部自定义 SQL
const sqls = await client.sql.listSqls();
}BFF 端点查询(仅 client-ak)
import { ClientAkBffClient } from "@lovrabet/sdk";
if (client.bff instanceof ClientAkBffClient) {
// 脚本不存在或为 personalOnly 时返回 null
const summary = await client.bff.getEndpoint("sqlTest");
// 当前应用下全部独立端点脚本
const endpoints = await client.bff.listEndpoints();
// 按脚本主键查询详情(personalOnly 仅创建者本人可读)
const script = await client.bff.getScript(12);
}数据集可恢复导入(仅 client-ak)
导入能力位于 ClientAkModel。由于 client.models.X 的静态类型是 BaseModelMethods,需要显式收窄为 ClientAkModel。
import { ClientAkModel } from "@lovrabet/sdk";
const model = client.getModel("orders") as ClientAkModel;
const contract = await model.importContract<{
schemaFingerprint: string;
maxBatchSize: number;
maxBatchBytes: number;
}>();
const { ids, count } = await model.importBatch({
schemaFingerprint: contract.schemaFingerprint,
items: rows, // 1~500 条
// taskId: 42, // auto-id 导入时传入本次任务的正安全整数 ID
});
// body 只传 { ids }
const { items } = await model.importReadback<Order>(ids);
// 图片导入为 multipart/form-data
await model.importImage({
importId: "imp-1",
contentHash: "sha256-...",
file: blob,
fileName: "logo.png",
});
await model.importImageReadback({
importId: "imp-1",
contentHash: "sha256-...",
type: "image/png",
size: blob.size,
});Personal BFF 管理(双模式)
const scripts = await client.personal?.bff.list();
const script = await client.personal?.bff.get(12);
await client.personal?.bff.create({
functionName: "myFn",
scriptContent: "...",
description: "说明",
});
await client.personal?.bff.update(12, { description: "新说明" });
await client.personal?.bff.execute({ scriptId: 12, params: { id: 1 } });📖 API 文档
主要导出
import {
// 客户端
createClient,
// Token 生成
generateOpenApiToken,
TokenGenerator,
isTokenExpiring,
getTokenRemainingTime,
// 配置管理
registerModels,
getRegisteredModels,
getRegisteredConfigNames,
unregisterModels,
clearAllRegistrations,
// 环境配置
getApiEndpoint,
getAvailableRegions,
// 错误处理
LovrabetError,
// API 命名空间
ApiNamespace,
// 业务域命名空间(v1.6.0)
PermitClient,
ArtifactClient,
ApprovalClient,
AppClient,
DatasetMetaClient,
ManagementClient,
ClientAkSqlClient,
ClientAkBffClient,
// 运行态服务命名空间
ServicesNamespace,
FileClient,
OcrClient,
OcrType,
OCR_TYPES,
// 认证管理(高级用法)
AuthManager,
// 模型类(高级用法和扩展)
AbstractBaseModel,
OpenApiModel,
WebApiModel,
ClientAkModel,
ModelFactory,
// 排序枚举
SortOrder,
// TypeScript 类型
type ClientConfig,
type LovrabetClient,
type ModelConfig,
type ModelsConfig,
type TokenResult,
type GenerateTokenParams,
type ListResponse,
type ListParams,
type FilterParams,
type WhereCondition,
type FieldCondition,
type ConditionOperator,
type SortList,
type SelectOption,
type SelectOptionsParams,
type SqlExecuteRequest,
type SqlExecuteResult,
type CustomSqlQueryRequest,
type UserCustomSqlDTO,
type DataList,
type PageResult,
type DatasetRequest,
type DatasetMeta,
type DatasetDrivenDataQuery,
type RuntimeArtifactDTO,
type ArtifactUpsertRequest,
type ApprovalFlowTaskItem,
type ApprovalFlowTaskDetail,
type ApprovalFlowApproveRequest,
type ApprovalFlowTransferRequest,
type AppRoleVO,
type AppRoleCreateParam,
type AppRoleUpdateParam,
type RoleUserUpdateParam,
type ResourcePermitDTO,
type MenuPermitDTO,
type RuntimeIdentityPermission,
type UserAppRelation,
type EndpointScriptSummary,
type ScriptDefinition,
type AppEmployee,
type LoginUserInfo,
type SmartApp,
type NotificationChannelConfig,
type ScriptLogQuery,
type DatasetImportBatchRequest,
type DatasetImportBatchResult,
type DatasetImportReadbackResult,
type PersonalBffScript,
type PersonalBffUpsertRequest,
type Environment,
type BaseModelMethods,
type LovrabetServices,
type OcrService,
type OcrRecognizeRequest,
type OcrRecognizeResponse,
} from "@lovrabet/sdk";客户端配置
interface ClientConfig {
appCode?: string; // 应用代码
accessKey?: string; // OpenAPI 密钥(服务端)
secretKey?: string; // 密钥(可选)
token?: string; // 预生成 token(浏览器)
timestamp?: number; // token 时间戳
region?: "cn" | "id"; // 官方节点,默认 cn
runtimeDomain?: string; // 企业独立部署覆盖,优先于 region
models?: ModelsConfig; // 模型配置
}官方印尼节点:
const client = createClient({
appCode: "app-xxx",
region: "id"
});企业独立部署直接传入最终 runtimeDomain:
const client = createClient({
appCode: "app-xxx",
runtimeDomain: "https://runtime.customer.example.com"
});解析优先级为:runtimeDomain → 兼容字段 serverUrl → 页面注入的
RUNTIME_API_DOMAIN → region 对应的官方 Runtime Domain → 默认中国大陆节点。
SDK 会对显式 Runtime 地址去除首尾空白和尾斜杠,并拒绝非法 URL、凭证、
query 或 fragment。未传 region 时默认 cn;显式传入 cn / id 以外的值
会抛出 INVALID_REGION,不会静默请求其他区域。
路由配置来源
SDK 不会在运行时访问 lovrabet-routing 仓库,也不会打开 CLI 的企业路由
JSON 文件。官方国家/地区的 Runtime Domain 在开发阶段由独立
lovrabet-routing 仓库生成到
src/config/generated-region-endpoints.ts,然后随 @lovrabet/sdk npm 包一起
发布。生成文件包含路由目录 SHA-256,用于检查三个消费者是否来自同一份真源。
lovrabet-routing/catalog/official-routing.v1.json
│
│ npm run generate
▼
src/config/generated-region-endpoints.ts
│
│ SDK build and npm publish
▼
createClient({ region })官方目录中的 Domain 可以是所有消费者共用的字符串,也可以按消费者覆盖。
生成 SDK 快照时固定以 lovrabet-node-sdk 身份解析,优先读取 SDK 专属值,
否则读取 default;两者都没有则生成失败。
SDK 只消费 runtimeDomain。userDomain、apiDomain、skillDomain、
kbDomain 和 appDomain 属于 CLI 或其他平台能力,不应在 SDK 内复制路由
判断。
企业部署由应用或上游工具把最终 runtimeDomain 传给 SDK。Rabetbase 生成的
前端项目可通过 sdk-config.ts 注入该值;SDK 本身不依赖 Node.js 文件系统,
因此同一行为可用于浏览器和 Node.js。企业清单中的 lovrabet-node-sdk 专属值
只有在上游以 SDK 身份解析后才会生效,不能仅把清单文件放在项目中。
路由维护与发布
维护者不得直接编辑 src/config/generated-region-endpoints.ts。新增或调整官方
节点时,应在 lovrabet-routing 修改真源并执行 npm run generate,然后在本仓
执行类型检查、完整测试和构建。只发布路由仓不会改变已安装 SDK;生成快照必须
随新的 SDK 版本发布,并与两个 CLI 的快照放在同一发布批次。
CRUD 操作
// 查询列表(返回 tableData 与 paging: totalCount/currentPage/pageSize)
const response = await client.models.users.getList({
currentPage: 1,
pageSize: 20,
});
console.log(response.paging.totalCount); // 总数量
console.log(response.tableData); // 数据列表
// 查询列表(带排序)
const sortedUsers = await client.models.users.getList(
{ currentPage: 1, pageSize: 20 },
[
{ createTime: "desc" }, // 按创建时间降序
{ name: "asc" }, // 再按名称升序
]
);
// 获取单条记录
const user = await client.models.users.getOne("user-id");
// 创建
const newUser = await client.models.users.create({
name: "John Doe",
email: "[email protected]",
});
// 更新(支持单条和批量)
// 单条更新
const updated = await client.models.users.update("user-id", {
name: "Jane Doe",
});
// 批量更新(最多 1000 条)
const updatedBatch = await client.models.users.update(
["user-id-1", "user-id-2", "user-id-3"],
{ status: "inactive" }
);
// 删除(支持单条和批量)
// 单条删除
await client.models.users.delete("user-id");
// 批量删除(最多 1000 条)
await client.models.users.delete(["user-id-1", "user-id-2", "user-id-3"]);
// 获取下拉选项(WebAPI 模式)
const options = await client.models.users.getSelectOptions({
code: "user_id", // 用作选项值的字段名
label: "user_name", // 用作显示文本的字段名
});
// 返回: [{ label: '张三', value: 'user001' }, { label: '李四', value: 'user002' }]
// 聚合查询
const result = await client.models.orders.aggregate({
where: { status: { $eq: "completed" } },
groupBy: ["region"],
aggregate: [
{ column: "id", type: "COUNT", alias: "orderCount" },
{ column: "amount", type: "SUM", alias: "totalAmount" },
{ column: "amount", type: "AVG", alias: "avgAmount" }
],
currentPage: 1,
pageSize: 20
});🔍 高级过滤查询(推荐)
filter() 方法提供比 getList() 更强大的查询能力,支持复杂的条件筛选、字段选择和排序。
基础用法
// 简单条件查询
const result = await client.models.users.filter({
where: {
age: { $gte: 18 } // 年龄 >= 18
},
currentPage: 1,
pageSize: 20
});
console.log(result.tableData); // 数据列表
console.log(result.paging.totalCount); // 总数量复杂条件组合
const result = await client.models.users.filter({
where: {
$and: [
// 年龄区间
{ age: { $gte: 18, $lte: 45 } },
// 国家在集合内
{ country: { $in: ['中国', '美国', '日本'] } },
// VIP 字段非空
{ vip: { $ne: null } },
// 名称模糊匹配
{ name: { $contain: 'hello' } }
]
},
// 选择返回的字段
select: ['id', 'name', 'age', 'country', 'lastLoginAt'],
// 多字段排序:先按登录时间倒序,再按名称升序
orderBy: [
{ lastLoginAt: "desc" },
{ name: "asc" }
],
currentPage: 1,
pageSize: 20
});支持的操作符
| 操作符 | 说明 | 示例 |
|--------|------|------|
| $eq | 等于 | { status: { $eq: 'active' } } |
| $ne | 不等于 | { status: { $ne: 'deleted' } } |
| $gt | 大于 | { age: { $gt: 18 } } |
| $lt | 小于 | { age: { $lt: 65 } } |
| $gte(推荐)/ $gteq(兼容旧写法) | 大于等于 | { age: { $gte: 18 } } |
| $lte(推荐)/ $lteq(兼容旧写法) | 小于等于 | { age: { $lte: 65 } } |
| $in | 在集合内 | { country: { $in: ['中国', '美国'] } } |
| $contain | 包含(模糊匹配) | { name: { $contain: 'test' } } |
| $startWith | 以...开头 | { name: { $startWith: 'Mr.' } } |
| $endWith | 以...结尾 | { email: { $endWith: '@example.com' } } |
| $notNull | 非空判断 | { app_code: { $notNull: true } } |
逻辑连接符
// $and - 所有条件都满足
const result = await client.models.users.filter({
where: {
$and: [
{ age: { $gte: 18 } },
{ status: { $eq: 'active' } }
]
}
});
// $or - 任一条件满足
const result = await client.models.users.filter({
where: {
$or: [
{ country: { $eq: '中国' } },
{ country: { $eq: '美国' } }
]
}
});
// 嵌套条件
const result = await client.models.users.filter({
where: {
$and: [
{ age: { $gte: 18 } },
{
$or: [
{ country: { $eq: '中国' } },
{ country: { $eq: '美国' } }
]
}
]
}
});字段选择
// 只返回指定字段
const result = await client.models.users.filter({
select: ['id', 'name', 'email'],
currentPage: 1,
pageSize: 20
});
// 访问 JSON 字段
const result = await client.models.users.filter({
select: ['id', "value['name']", 'age'],
currentPage: 1,
pageSize: 20
});⚠️ 操作限制
OpenAPI 模式暂不支持以下操作:
- ❌
delete()- 删除操作(OpenAPI 不支持;WebAPI 与 Client AK 模式可用) - ❌
getSelectOptions()- 获取下拉选项(OpenAPI 不支持;WebAPI 与 Client AK 模式可用)
// OpenAPI 模式调用 delete 会抛出错误
try {
await client.models.users.delete("user-id");
} catch (error) {
// Error: OpenAPI 模式暂不支持 delete 操作。如需删除数据,请使用 WebAPI 模式(Cookie 认证)。
}
// 如需删除,请使用 WebAPI 模式(Cookie 认证)
const webApiClient = createClient({
appCode: "your-app-code",
// 不提供 token/accessKey,自动使用 Cookie 认证
models: [
{ tableName: "users", datasetCode: "abc123def456", alias: "users" },
],
});
await webApiClient.models.users.delete("user-id"); // ✅ 可以使用🛡️ 安全最佳实践
❌ 错误做法
// 绝不要在客户端代码中硬编码 accessKey
const client = createClient({
authMode: "openapi",
accessKey: "sk_live_xxx", // 危险!会暴露密钥
});✅ 正确做法
服务端:
// 使用环境变量存储敏感信息
const client = createClient({
authMode: "openapi", // 使用 accessKey 时必须显式声明;缺省 authMode 一律走 cookie,accessKey 会被忽略
accessKey: process.env.LOVRABET_ACCESS_KEY,
});客户端:
// 使用服务端生成的 token(OpenAPI 预计算 token 必须配对 timestamp)
const { token, timestamp } = await fetch("/api/token").then((r) => r.json());
const client = createClient({ authMode: "openapi", token, timestamp });🎯 选择正确的认证模式
| 环境 | 认证方式 | 使用场景 |
| -------------------- | ----------- | ----------------------- |
| Node.js 服务端 | accessKey | SSR、API 路由、后端服务 |
| 浏览器(未登录) | token | 公开数据访问、匿名用户 |
| 浏览器(已登录) | Cookie | 已认证用户、私有数据 |
📚 文档
- 文档中心 - 完整的文档导航
- 使用指南 - 完整的 SDK 使用指南 ⭐
- OpenAPI 完整指南 - OpenAPI 实现原理和使用案例 ⭐
- 使用指南 - 详细的使用文档和示例
- API 参考 - 完整的 API 文档
- 架构分析 - SDK 内部架构说明
📝 What's New
v1.2.5 (2026-01-02)
错误处理增强 (Error Handling):
🤖 AI 友好的错误信息 -
LovrabetError新增description字段,提供 LLM 可理解的错误描述和修复建议try { await client.models.users.create({ invalid_field: 'value' }); } catch (error) { if (error instanceof LovrabetError) { console.log(error.message); // "invalid_field列不存在" console.log(error.code); // "SERVER_ERROR" console.log(error.description); // "服务端返回错误: invalid_field列不存在。错误类型: 参数错误。状态码: 400。建议: 字段 \"invalid_field\" 不存在,请检查字段名是否拼写正确; 使用 MCP 工具获取数据集的正确字段列表" } }错误类型覆盖:
MODEL_NOT_FOUND- 模型不存在,提供可用模型列表CONFIG_NOT_FOUND- 配置不存在,提供已注册配置列表SERVER_ERROR- 服务端参数错误,提供字段修复建议401 权限不足- 登录过期/appCode无权限/HTTPS/CORS 等建议TIMEOUT- 请求超时,提供超时配置建议
v1.2.2 (2025-12-30)
重大更新 (Major Changes):
🏗️ 模型管理器重构 - 支持基于
datasetCode的统一访问方式// 新格式:数组配置(推荐) const client = createClient({ appCode: "your-app-code", models: [ { tableName: "users", datasetCode: "ds-001", alias: "users", name: "用户" }, { tableName: "posts", datasetCode: "ds-002" } ] }); // 多种访问方式 client.models.users.getList(); // 通过 alias 访问 client.models["dataset_ds-001"].getList(); // 通过 datasetCode 访问 client.getModel(0).getList(); // 通过索引访问核心改进:
- 使用
datasetCode作为模型的唯一标识符 - 支持
dataset_{datasetCode}前缀格式的标准化访问 - 新增数组格式配置,同时保持对象格式向下兼容
- 老格式对象配置中的 key 自动作为默认 alias
- 使用
✨ 模型管理器增强 - 新增多个辅助方法
// 获取模型详细信息(用于 UI 展示) const details = client.getModelListDetails(); // [ // { value: 'dataset_ds-001', label: 'users[用户]', alias: 'users', name: '用户' }, // { value: 'dataset_ds-002', label: 'dataset_ds-002' } // ] // 获取所有别名 const aliases = client.models.listAliases();🔧 环境管理方法更新
// 切换环境(方法名变更) client.setEnvironment("daily"); // 新方法名 client.getEnvironment(); // 获取当前环境
代码质量 (Code Quality):
- 清理过期文档
- 增强类型安全
- 统一代码规范
v1.1.24 (2025-12-03)
新增功能 (New Features):
✨ Excel 导出功能 - 新增
excelExport()方法,支持将数据集导出为 Excel 文件// 导出所有数据 const fileUrl = await client.models.users.excelExport(); window.open(fileUrl, '_blank'); // 按条件筛选导出 const fileUrl = await client.models.users.excelExport({ status: 'active', createTime: '2025-01-01' });核心特性:
- 返回可下载的文件 URL(OSS 地址)
- 支持传递筛选条件(
ListParams) - 直接使用
window.open()打开下载 - 仅 WebAPI 模式支持,OpenAPI 模式不支持
v1.1.22 (2025-11-21)
新增功能 (New Features):
✨ 新增复杂查询接口 filter() - 以同时支持WebAPI和OpenAPI
const client = createClient({ appCode: "your-app-code", authMode: "openapi", // 使用 accessKey 时必须显式声明;缺省 authMode 一律走 cookie accessKey: process.env.LOVRABET_ACCESS_KEY, // OpenAPI 模式下使用 filter(v1.1.22+) models: [ { tableName: "users", datasetCode: "abc123def456", alias: "users" }, ], }); // 现在可以使用 filter 进行复杂查询 const result = await client.models.users.filter({ where: { $and: [ { age: { $gte: 18, $lte: 45 } }, { country: { $in: ["中国", "美国"] } }, ], }, select: ["id", "name", "age"], orderBy: [{ createTime: "desc" }], currentPage: 1, pageSize: 20, });- 支持所有条件操作符(
$eq,$ne,$gte,$lte,$in,$contain等) - 支持
$and/$or逻辑组合 - 支持字段选择(
select)和多字段排序(orderBy) - 与 WebAPI 模式保持一致的 API 接口
- 支持所有条件操作符(
v1.1.19 (2025-11-10)
新增功能 (New Features):
✨ SQL API 支持 - 新增
client.api.executeSql()方法,支持执行自定义 SQL 查询// 执行 SQL 查询 const data = await client.api.executeSql("fc8e7777-06e3847d"); // 应用层检查执行结果 if (data.execSuccess && data.execResult) { data.execResult.forEach((row) => { console.log(row); }); }核心特性:
- 支持参数化查询,防止 SQL 注入
- 完整的 TypeScript 类型支持(泛型)
- 返回
{ execSuccess: boolean, execResult?: T[] }结构 - 应用层负责检查
execSuccess状态
适用场景:
- 复杂数据统计和聚合查询
- 跨表关联查询
- 自定义报表数据获取
- 灵活的数据分析需求
🏗️ API 命名空间架构 - 新增
client.api命名空间,用于管理通用 API 调用
详细说明参考:说明文档
v1.1.17 (2025-10-18)
新增功能 (New Features):
✨ 下拉选项接口 - 新增
getSelectOptions方法,用于快速获取下拉选项数据// 获取下拉选项(WebAPI 模式) const options = await client.models.users.getSelectOptions({ code: "user_id", // 用作选项值的字段名 label: "user_name", // 用作显示文本的字段名 }); // 返回格式: // [ // { label: '张三', value: 'user001' }, // { label: '李四', value: 'user002' }, // { label: '王五', value: 'user003' } // ] // 在 React 中使用 <Select> {options.map((option) => ( <Option key={option.value} value={option.value}> {option.label} </Option> ))} </Select>;- 返回标准化的
{ label, value }格式,直接适用于前端组件 - 自动从数据表字段映射为选项数据
- 适用于 Select、Radio、Checkbox 等表单组件
- 仅 WebAPI 模式(Cookie 认证)支持
- 返回标准化的
操作限制说明 (Restrictions):
⚠️ 明确 OpenAPI 模式限制 - OpenAPI 模式暂不支持以下操作:
- ❌
delete()- 删除操作(如需删除,请使用 WebAPI 模式) - ❌
getSelectOptions()- 获取下拉选项(仅 WebAPI 模式支持)
// OpenAPI 模式调用这些方法会抛出明确的错误提示 try { await client.models.users.delete("user-id"); } catch (error) { // Error: OpenAPI 模式暂不支持 delete 操作。如需删除数据,请使用 WebAPI 模式(Cookie 认证)。 }- ❌
类型定义 (Type Definitions):
新增
SelectOption接口:interface SelectOption { label: string; // 显示文本 value: string; // 选项值 }新增
SelectOptionsParams接口:interface SelectOptionsParams { code: string; // 用作选项值的字段名 label: string; // 用作显示文本的字段名 }
v1.1.16 (2025-10-15)
新增功能 (New Features):
✨ 列表排序支持 -
getList方法现在支持多字段排序功能// 单字段排序 const users = await client.models.users.getList( { currentPage: 1, pageSize: 20 }, [{ createTime: "desc" }] ); // 多字段排序 const products = await client.models.products.getList( { currentPage: 1, pageSize: 20 }, [ { priority: "desc" }, // 优先级降序 { createTime: "desc" }, // 创建时间降序 { name: "asc" }, // 名称升序 ] );- 使用简洁的 key-value 格式:
{ "字段名": "asc" | "desc" } - 支持多字段组合排序
- 同时支持 OpenAPI 和 WebAPI 两种模式
- 向后兼容,sortList 参数可选
- 使用简洁的 key-value 格式:
v1.1.14 (2025-10-11)
新增功能 (New Features):
✨ OpenAPI Create/Update 支持 - OpenAPI 模式现已支持完整的 CRUD 操作
// 创建记录 const newUser = await client.models.users.create({ name: "John Doe", email: "[email protected]", }); // 更新记录 const updated = await client.models.users.update(userId, { name: "Jane Doe", });- 认证信息自动添加到请求头(X-Token、X-Time-Stamp)
- 请求体只包含业务数据(
appCode、datasetCode、paramMap) - 与查询接口保持一致的架构设计
🐛 调试日志功能 - 新增
debug选项,便于开发调试const client = createClient({ appCode: "your-app", authMode: "openapi", // 使用 accessKey 时必须显式声明;缺省 authMode 一律走 cookie accessKey: process.env.ACCESS_KEY, options: { debug: true, // 启用调试日志 }, });- 在控制台输出完整的 HTTP 请求信息(URL、Headers、Body)
- 便于排查接口调用问题
- 推荐仅在开发环境启用
文档更新 (Documentation):
- 📚 新增 调试指南 - 详细说明如何使用调试功能
- 📚 更新 OpenAPI 文档 - 添加 create/update 接口说明和软删除建议
- 📚 优化 CSRF 防护说明 - 明确应用层与 API 层的职责分离
v1.1.11 (2025-10-09)
新增功能 (New Features):
✨ 后端 Token 生成器 - 新增
generateOpenApiToken()函数,支持服务端生成 Tokenimport { generateOpenApiToken } from "@lovrabet/sdk"; const { token, timestamp } = await generateOpenApiToken({ appCode: "your-app", datasetCode: "ds-001", accessKey: process.env.ACCESS_KEY, });
v1.1.8 (2025-09-30)
新增功能 (New Features):
- 🛡️ 浏览器安全警告 - 新增浏览器环境 accessKey 使用安全警告机制
- 在浏览器中使用 accessKey 会触发明确的错误提示
- 引导开发者使用正确的 Token 认证方式
改进 (Improvements):
- 📚 文档优化 - 修正浏览器安全警告中的文档链接地址
v1.1.5 (2025-09-27)
重大变更 (Breaking Changes):
- 🔴 统一认证架构 - 重构认证系统,实现 OpenAPI 双模式支持(AccessKey + Token)
新增功能 (New Features):
- ✨ OpenAPI 双模式 - 支持服务端 AccessKey 和浏览器 Token 两种认证方式
- 🔑 Token 管理工具 - 新增 Token 生命周期管理相关工具函数
🤝 反馈
如遇到问题或有功能建议,欢迎提交 Issue!
📄 许可证
专有软件(Proprietary) - 详见 LICENSE 文件
本 SDK 为 Lovrabet 开放平台的专有组件:
- ✅ 允许使用 - 可以安装并在项目中集成本 SDK
- ✅ 应用分发 - 可以分发包含本 SDK 的应用程序
- ✅ 商业使用 - 可用于个人和商业项目
- ❌ 不得修改 - 不得修改、反编译或逆向工程
- ❌ 不得单独分发 - 不得单独复制或分发本 SDK
- ❌ 源代码不公开 - 本 SDK 为闭源软件
如需商业授权或有疑问,请访问 lovrabet.com
独立知识库服务
通过 createKnowledgeClient({ kbServiceDomain, accessKey }) 调用个人知识库的 page/getOne/create/update 和知识搜索。KB origin 必须由调用方显式配置,客户端不从 Runtime 或 Region 推导地址。每个方法只发送一次请求;文件读取、分页聚合、CAS 前读和写后核对由调用方负责。
完整请求、返回类型和错误处理见 Knowledge Service Contract。
