客户端
客户端
页面只导入一个值:import { bolt } from '$bolt'。$bolt 是 @norbital-ai/bolt/page 的生成别名,由工作区的名称索引以与服务器代码相同的类型进行类型化,因此未知的集合、字段、查询、操作、输入或消息键在 bolt check 中都是类型错误,与服务器端完全一致。
<script lang="ts">
// src/data/collection/job_assignments/+representation.svelte (templates/field-operations, excerpt)
import { bolt, live } from '$bolt';
import type { RecordView } from '@norbital-ai/ui';
let { view }: { view: RecordView<'job_assignments'> } = $props();
const record = $derived(view.mode === 'update' ? view.record : null);
// one row by id, with the fields it names
const site = live(() => record === null ? null
: bolt.live(bolt.get('sites', record.site_id, { name: true })));
// a page of rows: every read states limit or all
const variations = live(() =>
record == null
? null
: bolt.live(bolt.read('variation_requests', {
where: { job_assignment_id: { eq: record.id } },
orderBy: { requested_at: 'desc' },
limit: 50
}))
);
</script> 读取
read(collection, { where?, select?, orderBy?, search?, limit | all })——一页行。没有默认页大小:每次读取都要声明limit或all: true。get(collection, id, select?)——一行,或null。aggregate(collection, { count?, sum?, avg?, min?, max?, by?, where? })——对范围汇总出一行,或按组分页。similar(collection, '<search>', input, options)——具名相似度搜索。history(collection, id, { at? })——一行的修订记录,或它在某个时刻、某个修订或某个审批恢复点时的状态。query('<c>.<q>', input)——集合查询。
每个调用都返回一个尚未执行的查询:await 读取一次,bolt.live(q) 让它保持实时。读取只在失败时抛出拒绝。
写入与运行
// src/app/field_ops_controller/+dispatch.page.svelte (templates/field-operations, excerpt)
import { Effect } from 'effect';
import { bolt, fork } from '$bolt';
import type { Id } from '@norbital-ai/bolt';
import { downloadTextFiles } from '@norbital-ai/ui';
let review = $state<{ id: Id<'sys_run'> } | null>(null);
function runReview() {
const started = bolt.start('review_job_assignment_suspicion', {});
review = { id: started.id }; // only plain data enters $state
fork(started);
}
// src/app/hr_controller/payroll/+payroll.page.svelte (templates/hr-payroll, excerpt)
const exportPayroll = (ids: readonly Id<'payroll_run'>[]) =>
bolt.query('payroll_run.export_payroll', { ids }).pipe(
Effect.tap(({ documents }) => Effect.sync(() => downloadTextFiles(documents, 'payroll-exports.zip')))
); // return the Effect to toolbar.actions act('<c>.create' | '<c>.update' | '<c>.delete' | '<c>.<action>', input)——所有写入。它返回一个Outcome,从不抛出拒绝。start(automation, input)——发起一次自动化运行并返回其RunHandle,可以 await。upload(file, '<c>.<field>')——为file字段存储文件,在保存字节之前检查accept与max。fileUrl(ref)是其下载链接,只有当查看者可以读取保存该文件的字段时才会提供。approvals.process(requestId, decision)与approvals.withdraw(requestId)——审批决定。
谁与何处
bolt.actor 是正在查看页面的人;按 actor.id 关联行,而不是按显示名称。bolt.t(key, vars?) 翻译一个消息目录键,bolt.locale 是查看者的区域设置。登录、登出与邀请都是外壳页面,不属于 bolt。
视图会替你完成大部分工作
@norbital-ai/ui 的视图仅凭一个 of 属性就能自行读取、分页、筛选、写入并打开记录。只有在需要计算面板、仪表盘数值或自定义按钮时才直接使用 bolt:参见 UI 组件。