# @animalabs/chronicle 中文文档

规范地址：https://npmcn.com/package/%40animalabs/chronicle/

版本：0\.6\.0
npm 元数据更新时间：2026-11-07T01:03:03.756Z
中文译文更新时间：2026-11-07T01:06:32.202Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40animalabs/chronicle
原文对照：https://npmcn.com/package/%40animalabs/chronicle/?lang=original
项目主页：https://github.com/anima-research/chronicle#readme
源码仓库：https://github.com/anima-research/chronicle

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：15.4 MB，9 个文件
- 模块格式：CommonJS
- TypeScript 类型：包内自带
- Node.js 要求：\>=22\.12\.0
- 运行依赖：0 个
- 首次发布：2026-03-31T14:09:49.774Z
- 最新版本发布：2026-11-07T01:03:03.314Z
- 近 30 天下载量：24670（2026-10-06 至 2026-11-04，npm 官方下载统计）
- 已发布版本数：11（近 12 个月 11 个）

## 中文简介

具有因果关系的分行旅行记录库

## 安装

```sh
npm install @animalabs/chronicle
```

也可以使用：`pnpm add @animalabs/chronicle`、`yarn add @animalabs/chronicle`、`bun add @animalabs/chronicle`

## README

# 编年史

**编年史(英語:listle)是一所以仅以附加记录为主的嵌入式商店,有复制于写作的分行和时间出行.** 被命名的状态作为该日志的更改而保留,因此其中任何一种状态都可以像以前一样被读取,每个分支都会改变自己的状态. 大型数据或共享数据在一个内容地址的blob商店中。 核心是Rust图书馆,Node.js通过它使用 `@animalabs/chronicle`。 。 。 .

编年史是康乃馨的存储层: [代理框架](https://github.com/anima-research/agent-framework), (中文(简体) ), [上下文管理器](https://github.com/anima-research/context-manager) 财务报告和审定财务报表 [连接主机](https://github.com/anima-research/connectome-host) 保存他们的记录,并在其中声明.

## 安装

```sh
npm install @animalabs/chronicle
```

该套件包括Linux(x64和arm64,glibc),macOS(x64和arm64)和Windows(x64)的预建本土模块. 在另一个平台上,从源头构建模块(参见 [建设和贡献](https://npmcn.com/package/%40animalabs/chronicle/#readme-building-and-contributing)) (中文(简体) ).

## 先看一眼,再看一眼

在您安装了软件包的新目录中, 请保存为 `example.mjs` 运行 `node example.mjs`。 。 。 .

```js
import { JsStore } from '@animalabs/chronicle';

// A new store: registerState and createBranch throw for a state or branch
// that already exists, so run this once, or delete ./my-store first.
const store = JsStore.openOrCreate({ path: './my-store' });

// Records: an append-only log. IDs are decimal strings.
const asked = store.appendJson('message', { from: 'user', text: 'Hello' });
const answered = store.appendJsonWithLinks('message', { from: 'agent', text: 'Hi!' }, { causedBy: [asked.id] });
store.getEffects(asked.id);                // [answered.id]

// States: named values kept as changes in the log.
store.registerState({ id: 'messages', strategy: 'append_log' });
store.appendToStateJson('messages', { text: 'Hello' });
const before = store.currentSequence();
store.appendToStateJson('messages', { text: 'Hi!' });
store.getStateJson('messages');            // [{ text: 'Hello' }, { text: 'Hi!' }]
store.getStateJsonAt('messages', before);  // [{ text: 'Hello' }]

// Branches: copy-on-write. A store starts with one branch, `main`.
store.createBranch('experiment');
store.switchBranch('experiment');
store.appendToStateJson('messages', { text: 'Only on experiment' });
store.switchBranch('main');
store.getStateJson('messages');            // still the two items

// Blobs: bytes stored under their SHA-256 hash.
const hash = store.storeBlob(Buffer.from('large data'), 'text/plain');
store.getBlob(hash);                       // the same bytes

store.close();
```

每一种方法都是同步的: 它运行在调用线条上 `pollSubscriptionTimeout` 屏蔽它直到超时。 包裹 `index.d.ts`从 `src/napi.rs` 列出所有方法及其选项.

## 店里有这么多东西

### 记录

每个记录都有一个ID,一个序列号,一个类型,一个有效载荷,一个时间戳,以及两个与其他记录的链接列表: `causedBy` 为它的原因 `linkedTo` 对于任何其他关系. `append` 以有效载荷为字节并 `appendJson` 作为JSON,和 `WithLinks` 每个链接的窗体. `getEffects(id)` 记录 `causedBy` 记录的名字,和 `getLinksTo(id)` (b) 其 `linkedTo` 已经.

`getRecord` 用身份证读取任何记录 `getRecords(ids)` 在一个呼叫中,按给出的顺序读取几个 `null` 对于一个没有记录的ID. `getRecordIdsByType` 列出一个类型在所有分支中的记录ID,按照上升的ID顺序,所以最后一个是最新的;ID是十进制字符串,所以把它们作为数字比较. 记录一个被撕裂了的尾巴滴出在开放, 和他们的ID再次发布到新的记录, 可能是另一种类型。 所以在坠机前的最后一张ID可以在之后命名不同的记录,然后取出一个伯爵就可以跳过新的记录. `query` 读取当前分支所附加的记录,最多按序列范围和类型分列 `limit` 他们中有10 000人.

### 国家

一个状态是一个被命名的值,其每个变化都是日志中的记录. 这就是让你读它 因为它在早期序列(`getStateAt`, (中文(简体) ), `getStateJsonAt`),并让每个分支分别修改. 以三种战略之一登记一个国家:

-   **`snapshot`**: 每个更改都存储整个值 (`setState`, (中文(简体) ), `setStateJson`) (中文(简体) ).
    
-   **`append_log`**: 您附加到的列表( s)`appendToState`, (中文(简体) ), `appendToStateJson`),按索引编辑(`editStateItem`)和按范围调整(`redactStateItems`) (中文(简体) ). 更改被存储为操作 。 一跑一跑的附子会得到周期性的三角洲快照,所以一读不会回放整个历史,随着列表的增长,完整的快照会更加相去甚远. `deltaSnapshotEvery` (默认100个)和 `fullSnapshotEvery` (10) 设定基准间隔. `getStateLen`, (中文(简体) ), `getStateSlice`, (中文(简体) ), `getStateTail` 财务报告和审定财务报表 `getStateItemJson` 读取列表的一部分,以及 `getStateItemsJson(id, ordinals)` 在一个呼叫中逐项读取.
    
    `appendManyToStateJson(id, items)` 以一个顺序将多个项目作为单项记录,然后以 `firstOrdinal`,它的第一个项目是: 清单的长度就在批次之前,因为 `getStateLen` 一旦批次数到项目(项目) `i` 处于 `firstOrdinal + i`;在一个其值不是JSON阵列的列表中,或者持有一个不属于JSON的物品,批量取出所储存的计数,被记录下来,然后状态未能实现,就像一个 `appendToState` ,所以项目不能 读在那些指数。 记录错误的0.1.0到0.4.x的计数,在开通后,在每家分店的第一批清单中重新列出 `compactState`。阅读器将每个项目视为单独附件,但历史读取或按顺序切出的一个分支保留或降下批次整体,因为它有一个序列,批次算作向快照间隔的一个操作。 坠机会保持或失去一个批量的完整, 从来没有一部分。 商店的第一批更改格式:见 `MANIFEST` 下级 [磁盘](https://npmcn.com/package/%40animalabs/chronicle/#readme-on-disk)。 。 。 .
    
    `appendToStateJsonWithIdentity(id, item, idField, sequenceField)` 附加一个 JSON 对象,并附有它写在这两个键上的记录的ID和顺序,因此,该物品在日志中带有它自己的位置. `appendManyToStateJsonWithIdentity(id, items, idField, sequenceField)` 是其分批的形式: 分批的每件物品都带有分批记录的ID和序列, 因此在分批物品的序列中切出一个分支保存整个批次, 在投放前一个分支保存一个序列. 它抛出和写什么 `idField` 财务报告和审定财务报表 `sequenceField` 名称为相同的密钥; 单式为这样的一对并留下该密钥的序列, 高于 id.
    
-   **`tree`**: 从路径到取名blobs的条目的地图(`treeSet`, (中文(简体) ), `treeRemove`, (中文(简体) ), `treeGet`, (中文(简体) ), `treeList`, (中文(简体) ), `treeDiff`等),默认情况下每50个操作有一个三角洲快照.
    

店里有登记 所以又开了一家店, `registerState` 扔出“ 状态已经存在 ” , 用于以前注册的状态 ; `listStates()` 列出他们.

`registerStateFieldIndex` 保留列表项目中的一个字段的索引,用于范围查询和平等查询(`queryStateIndexRange`, (中文(简体) ), `queryStateIndexEq`) (中文(简体) ). 它属于它注册的分行: 更改另一个分行的状态会掉下它, 而查询返回 `null` 直到它再次注册.

### 分支

`createBranch(name, from)` 在首端启动分支 `from`,或当 `from` 被排除在外. `createBranchAt(name, from, sequence)` 以较早的顺序开始 `from`一个新的分支开始于其母国的当时。 记录和状态变化到当前分支, 该分支 `switchBranch` 选择.

### 块

`storeBlob` 将字节保存在他们的SHA-256散列下,所以同一内容存储一次,以及 `getBlob` 读回来. `getBlob` 检查文件的校验和散列, 并扔出一个不全的文件; 当商店不再持有存储中的blob 时, 同样的内容会再次替换这样的文件 。 超过65,535字节的内容类型被拒绝. 一个开放的商店保留它最近写的或读取的blobs作为内存(`blobCacheSize` 其中,1000个默认)和从那里得到的答案,所以从店外对一个blob文件造成的损坏,只显示在blob离开缓存或商店重新开放时.

### 订阅

`subscribe(config)` 返回一个ID。 装扮的 `filter` 指定记录类型和接下来的状态,并打开您想要的每一种事件 `includeRecords`, (中文(简体) ), `includeStateChanges` 财务报告和审定财务报表 `includeBranchEvents`,除非设定,否则关闭。 还需要一个 `branch`现场活动可以从任何分支到达。 事件只涉及一个 已经赶上的订阅 `catchUpSubscription` 就是让事情发生 与一个 `fromSequence` 在配置中,它首先按当时的状态发送每个后续状态(或现在的状态,如果当时不存在),并重放当前分支自此所附加的匹配记录. 一个大于配置的状态 `maxSnapshotBytes` 剪切( 默认为 10 MiB) 以适应: 列表只发送其最新项目, 其它任何值发送 `null`。该活动 `data`一个JSON弦,然后 `truncated` 设定和列表, `from_index` 财务报告和审定财务报表 `total_length` 说哪部分到了 变化 `subscribe` 财务报告和审定财务报表 `catchUpSubscription` 只有在重播时才能到达 之后,每个 `pollSubscription` 返回下一个事件,或 `null` 当没有人等待的时候 `pollSubscriptionTimeout` 等待一个超时。 用户允许 `bufferSize` 事件(默认情况下为1,000起) 堆积起来的无保值被删除.

## 磁盘

商店是一个目录。 日志是 `records.log`,州链的头在 `state.bin`字段索引 `state-indexes.bin`,在 `branches.bin`下方的块 `blobs/`。 。 。 . `MANIFEST` 命名格式版本: 1, 每次发行打开它, 直到它移动到 2 , 就在商店的第一个批次附件之前(`appendManyToStateJson` 或者说 `appendManyToStateJsonWithIdentity`,或 `AppendMany` 从Rust)所写的. 从那时起,它就保持2, 即使这批失败, 并释放那些 先前的批次附件 拒绝打开商店 `MANIFEST` (对于一个链接的,持有它的目标的)是同步的(在Windows上,这个进程可能不打开该目录,或者在一个不同步目录的系统上,一个功率损失可以撤销移动到版本2并保留批次;见"在更改日志中断开";如果移动本身失败,调用和批次没有被记录下来,如果移动失败后只有目录同步,它也会抛出 `MANIFEST` 已经在第2版了. `create` 拒绝已经持有商店的目录, 因此它从来不写商店的 `MANIFEST` 返回到 1: 打开可能存在的商店 `openOrCreate` 或者说 `open`。 。 。 . `LOCK` 在商店开放时持有. `LOCK` 必须是正则文件, 或缺失: 打开并创建拒绝链接或其他任何内容 。 不要删除或替换 `LOCK` 当商店打开时: 下一个打开会锁起新文件, 并打开商店 .

-   **一次一个过程.** 店开着,又开着 `open` 它与“ Store 被另一个进程锁定” 失败。 与 `LOCK` 一个正则文件,一个由于任何其他原因而失败的锁会因 I/O 错误而失败.
    
-   **杜尔比特.** 日志被冲入每100个记录的磁盘中,因此机器崩溃可能损失到最后100个. `sync()` 财务报告和审定财务报表 `close()` 冲去保存 `state.bin` 财务报告和审定财务报表 `branches.bin`,每个字写入一个新的文件,然后替换旧文件,所以在 `sync()` 保存之前或之后保存每个文件。 注册状态,改变策略,并在返回前创建、删除或切换分支. `storeBlob` 将 blob 写入新文件 `blobs/`,将其冲出并重命名为返回前的位置,所以一个崩溃会留下文件整体,或者像呼叫前一样(缺席,或者它正在替换被损坏的复制件). 当一个进程被杀死或提前退出时 `close()` (坠机,Ctrl-C等信号,或 `process.exit()`),下个开口截取了部分被写入的上个记录,将每个分支的头抬到日志上,并重放状态自上个保存后更改了日志所持有,但保存的分支上保存的则不持有. `recovery()` 报告它发现了什么。 打开也会删除新文件( O)`.chronicle-blob-*` 内 `blobs/`在重名前被切断的blob商店。 一旦日志的同步失败, `sync()` 丢出错误开始 `Durability lost:` 直到店铺重新开业, 上面的电话也一样,在返回之前。 打电话 `close()` 离开之前,和 `sync()` 更改后,您不能输.
    
-   **看着里面.** 存储为二进制. `tools/` 有读取的 Python 脚本 `records.log` {\\fn黑体\\fs20\\shad2\\2ah82\\3ah20\\4ah33\\fscx95\\3ch592001\\be1}直接地,没有人会拿走.. `LOCK`在经营写作机前关闭商店.
    
    -   `trim-records-log.py <store>/records.log` 报告一条被撕裂的尾巴 用它修剪 `--apply` 过道后.
    -   `scan-delta-poison.py` 检查 `append_log` 毒药三角洲的快照, `rebuild-state-index.py` 重写 `state.bin` 从日志,和 `chain-report.py` 报告命名状态的更新链 `messages`。这三人读取状态变化为 JSON, 0: 3: 0 之前的编码, 并用一个自此写出的错误止步, 这是 MessagePack .
    
    `server/` 是快取的中间软件, Vue 应用程序在其中 `ui/`浏览商店 Npm 包里也没有.
    

## 从Rust取出

图书馆是 `chronicle` 装入箱子 `src/`它不在箱子上, io,在哪里 `chronicle` 这是另一个项目,所以依赖它从Git:

```toml
[dependencies]
chronicle = { git = "https://github.com/anima-research/chronicle" }
serde_json = "1"
```

```rust
use chronicle::{RecordInput, Store, StoreConfig};
use serde_json::json;

fn main() -> chronicle::Result<()> {
    let store = Store::open_or_create(StoreConfig {
        path: "./my-store".into(),
        ..Default::default()
    })?;
    let record = store.append(RecordInput::json("message", &json!({ "text": "Hello" }))?)?;
    println!("record {} at sequence {}", record.id.0, record.sequence.0);
    Ok(())
}
```

`examples/store_probe.rs`量度状态读取在真实商店的副本上 也可以直接使用箱子 节点绑定是 `src/napi.rs`,与 `napi-bindings` 特性.

## 建设和贡献

[捐款额](https://npmcn.com/package/%40animalabs/chronicle/CONTRIBUTING.md) 涵盖建筑和测试`cargo test`,本地模块的构造,和 `npm test`),拉动请求应包含什么,更改日志片段在 `changelog.d/`并释放.

## 文档

-   [文献/日志](https://npmcn.com/package/%40animalabs/chronicle/docs/loom-of-looms.md):分枝原木的代数规格,和嵌入原木的原木.
-   [文档/rfc-状态-原产地](https://npmcn.com/package/%40animalabs/chronicle/docs/rfc-state-item-origins.md)\- 关于记录记录的RFC草案,记录产生了一个记录的每个项目 `append_log` 状态.
-   [变动情况](https://npmcn.com/package/%40animalabs/chronicle/CHANGELOG.md):每次发行都改变了什么.

## 许可证

麻省理工学院,作为 `package.json` 财务报告和审定财务报表 `Cargo.toml` 声明.

## 常见问题

### @animalabs/chronicle 是什么？

具有因果关系的分行旅行记录库

### 如何安装 @animalabs/chronicle？

在项目目录运行 npm install @animalabs/chronicle，或使用 pnpm add / yarn add / bun add。

### @animalabs/chronicle 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @animalabs/chronicle 支持 ESM 还是 CommonJS？

以 CommonJS 形式发布，可以直接 require\(\)；在 ES Module 项目中也可以用 import 引入（Node\.js 会把 module\.exports 作为默认导出）。

### @animalabs/chronicle 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=22\.12\.0。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @animalabs/chronicle 可以商用吗？

@animalabs/chronicle 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @animalabs/chronicle 的下载量有多少？

根据 npm 官方下载统计，@animalabs/chronicle 在 2026/10/06 – 2026/11/04 这 30 天内被下载 24,670 次，日均约 822 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @animalabs/chronicle 还在维护吗？

最新版本 0\.6\.0 发布于 2026/11/07（今天）；至今共发布 11 个版本，其中过去 12 个月发布了 11 个；首次发布于 2026/03/31。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

