@takagaki/cortex-decisions-viewer 0.12.200 → 0.12.210
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/dist/build.js +64 -83
- package/dist/canonical-evidence.js +11 -191
- package/dist/change-log.js +1 -1
- package/dist/config-flow.js +462 -0
- package/dist/config-mode.js +57 -0
- package/dist/confirmed.js +2 -2
- package/dist/engine/decision-v2-fields.js +188 -0
- package/dist/engine/ref-tokens.js +276 -0
- package/dist/engine/related-materials-field.js +148 -0
- package/dist/engine/supersedes-index.js +257 -0
- package/dist/google-confirm-page.js +14 -3
- package/dist/ledger-dir.js +1 -0
- package/dist/memory-dir.js +40 -0
- package/dist/rederivation.js +17 -8
- package/dist/render.js +896 -74
- package/dist/story.js +13 -9
- package/dist/supersedes.js +12 -110
- package/dist/view-dir.js +100 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -74,8 +74,10 @@ cortex-decisions-viewer build [dir] [options]
|
|
|
74
74
|
|
|
75
75
|
```bash
|
|
76
76
|
npm install
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
node ../scripts/ci/assemble-engine.mjs # engine の原本を viewer/engine-src/ に組み立てる(engine の場所は ENGINE_ROOT、無ければ tools の隣の ../cortex-engine)
|
|
78
|
+
# npm test は pretest が回す。build・demo を単独で回す前は手で
|
|
79
|
+
npm run build # tsc → dist/ と、engine の原本を束ねて dist/engine/(先に組み立て)
|
|
80
|
+
npm run demo # 中で build を呼ぶ(先に組み立て)。../../aidd-project-cortex の Cortex/ をビルドして .demo-site/ に出力
|
|
79
81
|
```
|
|
80
82
|
|
|
81
83
|
## 位置づけ(cortex-tools モノレポ)
|
package/dist/build.js
CHANGED
|
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.REFERENCES_SECTIONS = exports.RELATED_MATERIALS_SECTIONS = exports.RELATED_URL_MAX = exports.RELATED_MATERIALS_FIELD = exports.
|
|
39
|
+
exports.REFERENCES_SECTIONS = exports.RELATED_MATERIALS_SECTIONS = exports.DECISION_FIRMNESS = exports.RELATED_URL_MAX = exports.RELATED_MATERIALS_FIELD = exports.relatedMaterialsRead = exports.isRelatedUrl = exports.GRAPH_RELS = exports.GRAPH_RECORD_TYPES = exports.GUIDE_VIDEO_SOURCES = exports.PROJECT_ICON_EXTS = exports.MIRROR_RELS = void 0;
|
|
40
40
|
exports.sanitizeBody = sanitizeBody;
|
|
41
41
|
exports.copyHashedAsset = copyHashedAsset;
|
|
42
42
|
exports.buildGuideAssets = buildGuideAssets;
|
|
@@ -61,8 +61,6 @@ exports.build = build;
|
|
|
61
61
|
exports.decisionFirmnessOf = decisionFirmnessOf;
|
|
62
62
|
exports.decisionStrandOf = decisionStrandOf;
|
|
63
63
|
exports.decidedByOf = decidedByOf;
|
|
64
|
-
exports.isRelatedUrl = isRelatedUrl;
|
|
65
|
-
exports.relatedMaterialsRead = relatedMaterialsRead;
|
|
66
64
|
exports.relatedMaterialsView = relatedMaterialsView;
|
|
67
65
|
exports.attachRecordReferences = attachRecordReferences;
|
|
68
66
|
exports.attachRelatedMaterials = attachRelatedMaterials;
|
|
@@ -78,15 +76,19 @@ const evidence_names_1 = require("./evidence-names");
|
|
|
78
76
|
const change_log_1 = require("./change-log");
|
|
79
77
|
const repo_1 = require("./repo");
|
|
80
78
|
const engagement_1 = require("./engagement");
|
|
79
|
+
const memory_dir_1 = require("./memory-dir");
|
|
81
80
|
const story_1 = require("./story");
|
|
82
81
|
const ranking_1 = require("./ranking");
|
|
83
82
|
const memory_evidence_1 = require("./memory-evidence");
|
|
84
83
|
const hands_1 = require("./hands");
|
|
84
|
+
const config_mode_1 = require("./config-mode");
|
|
85
85
|
const rederivation_1 = require("./rederivation");
|
|
86
86
|
const external_id_1 = require("./external-id");
|
|
87
87
|
const member_origin_1 = require("./member-origin");
|
|
88
88
|
const home_project_1 = require("./home-project");
|
|
89
89
|
const supersedes_1 = require("./supersedes");
|
|
90
|
+
const decision_v2_fields_1 = require("./engine/decision-v2-fields");
|
|
91
|
+
const related_materials_field_1 = require("./engine/related-materials-field");
|
|
90
92
|
const minify_1 = require("./minify");
|
|
91
93
|
const google_confirm_page_1 = require("./google-confirm-page");
|
|
92
94
|
const render_1 = require("./render");
|
|
@@ -259,6 +261,8 @@ function memberSearchText(data, sectionDir, base) {
|
|
|
259
261
|
}
|
|
260
262
|
/** dir未指定時の探索候補(新構成 → 旧構成の順) */
|
|
261
263
|
const DEFAULT_DIR_CANDIDATES = ["Cortex/Decisions/records", "Cortex/Decisions", "Decisions"];
|
|
264
|
+
/** 組み直した案件(木に `Memory/Home.md`)の探索候補。**この木のときだけ先に探す**(今までの案件は上の 3 つのまま) */
|
|
265
|
+
const MEMORY_DIR_CANDIDATES = ["Memory/Decisions/records", "Memory/Decisions"];
|
|
262
266
|
/**
|
|
263
267
|
* Decisionsディレクトリを解決する。明示指定があればそれを使い、
|
|
264
268
|
* 未指定なら Cortex/Decisions/records → Cortex/Decisions → Decisions の順で存在するものを採用する。
|
|
@@ -266,7 +270,9 @@ const DEFAULT_DIR_CANDIDATES = ["Cortex/Decisions/records", "Cortex/Decisions",
|
|
|
266
270
|
async function resolveDecisionsDir(dir) {
|
|
267
271
|
if (dir)
|
|
268
272
|
return path.resolve(dir);
|
|
269
|
-
|
|
273
|
+
const rebuilt = (await (0, memory_dir_1.memoryDirOfTree)(process.cwd())) === "Memory";
|
|
274
|
+
const candidates = rebuilt ? [...MEMORY_DIR_CANDIDATES, ...DEFAULT_DIR_CANDIDATES] : DEFAULT_DIR_CANDIDATES;
|
|
275
|
+
for (const candidate of candidates) {
|
|
270
276
|
const abs = path.resolve(candidate);
|
|
271
277
|
try {
|
|
272
278
|
const st = await node_fs_1.promises.stat(abs);
|
|
@@ -277,7 +283,7 @@ async function resolveDecisionsDir(dir) {
|
|
|
277
283
|
// 存在しない候補はスキップ
|
|
278
284
|
}
|
|
279
285
|
}
|
|
280
|
-
throw new Error(`Decisionsディレクトリが見つかりません(${
|
|
286
|
+
throw new Error(`Decisionsディレクトリが見つかりません(${candidates.join(" / ")} を確認してください)`);
|
|
281
287
|
}
|
|
282
288
|
/**
|
|
283
289
|
* DecisionsディレクトリからCortex(Gold層)ルートを推定する。
|
|
@@ -289,7 +295,7 @@ function resolveKnowledgeRoot(decisionsDirAbs) {
|
|
|
289
295
|
if (path.basename(decisionsDirAbs) === "records" && path.basename(parent) === "Decisions") {
|
|
290
296
|
return path.dirname(parent);
|
|
291
297
|
}
|
|
292
|
-
if (path.basename(decisionsDirAbs) === "Decisions" && path.basename(parent) === "Cortex") {
|
|
298
|
+
if (path.basename(decisionsDirAbs) === "Decisions" && (path.basename(parent) === "Cortex" || path.basename(parent) === "Memory")) {
|
|
293
299
|
return parent;
|
|
294
300
|
}
|
|
295
301
|
return null;
|
|
@@ -1855,7 +1861,7 @@ function buildGraph(decisions, sections, targetUrls, repoBaseUrl, book = (0, evi
|
|
|
1855
1861
|
if (ev)
|
|
1856
1862
|
return addEvidence(ev);
|
|
1857
1863
|
// 点にしない記録(Home・レポート・その他)へのパス参照は、根拠ではない。点を作らず数える
|
|
1858
|
-
if (/(^|\/)Cortex\/(Home\.md|[^/]+\/records\/)/.test(target)) {
|
|
1864
|
+
if (/(^|\/)(?:Cortex|Memory)\/(Home\.md|[^/]+\/records\/)/.test(target)) {
|
|
1859
1865
|
unresolvedTargets++;
|
|
1860
1866
|
return null;
|
|
1861
1867
|
}
|
|
@@ -2438,11 +2444,28 @@ async function buildSite(opts, views) {
|
|
|
2438
2444
|
// 解決するので、あとから作ると空の表・空の実在判定が渡り、**実在するパスまで
|
|
2439
2445
|
// 「リンクにしない」に倒れて全滅する**(以前は順序が逆だった)。
|
|
2440
2446
|
const repoRoot = knowledgeRoot ? path.dirname(knowledgeRoot) : path.dirname(dirAbs);
|
|
2447
|
+
// 記憶の置き場の名前(知識の根の名前から。`Cortex` ならこれまでと同じ出力)
|
|
2448
|
+
const memoryDir = (0, memory_dir_1.memoryDirOfRoot)(knowledgeRoot);
|
|
2441
2449
|
// 帳簿 9 本の置き場(既定は `<repoRoot>/.cortex`)。読み口はこの 1 つから読む(根拠の台帳はいつも `<repoRoot>/.cortex`)
|
|
2442
2450
|
let ledgerDir = (0, ledger_dir_1.ledgerDirOf)(repoRoot, opts.ledgerDir);
|
|
2443
|
-
//
|
|
2444
|
-
|
|
2445
|
-
|
|
2451
|
+
// 設定の置き場のヒント(便 DS-8b)。組み直した案件(木に `Memory/Home.md`)だけ `table`。最初の描画(骨組みを出すか)だけに使い、応答の mode が勝つ
|
|
2452
|
+
const configMode = await (0, config_mode_1.configModeHint)(repoRoot);
|
|
2453
|
+
// 🔴 組み直した案件(v2・cortex-engine #1911 便 DC-2f・設計書 D-4)は封筒を無条件で採用し、帳簿 8 本を `.cortex` から読まない(根拠の台帳は今どおり)。
|
|
2454
|
+
// 封筒が無い・確かめられない回は投げる(空の画面を緑で配らない)。下の倒れ(catch)は組み直していない案件だけ
|
|
2455
|
+
const rebuilt = configMode === "table";
|
|
2456
|
+
if (rebuilt) {
|
|
2457
|
+
if (!opts.viewDir)
|
|
2458
|
+
throw new Error("組み直した案件(Memory/Home.md)のビルドには記憶側の画面用ファイル(--view-dir)が要ります(帳簿は .cortex から組みません)");
|
|
2459
|
+
const plan = await (0, view_dir_1.planViewsV2)(path.resolve(opts.viewDir));
|
|
2460
|
+
const merged = await (0, view_dir_1.mergeViewsV2)(plan);
|
|
2461
|
+
views.cleanup = merged.cleanup;
|
|
2462
|
+
views.counts = plan.counts;
|
|
2463
|
+
ledgerDir = merged.dir;
|
|
2464
|
+
console.log((0, view_dir_1.viewCountsLineV2)(plan.counts));
|
|
2465
|
+
}
|
|
2466
|
+
else if (opts.viewDir) {
|
|
2467
|
+
// 記憶 v2 の画面用ファイル(cortex-engine #1094 鎖 3 V10b)。checkout の HEAD とそろう封筒だけを差し替えた合わせの置き場を読む
|
|
2468
|
+
// 合わせの置き場を作れなかった回(一時フォルダの失敗など)は今の帳簿の置き場のまま組む(取り出しの失敗で画面を止めない・数は出さない)
|
|
2446
2469
|
try {
|
|
2447
2470
|
const plan = await (0, view_dir_1.planViews)(path.resolve(opts.viewDir), (0, view_dir_1.checkoutGit)(repoRoot), ledgerDir);
|
|
2448
2471
|
const merged = await (0, view_dir_1.mergeLedgerDir)(ledgerDir, plan);
|
|
@@ -2469,7 +2492,7 @@ async function buildSite(opts, views) {
|
|
|
2469
2492
|
// 🔴 **強さ(`state` が on のときだけ中身が入る)だけの案件でも器を作る**——作らないと、活性度の帳簿を
|
|
2470
2493
|
// 持たない案件で用語集が目次(engine の `termRows`)と違う順になる。
|
|
2471
2494
|
// 確認の帳簿(`confirmation`)は並びには関係しない
|
|
2472
|
-
const ledgers = await (0, rederivation_1.readRederivationLedgers)(repoRoot, ledgerDir);
|
|
2495
|
+
const ledgers = await (0, rederivation_1.readRederivationLedgers)(repoRoot, ledgerDir, { v2: rebuilt });
|
|
2473
2496
|
const rederive = ledgers.activity.size || ledgers.strength.size
|
|
2474
2497
|
? { ledgers: ledgers, repoRoot: repoRoot }
|
|
2475
2498
|
: null;
|
|
@@ -2483,7 +2506,8 @@ async function buildSite(opts, views) {
|
|
|
2483
2506
|
// 人の手の表の写し(engine #1094 q-10)。あれば記憶 v2 の案件で、確認の印とボタンを出さない。**無い案件は今と 1 バイトも同じ**(キーごと無い)
|
|
2484
2507
|
// 「記憶 v2 の案件か」は 1 本の栓 `memory_v2_writer` の値で決める(cortex-engine #1094 鎖 3 V10b)。橋の間は写しとの OR
|
|
2485
2508
|
const memoryHands = await (0, hands_1.readMemoryHands)(repoRoot, ledgerDir);
|
|
2486
|
-
|
|
2509
|
+
// 組み直した案件(便 DC-2f)は記憶 v2 の案件(v1 の確認の帳簿を読まないので、確認の印とボタンを出さない)
|
|
2510
|
+
const memoryV2 = (0, hands_1.isMemoryV2)(opts.memoryV2Writer, memoryHands !== null) || rebuilt;
|
|
2487
2511
|
// 関係者の「つながっている ID」の出どころ(cortex-engine #1911 便 B4b)。`--view-dir` の中の平文を読むだけ。無い・読めない案件は何も付かない(今と 1 バイトも同じ)
|
|
2488
2512
|
const memberOrigins = opts.viewDir ? await (0, member_origin_1.readMemberOrigins)(path.resolve(opts.viewDir)) : null;
|
|
2489
2513
|
// 記録の id → リポジトリルート相対のパス(順位のファイルの鍵と突き合わせる。`repoRoot` 起点・cwd 起点にしない)
|
|
@@ -2637,9 +2661,9 @@ async function buildSite(opts, views) {
|
|
|
2637
2661
|
// 名前のリンク(story.screen-name-links): 日の relations の関係者・用語・ルールを、画面にある記録の表示名・別名・同義語で本文に当てる
|
|
2638
2662
|
(targets) => (0, story_1.storyNameLinks)(targets, storyNameSources(sections)),
|
|
2639
2663
|
// 本文の相対リンク(決定の記録など)の飛び先: 画面に載る記録(決定・各区画)のパス → id
|
|
2640
|
-
(repoPath) => idByRepoPath.get(repoPath));
|
|
2664
|
+
(repoPath) => idByRepoPath.get(repoPath), memoryDir);
|
|
2641
2665
|
// 週・月のあらすじ(#901)。週・月のファイルが無ければ null(キーごと出さない=今までと同じ画面)
|
|
2642
|
-
const storyDigest = await (0, story_1.readStoryDigest)(repoRoot, renderGroundingSection, (p) => repo.baseUrl ? `${repo.baseUrl}/edit/${repo.branch}/${encodePath(p)}` : null, storyToday, ledgerDir);
|
|
2666
|
+
const storyDigest = await (0, story_1.readStoryDigest)(repoRoot, renderGroundingSection, (p) => repo.baseUrl ? `${repo.baseUrl}/edit/${repo.branch}/${encodePath(p)}` : null, storyToday, ledgerDir, memoryDir);
|
|
2643
2667
|
// 案件アイコン(#348)。Cortex構成でなければ undefined(旧レイアウトには置き場が無い)
|
|
2644
2668
|
const projectIcon = await findProjectIcon(knowledgeRoot);
|
|
2645
2669
|
const site = {
|
|
@@ -2655,6 +2679,9 @@ async function buildSite(opts, views) {
|
|
|
2655
2679
|
homeGrounding: homeGrounding,
|
|
2656
2680
|
homeUpdatedAt: homeUpdatedAt,
|
|
2657
2681
|
...(memoryV2 ? { memoryV2: true } : {}),
|
|
2682
|
+
// 組み直した案件(`Memory`)だけ印を出す。今までの案件はキーごと無い(出力を変えない)
|
|
2683
|
+
...(memoryDir === "Memory" ? { memoryDir } : {}),
|
|
2684
|
+
...(configMode ? { configMode } : {}),
|
|
2658
2685
|
sections,
|
|
2659
2686
|
graph,
|
|
2660
2687
|
months,
|
|
@@ -2822,89 +2849,43 @@ async function parseDecision(raw, fileName, repoRelDir, repoBaseUrl, branch, ref
|
|
|
2822
2849
|
};
|
|
2823
2850
|
}
|
|
2824
2851
|
/**
|
|
2825
|
-
* 記憶 v2 の決定の新しい欄の読み手(#1094)。
|
|
2852
|
+
* 記憶 v2 の決定の新しい欄の読み手(#1094)と、関連の資料の欄 `related_materials` の読み手(cortex-engine #1094 F8)。
|
|
2826
2853
|
*
|
|
2827
|
-
*
|
|
2828
|
-
*
|
|
2829
|
-
* `
|
|
2854
|
+
* 原本は engine を束ねて引く(viewer/engine-src → dist/engine・便 R4): `scripts/gold/decision-v2-fields.mjs` の
|
|
2855
|
+
* `decisionV2FieldsOf`・`DECISION_FIRMNESS_READ_VALUES` と、`scripts/gold/related-materials-field.mjs` の `isRelatedUrl`・
|
|
2856
|
+
* `relatedMaterialsRead`・`RELATED_URL_MAX`・`RELATED_MATERIALS_FIELD`。規則はここに書かない。
|
|
2857
|
+
* 下の 3 つは欄 1 つの値を受ける viewer の薄い入口(原本は frontmatter 全体を受け、読めないとき `null`。viewer はキーごと載せないので `undefined`)。
|
|
2858
|
+
* 題材は engine から組み立てる `test/fixtures/decision-v2-fields-contract.json`・`related-materials-contract.json`
|
|
2859
|
+
* (`test/supersedes-view.test.mjs`・`test/related-materials-view.test.mjs` の「[契約] …」が流す)
|
|
2830
2860
|
*/
|
|
2831
|
-
|
|
2832
|
-
exports
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2861
|
+
var related_materials_field_2 = require("./engine/related-materials-field");
|
|
2862
|
+
Object.defineProperty(exports, "isRelatedUrl", { enumerable: true, get: function () { return related_materials_field_2.isRelatedUrl; } });
|
|
2863
|
+
Object.defineProperty(exports, "relatedMaterialsRead", { enumerable: true, get: function () { return related_materials_field_2.relatedMaterialsRead; } });
|
|
2864
|
+
Object.defineProperty(exports, "RELATED_MATERIALS_FIELD", { enumerable: true, get: function () { return related_materials_field_2.RELATED_MATERIALS_FIELD; } });
|
|
2865
|
+
Object.defineProperty(exports, "RELATED_URL_MAX", { enumerable: true, get: function () { return related_materials_field_2.RELATED_URL_MAX; } });
|
|
2866
|
+
/** 決定の固さの語彙(受け付ける語彙・並びも原本の `DECISION_FIRMNESS_READ_VALUES`) */
|
|
2867
|
+
exports.DECISION_FIRMNESS = decision_v2_fields_1.DECISION_FIRMNESS_READ_VALUES;
|
|
2838
2868
|
/** frontmatter の `firmness` → 語彙の 1 語。語彙外・文字列でないものは undefined(綴りはそのまま比べる) */
|
|
2839
2869
|
function decisionFirmnessOf(value) {
|
|
2840
|
-
return
|
|
2870
|
+
return (0, decision_v2_fields_1.decisionV2FieldsOf)({ firmness: value }).firmness ?? undefined;
|
|
2841
2871
|
}
|
|
2842
|
-
/**
|
|
2872
|
+
/**
|
|
2873
|
+
* frontmatter の `strand` → 筋の札の名前(前後の空白を落とす)。空・文字列でないものは undefined。
|
|
2874
|
+
* 🔴 viewer は `topic` でなく `strand` を読む。原本の札の読み(`topic` の読み=文字列で空でなければ trim)に `strand` の値を渡す
|
|
2875
|
+
*/
|
|
2843
2876
|
function decisionStrandOf(value) {
|
|
2844
|
-
return
|
|
2845
|
-
}
|
|
2846
|
-
/** `decided_by` の要素 1 つ。読めなければ null(文字列・知らない鍵・member と name/url の同居・member: で始まらない・空の name・http(s) でない url) */
|
|
2847
|
-
function decidedByEntryOf(entry) {
|
|
2848
|
-
if (entry == null || typeof entry !== "object" || Array.isArray(entry))
|
|
2849
|
-
return null;
|
|
2850
|
-
const o = entry;
|
|
2851
|
-
if (Object.keys(o).some((k) => !DECIDED_BY_KEYS.includes(k)))
|
|
2852
|
-
return null;
|
|
2853
|
-
const has = (k) => Object.prototype.hasOwnProperty.call(o, k);
|
|
2854
|
-
if (has("member")) {
|
|
2855
|
-
if (has("name") || has("url"))
|
|
2856
|
-
return null;
|
|
2857
|
-
return typeof o.member === "string" && MEMBER_ID_RE.test(o.member) ? { member: o.member } : null;
|
|
2858
|
-
}
|
|
2859
|
-
if (!has("name") || !isText(o.name))
|
|
2860
|
-
return null;
|
|
2861
|
-
if (o.url == null)
|
|
2862
|
-
return { name: o.name };
|
|
2863
|
-
return typeof o.url === "string" && DECIDED_BY_URL_RE.test(o.url) ? { name: o.name, url: o.url } : null;
|
|
2877
|
+
return (0, decision_v2_fields_1.decisionV2FieldsOf)({ topic: value }).topic ?? undefined;
|
|
2864
2878
|
}
|
|
2865
2879
|
/**
|
|
2866
|
-
* frontmatter の `decided_by` →
|
|
2880
|
+
* frontmatter の `decided_by` → 決めた人の並び。形の崩れた要素は**飛ばす**(原本の読み)。
|
|
2867
2881
|
* 欄がリストでなければ undefined(無いものとして扱う)。空のリストは []
|
|
2868
2882
|
*/
|
|
2869
2883
|
function decidedByOf(value) {
|
|
2870
|
-
|
|
2871
|
-
return undefined;
|
|
2872
|
-
return value.map(decidedByEntryOf).filter((e) => e !== null);
|
|
2873
|
-
}
|
|
2874
|
-
/**
|
|
2875
|
-
* 関連の資料の欄 `related_materials` の読み手(決定・ルール・用語・cortex-engine #1094 F8)。
|
|
2876
|
-
*
|
|
2877
|
-
* 🔴 **写し。正本は cortex-engine `scripts/gold/related-materials-field.mjs` の `isRelatedUrl` と `relatedMaterialsRead`**。
|
|
2878
|
-
* 同じバイト列の表 `test/fixtures/related-materials-contract.json` を両リポに置き、`test/related-materials-view.test.mjs` の
|
|
2879
|
-
* 「[契約] …」が全ケースを流す。規則を変えるときは engine の正本・この写し・表を同じ日に直す。
|
|
2880
|
-
* 重複も上限も見ない(人が足した分を落とさない)。`decided_by` の URL の門(`DECIDED_BY_URL_RE`)とは別の、engine と同じ門
|
|
2881
|
-
*/
|
|
2882
|
-
exports.RELATED_MATERIALS_FIELD = "related_materials";
|
|
2883
|
-
exports.RELATED_URL_MAX = 2000;
|
|
2884
|
-
// eslint-disable-next-line no-control-regex
|
|
2885
|
-
const RELATED_URL_RE = /^https?:\/\/[^\s<>"\[\]〔〕\u0000-\u001f\u007f]+$/i;
|
|
2886
|
-
/** 欄に置いてよい URL か(http(s)・空白と制御文字・`<>"[]〔〕` を含まない・ホストあり・2000 字まで) */
|
|
2887
|
-
function isRelatedUrl(v) {
|
|
2888
|
-
if (typeof v !== "string" || !v || v.length > exports.RELATED_URL_MAX)
|
|
2889
|
-
return false;
|
|
2890
|
-
if (!RELATED_URL_RE.test(v))
|
|
2891
|
-
return false;
|
|
2892
|
-
try {
|
|
2893
|
-
const u = new URL(v);
|
|
2894
|
-
return (u.protocol === "http:" || u.protocol === "https:") && u.hostname !== "";
|
|
2895
|
-
}
|
|
2896
|
-
catch {
|
|
2897
|
-
return false;
|
|
2898
|
-
}
|
|
2899
|
-
}
|
|
2900
|
-
/** frontmatter → 欄の URL の並び(通るものだけ・順はそのまま)。欄が無い・リストでなければ [] */
|
|
2901
|
-
function relatedMaterialsRead(fm) {
|
|
2902
|
-
const v = fm && typeof fm === "object" ? fm[exports.RELATED_MATERIALS_FIELD] : null;
|
|
2903
|
-
return Array.isArray(v) ? v.filter(isRelatedUrl) : [];
|
|
2884
|
+
return (0, decision_v2_fields_1.decisionV2FieldsOf)({ decided_by: value }).decidedBy ?? undefined;
|
|
2904
2885
|
}
|
|
2905
2886
|
/** 画面に載せる形(名前は根拠と同じ解決=帳の題名・URL の種類の名前)。通る URL が 0 本なら undefined(キーごと載せない) */
|
|
2906
2887
|
function relatedMaterialsView(fm, refs) {
|
|
2907
|
-
const urls = relatedMaterialsRead(fm);
|
|
2888
|
+
const urls = (0, related_materials_field_1.relatedMaterialsRead)(fm);
|
|
2908
2889
|
if (!urls.length)
|
|
2909
2890
|
return undefined;
|
|
2910
2891
|
return urls.map((u) => {
|
|
@@ -1,196 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.syntaxNormalizedUrl = exports.githubRefToken = exports.canonicalEvidence = void 0;
|
|
2
4
|
/**
|
|
3
|
-
* **根拠の名前の正規化**(cortex-engine #968 設計 v3 §1.4
|
|
4
|
-
*
|
|
5
|
-
* 正本は cortex-engine `scripts/gold/ref-tokens.mjs#canonicalEvidence`。ここはその TypeScript の写し
|
|
6
|
-
* (ビューアは engine のコードを import できないので、ロジックごと複製する)。
|
|
5
|
+
* **根拠の名前の正規化**(cortex-engine #968 設計 v3 §1.4)の viewer の入口。
|
|
7
6
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* 原本は engine を束ねて引く(viewer/engine-src → dist/engine・便 R4): cortex-engine `scripts/gold/ref-tokens.mjs` の
|
|
8
|
+
* `canonicalEvidence`・`syntaxNormalizedUrl`・`githubRefToken`。規則はここに書かない(ctx の Map/Record の両対応も原本が持つ)。
|
|
9
|
+
* 題材は engine から組み立てる `test/fixtures/evidence-canonical-cases.json`(`test/canonical-evidence.test.mjs` が流す)。
|
|
11
10
|
*
|
|
12
|
-
* 比べるのは `key`、画面がリンク先として使うのは `url
|
|
13
|
-
* 既存の印(`refTokens`)と同じ綴り(小文字の課題キー・`gh:o/r#N`)——ビューアの他の場所
|
|
14
|
-
* (課題キーをそのまま ID にする `classifyTarget` の旧経路)と当たり方をそろえるため。
|
|
15
|
-
*/
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.githubRefToken = void 0;
|
|
18
|
-
exports.syntaxNormalizedUrl = syntaxNormalizedUrl;
|
|
19
|
-
exports.canonicalEvidence = canonicalEvidence;
|
|
20
|
-
/** 課題キーだけの値(`PJ_CORTEX-12`)。engine の `ISSUE_KEY_RE` と同じ形の全体一致 */
|
|
21
|
-
const WHOLE_ISSUE_KEY_RE = /^([A-Z][A-Z0-9_]{1,20})-(\d+)$/;
|
|
22
|
-
/** `owner/repo#N` だけの値 */
|
|
23
|
-
const WHOLE_GH_SHORT_RE = /^([A-Za-z0-9._-]+)\/([A-Za-z0-9._-]+)#(\d+)$/;
|
|
24
|
-
/** スキーム・ホスト・残り(パス・クエリ・断片) */
|
|
25
|
-
const URL_PARTS_RE = /^(https?):\/\/([^/?#\s]+)([^\s]*)$/i;
|
|
26
|
-
/** GitHub の issue / PR を指す 2 つの綴りを同じ印にする(engine `githubRefToken` と同じ) */
|
|
27
|
-
const githubRefToken = (owner, repo, num) => `gh:${String(owner).toLowerCase()}/${String(repo).toLowerCase()}#${num}`;
|
|
28
|
-
exports.githubRefToken = githubRefToken;
|
|
29
|
-
/**
|
|
30
|
-
* URL の構文の正規化(RFC 3986 §6.2.2)。スキームとホストを小文字・既定のポートを落とす・`%xx` を大文字・
|
|
31
|
-
* 末尾の句読点と末尾の `/` を落とす。日本語はエンコードしない。URL でなければ `null`。
|
|
32
|
-
*/
|
|
33
|
-
function syntaxNormalizedUrl(raw) {
|
|
34
|
-
let t = String(raw ?? "").trim();
|
|
35
|
-
for (let prev = ""; prev !== t;) {
|
|
36
|
-
prev = t;
|
|
37
|
-
t = t.replace(/[).,;:、。]+$/, "").replace(/\/+$/, "");
|
|
38
|
-
}
|
|
39
|
-
const m = t.match(URL_PARTS_RE);
|
|
40
|
-
if (!m)
|
|
41
|
-
return null;
|
|
42
|
-
const scheme = m[1].toLowerCase();
|
|
43
|
-
let host = m[2].toLowerCase();
|
|
44
|
-
if ((scheme === "http" && host.endsWith(":80")) || (scheme === "https" && host.endsWith(":443"))) {
|
|
45
|
-
host = host.replace(/:\d+$/, "");
|
|
46
|
-
}
|
|
47
|
-
const rest = m[3].replace(/%[0-9a-f]{2}/gi, (x) => x.toUpperCase()).replace(/\/+(?=$|[?#])/, "");
|
|
48
|
-
return `${scheme}://${host}${rest}`;
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* 値全体が URL 1 本の形のときだけ、その URL を返す(素の URL・`[ラベル](URL)`・Slack の `<URL|ラベル>`)。
|
|
52
|
-
* パスに URL を添えた値・文の中に URL がある値は `null`。Markdown のエスケープ(`PJ\_CORTEX`)は外す
|
|
53
|
-
*/
|
|
54
|
-
function wholeUrlOf(value) {
|
|
55
|
-
const v = value.trim();
|
|
56
|
-
const m = v.match(/^(https?:\/\/\S+)$/i) ||
|
|
57
|
-
v.match(/^\[[^\]\n]*\]\((https?:\/\/[^)\s]+)\)$/i) ||
|
|
58
|
-
v.match(/^<(https?:\/\/[^|>\s]+)(?:\|[^>]*)?>$/i);
|
|
59
|
-
return m ? m[1].replace(/\\_/g, "_") : null;
|
|
60
|
-
}
|
|
61
|
-
function toMap(v) {
|
|
62
|
-
if (v instanceof Map)
|
|
63
|
-
return v;
|
|
64
|
-
return new Map(Object.entries(v ?? {}));
|
|
65
|
-
}
|
|
66
|
-
const queryOf = (rest) => {
|
|
67
|
-
const q = rest.indexOf("?");
|
|
68
|
-
if (q < 0)
|
|
69
|
-
return new URLSearchParams();
|
|
70
|
-
const h = rest.indexOf("#", q);
|
|
71
|
-
return new URLSearchParams(rest.slice(q + 1, h < 0 ? undefined : h));
|
|
72
|
-
};
|
|
73
|
-
const pathOf = (rest) => rest.replace(/[?#].*$/, "");
|
|
74
|
-
const fragmentOf = (rest) => {
|
|
75
|
-
const h = rest.indexOf("#");
|
|
76
|
-
return h < 0 ? "" : rest.slice(h + 1);
|
|
77
|
-
};
|
|
78
|
-
const GDOC_KINDS = new Set(["document", "spreadsheets", "presentation", "forms"]);
|
|
79
|
-
const BACKLOG_HOST_RE = /\.(?:backlog\.jp|backlog\.com|backlogtool\.com)$/;
|
|
80
|
-
/** 種類ごとの解き方(URL を構文正規化した後)。当たらなければ汎用の `url` 種に落ちる */
|
|
81
|
-
function canonicalOfUrl(url, ctx) {
|
|
82
|
-
const m = url.match(URL_PARTS_RE);
|
|
83
|
-
const host = m[2];
|
|
84
|
-
const rest = m[3];
|
|
85
|
-
const p = pathOf(rest);
|
|
86
|
-
const q = queryOf(rest);
|
|
87
|
-
if (host === "docs.google.com") {
|
|
88
|
-
const g = p.match(/^\/([a-z]+)\/(?:u\/\d+\/)?d\/([A-Za-z0-9_-]{10,})/);
|
|
89
|
-
if (g && GDOC_KINDS.has(g[1])) {
|
|
90
|
-
return { kind: "gdoc", key: `gfile:${g[2]}`, url: `https://docs.google.com/${g[1]}/d/${g[2]}` };
|
|
91
|
-
}
|
|
92
|
-
const fd = p.match(/^\/file\/d\/([A-Za-z0-9_-]{10,})/);
|
|
93
|
-
if (fd)
|
|
94
|
-
return { kind: "drive", key: `gfile:${fd[1]}`, url: `https://drive.google.com/file/d/${fd[1]}/view` };
|
|
95
|
-
}
|
|
96
|
-
if (host === "drive.google.com") {
|
|
97
|
-
const f = p.match(/^\/file\/(?:u\/\d+\/)?d\/([A-Za-z0-9_-]{10,})/);
|
|
98
|
-
const id = f ? f[1] : /^\/(?:open|uc)$/.test(p) && /^[A-Za-z0-9_-]{10,}$/.test(q.get("id") || "") ? q.get("id") : "";
|
|
99
|
-
if (id)
|
|
100
|
-
return { kind: "drive", key: `gfile:${id}`, url: `https://drive.google.com/file/d/${id}/view` };
|
|
101
|
-
const d = p.match(/^\/drive\/(?:u\/\d+\/)?folders\/([A-Za-z0-9_-]{10,})/);
|
|
102
|
-
if (d)
|
|
103
|
-
return { kind: "drive-folder", key: `gfolder:${d[1]}`, url: `https://drive.google.com/drive/folders/${d[1]}` };
|
|
104
|
-
}
|
|
105
|
-
if (host.endsWith(".slack.com")) {
|
|
106
|
-
const s = p.match(/^\/archives\/([A-Z0-9]+)(?:\/p(\d+))?$/);
|
|
107
|
-
if (s) {
|
|
108
|
-
const hosts = toMap(ctx.slackHosts);
|
|
109
|
-
const h = String(hosts.get(s[1]) || "").trim().toLowerCase() || host;
|
|
110
|
-
const thread = s[2] && /^\d+\.\d+$/.test(q.get("thread_ts") || "") ? q.get("thread_ts") : "";
|
|
111
|
-
const key = `slack:${s[1]}${s[2] ? `:p${s[2]}` : ""}`;
|
|
112
|
-
const tail = `${s[2] ? `/p${s[2]}` : ""}${thread ? `?thread_ts=${thread}` : ""}`;
|
|
113
|
-
return { kind: "slack", key, url: `https://${h}/archives/${s[1]}${tail}` };
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
if (host === "chat.google.com" || host === "mail.google.com") {
|
|
117
|
-
const room = host === "chat.google.com" ? p.match(/^\/(?:u\/\d+\/)?room\/([A-Za-z0-9_-]+)/) : null;
|
|
118
|
-
const inMail = host === "mail.google.com" && /^\/chat(?:\/|$)/.test(p) ? fragmentOf(rest).match(/^chat\/space\/([A-Za-z0-9_-]+)/) : null;
|
|
119
|
-
const space = (room && room[1]) || (inMail && inMail[1]);
|
|
120
|
-
if (space)
|
|
121
|
-
return { kind: "gchat", key: `gchat:${space}`, url: `https://chat.google.com/room/${space}` };
|
|
122
|
-
}
|
|
123
|
-
if (BACKLOG_HOST_RE.test(host)) {
|
|
124
|
-
const v = p.match(/^\/view\/([A-Z][A-Z0-9_]{1,20}-\d+)$/);
|
|
125
|
-
if (v) {
|
|
126
|
-
const c = fragmentOf(rest).match(/^comment-(\d+)$/);
|
|
127
|
-
const issue = v[1].toLowerCase();
|
|
128
|
-
return {
|
|
129
|
-
kind: "backlog",
|
|
130
|
-
key: c ? `${issue}#comment-${c[1]}` : issue,
|
|
131
|
-
url: `https://${host}/view/${v[1]}${c ? `#comment-${c[1]}` : ""}`,
|
|
132
|
-
...(c ? { parentKey: issue } : {}),
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
const w = p.match(/^\/alias\/wiki\/(\d+)$/);
|
|
136
|
-
if (w)
|
|
137
|
-
return { kind: "backlog", key: `backlog:${host}:wiki:${w[1]}`, url: `https://${host}/alias/wiki/${w[1]}` };
|
|
138
|
-
const d = p.match(/^\/document\/([^/]+)\/([0-9a-f]{32})$/i);
|
|
139
|
-
if (d)
|
|
140
|
-
return { kind: "backlog", key: `backlog:${host}:doc:${d[2].toLowerCase()}`, url: `https://${host}/document/${d[1]}/${d[2].toLowerCase()}` };
|
|
141
|
-
}
|
|
142
|
-
if (host === "figma.com" || host === "www.figma.com") {
|
|
143
|
-
const f = p.match(/^\/(design|file|proto|board)\/([A-Za-z0-9]{10,})(?:\/branch\/([A-Za-z0-9]{10,}))?(?:\/|$)/);
|
|
144
|
-
if (f) {
|
|
145
|
-
const fileKey = f[3] || f[2];
|
|
146
|
-
const node = String(q.get("node-id") || "").trim().replace(/%3A/gi, ":");
|
|
147
|
-
if (node && !/^\d+[-:]\d+$/.test(node))
|
|
148
|
-
return { kind: "url", key: `url:${url}`, url };
|
|
149
|
-
const n = node ? node.replace(":", "-") : "";
|
|
150
|
-
const seg = f[1] === "board" ? "board" : "design";
|
|
151
|
-
return { kind: "figma", key: `figma:${fileKey}${n ? `:${n.replace("-", ":")}` : ""}`, url: `https://www.figma.com/${seg}/${fileKey}${n ? `?node-id=${n}` : ""}` };
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
if (host === "github.com" || host === "www.github.com") {
|
|
155
|
-
const g = p.match(/^\/([^/]+)\/([^/]+)\/(?:issues|pull|discussions)\/(\d+)$/);
|
|
156
|
-
if (g) {
|
|
157
|
-
const [o, r] = [g[1].toLowerCase(), g[2].toLowerCase()];
|
|
158
|
-
const issue = (0, exports.githubRefToken)(o, r, g[3]);
|
|
159
|
-
const c = fragmentOf(rest).match(/^(issuecomment-\d+|discussioncomment-\d+|discussion_r\d+|pullrequestreview-\d+)$/);
|
|
160
|
-
return {
|
|
161
|
-
kind: "github",
|
|
162
|
-
key: c ? `${issue}#${c[1]}` : issue,
|
|
163
|
-
url: `https://github.com/${o}/${r}/issues/${g[3]}${c ? `#${c[1]}` : ""}`,
|
|
164
|
-
...(c ? { parentKey: issue } : {}),
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
return { kind: "url", key: `url:${url}`, url };
|
|
169
|
-
}
|
|
170
|
-
/**
|
|
171
|
-
* 根拠の名前の正規化。解けない入力(Gold の ID・`minute:`・`material:`・`design:`・リポのパス・
|
|
172
|
-
* ホストの決まらない課題キー・URL が 2 本以上の値)は `null`。engine 版と 1:1 対応。
|
|
11
|
+
* 比べるのは `key`、画面がリンク先として使うのは `url`。
|
|
173
12
|
*/
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
const key = raw.match(WHOLE_ISSUE_KEY_RE);
|
|
179
|
-
if (key) {
|
|
180
|
-
const domains = toMap(ctx.backlogDomains);
|
|
181
|
-
const host = String(domains.get(key[1]) || "")
|
|
182
|
-
.trim()
|
|
183
|
-
.toLowerCase()
|
|
184
|
-
.replace(/^https?:\/\//, "")
|
|
185
|
-
.replace(/\/+$/, "");
|
|
186
|
-
return host ? canonicalOfUrl(`https://${host}/view/${raw}`, ctx) : null;
|
|
187
|
-
}
|
|
188
|
-
const gh = raw.match(WHOLE_GH_SHORT_RE);
|
|
189
|
-
if (gh)
|
|
190
|
-
return canonicalOfUrl(`https://github.com/${gh[1]}/${gh[2]}/issues/${gh[3]}`, ctx);
|
|
191
|
-
const url = wholeUrlOf(raw);
|
|
192
|
-
if (!url)
|
|
193
|
-
return null;
|
|
194
|
-
const normalized = syntaxNormalizedUrl(url);
|
|
195
|
-
return normalized ? canonicalOfUrl(normalized, ctx) : null;
|
|
196
|
-
}
|
|
13
|
+
var ref_tokens_1 = require("./engine/ref-tokens");
|
|
14
|
+
Object.defineProperty(exports, "canonicalEvidence", { enumerable: true, get: function () { return ref_tokens_1.canonicalEvidence; } });
|
|
15
|
+
Object.defineProperty(exports, "githubRefToken", { enumerable: true, get: function () { return ref_tokens_1.githubRefToken; } });
|
|
16
|
+
Object.defineProperty(exports, "syntaxNormalizedUrl", { enumerable: true, get: function () { return ref_tokens_1.syntaxNormalizedUrl; } });
|
package/dist/change-log.js
CHANGED
|
@@ -46,7 +46,7 @@ exports.CHANGE_LOG_MAX_ROWS = 50;
|
|
|
46
46
|
/** `why` は 1 行 3 本まで(engine の `WHY_MAX`)。これも画面の表示の絞り(`common.delete-cap-exceptions`・元のリンクはコミットに残る) */
|
|
47
47
|
const WHY_MAX = 3;
|
|
48
48
|
/** 載る記録のパス(4 区画の記録。README・雛形・入れ子は載らない) */
|
|
49
|
-
const RECORD_PATH = /^Cortex\/(Decisions|Glossary|Members|Rules)\/records\/[^/]+\.md$/;
|
|
49
|
+
const RECORD_PATH = /^(?:Cortex|Memory)\/(Decisions|Glossary|Members|Rules)\/records\/[^/]+\.md$/;
|
|
50
50
|
const isObj = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
|
|
51
51
|
/** メールの形(URL の部分を除いて見る) */
|
|
52
52
|
// engine `change-log.mjs` の定義と同じ(緩めると、URL の後ろのメールや非 ASCII のローカル部が通る)
|