@de-otio/bibcheck 0.1.0
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/LICENSE +21 -0
- package/README.md +147 -0
- package/dist/cache/fs-cache.d.ts +55 -0
- package/dist/cache/fs-cache.d.ts.map +1 -0
- package/dist/cache/fs-cache.js +264 -0
- package/dist/cache/fs-cache.js.map +1 -0
- package/dist/canonical.d.ts +29 -0
- package/dist/canonical.d.ts.map +1 -0
- package/dist/canonical.js +132 -0
- package/dist/canonical.js.map +1 -0
- package/dist/check.d.ts +140 -0
- package/dist/check.d.ts.map +1 -0
- package/dist/check.js +646 -0
- package/dist/check.js.map +1 -0
- package/dist/cli.d.ts +19 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +357 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +175 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +180 -0
- package/dist/config.js.map +1 -0
- package/dist/databases/crossref.d.ts +53 -0
- package/dist/databases/crossref.d.ts.map +1 -0
- package/dist/databases/crossref.js +138 -0
- package/dist/databases/crossref.js.map +1 -0
- package/dist/databases/index.d.ts +12 -0
- package/dist/databases/index.d.ts.map +1 -0
- package/dist/databases/index.js +9 -0
- package/dist/databases/index.js.map +1 -0
- package/dist/databases/openalex.d.ts +29 -0
- package/dist/databases/openalex.d.ts.map +1 -0
- package/dist/databases/openalex.js +117 -0
- package/dist/databases/openalex.js.map +1 -0
- package/dist/databases/openlibrary.d.ts +26 -0
- package/dist/databases/openlibrary.d.ts.map +1 -0
- package/dist/databases/openlibrary.js +79 -0
- package/dist/databases/openlibrary.js.map +1 -0
- package/dist/databases/worldcat.d.ts +33 -0
- package/dist/databases/worldcat.d.ts.map +1 -0
- package/dist/databases/worldcat.js +145 -0
- package/dist/databases/worldcat.js.map +1 -0
- package/dist/doctor.d.ts +44 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +386 -0
- package/dist/doctor.js.map +1 -0
- package/dist/existence.d.ts +70 -0
- package/dist/existence.d.ts.map +1 -0
- package/dist/existence.js +308 -0
- package/dist/existence.js.map +1 -0
- package/dist/http.d.ts +97 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +543 -0
- package/dist/http.js.map +1 -0
- package/dist/identifiers.d.ts +44 -0
- package/dist/identifiers.d.ts.map +1 -0
- package/dist/identifiers.js +111 -0
- package/dist/identifiers.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/linkage.d.ts +29 -0
- package/dist/linkage.d.ts.map +1 -0
- package/dist/linkage.js +73 -0
- package/dist/linkage.js.map +1 -0
- package/dist/markdown/blocks.d.ts +19 -0
- package/dist/markdown/blocks.d.ts.map +1 -0
- package/dist/markdown/blocks.js +69 -0
- package/dist/markdown/blocks.js.map +1 -0
- package/dist/markdown/citekeys.d.ts +22 -0
- package/dist/markdown/citekeys.d.ts.map +1 -0
- package/dist/markdown/citekeys.js +100 -0
- package/dist/markdown/citekeys.js.map +1 -0
- package/dist/markdown/glob.d.ts +18 -0
- package/dist/markdown/glob.d.ts.map +1 -0
- package/dist/markdown/glob.js +26 -0
- package/dist/markdown/glob.js.map +1 -0
- package/dist/markdown/prose.d.ts +19 -0
- package/dist/markdown/prose.d.ts.map +1 -0
- package/dist/markdown/prose.js +81 -0
- package/dist/markdown/prose.js.map +1 -0
- package/dist/output/json.d.ts +21 -0
- package/dist/output/json.d.ts.map +1 -0
- package/dist/output/json.js +24 -0
- package/dist/output/json.js.map +1 -0
- package/dist/output/markdown.d.ts +21 -0
- package/dist/output/markdown.d.ts.map +1 -0
- package/dist/output/markdown.js +194 -0
- package/dist/output/markdown.js.map +1 -0
- package/dist/output/sarif.d.ts +31 -0
- package/dist/output/sarif.d.ts.map +1 -0
- package/dist/output/sarif.js +322 -0
- package/dist/output/sarif.js.map +1 -0
- package/dist/output/text.d.ts +27 -0
- package/dist/output/text.d.ts.map +1 -0
- package/dist/output/text.js +212 -0
- package/dist/output/text.js.map +1 -0
- package/dist/phrases/load.d.ts +34 -0
- package/dist/phrases/load.d.ts.map +1 -0
- package/dist/phrases/load.js +148 -0
- package/dist/phrases/load.js.map +1 -0
- package/dist/phrases.d.ts +27 -0
- package/dist/phrases.d.ts.map +1 -0
- package/dist/phrases.js +116 -0
- package/dist/phrases.js.map +1 -0
- package/dist/schema/csl.d.ts +429 -0
- package/dist/schema/csl.d.ts.map +1 -0
- package/dist/schema/csl.js +101 -0
- package/dist/schema/csl.js.map +1 -0
- package/dist/schema/output.d.ts +1116 -0
- package/dist/schema/output.d.ts.map +1 -0
- package/dist/schema/output.js +419 -0
- package/dist/schema/output.js.map +1 -0
- package/dist/suppression.d.ts +106 -0
- package/dist/suppression.d.ts.map +1 -0
- package/dist/suppression.js +134 -0
- package/dist/suppression.js.map +1 -0
- package/dist/version.d.ts +11 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +14 -0
- package/dist/version.js.map +1 -0
- package/dist/worklist.d.ts +32 -0
- package/dist/worklist.d.ts.map +1 -0
- package/dist/worklist.js +211 -0
- package/dist/worklist.js.map +1 -0
- package/package.json +82 -0
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SARIF v2.1.0 renderer for bibcheck output.
|
|
3
|
+
*
|
|
4
|
+
* Uses node-sarif-builder for the structural boilerplate (tool/driver, rules,
|
|
5
|
+
* results) and then post-processes the resulting object to add:
|
|
6
|
+
* - originalUriBaseIds so that relative artifact URIs resolve correctly
|
|
7
|
+
* - partialFingerprints (sha256-based, stable across runs) for deduplication
|
|
8
|
+
*
|
|
9
|
+
* The library does not expose a first-class API for those two fields, so they
|
|
10
|
+
* are injected directly onto the mutable run/result objects that the builders
|
|
11
|
+
* expose as public properties (`.run` and `.result`). This is the documented
|
|
12
|
+
* escape hatch in node-sarif-builder.
|
|
13
|
+
*
|
|
14
|
+
* Worklist items are NOT emitted as SARIF results. They are informational and
|
|
15
|
+
* would cause spurious CI failures if surfaced as findings.
|
|
16
|
+
*/
|
|
17
|
+
import { createHash } from 'node:crypto';
|
|
18
|
+
import { SarifBuilder, SarifRunBuilder, SarifResultBuilder, SarifRuleBuilder, } from 'node-sarif-builder';
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Constants
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
const INFORMATION_URI = 'https://github.com/de-otio/bibcheck';
|
|
23
|
+
const DOCS_BASE = 'https://github.com/de-otio/bibcheck/blob/main/docs/output-schema.md';
|
|
24
|
+
const STATIC_RULES = [
|
|
25
|
+
{
|
|
26
|
+
id: 'bibcheck/existence/metadata-mismatch',
|
|
27
|
+
shortDescription: 'Existence check found a metadata mismatch against the databases.',
|
|
28
|
+
anchor: 'existence-metadata-mismatch',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: 'bibcheck/existence/not-found-in-databases',
|
|
32
|
+
shortDescription: 'The entry was not found in any applicable bibliographic database (a fabrication signal).',
|
|
33
|
+
anchor: 'existence-not-found-in-databases',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: 'bibcheck/existence/unverifiable',
|
|
37
|
+
shortDescription: 'Existence of the entry could not be verified in any database.',
|
|
38
|
+
anchor: 'existence-unverifiable',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id: 'bibcheck/identifiers/malformed',
|
|
42
|
+
shortDescription: 'A DOI/ISBN/URL identifier is malformed or has a bad checksum (a fabrication signal).',
|
|
43
|
+
anchor: 'identifiers-malformed',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
id: 'bibcheck/canonical/dead-url',
|
|
47
|
+
shortDescription: 'The canonical URL for this entry is dead (no response).',
|
|
48
|
+
anchor: 'canonical-dead-url',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: 'bibcheck/canonical/wrong-host',
|
|
52
|
+
shortDescription: 'The canonical URL points to a non-approved host.',
|
|
53
|
+
anchor: 'canonical-wrong-host',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: 'bibcheck/canonical/live-url-not-archived-snapshot',
|
|
57
|
+
shortDescription: 'The URL is live but is not a stable archived snapshot.',
|
|
58
|
+
anchor: 'canonical-live-url-not-archived-snapshot',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
id: 'bibcheck/canonical/no-url-on-pre-doi-entry',
|
|
62
|
+
shortDescription: 'A pre-DOI entry is missing a canonical URL.',
|
|
63
|
+
anchor: 'canonical-no-url-on-pre-doi-entry',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
id: 'bibcheck/linkage/unresolved',
|
|
67
|
+
shortDescription: 'A @citekey reference in the prose has no matching bibliography entry.',
|
|
68
|
+
anchor: 'linkage-unresolved',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: 'bibcheck/linkage/orphan',
|
|
72
|
+
shortDescription: 'A bibliography entry is never cited in any document (informational; reverse linkage).',
|
|
73
|
+
anchor: 'linkage-orphan',
|
|
74
|
+
},
|
|
75
|
+
];
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
// Fingerprint helper
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
function fingerprint(ruleId, fileUri, startLine, messageText) {
|
|
80
|
+
const payload = JSON.stringify({ ruleId, fileUri, startLine, messageText });
|
|
81
|
+
return createHash('sha256').update(payload).digest('hex').slice(0, 16);
|
|
82
|
+
}
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// Result builders
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
/** Add a result to the run with a partialFingerprint attached. */
|
|
87
|
+
function addResult(sarifRunBuilder, opts) {
|
|
88
|
+
const builder = new SarifResultBuilder();
|
|
89
|
+
const initOpts = {
|
|
90
|
+
level: opts.level,
|
|
91
|
+
messageText: opts.messageText,
|
|
92
|
+
ruleId: opts.ruleId,
|
|
93
|
+
fileUri: opts.fileUri,
|
|
94
|
+
};
|
|
95
|
+
// startLine must be >= 1 for the library; 0 means "no line info"
|
|
96
|
+
if (opts.startLine != null && opts.startLine >= 1) {
|
|
97
|
+
initOpts.startLine = opts.startLine;
|
|
98
|
+
}
|
|
99
|
+
builder.initSimple(initOpts);
|
|
100
|
+
// Inject uriBaseId so the relative URI resolves against PROJECTROOT.
|
|
101
|
+
const physLoc = builder.result.locations?.[0]?.physicalLocation;
|
|
102
|
+
if (physLoc?.artifactLocation) {
|
|
103
|
+
physLoc.artifactLocation.uriBaseId = 'PROJECTROOT';
|
|
104
|
+
}
|
|
105
|
+
// Inject partialFingerprints for deduplication.
|
|
106
|
+
const fp = fingerprint(opts.ruleId, opts.fileUri, opts.startLine ?? 0, opts.messageText);
|
|
107
|
+
builder.result['partialFingerprints'] = {
|
|
108
|
+
bibcheckV1: fp,
|
|
109
|
+
};
|
|
110
|
+
sarifRunBuilder.addResult(builder);
|
|
111
|
+
}
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
// Existence findings
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
function addExistenceResults(output, sarifRunBuilder) {
|
|
116
|
+
for (const entry of output.entries) {
|
|
117
|
+
const existence = entry.existence;
|
|
118
|
+
if (existence == null)
|
|
119
|
+
continue;
|
|
120
|
+
if (existence.status === 'metadata-mismatch') {
|
|
121
|
+
addResult(sarifRunBuilder, {
|
|
122
|
+
ruleId: 'bibcheck/existence/metadata-mismatch',
|
|
123
|
+
level: 'error',
|
|
124
|
+
messageText: `Existence metadata mismatch for @${entry.citekey}: database records do not match the bibliography entry.`,
|
|
125
|
+
fileUri: 'sources.json',
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
else if (existence.status === 'not-found-in-databases') {
|
|
129
|
+
// Absence is a fabrication signal and gates by default (Q1).
|
|
130
|
+
addResult(sarifRunBuilder, {
|
|
131
|
+
ruleId: 'bibcheck/existence/not-found-in-databases',
|
|
132
|
+
level: 'error',
|
|
133
|
+
messageText: `@${entry.citekey} was not found in any applicable bibliographic database.`,
|
|
134
|
+
fileUri: 'sources.json',
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
140
|
+
// Identifier findings (T21/T22): malformed/bad-checksum local identifiers
|
|
141
|
+
// ---------------------------------------------------------------------------
|
|
142
|
+
function addIdentifierResults(output, sarifRunBuilder) {
|
|
143
|
+
for (const entry of output.entries) {
|
|
144
|
+
const ids = entry.identifiers;
|
|
145
|
+
if (ids == null)
|
|
146
|
+
continue;
|
|
147
|
+
const problems = [];
|
|
148
|
+
if (ids.doi === 'malformed')
|
|
149
|
+
problems.push('DOI malformed');
|
|
150
|
+
if (ids.isbn === 'malformed')
|
|
151
|
+
problems.push('ISBN malformed');
|
|
152
|
+
if (ids.isbn === 'bad-checksum')
|
|
153
|
+
problems.push('ISBN bad checksum');
|
|
154
|
+
if (ids.url === 'malformed')
|
|
155
|
+
problems.push('URL malformed');
|
|
156
|
+
if (problems.length === 0)
|
|
157
|
+
continue;
|
|
158
|
+
addResult(sarifRunBuilder, {
|
|
159
|
+
ruleId: 'bibcheck/identifiers/malformed',
|
|
160
|
+
level: 'error',
|
|
161
|
+
messageText: `Malformed identifier for @${entry.citekey}: ${problems.join(', ')}.`,
|
|
162
|
+
fileUri: 'sources.json',
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// ---------------------------------------------------------------------------
|
|
167
|
+
// Canonical findings
|
|
168
|
+
// ---------------------------------------------------------------------------
|
|
169
|
+
const CANONICAL_ERROR_STATUSES = new Set([
|
|
170
|
+
'dead-url',
|
|
171
|
+
'wrong-host',
|
|
172
|
+
'live-url-not-archived-snapshot',
|
|
173
|
+
'no-url-on-pre-doi-entry',
|
|
174
|
+
]);
|
|
175
|
+
function addCanonicalResults(output, sarifRunBuilder) {
|
|
176
|
+
for (const entry of output.entries) {
|
|
177
|
+
const canonical = entry.canonical;
|
|
178
|
+
if (canonical == null)
|
|
179
|
+
continue;
|
|
180
|
+
if (!CANONICAL_ERROR_STATUSES.has(canonical.status))
|
|
181
|
+
continue;
|
|
182
|
+
const urlPart = canonical.url != null ? ` (${canonical.url})` : '';
|
|
183
|
+
addResult(sarifRunBuilder, {
|
|
184
|
+
ruleId: `bibcheck/canonical/${canonical.status}`,
|
|
185
|
+
level: 'error',
|
|
186
|
+
messageText: `Canonical URL issue [${canonical.status}] for @${entry.citekey}${urlPart}.`,
|
|
187
|
+
fileUri: 'sources.json',
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// ---------------------------------------------------------------------------
|
|
192
|
+
// Linkage findings
|
|
193
|
+
// ---------------------------------------------------------------------------
|
|
194
|
+
function addLinkageResults(output, sarifRunBuilder) {
|
|
195
|
+
for (const entry of output.linkage) {
|
|
196
|
+
if (entry.status !== 'unresolved')
|
|
197
|
+
continue;
|
|
198
|
+
const sortedRefs = [...entry.references].sort((a, b) => {
|
|
199
|
+
const fc = a.file.localeCompare(b.file);
|
|
200
|
+
return fc !== 0 ? fc : a.line - b.line;
|
|
201
|
+
});
|
|
202
|
+
for (const ref of sortedRefs) {
|
|
203
|
+
addResult(sarifRunBuilder, {
|
|
204
|
+
ruleId: 'bibcheck/linkage/unresolved',
|
|
205
|
+
level: 'error',
|
|
206
|
+
messageText: `Unresolved @${entry.citekey} reference — no matching bibliography entry.`,
|
|
207
|
+
fileUri: ref.file,
|
|
208
|
+
startLine: ref.line,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Orphaned bibliography entries (reverse linkage, H2). Emitted at `note` level
|
|
215
|
+
* so they surface in Code Scanning as informational annotations and never
|
|
216
|
+
* cause a CI failure. Bibliography-level, so anchored to sources.json.
|
|
217
|
+
*/
|
|
218
|
+
function addOrphanResults(output, sarifRunBuilder) {
|
|
219
|
+
for (const entry of output.linkage) {
|
|
220
|
+
if (entry.status !== 'orphan')
|
|
221
|
+
continue;
|
|
222
|
+
addResult(sarifRunBuilder, {
|
|
223
|
+
ruleId: 'bibcheck/linkage/orphan',
|
|
224
|
+
level: 'note',
|
|
225
|
+
messageText: `@${entry.citekey} is in the bibliography but is never cited in any document (informational).`,
|
|
226
|
+
fileUri: 'sources.json',
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
// ---------------------------------------------------------------------------
|
|
231
|
+
// Phrase flag findings
|
|
232
|
+
// ---------------------------------------------------------------------------
|
|
233
|
+
function addPhraseFlagResults(output, sarifRunBuilder) {
|
|
234
|
+
for (const flag of output.phraseFlags) {
|
|
235
|
+
if (flag.status !== 'flagged')
|
|
236
|
+
continue;
|
|
237
|
+
addResult(sarifRunBuilder, {
|
|
238
|
+
ruleId: `bibcheck/phrase/${flag.patternKey}`,
|
|
239
|
+
level: 'warning',
|
|
240
|
+
messageText: `phrase: "${flag.matchedText}"`,
|
|
241
|
+
fileUri: flag.file,
|
|
242
|
+
startLine: flag.line,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
// ---------------------------------------------------------------------------
|
|
247
|
+
// Dynamic rule collection (phrase pattern rules)
|
|
248
|
+
// ---------------------------------------------------------------------------
|
|
249
|
+
function collectPhraseRules(output) {
|
|
250
|
+
const seen = new Set();
|
|
251
|
+
const rules = [];
|
|
252
|
+
for (const flag of output.phraseFlags) {
|
|
253
|
+
const id = `bibcheck/phrase/${flag.patternKey}`;
|
|
254
|
+
if (!seen.has(id)) {
|
|
255
|
+
seen.add(id);
|
|
256
|
+
rules.push({
|
|
257
|
+
id,
|
|
258
|
+
shortDescription: `Phrase denylist match for pattern key "${flag.patternKey}".`,
|
|
259
|
+
anchor: `phrase-${flag.patternKey}`,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return rules;
|
|
264
|
+
}
|
|
265
|
+
// ---------------------------------------------------------------------------
|
|
266
|
+
// Public API
|
|
267
|
+
// ---------------------------------------------------------------------------
|
|
268
|
+
/**
|
|
269
|
+
* Render a validated Output as a SARIF v2.1.0 JSON string.
|
|
270
|
+
*
|
|
271
|
+
* The SARIF document contains one run with:
|
|
272
|
+
* - tool.driver with all applicable rules defined
|
|
273
|
+
* - originalUriBaseIds.PROJECTROOT so relative file URIs resolve correctly
|
|
274
|
+
* - one result per finding (existence mismatches, canonical errors, linkage
|
|
275
|
+
* unresolved, phrase flags), with partialFingerprints for deduplication
|
|
276
|
+
*
|
|
277
|
+
* Worklist items are not emitted as SARIF results (they are informational).
|
|
278
|
+
* Acknowledged phrase flags are not emitted (only status: 'flagged' entries).
|
|
279
|
+
*/
|
|
280
|
+
export function renderSarif(output) {
|
|
281
|
+
// Collect dynamic rules from phrase flags.
|
|
282
|
+
const phraseRules = collectPhraseRules(output);
|
|
283
|
+
const allRuleDefs = [...STATIC_RULES, ...phraseRules];
|
|
284
|
+
// Build the run.
|
|
285
|
+
const version = output.tool.version;
|
|
286
|
+
const sarifRunBuilder = new SarifRunBuilder().initSimple({
|
|
287
|
+
toolDriverName: 'bibcheck',
|
|
288
|
+
toolDriverVersion: version,
|
|
289
|
+
url: INFORMATION_URI,
|
|
290
|
+
});
|
|
291
|
+
// Add semanticVersion directly (node-sarif-builder doesn't expose a setter).
|
|
292
|
+
sarifRunBuilder.run.tool.driver['semanticVersion'] =
|
|
293
|
+
version;
|
|
294
|
+
// Register all rules.
|
|
295
|
+
for (const ruleDef of allRuleDefs) {
|
|
296
|
+
const ruleBuilder = new SarifRuleBuilder().initSimple({
|
|
297
|
+
ruleId: ruleDef.id,
|
|
298
|
+
shortDescriptionText: ruleDef.shortDescription,
|
|
299
|
+
helpUri: `${DOCS_BASE}#${ruleDef.anchor}`,
|
|
300
|
+
});
|
|
301
|
+
sarifRunBuilder.addRule(ruleBuilder);
|
|
302
|
+
}
|
|
303
|
+
// Add findings.
|
|
304
|
+
addIdentifierResults(output, sarifRunBuilder);
|
|
305
|
+
addExistenceResults(output, sarifRunBuilder);
|
|
306
|
+
addCanonicalResults(output, sarifRunBuilder);
|
|
307
|
+
addLinkageResults(output, sarifRunBuilder);
|
|
308
|
+
addOrphanResults(output, sarifRunBuilder);
|
|
309
|
+
addPhraseFlagResults(output, sarifRunBuilder);
|
|
310
|
+
// Inject originalUriBaseIds so relative URIs resolve to the project root.
|
|
311
|
+
sarifRunBuilder.run['originalUriBaseIds'] = {
|
|
312
|
+
PROJECTROOT: { uri: 'file:///$PROJECT_ROOT/' },
|
|
313
|
+
};
|
|
314
|
+
// Build the top-level SARIF document.
|
|
315
|
+
const sarifBuilder = new SarifBuilder();
|
|
316
|
+
sarifBuilder.addRun(sarifRunBuilder);
|
|
317
|
+
const sarifObj = sarifBuilder.buildSarifOutput();
|
|
318
|
+
// Override $schema to use https (the library defaults to http).
|
|
319
|
+
sarifObj['$schema'] = 'https://json.schemastore.org/sarif-2.1.0.json';
|
|
320
|
+
return JSON.stringify(sarifObj, null, 2) + '\n';
|
|
321
|
+
}
|
|
322
|
+
//# sourceMappingURL=sarif.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sarif.js","sourceRoot":"","sources":["../../src/output/sarif.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compact text renderer for bibcheck output.
|
|
3
|
+
*
|
|
4
|
+
* Produces one line per finding in the format:
|
|
5
|
+
* <file>:<line>: <level>: <message>
|
|
6
|
+
*
|
|
7
|
+
* For findings without a specific file/line (existence-layer results tied to
|
|
8
|
+
* the bibliography source, not a prose file), uses "sources.json:0" as a
|
|
9
|
+
* stand-in artifact location.
|
|
10
|
+
*
|
|
11
|
+
* A summary line is appended at the end:
|
|
12
|
+
* <n> errors, <m> warnings, <k> notes
|
|
13
|
+
*/
|
|
14
|
+
import type { Output } from '../schema/output.js';
|
|
15
|
+
/**
|
|
16
|
+
* Render a validated Output as a compact text string.
|
|
17
|
+
*
|
|
18
|
+
* Format: one line per finding as `<file>:<line>: <level>: <message>`,
|
|
19
|
+
* followed by a summary line with counts.
|
|
20
|
+
*
|
|
21
|
+
* Level mapping (same as SARIF):
|
|
22
|
+
* - error: existence metadata-mismatch, canonical dead-url/wrong-host/etc, linkage unresolved
|
|
23
|
+
* - warning: phrase flags (flagged, not acknowledged)
|
|
24
|
+
* - note: existence unverifiable, worklist items, orphaned bibliography entries
|
|
25
|
+
*/
|
|
26
|
+
export declare function renderText(output: Output): string;
|
|
27
|
+
//# sourceMappingURL=text.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../src/output/text.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAyKlD;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CA4BjD"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compact text renderer for bibcheck output.
|
|
3
|
+
*
|
|
4
|
+
* Produces one line per finding in the format:
|
|
5
|
+
* <file>:<line>: <level>: <message>
|
|
6
|
+
*
|
|
7
|
+
* For findings without a specific file/line (existence-layer results tied to
|
|
8
|
+
* the bibliography source, not a prose file), uses "sources.json:0" as a
|
|
9
|
+
* stand-in artifact location.
|
|
10
|
+
*
|
|
11
|
+
* A summary line is appended at the end:
|
|
12
|
+
* <n> errors, <m> warnings, <k> notes
|
|
13
|
+
*/
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Helpers
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
function fmt(f) {
|
|
18
|
+
return `${f.file}:${f.line}: ${f.level}: ${f.message}`;
|
|
19
|
+
}
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Finding collectors
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
function collectExistenceFindings(output) {
|
|
24
|
+
const findings = [];
|
|
25
|
+
for (const entry of output.entries) {
|
|
26
|
+
if (entry.existence?.status === 'metadata-mismatch') {
|
|
27
|
+
findings.push({
|
|
28
|
+
file: 'sources.json',
|
|
29
|
+
line: 0,
|
|
30
|
+
level: 'error',
|
|
31
|
+
message: `existence metadata-mismatch for @${entry.citekey}`,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
else if (entry.existence?.status === 'not-found-in-databases') {
|
|
35
|
+
// Absence is a fabrication signal and gates by default (Q1).
|
|
36
|
+
findings.push({
|
|
37
|
+
file: 'sources.json',
|
|
38
|
+
line: 0,
|
|
39
|
+
level: 'error',
|
|
40
|
+
message: `existence not-found-in-databases for @${entry.citekey}`,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
else if (entry.existence?.status === 'unverifiable') {
|
|
44
|
+
findings.push({
|
|
45
|
+
file: 'sources.json',
|
|
46
|
+
line: 0,
|
|
47
|
+
level: 'note',
|
|
48
|
+
message: `existence unverifiable for @${entry.citekey}`,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return findings;
|
|
53
|
+
}
|
|
54
|
+
/** Malformed/bad-checksum local identifiers (T21/T22). Gating → error level. */
|
|
55
|
+
function collectIdentifierFindings(output) {
|
|
56
|
+
const findings = [];
|
|
57
|
+
for (const entry of output.entries) {
|
|
58
|
+
const ids = entry.identifiers;
|
|
59
|
+
if (ids === null || ids === undefined)
|
|
60
|
+
continue;
|
|
61
|
+
const problems = [];
|
|
62
|
+
if (ids.doi === 'malformed')
|
|
63
|
+
problems.push('doi malformed');
|
|
64
|
+
if (ids.isbn === 'malformed')
|
|
65
|
+
problems.push('isbn malformed');
|
|
66
|
+
if (ids.isbn === 'bad-checksum')
|
|
67
|
+
problems.push('isbn bad-checksum');
|
|
68
|
+
if (ids.url === 'malformed')
|
|
69
|
+
problems.push('url malformed');
|
|
70
|
+
if (problems.length === 0)
|
|
71
|
+
continue;
|
|
72
|
+
findings.push({
|
|
73
|
+
file: 'sources.json',
|
|
74
|
+
line: 0,
|
|
75
|
+
level: 'error',
|
|
76
|
+
message: `identifier ${problems.join(', ')} for @${entry.citekey}`,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
return findings;
|
|
80
|
+
}
|
|
81
|
+
function collectCanonicalFindings(output) {
|
|
82
|
+
const findings = [];
|
|
83
|
+
const errorStatuses = new Set([
|
|
84
|
+
'dead-url',
|
|
85
|
+
'wrong-host',
|
|
86
|
+
'live-url-not-archived-snapshot',
|
|
87
|
+
'no-url-on-pre-doi-entry',
|
|
88
|
+
]);
|
|
89
|
+
for (const entry of output.entries) {
|
|
90
|
+
const canon = entry.canonical;
|
|
91
|
+
if (canon == null)
|
|
92
|
+
continue;
|
|
93
|
+
if (errorStatuses.has(canon.status)) {
|
|
94
|
+
const urlPart = canon.url != null ? ` (${canon.url})` : '';
|
|
95
|
+
findings.push({
|
|
96
|
+
file: 'sources.json',
|
|
97
|
+
line: 0,
|
|
98
|
+
level: 'error',
|
|
99
|
+
message: `canonical ${canon.status} for @${entry.citekey}${urlPart}`,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return findings;
|
|
104
|
+
}
|
|
105
|
+
function collectLinkageFindings(output) {
|
|
106
|
+
const findings = [];
|
|
107
|
+
for (const entry of output.linkage) {
|
|
108
|
+
if (entry.status !== 'unresolved')
|
|
109
|
+
continue;
|
|
110
|
+
const sortedRefs = [...entry.references].sort((a, b) => {
|
|
111
|
+
const fc = a.file.localeCompare(b.file);
|
|
112
|
+
return fc !== 0 ? fc : a.line - b.line;
|
|
113
|
+
});
|
|
114
|
+
for (const ref of sortedRefs) {
|
|
115
|
+
findings.push({
|
|
116
|
+
file: ref.file,
|
|
117
|
+
line: ref.line,
|
|
118
|
+
level: 'error',
|
|
119
|
+
message: `unresolved linkage @${entry.citekey}`,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return findings;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Orphaned bibliography entries (reverse linkage, H2). Informational only —
|
|
127
|
+
* `note` level, never gating. Bibliography-level finding, so uses the
|
|
128
|
+
* sources.json:0 stand-in location.
|
|
129
|
+
*/
|
|
130
|
+
function collectOrphanFindings(output) {
|
|
131
|
+
const findings = [];
|
|
132
|
+
for (const entry of output.linkage) {
|
|
133
|
+
if (entry.status !== 'orphan')
|
|
134
|
+
continue;
|
|
135
|
+
findings.push({
|
|
136
|
+
file: 'sources.json',
|
|
137
|
+
line: 0,
|
|
138
|
+
level: 'note',
|
|
139
|
+
message: `orphaned bibliography entry @${entry.citekey} (never cited; informational)`,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
return findings;
|
|
143
|
+
}
|
|
144
|
+
function collectPhraseFlagFindings(output) {
|
|
145
|
+
const findings = [];
|
|
146
|
+
for (const flag of output.phraseFlags) {
|
|
147
|
+
if (flag.status !== 'flagged')
|
|
148
|
+
continue;
|
|
149
|
+
findings.push({
|
|
150
|
+
file: flag.file,
|
|
151
|
+
line: flag.line,
|
|
152
|
+
level: 'warning',
|
|
153
|
+
message: `phrase [${flag.patternKey}]: "${flag.matchedText}"`,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
return findings;
|
|
157
|
+
}
|
|
158
|
+
function collectWorklistFindings(output) {
|
|
159
|
+
const findings = [];
|
|
160
|
+
for (const item of output.worklist) {
|
|
161
|
+
findings.push({
|
|
162
|
+
file: item.file,
|
|
163
|
+
line: item.line,
|
|
164
|
+
level: 'note',
|
|
165
|
+
message: `worklist [${item.type}] ${item.citation} — ${item.recommendedAction}`,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
return findings;
|
|
169
|
+
}
|
|
170
|
+
// ---------------------------------------------------------------------------
|
|
171
|
+
// Public API
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
/**
|
|
174
|
+
* Render a validated Output as a compact text string.
|
|
175
|
+
*
|
|
176
|
+
* Format: one line per finding as `<file>:<line>: <level>: <message>`,
|
|
177
|
+
* followed by a summary line with counts.
|
|
178
|
+
*
|
|
179
|
+
* Level mapping (same as SARIF):
|
|
180
|
+
* - error: existence metadata-mismatch, canonical dead-url/wrong-host/etc, linkage unresolved
|
|
181
|
+
* - warning: phrase flags (flagged, not acknowledged)
|
|
182
|
+
* - note: existence unverifiable, worklist items, orphaned bibliography entries
|
|
183
|
+
*/
|
|
184
|
+
export function renderText(output) {
|
|
185
|
+
const allFindings = [
|
|
186
|
+
...collectIdentifierFindings(output),
|
|
187
|
+
...collectExistenceFindings(output),
|
|
188
|
+
...collectCanonicalFindings(output),
|
|
189
|
+
...collectLinkageFindings(output),
|
|
190
|
+
...collectOrphanFindings(output),
|
|
191
|
+
...collectPhraseFlagFindings(output),
|
|
192
|
+
...collectWorklistFindings(output),
|
|
193
|
+
];
|
|
194
|
+
// Sort for determinism: file, line, level, message.
|
|
195
|
+
allFindings.sort((a, b) => {
|
|
196
|
+
const fc = a.file.localeCompare(b.file);
|
|
197
|
+
if (fc !== 0)
|
|
198
|
+
return fc;
|
|
199
|
+
if (a.line !== b.line)
|
|
200
|
+
return a.line - b.line;
|
|
201
|
+
if (a.level !== b.level)
|
|
202
|
+
return a.level.localeCompare(b.level);
|
|
203
|
+
return a.message.localeCompare(b.message);
|
|
204
|
+
});
|
|
205
|
+
const errors = allFindings.filter((f) => f.level === 'error').length;
|
|
206
|
+
const warnings = allFindings.filter((f) => f.level === 'warning').length;
|
|
207
|
+
const notes = allFindings.filter((f) => f.level === 'note').length;
|
|
208
|
+
const lines = allFindings.map(fmt);
|
|
209
|
+
lines.push(`${errors} errors, ${warnings} warnings, ${notes} notes`);
|
|
210
|
+
return lines.join('\n') + '\n';
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=text.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/output/text.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phrase denylist loader.
|
|
3
|
+
*
|
|
4
|
+
* Reads a project-supplied TOML file, validates it with Zod, compiles each
|
|
5
|
+
* pattern with RE2JS (linear-time guarantees; ReDoS-safe), and returns the
|
|
6
|
+
* resulting CompiledPattern[].
|
|
7
|
+
*/
|
|
8
|
+
import { RE2JS } from 're2js';
|
|
9
|
+
export interface DenylistEntry {
|
|
10
|
+
key: string;
|
|
11
|
+
regex: string;
|
|
12
|
+
flags: string;
|
|
13
|
+
referenceUrl: string | null;
|
|
14
|
+
description?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface CompiledPattern {
|
|
17
|
+
key: string;
|
|
18
|
+
regex: string;
|
|
19
|
+
flags: string;
|
|
20
|
+
compiled: ReturnType<typeof RE2JS.compile>;
|
|
21
|
+
referenceUrl: string | null;
|
|
22
|
+
description?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface LoadDenylistOptions {
|
|
25
|
+
path: string;
|
|
26
|
+
cwd?: string;
|
|
27
|
+
}
|
|
28
|
+
export declare class PhraseLoaderError extends Error {
|
|
29
|
+
readonly cause?: unknown | undefined;
|
|
30
|
+
name: "PhraseLoaderError";
|
|
31
|
+
constructor(message: string, cause?: unknown | undefined);
|
|
32
|
+
}
|
|
33
|
+
export declare function loadDenylist(opts: LoadDenylistOptions): Promise<CompiledPattern[]>;
|
|
34
|
+
//# sourceMappingURL=load.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load.d.ts","sourceRoot":"","sources":["../../src/phrases/load.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAM9B,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,UAAU,CAAC,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC;IAC3C,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAMD,qBAAa,iBAAkB,SAAQ,KAAK;aAGY,KAAK,CAAC,EAAE,OAAO;IAF5D,IAAI,EAAG,mBAAmB,CAAU;gBAEjC,OAAO,EAAE,MAAM,EAA2B,KAAK,CAAC,EAAE,OAAO,YAAA;CAMtE;AA4DD,wBAAsB,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAiFxF"}
|