@grainulation/mill 1.0.0 → 1.0.1
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/CODE_OF_CONDUCT.md +25 -0
- package/CONTRIBUTING.md +101 -0
- package/README.md +90 -42
- package/bin/mill.js +233 -67
- package/lib/exporters/csv.js +35 -30
- package/lib/exporters/json-ld.js +19 -13
- package/lib/exporters/markdown.js +83 -44
- package/lib/exporters/pdf.js +15 -15
- package/lib/formats/bibtex.js +41 -34
- package/lib/formats/changelog.js +27 -26
- package/lib/formats/confluence-adf.js +312 -0
- package/lib/formats/csv.js +41 -37
- package/lib/formats/dot.js +45 -34
- package/lib/formats/evidence-matrix.js +17 -16
- package/lib/formats/executive-summary.js +89 -41
- package/lib/formats/github-issues.js +40 -33
- package/lib/formats/graphml.js +45 -32
- package/lib/formats/html-report.js +110 -63
- package/lib/formats/jira-csv.js +30 -29
- package/lib/formats/json-ld.js +6 -6
- package/lib/formats/markdown.js +53 -36
- package/lib/formats/ndjson.js +6 -6
- package/lib/formats/obsidian.js +43 -35
- package/lib/formats/opml.js +38 -28
- package/lib/formats/ris.js +29 -23
- package/lib/formats/rss.js +31 -28
- package/lib/formats/sankey.js +16 -15
- package/lib/formats/slide-deck.js +145 -57
- package/lib/formats/sql.js +57 -53
- package/lib/formats/static-site.js +64 -52
- package/lib/formats/treemap.js +16 -15
- package/lib/formats/typescript-defs.js +79 -76
- package/lib/formats/yaml.js +58 -40
- package/lib/formats.js +16 -16
- package/lib/index.js +5 -5
- package/lib/json-ld-common.js +37 -31
- package/lib/publishers/clipboard.js +21 -19
- package/lib/publishers/static.js +27 -12
- package/lib/serve-mcp.js +158 -83
- package/lib/server.js +252 -142
- package/package.json +7 -3
package/lib/formats/obsidian.js
CHANGED
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
* Zero dependencies — node built-in only.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
export const name =
|
|
10
|
-
export const extension =
|
|
11
|
-
export const mimeType =
|
|
12
|
-
export const description =
|
|
9
|
+
export const name = "obsidian";
|
|
10
|
+
export const extension = ".md";
|
|
11
|
+
export const mimeType = "text/markdown; charset=utf-8";
|
|
12
|
+
export const description =
|
|
13
|
+
"Claims as Obsidian vault page (YAML front matter, wikilinks, backlinks)";
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Convert a compilation object to Obsidian markdown.
|
|
@@ -19,37 +20,44 @@ export const description = 'Claims as Obsidian vault page (YAML front matter, wi
|
|
|
19
20
|
export function convert(compilation) {
|
|
20
21
|
const meta = compilation.meta || {};
|
|
21
22
|
const claims = compilation.claims || [];
|
|
22
|
-
const sprint = meta.sprint ||
|
|
23
|
-
const question = meta.question ||
|
|
24
|
-
const audience = meta.audience ||
|
|
23
|
+
const sprint = meta.sprint || "unknown";
|
|
24
|
+
const question = meta.question || "";
|
|
25
|
+
const audience = meta.audience || "";
|
|
25
26
|
|
|
26
27
|
const grouped = groupByType(claims);
|
|
27
28
|
const lines = [];
|
|
28
29
|
|
|
29
30
|
// YAML front matter
|
|
30
|
-
lines.push(
|
|
31
|
+
lines.push("---");
|
|
31
32
|
lines.push(`sprint: ${sprint}`);
|
|
32
33
|
if (question) lines.push(`question: "${escapeFrontMatter(question)}"`);
|
|
33
34
|
if (audience) lines.push(`audience: "${escapeFrontMatter(audience)}"`);
|
|
34
35
|
lines.push(`claim_count: ${claims.length}`);
|
|
35
|
-
lines.push(
|
|
36
|
-
lines.push(
|
|
37
|
-
lines.push(
|
|
36
|
+
lines.push("tags: [wheat, sprint, export]");
|
|
37
|
+
lines.push("---");
|
|
38
|
+
lines.push("");
|
|
38
39
|
|
|
39
40
|
// Title
|
|
40
41
|
lines.push(`# Sprint: ${sprint}`);
|
|
41
|
-
lines.push(
|
|
42
|
+
lines.push("");
|
|
42
43
|
|
|
43
44
|
if (question) {
|
|
44
45
|
lines.push(`> ${question}`);
|
|
45
|
-
lines.push(
|
|
46
|
+
lines.push("");
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
// Claims grouped by type
|
|
49
|
-
lines.push(
|
|
50
|
-
lines.push(
|
|
51
|
-
|
|
52
|
-
const typeOrder = [
|
|
50
|
+
lines.push("## Claims");
|
|
51
|
+
lines.push("");
|
|
52
|
+
|
|
53
|
+
const typeOrder = [
|
|
54
|
+
"constraint",
|
|
55
|
+
"factual",
|
|
56
|
+
"estimate",
|
|
57
|
+
"risk",
|
|
58
|
+
"recommendation",
|
|
59
|
+
"feedback",
|
|
60
|
+
];
|
|
53
61
|
const seen = new Set();
|
|
54
62
|
|
|
55
63
|
for (const type of typeOrder) {
|
|
@@ -66,22 +74,22 @@ export function convert(compilation) {
|
|
|
66
74
|
}
|
|
67
75
|
|
|
68
76
|
// Connections section
|
|
69
|
-
lines.push(
|
|
70
|
-
lines.push(
|
|
71
|
-
lines.push(
|
|
77
|
+
lines.push("## Connections");
|
|
78
|
+
lines.push("");
|
|
79
|
+
lines.push("- Related: [[compilation]] [[claims]]");
|
|
72
80
|
|
|
73
81
|
if (compilation.certificate) {
|
|
74
|
-
lines.push(
|
|
82
|
+
lines.push("- Certificate: [[certificate]]");
|
|
75
83
|
}
|
|
76
84
|
|
|
77
|
-
lines.push(
|
|
78
|
-
return lines.join(
|
|
85
|
+
lines.push("");
|
|
86
|
+
return lines.join("\n") + "\n";
|
|
79
87
|
}
|
|
80
88
|
|
|
81
89
|
function groupByType(claims) {
|
|
82
90
|
const groups = {};
|
|
83
91
|
for (const claim of claims) {
|
|
84
|
-
const type = claim.type ||
|
|
92
|
+
const type = claim.type || "other";
|
|
85
93
|
if (!groups[type]) groups[type] = [];
|
|
86
94
|
groups[type].push(claim);
|
|
87
95
|
}
|
|
@@ -89,14 +97,14 @@ function groupByType(claims) {
|
|
|
89
97
|
}
|
|
90
98
|
|
|
91
99
|
function renderGroup(type, claims) {
|
|
92
|
-
const label = capitalize(type) +
|
|
100
|
+
const label = capitalize(type) + "s";
|
|
93
101
|
const lines = [];
|
|
94
102
|
|
|
95
103
|
lines.push(`### ${label}`);
|
|
96
104
|
|
|
97
105
|
for (const claim of claims) {
|
|
98
|
-
const id = claim.id ||
|
|
99
|
-
const content = claim.content || claim.text ||
|
|
106
|
+
const id = claim.id || "???";
|
|
107
|
+
const content = claim.content || claim.text || "";
|
|
100
108
|
const summary = truncate(content, 120);
|
|
101
109
|
const evidence = getEvidence(claim);
|
|
102
110
|
const tags = formatTags(claim);
|
|
@@ -104,22 +112,22 @@ function renderGroup(type, claims) {
|
|
|
104
112
|
lines.push(`- [[${id}]] ${summary} #${type} #${evidence}${tags}`);
|
|
105
113
|
}
|
|
106
114
|
|
|
107
|
-
lines.push(
|
|
115
|
+
lines.push("");
|
|
108
116
|
return lines;
|
|
109
117
|
}
|
|
110
118
|
|
|
111
119
|
function getEvidence(claim) {
|
|
112
|
-
if (typeof claim.evidence ===
|
|
113
|
-
if (typeof claim.evidence ===
|
|
114
|
-
return claim.evidence.tier || claim.evidence_tier ||
|
|
120
|
+
if (typeof claim.evidence === "string") return claim.evidence;
|
|
121
|
+
if (typeof claim.evidence === "object" && claim.evidence !== null) {
|
|
122
|
+
return claim.evidence.tier || claim.evidence_tier || "stated";
|
|
115
123
|
}
|
|
116
|
-
return claim.evidence_tier ||
|
|
124
|
+
return claim.evidence_tier || "stated";
|
|
117
125
|
}
|
|
118
126
|
|
|
119
127
|
function formatTags(claim) {
|
|
120
128
|
const tags = Array.isArray(claim.tags) ? claim.tags : [];
|
|
121
|
-
if (tags.length === 0) return
|
|
122
|
-
return
|
|
129
|
+
if (tags.length === 0) return "";
|
|
130
|
+
return " " + tags.map((t) => `#${t.replace(/\s+/g, "-")}`).join(" ");
|
|
123
131
|
}
|
|
124
132
|
|
|
125
133
|
function capitalize(str) {
|
|
@@ -128,7 +136,7 @@ function capitalize(str) {
|
|
|
128
136
|
|
|
129
137
|
function truncate(str, max) {
|
|
130
138
|
if (str.length <= max) return str;
|
|
131
|
-
return str.slice(0, max - 3) +
|
|
139
|
+
return str.slice(0, max - 3) + "...";
|
|
132
140
|
}
|
|
133
141
|
|
|
134
142
|
function escapeFrontMatter(str) {
|
package/lib/formats/opml.js
CHANGED
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
* Zero dependencies — node built-in only.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
export const name =
|
|
10
|
-
export const extension =
|
|
11
|
-
export const mimeType =
|
|
12
|
-
export const description =
|
|
9
|
+
export const name = "opml";
|
|
10
|
+
export const extension = ".opml";
|
|
11
|
+
export const mimeType = "text/x-opml; charset=utf-8";
|
|
12
|
+
export const description =
|
|
13
|
+
"Claims as OPML 2.0 outline grouped by type (for RSS readers and outliners)";
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Convert a compilation object to OPML XML.
|
|
@@ -19,7 +20,7 @@ export const description = 'Claims as OPML 2.0 outline grouped by type (for RSS
|
|
|
19
20
|
export function convert(compilation) {
|
|
20
21
|
const meta = compilation.meta || {};
|
|
21
22
|
const claims = compilation.claims || [];
|
|
22
|
-
const sprint = meta.sprint ||
|
|
23
|
+
const sprint = meta.sprint || "unknown";
|
|
23
24
|
|
|
24
25
|
const grouped = groupByType(claims);
|
|
25
26
|
const lines = [];
|
|
@@ -27,9 +28,16 @@ export function convert(compilation) {
|
|
|
27
28
|
lines.push('<?xml version="1.0" encoding="utf-8"?>');
|
|
28
29
|
lines.push('<opml version="2.0">');
|
|
29
30
|
lines.push(` <head><title>${esc(sprint)}</title></head>`);
|
|
30
|
-
lines.push(
|
|
31
|
-
|
|
32
|
-
const typeOrder = [
|
|
31
|
+
lines.push(" <body>");
|
|
32
|
+
|
|
33
|
+
const typeOrder = [
|
|
34
|
+
"constraint",
|
|
35
|
+
"factual",
|
|
36
|
+
"estimate",
|
|
37
|
+
"risk",
|
|
38
|
+
"recommendation",
|
|
39
|
+
"feedback",
|
|
40
|
+
];
|
|
33
41
|
const seen = new Set();
|
|
34
42
|
|
|
35
43
|
for (const type of typeOrder) {
|
|
@@ -46,16 +54,16 @@ export function convert(compilation) {
|
|
|
46
54
|
}
|
|
47
55
|
}
|
|
48
56
|
|
|
49
|
-
lines.push(
|
|
50
|
-
lines.push(
|
|
57
|
+
lines.push(" </body>");
|
|
58
|
+
lines.push("</opml>");
|
|
51
59
|
|
|
52
|
-
return lines.join(
|
|
60
|
+
return lines.join("\n") + "\n";
|
|
53
61
|
}
|
|
54
62
|
|
|
55
63
|
function groupByType(claims) {
|
|
56
64
|
const groups = {};
|
|
57
65
|
for (const claim of claims) {
|
|
58
|
-
const type = claim.type ||
|
|
66
|
+
const type = claim.type || "other";
|
|
59
67
|
if (!groups[type]) groups[type] = [];
|
|
60
68
|
groups[type].push(claim);
|
|
61
69
|
}
|
|
@@ -63,29 +71,31 @@ function groupByType(claims) {
|
|
|
63
71
|
}
|
|
64
72
|
|
|
65
73
|
function renderGroup(type, claims) {
|
|
66
|
-
const label = capitalize(type) +
|
|
74
|
+
const label = capitalize(type) + "s";
|
|
67
75
|
const lines = [];
|
|
68
76
|
|
|
69
77
|
lines.push(` <outline text="${esc(label)} (${claims.length})">`);
|
|
70
78
|
|
|
71
79
|
for (const claim of claims) {
|
|
72
|
-
const id = claim.id ||
|
|
73
|
-
const content = claim.content || claim.text ||
|
|
80
|
+
const id = claim.id || "???";
|
|
81
|
+
const content = claim.content || claim.text || "";
|
|
74
82
|
const summary = truncate(content, 100);
|
|
75
83
|
const evidence = getEvidence(claim);
|
|
76
|
-
lines.push(
|
|
84
|
+
lines.push(
|
|
85
|
+
` <outline text="${esc(id)}: ${esc(summary)}" _note="evidence: ${esc(evidence)}"/>`,
|
|
86
|
+
);
|
|
77
87
|
}
|
|
78
88
|
|
|
79
|
-
lines.push(
|
|
89
|
+
lines.push(" </outline>");
|
|
80
90
|
return lines;
|
|
81
91
|
}
|
|
82
92
|
|
|
83
93
|
function getEvidence(claim) {
|
|
84
|
-
if (typeof claim.evidence ===
|
|
85
|
-
if (typeof claim.evidence ===
|
|
86
|
-
return claim.evidence.tier || claim.evidence_tier ||
|
|
94
|
+
if (typeof claim.evidence === "string") return claim.evidence;
|
|
95
|
+
if (typeof claim.evidence === "object" && claim.evidence !== null) {
|
|
96
|
+
return claim.evidence.tier || claim.evidence_tier || "stated";
|
|
87
97
|
}
|
|
88
|
-
return claim.evidence_tier ||
|
|
98
|
+
return claim.evidence_tier || "stated";
|
|
89
99
|
}
|
|
90
100
|
|
|
91
101
|
function capitalize(str) {
|
|
@@ -94,15 +104,15 @@ function capitalize(str) {
|
|
|
94
104
|
|
|
95
105
|
function truncate(str, max) {
|
|
96
106
|
if (str.length <= max) return str;
|
|
97
|
-
return str.slice(0, max - 3) +
|
|
107
|
+
return str.slice(0, max - 3) + "...";
|
|
98
108
|
}
|
|
99
109
|
|
|
100
110
|
function esc(str) {
|
|
101
|
-
if (str == null) return
|
|
111
|
+
if (str == null) return "";
|
|
102
112
|
return String(str)
|
|
103
|
-
.replace(/&/g,
|
|
104
|
-
.replace(/</g,
|
|
105
|
-
.replace(/>/g,
|
|
106
|
-
.replace(/"/g,
|
|
107
|
-
.replace(/'/g,
|
|
113
|
+
.replace(/&/g, "&")
|
|
114
|
+
.replace(/</g, "<")
|
|
115
|
+
.replace(/>/g, ">")
|
|
116
|
+
.replace(/"/g, """)
|
|
117
|
+
.replace(/'/g, "'");
|
|
108
118
|
}
|
package/lib/formats/ris.js
CHANGED
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
* Zero dependencies — node built-in only.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
export const name =
|
|
10
|
-
export const extension =
|
|
11
|
-
export const mimeType =
|
|
12
|
-
export const description =
|
|
9
|
+
export const name = "ris";
|
|
10
|
+
export const extension = ".ris";
|
|
11
|
+
export const mimeType = "application/x-research-info-systems; charset=utf-8";
|
|
12
|
+
export const description =
|
|
13
|
+
"Claims as RIS citation records (one TY-ER block per claim)";
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Convert a compilation object to RIS.
|
|
@@ -19,33 +20,38 @@ export const description = 'Claims as RIS citation records (one TY-ER block per
|
|
|
19
20
|
export function convert(compilation) {
|
|
20
21
|
const claims = compilation.claims || [];
|
|
21
22
|
const meta = compilation.meta || {};
|
|
22
|
-
const author = meta.sprint ? `wheat sprint: ${meta.sprint}` :
|
|
23
|
+
const author = meta.sprint ? `wheat sprint: ${meta.sprint}` : "wheat sprint";
|
|
23
24
|
const year = new Date().getFullYear().toString();
|
|
24
25
|
|
|
25
26
|
if (claims.length === 0) {
|
|
26
|
-
return
|
|
27
|
+
return "";
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
const records = claims.map(claim => claimToRecord(claim, author, year));
|
|
30
|
-
return records.join(
|
|
30
|
+
const records = claims.map((claim) => claimToRecord(claim, author, year));
|
|
31
|
+
return records.join("\n") + "\n";
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
function claimToRecord(claim, author, year) {
|
|
34
|
-
const id = String(claim.id ||
|
|
35
|
-
const title = claim.content || claim.text ||
|
|
36
|
-
const type = claim.type ||
|
|
37
|
-
const evidence =
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
const id = String(claim.id || "unknown");
|
|
36
|
+
const title = claim.content || claim.text || "";
|
|
37
|
+
const type = claim.type || "";
|
|
38
|
+
const evidence =
|
|
39
|
+
typeof claim.evidence === "string"
|
|
40
|
+
? claim.evidence
|
|
41
|
+
: (claim.evidence?.tier ?? claim.evidence_tier ?? "");
|
|
42
|
+
const status = claim.status || "";
|
|
41
43
|
const tags = Array.isArray(claim.tags) ? claim.tags : [];
|
|
42
|
-
const confidence =
|
|
44
|
+
const confidence =
|
|
45
|
+
claim.confidence != null ? `, confidence: ${claim.confidence}` : "";
|
|
43
46
|
|
|
44
|
-
const noteParts =
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
const noteParts =
|
|
48
|
+
[
|
|
49
|
+
type ? `type: ${type}` : "",
|
|
50
|
+
evidence ? `evidence: ${evidence}` : "",
|
|
51
|
+
status ? `status: ${status}` : "",
|
|
52
|
+
]
|
|
53
|
+
.filter(Boolean)
|
|
54
|
+
.join(", ") + confidence;
|
|
49
55
|
|
|
50
56
|
const lines = [
|
|
51
57
|
`TY - GEN`,
|
|
@@ -64,7 +70,7 @@ function claimToRecord(claim, author, year) {
|
|
|
64
70
|
}
|
|
65
71
|
|
|
66
72
|
lines.push(`ER - `);
|
|
67
|
-
lines.push(
|
|
73
|
+
lines.push("");
|
|
68
74
|
|
|
69
|
-
return lines.join(
|
|
75
|
+
return lines.join("\n");
|
|
70
76
|
}
|
package/lib/formats/rss.js
CHANGED
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
* Zero dependencies — node built-in only.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
export const name =
|
|
10
|
-
export const extension =
|
|
11
|
-
export const mimeType =
|
|
12
|
-
export const description =
|
|
9
|
+
export const name = "rss";
|
|
10
|
+
export const extension = ".xml";
|
|
11
|
+
export const mimeType = "application/atom+xml; charset=utf-8";
|
|
12
|
+
export const description = "Claims as Atom XML feed (one entry per claim)";
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Convert a compilation object to Atom XML.
|
|
@@ -19,8 +19,8 @@ export const description = 'Claims as Atom XML feed (one entry per claim)';
|
|
|
19
19
|
export function convert(compilation) {
|
|
20
20
|
const claims = compilation.claims || [];
|
|
21
21
|
const meta = compilation.meta || {};
|
|
22
|
-
const sprintName = meta.sprint ||
|
|
23
|
-
const updated = new Date().toISOString().replace(/\.\d{3}Z$/,
|
|
22
|
+
const sprintName = meta.sprint || "unnamed";
|
|
23
|
+
const updated = new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
24
24
|
|
|
25
25
|
const lines = [];
|
|
26
26
|
lines.push(`<?xml version="1.0" encoding="utf-8"?>`);
|
|
@@ -38,27 +38,30 @@ export function convert(compilation) {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
lines.push(`</feed>`);
|
|
41
|
-
lines.push(
|
|
41
|
+
lines.push("");
|
|
42
42
|
|
|
43
|
-
return lines.join(
|
|
43
|
+
return lines.join("\n");
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
function claimToEntry(claim) {
|
|
47
|
-
const id = String(claim.id ||
|
|
48
|
-
const text = claim.content || claim.text ||
|
|
49
|
-
const type = claim.type ||
|
|
50
|
-
const status = claim.status ||
|
|
51
|
-
const evidence =
|
|
52
|
-
|
|
53
|
-
|
|
47
|
+
const id = String(claim.id || "unknown");
|
|
48
|
+
const text = claim.content || claim.text || "";
|
|
49
|
+
const type = claim.type || "";
|
|
50
|
+
const status = claim.status || "";
|
|
51
|
+
const evidence =
|
|
52
|
+
typeof claim.evidence === "string"
|
|
53
|
+
? claim.evidence
|
|
54
|
+
: (claim.evidence?.tier ?? claim.evidence_tier ?? "");
|
|
54
55
|
const tags = Array.isArray(claim.tags) ? claim.tags : [];
|
|
55
|
-
const updated = new Date().toISOString().replace(/\.\d{3}Z$/,
|
|
56
|
+
const updated = new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
56
57
|
|
|
57
58
|
const summaryParts = [
|
|
58
|
-
type ? `type: ${type}` :
|
|
59
|
-
evidence ? `evidence: ${evidence}` :
|
|
60
|
-
status ? `status: ${status}` :
|
|
61
|
-
]
|
|
59
|
+
type ? `type: ${type}` : "",
|
|
60
|
+
evidence ? `evidence: ${evidence}` : "",
|
|
61
|
+
status ? `status: ${status}` : "",
|
|
62
|
+
]
|
|
63
|
+
.filter(Boolean)
|
|
64
|
+
.join(", ");
|
|
62
65
|
|
|
63
66
|
const lines = [];
|
|
64
67
|
lines.push(` <entry>`);
|
|
@@ -81,20 +84,20 @@ function claimToEntry(claim) {
|
|
|
81
84
|
|
|
82
85
|
lines.push(` </entry>`);
|
|
83
86
|
|
|
84
|
-
return lines.join(
|
|
87
|
+
return lines.join("\n");
|
|
85
88
|
}
|
|
86
89
|
|
|
87
90
|
function escapeXml(value) {
|
|
88
|
-
if (value == null) return
|
|
91
|
+
if (value == null) return "";
|
|
89
92
|
return String(value)
|
|
90
|
-
.replace(/&/g,
|
|
91
|
-
.replace(/</g,
|
|
92
|
-
.replace(/>/g,
|
|
93
|
-
.replace(/"/g,
|
|
94
|
-
.replace(/'/g,
|
|
93
|
+
.replace(/&/g, "&")
|
|
94
|
+
.replace(/</g, "<")
|
|
95
|
+
.replace(/>/g, ">")
|
|
96
|
+
.replace(/"/g, """)
|
|
97
|
+
.replace(/'/g, "'");
|
|
95
98
|
}
|
|
96
99
|
|
|
97
100
|
function truncate(str, max) {
|
|
98
101
|
if (str.length <= max) return str;
|
|
99
|
-
return str.slice(0, max - 3) +
|
|
102
|
+
return str.slice(0, max - 3) + "...";
|
|
100
103
|
}
|
package/lib/formats/sankey.js
CHANGED
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
* Zero dependencies — node built-in only.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
export const name =
|
|
10
|
-
export const extension =
|
|
11
|
-
export const mimeType =
|
|
12
|
-
export const description =
|
|
9
|
+
export const name = "sankey";
|
|
10
|
+
export const extension = ".json";
|
|
11
|
+
export const mimeType = "application/json; charset=utf-8";
|
|
12
|
+
export const description =
|
|
13
|
+
"Claims as Sankey flow JSON (type -> evidence tier -> status)";
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Convert a compilation object to Sankey JSON.
|
|
@@ -25,9 +26,9 @@ export function convert(compilation) {
|
|
|
25
26
|
const nodeSet = new Set();
|
|
26
27
|
|
|
27
28
|
for (const claim of claims) {
|
|
28
|
-
const type =
|
|
29
|
-
const evidence =
|
|
30
|
-
const status =
|
|
29
|
+
const type = "type:" + (claim.type || "other");
|
|
30
|
+
const evidence = "evidence:" + getEvidence(claim) || "evidence:unknown";
|
|
31
|
+
const status = "status:" + (claim.status || "unknown");
|
|
31
32
|
|
|
32
33
|
nodeSet.add(type);
|
|
33
34
|
nodeSet.add(evidence);
|
|
@@ -44,29 +45,29 @@ export function convert(compilation) {
|
|
|
44
45
|
|
|
45
46
|
const nodes = Array.from(nodeSet)
|
|
46
47
|
.sort()
|
|
47
|
-
.map(id => ({ id }));
|
|
48
|
+
.map((id) => ({ id }));
|
|
48
49
|
|
|
49
50
|
const links = [];
|
|
50
51
|
|
|
51
52
|
for (const [key, value] of Object.entries(typeToEvidence)) {
|
|
52
|
-
const [source, target] = key.split(
|
|
53
|
+
const [source, target] = key.split("|||");
|
|
53
54
|
links.push({ source, target, value });
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
for (const [key, value] of Object.entries(evidenceToStatus)) {
|
|
57
|
-
const [source, target] = key.split(
|
|
58
|
+
const [source, target] = key.split("|||");
|
|
58
59
|
links.push({ source, target, value });
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
const result = { nodes, links };
|
|
62
63
|
|
|
63
|
-
return JSON.stringify(result, null, 2) +
|
|
64
|
+
return JSON.stringify(result, null, 2) + "\n";
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
function getEvidence(claim) {
|
|
67
|
-
if (typeof claim.evidence ===
|
|
68
|
-
if (typeof claim.evidence ===
|
|
69
|
-
return claim.evidence.tier || claim.evidence_tier ||
|
|
68
|
+
if (typeof claim.evidence === "string") return claim.evidence;
|
|
69
|
+
if (typeof claim.evidence === "object" && claim.evidence !== null) {
|
|
70
|
+
return claim.evidence.tier || claim.evidence_tier || "";
|
|
70
71
|
}
|
|
71
|
-
return claim.evidence_tier ||
|
|
72
|
+
return claim.evidence_tier || "";
|
|
72
73
|
}
|