六爻纳甲排盘的 TypeScript 实现。
移植自 Python 库 najia(MIT,作者 bopo),并保留其版权声明。
状态:移植完成,并在全部 4⁶ = 4096 种起卦组合上与 Python 实现验证一致。
import { cast } from "@taoracle/najia";
// 六爻,初爻在前:1 单(阳静) 2 拆(阴静) 3 重(阳动) 4 交(阴动)
const reading = cast([2, 2, 1, 2, 4, 2], { date: new Date(2026, 6, 26, 14), guaci: true });
reading.gua.name; // 卦名
reading.gua.gong; // 卦宫
reading.gua.qin6; // 六亲(每爻)
reading.gua.qinx; // 干支五行(每爻)
reading.shiy; // { shi, ying } 世应爻
reading.god6; // 六神,按日干起
reading.dong; // 动爻位置(0 起)
reading.bian; // 变卦,无动爻时为 null
reading.hide; // 伏神,六亲齐全时为 null
reading.ganzhi; // { year, month, day, hour, xkong }| 模块 | 内容 |
|---|---|
src/const.ts |
纳甲表、六十四卦、六神、六亲、五行、旬空、卦宫 |
src/utils.ts |
纳甲配干支、世应爻、卦宫、游魂归魂、六冲六合、卦型、六亲、六神、旬空、干支五行 |
src/calendar.ts |
年月日时干支与旬空,基于 tyme4ts |
src/najia.ts |
起卦主流程、变卦、伏神、卦辞 |
src/data/guaci.json |
六十四卦卦辞,转录自中文维基文库(见下) |
全部 4⁶ = 4096 种起卦组合 × 5 个日期,99,776 个字段与 Python 实现零差异。
test/fixtures/parity.json 固化了这个结果:全空间取一个 SHA-256 聚合哈希,另有 48 个精选详例覆盖八个卦宫与变卦/伏神/动爻的各个分支。CI 不需要 Python 解释器即可验证一致性。
日期干支层单独验证:336 个时点(含立春、节气、子时、闰年、跨年边界)× 5 个字段,与 lunar_python 零差异。
重新生成 fixture 需要参考实现,见 tools/make-parity-fixture.ts。
移植过程中发现的原版缺陷,这里做了修正。每一处都会改变输出或 API,所以逐条列出:
1. 移除 set_shi_yao 的第三个返回值 index。
上游 compile() 从不使用它,始终把世爻传给 palace()。而对 8 个游魂卦,两者给出不同的卦宫(火地晋用世爻得乾宫,用 index 得离宫)。留着它就是留一个静默出错的入口。
2. seat(伏神位置)改为升序确定输出。
上游用 set 差集迭代产生顺序,Python 的字符串哈希按进程随机化,同样的输入在不同次运行会得到不同顺序。
3. 字符串入参不再静默失效。
上游 _transform 写的是 if 3 in params,当 params 是字符串时该判断恒为假——传字符串会静默地不产生变卦、不识别动爻。现在入参统一归一化并校验。
4. 拒绝非法日期。
lunar_python 接受 1900-2-29 这类不存在的日期(1900 非闰年)并给出结果,tyme4ts 会报错。测试中 24 个这类时点在本实现下抛异常。
5. 晚子时流派显式可选。
23:00–24:00 的日柱归属有两种成法,两个上游库的默认值恰好相反。本实现默认 day-stays(与 Python 一致,保证迁移不改变任何人的卦),可通过 lateZi: "day-advances" 切换。时柱不受影响——两个库都用次日干起。
6. 卦辞整体换源。 不沿用上游那份采集文本,改为从中文维基文库转录,见「卦辞数据的来源」。上游 64 卦里有 4 卦为错或为空,乾卦缺全部逐爻小象,正文夹着站点水印。
7. 不移植渲染层与 CLI。
上游的 jinja2 模板与 click 命令行不在范围内,结构化输出交给调用方格式化。因此本库零运行时依赖(除 tyme4ts)。
src/data/guaci.json 由 tools/build-guaci.py 从中文维基文库的《周易》转录而来:
- 原文:https://zh.wikisource.org/wiki/周易
- 《周易》本身属公有领域;维基文库的转录内容以 CC BY-SA 4.0 授权,本仓库据此注明来源
- 全 64 卦,每卦包含卦辞、彖传、大象、六(七)爻爻辞与逐爻小象
- 繁体转简体,并逐卦校验爻辞与小象一一对应
它是从命理网站 sm.aa963.com 采集的,判定依据是该站防盗水印直接留在正文里。除此之外还有:
| 问题 | 影响 |
|---|---|
| txt→pickle 切分错位 | 泽山咸 粘着整条雷风恒,震为雷 粘着整条艮为山;4 卦卦辞为错或为空 |
| 乾卦缺全部逐爻小象 | 8 条缺失 |
| 站点水印 3 处、BBCode 残留 1 处 | 会被当作经文读给用户 |
| 转写错字 | 如乾卦九二作「见龙再田」 |
这些缺陷在上游至今仍然存在(get_guaci('雷风恒') 返回 None),且其对卦辞的全部测试只有一行 assert get_guaci('乾为天')——在以上所有缺陷下都是绿的。
opencc的t2s会把「乾」转成「干」(乾燥/干燥),但《周易》的卦名与「乾乾」必须保留「乾」。已加白名单。t2s会把个别字转到 Unicode 扩展区:餗 → 𫗧 (U+2B5E7)、繻 → 𦈡 (U+26221),这些字在多数系统显示为缺字方块。转换改为逐字进行,并拒绝任何"把常用字推入扩展区"的转换。- 维基文库正文里夹有校勘注,如「保合大和〈一作太和〉」。这是校勘信息不是经文,已剥离。
- 否卦的爻位用逗号而非冒号分隔,已统一为冒号。
以上四项都有测试或生成期断言守着。
维基文库不是校勘本,转录本身亦有讹误。修正记在 tools/build-guaci.py 的 CORRECTIONS 表里,重新生成时自动复用,且待修正字符串一旦消失即报错,避免修正被静默丢弃。
每条修正都必须能从本数据内部证成,不依赖任何外部来源保持可访问:
| 卦 | 原 | 改为 | 内部依据 |
|---|---|---|---|
| 地雷复 初九 | 不复远 | 不远复 | 紧随其后的象曰作「不远之复」 |
| 地雷复 初九 | 无袛悔 | 无祗悔 | 袛为衣部,指内衣,此处无义 |
文本只有单一来源,所以校验放在仓库内部,不联网也能跑:
- 爻位标签必须与卦码一致。 卦码说初爻为阳,标签就必须是「初九」而非「初六」——这把文本数据和计算逻辑互相钉死,错位或串行的爻辞会立刻暴露。上面复卦那处字序颠倒就是这么定位的。
- 每条爻辞恰好配一条小象,另有一条大象
- 每卦恰好一条彖曰
- 首行卦名必须是本卦,正文不得夹带别卦标题
- 不含校勘注、站点水印、Unicode 扩展区字符
见 test/guaci.test.ts。
npm 上没有成熟的 TS 纳甲实现(现有几个包都还很稚嫩),而纯 TypeScript 的技术栈需要它。紫微斗数有 iztro 原生 TS 实现、农历有 tyme4ts,六爻纳甲是唯一没有上游可用的一环。
一个算错的卦不会抛异常,也不会看起来有问题——它会输出一个自洽、完整、措辞确定的卦象,而它是错的。使用者无法察觉,作者也无法察觉。
所以这个移植不靠"仔细写"来保证正确,靠差分验证:
- 以 Python
najia为基准(oracle) - 穷举卦象输入空间(六爻的阴阳与动变,共 4^6 = 4096 种基本组合,再乘日期与性别维度)
- 逐字段比对,差异为零才算完成
六爻在这一点上比紫微有利:输入空间是有限且可穷举的,不像出生时间那样连续。理论上可以做到完全穷举,而不是抽样。
Python 原库共 758 行,其中需要移植的是计算核心:
| 原文件 | 行数 | 是否移植 | 说明 |
|---|---|---|---|
najia.py |
320 | 是 | 排盘主逻辑 |
utils.py |
302 | 是 | 干支、纳甲、六亲等推导 |
const.py |
96 | 是 | 常量表 |
__main__.py |
35 | 否 | CLI |
data/standard.tpl |
1.2K | 否 | jinja2 文本模板,属表现层 |
data/guaci.pkl |
57K | 否 | 采集文本,已弃用;卦辞改从维基文库转录 |
bun add @taoracle/najia # 或 npm install @taoracle/najiabun install
bun run typecheck
bun test
bun run build # tsc 输出 ESM + .d.ts 到 dist/版本号改好后打标签即可,.github/workflows/release.yml 会跑完整校验再发布:
# package.json 的 version 与标签必须一致,否则工作流会拒绝发布
git tag v0.1.0 && git push --tags不需要任何 token。 该包在 npm 上配置了 trusted publishing:npm 直接校验 GitHub Actions 签发的 OIDC 令牌,只接受本仓库该工作流的发布请求,因此没有长期 凭据需要保管或轮换。发布会自动带上 provenance,在公共 registry 留下「此包由此 仓库此提交构建」的可验证记录。
本地发布仍然可行(需登录并输入 OTP),但拿不到 provenance:
npm login
npm publish # prepublishOnly 会先跑 typecheck + test + build代码是 MIT,见 LICENSE,其中保留了原 Python 实现(bopo/najia)的版权声明。
卦辞数据的授权与代码不同:《周易》本文属公有领域,但维基文库的转录按站点条款为
CC BY-SA 4.0。若你需要闭源再分发这份数据,请先读 NOTICE.md——那里说明了争议点和
两条替换路径。