メインコンテンツまでスキップ

TypeScript API

@kensnzk/koyu.muro を読み、検査し、問いに答え、形を導き、図を吐くライブラリである。CLI が答えるものはすべてこの API が答える。koyu コマンドkoyu-mcp サーバー・この API は同じ導出の三つの入口であり、どれかにしか無い答えというものは無い。

npm install @kensnzk/koyu

実行時依存はゼロである。パッケージが引くのは Node 標準モジュールだけで、それも @kensnzk/koyu/node の中だけに閉じている。動作環境は Node 22 以上 (engines.node>=22)。

四つの入口

import { parse, checkDiagnostics, derive } from "@kensnzk/koyu";
import { parseFile, parseFileWith } from "@kensnzk/koyu/node";
import { validate, VALIDATION_RULES } from "@kensnzk/koyu/validate";
import { svgPlan, svgAxo } from "@kensnzk/koyu/draw";
入口中身node:fs
@kensnzk/koyu面の全部 — 解析・診断・問い・導出・生成・差分・検証引かない
@kensnzk/koyu/nodeparseFile parseFileWith の二つだけ引く
@kensnzk/koyu/validatevalidate VALIDATION_RULES と型 Finding ValidationRule引かない
@kensnzk/koyu/drawsvgPlan svgAxo と型 PlanOptions AxoOptions引かない

ルートは node:fsnode:path も引かない。ブラウザ・Web Worker・エッジランタイムでそのまま動く。ファイルシステムを触る入口だけが /node に分離してある。分けてあるのはパーサ本体を純粋に保つためで、合成 (import の解決) は「レイヤーをどう読むか」という関数を外から受け取る形になっており、fs はその実装の一つでしかない。ブラウザは仮想ファイル群 (parseFiles) か独自ローダー (parseWith) を渡す — 解析と合成

/validate/draw はルートが再輸出している面の一部でもある。領域を混ぜないための別入口であって、別の実装ではない。ルートから validate を呼んでも @kensnzk/koyu/validate から呼んでも同じ関数である。(/validate のモジュールには Finding を組み立てる補助 finding も出ているが、これはルートが再輸出していない — 下の一覧に無い名は約束の外にある。)

面は書き下されている

パッケージのルートは export * を使わない。モジュールに export を足した瞬間に、誰も宣言していない約束が凍る面に増えてしまうからである。約束は書き下されていなければならない。

したがってこの面の全部は、src/index.ts に一つずつ書かれた名の集合である — 実行時の値が 59、型が 77。ここに無い名は、ソースの中にあっても約束ではない。

面に載る基準は四つある。

  1. パッケージの外 (ビューワー・評価ハーネス・スクリプト・エディタ拡張) が実際に呼ぶ
  2. CLI か MCP が答えるものを API からも答えるために要る
  3. 導出として名指しで約束されている
  4. テストが契約として固定している

core のモジュール同士が引き合うだけの配管は面ではない。型は、載せた値の署名を書き下すのに要るものだけが載る。

最初のプログラム

読み込み、検査し、面積を出す。これだけで一巡している。

import { checkDiagnostics, areaM2 } from "@kensnzk/koyu";
import { parseFile } from "@kensnzk/koyu/node";

const model = parseFile("examples/two-rooms.muro");

const diags = checkDiagnostics(model);
console.log(`${model.name} — spaces ${model.spaces.size} / diagnostics ${diags.length}`);
for (const d of diags) console.log(`${d.severity} ${d.code} ${d.message}`);

for (const s of model.spaces.values()) {
console.log(`${s.path}\t${s.type}\t${areaM2(s) ?? "-"}`);
}
二室 — spaces 3 / diagnostics 0
/L1/a	room	16.2
/L1/b	room	16.2
/out	exterior	-

model.spacesMap<string, Space>model.boundariesBoundary[] である。パスが空間の同一性であり、境界はどちらの空間にも属さない第一級の関係として配列に並ぶ — Model と構成型

診断が空でも建物が使えるとは限らない。checkDiagnostics が言うのは「書かれたものがデータとして矛盾していない」までである。扉を一枚も書かない二階建ては、診断が空のまま完全に密封される。建築の側の判断は validate が別に言う。

頁の地図

何を引くか
Model と構成型Model Space Boundary Zone Level Opening ほか、書かれた構成の型
解析と合成parse parseFiles parseWith parseFile parseFileWith tokenize LayerLoader
診断checkDiagnostics check DIAGNOSTIC_CODES Diagnostic CheckResult
検証validate VALIDATION_RULES Finding ValidationRule
モデルへの問いdoorsBetween neighbors areaM2 siteReport daylightInputs ほか
形の導出deriveForm の全構成型、DERIVATION_CONSTANTS TOLERANCES
実体と生成物thicken band columnRect runPrism slabs verticalRuns ほか
図の生成svgPlan svgAxo とその選択肢
正準JSONtoCanonical
意味差分semanticDiff renderDiff ModelDiff
同一性の生成newUids
幾何の小物polygonAreaM2 pointInPolygon polyBounds rectToPoly envelopeGaps
エラーSourceError srcRef
言語版SUPPORTED_LANGUAGE_VERSIONS DEFAULT_LANGUAGE_VERSION

三つの領域

面の中身は三つに割れていて、割れ方そのものが約束の一部である。

領域何を言うか凍るか
core構成の整合と、そこから導かれる数と形凍る — 意味論を変える変更は言語版を上げる
検証建築的な判定 (Finding)凍らない — 増える・精度が上がる・捨てられる
生成SVG の中身凍らない — 見た目は自由に変わる

core は合否を言わない。面積・線分・凸片・立体を返すところまでが core であり、「足りているか」「守られているか」は検証が言う。この分離は型に現れている — core は Diagnostic { code, severity } を返し、検証は Finding { rule, level } を返す。フィールド名から違うので、二つの配列は取り違えようがない。

生成 (svgPlan / svgAxo) が返す SVG の中身は約束の外にある。同じ入力から同じ形が出ることは約束されるが、同じバイトが出ることは約束されない。色・線種・書体・記号の見た目は断りなく変わる。図を機械で比べるなら toCanonicalderive の返り値を比べる。