@ 12- apps/ report- builder (创建器)
不可知报告-构建器库(FUT-130):报告是 声明 JSON
规格 ——由UI作者,每个租户省下,或由LLMs通过MCP撰写
对照已登记东道方 字段目录,编译为中性
查询 IR,通过主机执行 数据源适配器,并交付给
可序列 表格模型 或一个 @12-apps/ui 图表Spec。 。 。 .
ReportSpec (JSON) ──parse──▶ zod ──compile──▶ CompiledQuery IR ──execute──▶ rows ──render──▶ table | ChartSpec
▲ ▲
FieldCatalog ReportDataSource (host, tenant-scoped)
便携式合同: 从apps/*进口的零没有Prisma 没有Next.js..
而且,从4.0.0, 没有主机数据。目录、内置报告、
启动器、块模板、实体-许可地图、适配器和
租户时钟是东道主的 以配置方式到达
管道、终点、屏幕、周期、文件寿命周期和
一次许可( e)reports:manage)为自己的编辑. 唯一的工作空间
依赖是 @12-apps/ui/charts (仅类型,用于 ChartSpec) (中文(简体) ).
安保模式
- 光谱是 数据,永远不要代码 ——它们只能命名主机所设置的字段 目录。 无法表达原始 SQL 或物理列.
- 租户范围是适配者的工作: 每一个
execute()执行 必须限制行 呼叫的租户,无论要求什么. - 未知的实体/领域/汇总被拒
compileReport与 可操作信件( 它们列出可用的 IS) 因此 LLM 作者可以 自我纠正. maxRows硬盖输出行计数(通过 LLM- facing enders).
东道国一体化(三个步骤)
import {
defineCatalog,
createMemoryDataSource, // or your own ReportDataSource
runReport,
} from '@12-apps/report-builder';
// 1. Catalog: which entities/fields may be queried.
const catalog = defineCatalog({
entities: {
orders: {
label: 'Pedidos',
fields: {
createdAt: { label: 'Data', type: 'date', role: 'dimension' },
method: { label: 'Forma de pagamento', type: 'string', role: 'dimension' },
totalCents: { label: 'Receita', type: 'money', role: 'measure' },
},
},
},
});
// 2. Adapter: execute(query) => rows. MUST scope to the tenant.
const adapter = createMemoryDataSource({ orders: myRows });
// A real host implements ReportDataSource over its own storage —
// e.g. Prisma reads WHERE clientId = tenant, folded through
// executeCompiledQuery (exported) or translated to groupBy.
// 3. Run a spec.
const result = await runReport(
{
entity: 'orders',
dimensions: [{ field: 'createdAt', timeGrain: 'day' }],
measures: [{ field: 'totalCents' }],
presentation: { kind: 'chart', chartType: 'area' },
},
{ catalog, adapter, maxRows: 500 },
);
// result.render is serializable:
// { kind: 'chart', chartSpec, rows } → render with <SpecChart /> from @12-apps/ui/charts
// { kind: 'table', columns, rows } → render with any table; columns carry format hints
光谱形状( v1)
{
"version": 1,
"entity": "orders",
"dimensions": [{ "field": "createdAt", "timeGrain": "day" }], // 0–2; grains: day|week|month
"measures": [{ "field": "totalCents", "aggregation": "sum", "alias": "revenue" }], // 1–10
"filters": [{ "field": "method", "operator": "in", "values": ["PIX"] }], // eq|neq|in|gte|lte|between
"sort": [{ "by": "revenue", "direction": "desc" }],
"limit": 100,
"presentation": { "kind": "chart", "chartType": "area" } // or { "kind": "table" }
}
总数: sum | avg | count | min | max | count_distinct | p50 | p90 | p95 | ratio,允许每个字段类型/作用(缺省为
sum; 允许尺寸 count编号 :count_distinct) (中文(简体) ). 图表显示需要
精确的一维(皮/杜努,另外精确的一度)
还有一张桌子.
百分率、比率、抑制和格式(FUT-454)
{
"entity": "order_items",
"timeZone": "America/Sao_Paulo", // IANA; date buckets use THIS clock
"measures": [
// Continuous percentile: linear interpolation (R-7), not nearest-rank.
{ "field": "prepSeconds", "aggregation": "p90", "alias": "p90",
"minSample": 20 }, // below 20 eligible rows → suppressed
// Ratio OF SUMS — sum(late) / sum(lines), never the mean of row ratios.
{ "field": "lateLines", "aggregation": "ratio", "denominator": "lines",
"alias": "lateRate", "format": "percent" }
]
}
minSample: N在执行器中执行: N以下符合条件的行 单元格SUPPRESSED标记和数字从未计算到 反应,因此无法从其他领域回收。 合格=行数 包含非正数输入(用于ratio,带分母的行。 被镇压 单元格排序为空,因此排名也不能泄露隐藏顺序.- 除以0为
null没有0- 做成同一个em-dash a 缺少值得到. format(测量,或在目录字段中宣布) 表格列、 KPI 平板、 CSV 导出和 API 元数据:duration(单位:秒)1h 23m编号 :12m 30s和percent(a 0-1 比率)84%,一个 十进制(在有意义的情况下)brl | integer | decimal | text图表 提交duration作为简单的小数@12-apps/ui还没有持续时间轴 .formatReportValue编号 :formatKpiFigure单一格式接合; sPA的制作人代表不能不同意这样屏幕和CSV.
公约
- 钱是整数百分之一 结束到结束; 渲染地图
money字段改为 页:1brl数字格式@12-apps/ui/charts。 。 。 . - 约会桶使用租户的IANA区 (单位:千美元)
spec.timeZone→CompileOptions.timeZone——协调世界时),已解决CompiledQuery.timeZone。 。 。 . 上个月是America/Sao_Paulo:一个国家的贸易日 每个不给名字的打电话者 挂起的地表从未到达过ReportBuilderServerConfig.timeZone需要时间。 转换 纯洁Intl,不是SQL,因为管道取出行并叠入 process – PostgreSQL, PGlite 和内存上相同的桶. 范围 WINDOWS 保持协调世界时半开[from, toExclusive)。 。 。 .truncateDateToGrain实值 导出( 未通过区域时为UTC) 因此主机适配器折叠行中 程序桶与参考执行相同. executeCompiledQuery(rows, query)作为语义引用导出 : 主机适配器必须与其过滤器/组/组行为相匹配.
挂载地表
上面的引擎是图书馆的一半. @12-apps/report-builder/server 挂载
二、结论和建议 /react 搭载SCREENS, 和两个主机
需要配置的词汇 :
const { routes } = createApiReportBuilder({
catalog, // yours
adapter: ({ actor, window }) => …, // yours, tenant + window scoped
db: () => getSavedReportDb(), // yours, through the structural seam
timeZone: 'Europe/Lisbon', // yours — required, no fallback
entityPermission: { loans: 'library:lending:read' }, // yours, every entity
systemReports: [], // yours; `[]` is a complete answer
starters: { loans: … }, // yours; `{}` is a complete answer
copy: PT_BR_REPORT_ENGINE_COPY, // yours — the words a run renders
messages: PT_BR_REPORT_SERVER_MESSAGES, // yours — the words a refusal reads
});
const { page } = createWebReportBuilder({
tenantSlug,
surface: { systemReports, systemDashboards, sections, blockTemplates, timeZone },
copy: {
engine: PT_BR_REPORT_ENGINE_COPY, // the same object the API takes
blankTemplate: PT_BR_BLANK_BLOCK_TEMPLATE_COPY,
},
});
词是你的,没有默认(FUT-760)
copy 财务报告和审定财务报表 messages 要求货船不退后。 用过
在 pt-BR 中编译—— spec 句子,列标题,理由 a
演示文稿不可用, API 的每次拒绝都给出了答案
产品中的葡萄牙语是每个被收养者的沉默默认,没有字段可读
拒绝它,没有没有说.
作为NAMED包装的已退役措辞的船舶PT_BR_REPORT_ENGINE_COPY, (中文(简体) ),
PT_BR_REPORT_SERVER_MESSAGES, (中文(简体) ), PT_BR_BLANK_BLOCK_TEMPLATE_COPY因此通过
他们改变不了屏幕上的任何内容
d. 通过相同的 ReportEngineCopy 对象对两个半径:运行的主机
报告服务器侧并把它们放入浏览器中,否则就可以
在导出时打印一列,在屏幕上打印一列.
翻译是指写出自己的对象,而不是编辑包. 请注意:
copy.spec.sentence 是一个TEMPLATE,而不是一个连接:它接收到
组装条款(措施、实体、集团By、拆分By、过滤器、限制)和
决定他们所说的顺序, 因为文字顺序是部分
不是幸存的翻译.
直接挂载屏幕组件, 外部 createWebReportBuilder,掷出:
复制件没有有意义的空值, 所以第一个时会指定缺少的提供者
转换而不是隐藏在空白标签后.
两者都对大会投出一个布线错误——一个未被放大的实体,一个内在的
不要根据您的目录来编译, 一个命名一个报告的仪表板块
这个区域无法解决
静悄悄地不见了一块. ADOPTING.md 有完整的合同和
3.0.1 — 4.0.0 移徙表.
主持人也作曲 REPORT_BUILDER_PERMISSIONS 输入自己的非洲区域局目录:一
编号, reports:manage,该选项将打开此软件包的编辑器及其
/reports/custom/** 写作。 阅读由主机自行决定
entityPermission 等级.