npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@chuigong/dangan

v0.1.0

Published

DangAn sidecar NDJSON (four-line) format utilities

Downloads

68

Readme

@chuigong/dangan

DangAn(档案)是一个“sidecar NDJSON(四行 JSON Lines)”格式与工具集,用于为任意内容文件在同目录维护一个同名 .json 侧车文件:

  • a.jpga.jpg.json
  • b.mdb.md.json
  • c.tar.gzc.tar.gz.json

本包只提供纯逻辑(解析/格式化/合并/历史策略/sidecar key 规则)与可选的流程/Node 便捷入口:

  • 默认入口 @chuigong/dangan:跨平台纯逻辑(不绑定 Node/浏览器/小程序)
  • 子入口 @chuigong/dangan/node:Node/Electron/脚本可用的 fs adapter(核心入口仍无 Node 依赖)

核心原则:

  • DangAn 只负责“内容与规则”(四行 NDJSON 的结构、合并策略、历史策略、sidecar 命名规则)
  • DangAn 不负责“存储位置与权限”(文件系统 / IndexedDB / 云端 / 小程序沙盒等由宿主决定)
  • sidecar 是一个逻辑概念:它可以是一个真实的 *.json 文件,也可以是一个“同名 key”的独立存储记录

安装

npm i @chuigong/dangan

DangAn 文件格式(四行 NDJSON)

DangAn 文件内容是 4 行,每行都是一个 JSON object(不是一个“大 JSON”),顺序固定:

  1. auto:自动生成/确定性字段(作者、时间、字数、文件指纹等)
  2. ai:AI(或其他非确定性生成器)写入的“当前有效版本”
  3. aiHistoryai 的版本历史列表
  4. nameHistory:文件名/路径历史(create/rename/move)

读取方式必须按“逐行 JSON.parse”处理,不要对整个文件内容做一次 JSON.parse(wholeText)

不同运行环境的“sidecar”落地方式

DangAn 的标准命名是“同名 + .json”。落地时分两种情况:

A. 有真实文件系统(Desktop / Node / Electron / Tauri)

目标:在同目录创建一个真实侧车文件 original.ext.json

  • 读取:先读 original.ext.json,再按需读 original.ext
  • 写入:原文件保存/更新后,同步更新其侧车文件
  • 更名/移动:更新原文件名/路径的同时,更新侧车文件名/路径,并追加一条 nameHistory 事件
  • 删除:原文件与侧车文件一起删除

在 Node/Electron/脚本场景可以使用子入口提供的 fs adapter(见文末)。

Tauri App(更详细)

Tauri 的关键点是:前端不是 Node 运行时,所以不要导入 @chuigong/dangan/node。你仍然使用默认入口 @chuigong/dangan,并在“宿主层”用 Tauri 的文件 API 实现存储。

推荐的两种组织方式:

  • App 管理的内容目录(推荐)
    • 把“主内容文件 + sidecar”统一放进 app 的数据目录(例如 AppData / Application Support)
    • 好处:权限简单、跨平台一致、迁移与备份更可控
  • 用户选择的外部目录
    • 主内容文件位于用户选择的文件夹(例如文库目录)
    • sidecar 与主内容同目录:original.ext.json
    • 需要处理:权限授权、路径变化、同名冲突、移动/重命名

实现要点(指导性):

  • 用 adapter 把 DangAn 接到 Tauri 的读写能力上
    • 实现 DangAnTextAdapterreadText(key) / writeText(key, text)
    • key 可以是“绝对路径”,也可以是你自己的“逻辑 key(再映射到路径)”
  • 写入要尽量原子化
    • 推荐:写到 *.json.tmp,再 rename 成 *.json
    • 避免:直接覆盖写导致半写入、或 app 崩溃留下损坏文件
  • 重命名/移动需要同时迁移 sidecar
    • 文件系统改名/移动后:sidecar 同步改名/移动
    • 追加 nameHistory 事件,保留可追溯链路
  • 跨平台路径与大小写
    • Windows/macOS 常见是大小写不敏感;path normalization 与冲突处理要在宿主层完成
    • 尽量不要把“路径字符串”当作唯一稳定 id;建议另存一个稳定 id(例如 uuid/哈希)写入 auto.id
  • 扫描策略(性能)
    • AI 先扫 sidecar:遍历目录时优先读 *.json,避免无谓读取大正文文件
    • 如果 sidecar 缺失:按需补齐(lazy ensure)

B. 没有真实文件系统或权限受限(Web / 移动端 / 小程序)

目标:用“同名 key”来模拟 sidecar:

  • 主文件 key:fileKey
  • 侧车 key:toDangAnSidecarKey(fileKey),等价于 ${fileKey}.json

常见存储选择:

  • IndexedDB:为 DangAn 单独建一个 store(推荐),value 保存四行文本或结构化对象
  • KV 存储:例如 localStorage(不推荐大内容)、小程序 storage、移动端 KV
  • 云端:用对象存储/数据库的 key 体系映射 sidecar key

重要:在这类环境里,“sidecar 是否对用户可见”完全由宿主 UI 决定。推荐做法是让 sidecar 只存在于专用存储域,不参与用户文件列表的枚举。

快速开始(纯逻辑)

import {
  createEmptyDangAn,
  parseDangAnText,
  formatDangAnText,
  updateAi,
  updateAuto,
  appendNameEvent
} from '@chuigong/dangan'

let doc = createEmptyDangAn({ now: Date.now(), name: 'hello.md' })

doc = updateAuto(doc, { updatedAt: Date.now(), wordCount: 123 })

doc = updateAi(
  doc,
  { model: 'my-model', keywords: ['dangAn'] },
  { now: Date.now(), maxHistoryItems: 20 }
)

doc = appendNameEvent(doc, { op: 'rename', from: 'hello.md', name: 'hello-2.md', at: Date.now() })

const text = formatDangAnText(doc)
const parsed = parseDangAnText(text)

推荐的宿主集成流程(指导性)

1) 创建(Create)

当宿主创建一个新内容文件/文档时:

  • 生成主文件 key(路径、ID、URL、对象 key 都可以)
  • createEmptyDangAn({ now, name }) 初始化
  • 把确定性字段写进 auto(例如 createdAt/updatedAt/作者/字数/指纹等)
  • 把四行文本保存到 sidecar(真实文件 *.json 或 sidecarKey)

2) 更新(Update)

当主文件内容变化时:

  • 重新计算确定性字段(例如 updatedAt、wordCount、fingerprint)
  • 只用 updateAuto 更新 auto 行,不要覆盖 ai
  • 保存 sidecar

3) AI 写入(AI Update)

当 AI 产生新的 tags/keywords/description/links 等时:

  • 只用 updateAi(doc, nextAi, { now, maxHistoryItems }) 更新 ai
  • 让库自动追加版本到 aiHistory
  • 保存 sidecar

4) 重命名/移动(Rename/Move)

当主文件发生 rename/move 时:

  • 先迁移 sidecar(文件系统改名;或更新主 key / sidecar key 的映射)
  • appendNameEvent 追加一条 nameHistory 事件
  • 保存 sidecar

5) 读取与检索(Scan -> Decide -> Read)

当你需要“让 AI 先扫元信息再决定读正文”:

  1. 枚举候选集合(按目录、标签、时间等)
  2. 对每个候选先读取 sidecar,拿到 auto/ai 的轻量信息
  3. 只对被选中的少数候选,再读取其主文件正文内容

这个流程能显著降低 token/IO,并让 AI 的选择更可控。

核心 API

解析与格式化

  • parseDangAnText(text): DangAnDoc:容错解析(缺行/乱序/空行均可)
  • formatDangAnText(doc): string:输出标准四行顺序
  • migrateDangAn(doc): { doc; changed }:把结构规范化到当前 schema(用于长期演进)

Sidecar 命名规则

  • toDangAnSidecarPath(path): string${path}.json
  • toDangAnSidecarKey(fileKey): string${fileKey}.json(适用于虚拟 key/IDB key 等)

合并与历史策略

  • createEmptyDangAn({ now?, auto?, ai?, name? }): DangAnDoc
  • updateAuto(doc, patch, policy?): DangAnDoc
    • 默认不允许在 auto 行覆盖 AI 字段;如确需覆盖可传 policy.allowAiFieldsInAuto = true
  • updateAi(doc, next, policy): DangAnDoc
    • policy.now 必填(由宿主提供时间源)
    • 自动将新版本写入 aiHistory.items 并按 maxHistoryItems 裁剪(默认 20)
  • appendNameEvent(doc, event): DangAnDoc

流程 API(带 adapter,不绑定环境)

本包提供了一个最小 “文本存储 adapter” 接口,用于把 core 逻辑串成 load/save/ensure 流程:

import type { DangAnTextAdapter } from '@chuigong/dangan'
import { ensureDangAnForFile, loadDangAn, saveDangAn } from '@chuigong/dangan'

DangAnTextAdapter 只要求:

  • readText(key): Promise<string | null>
  • writeText(key, text): Promise<void>

你可以在 Web/小程序/移动端用 IndexedDB/KV/沙盒文件 API 实现它,在桌面端用 Tauri/Electron/Node 实现它。

Node 子入口(可选)

在 Node/Electron/脚本场景,可直接使用内置的 fs adapter:

import { createNodeFsAdapter } from '@chuigong/dangan/node'
import { ensureDangAnForFile } from '@chuigong/dangan'

const adapter = createNodeFsAdapter()
const { doc } = await ensureDangAnForFile(adapter, '/abs/path/to/a.jpg', {
  now: Date.now(),
  name: 'a.jpg'
})

注意:浏览器/小程序/受限容器不要导入 @chuigong/dangan/node

本地测试(不发布)

如果你想在另一个项目里本地测试本包而不发布到 npm,推荐使用 npm pack 的 tgz 安装方式。见: