@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,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Suppression & source-type gating (T23).
|
|
3
|
+
*
|
|
4
|
+
* Makes the secure default (Q1 — `not-found-in-databases` and malformed
|
|
5
|
+
* identifiers gate `bibcheck check` unconditionally) *usable* by giving a
|
|
6
|
+
* reviewer two precise, auditable escape hatches that suppress a single
|
|
7
|
+
* finding without disabling the whole check:
|
|
8
|
+
*
|
|
9
|
+
* 1. Source-type gating rules (broad, declarative). A CSL `type` can opt out
|
|
10
|
+
* of the not-found gate via `[source_types] <type> = { gate_not_found =
|
|
11
|
+
* false }` — e.g. a pre-DOI manuscript for which no DOI was ever expected.
|
|
12
|
+
* 2. Per-entry allow-with-reason (specific). An entry's CSL `note` carries a
|
|
13
|
+
* `bibcheck-allow: <finding-type> (reason: ...)` convention, mirroring the
|
|
14
|
+
* phrases `<!-- bibcheck-allow: <key> -->` mechanism.
|
|
15
|
+
*
|
|
16
|
+
* This module is PURE: no I/O, no clock, no network. `check.ts` parses the CSL
|
|
17
|
+
* notes into `ParsedAllow[]` and calls `isGated` once per finding; only gated
|
|
18
|
+
* findings count toward the non-zero exit. Suppressed findings are NOT dropped
|
|
19
|
+
* from the output document — they remain in the entries/summary and are
|
|
20
|
+
* reported as `acknowledged` (informational), exactly like an acknowledged
|
|
21
|
+
* phrase.
|
|
22
|
+
*/
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Finding-type vocabulary
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
/**
|
|
27
|
+
* The gating finding kinds a suppression can target. Mirrors the gateable
|
|
28
|
+
* reasons in `check.ts` (`CHECK_NON_ZERO_REASON`):
|
|
29
|
+
* - 'not-found' → existence.status === 'not-found-in-databases'
|
|
30
|
+
* - 'malformed-identifier' → summary.malformedIdentifiers (per entry)
|
|
31
|
+
* - 'canonical-issue' → canonical.status in the problem set
|
|
32
|
+
* - 'metadata-mismatch' → existence.status === 'metadata-mismatch'
|
|
33
|
+
*/
|
|
34
|
+
export const FINDING_TYPES = [
|
|
35
|
+
'not-found',
|
|
36
|
+
'malformed-identifier',
|
|
37
|
+
'canonical-issue',
|
|
38
|
+
'metadata-mismatch',
|
|
39
|
+
];
|
|
40
|
+
function isFindingType(value) {
|
|
41
|
+
return FINDING_TYPES.includes(value);
|
|
42
|
+
}
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// Note parsing
|
|
45
|
+
//
|
|
46
|
+
// Convention (documented in docs/configuration.md):
|
|
47
|
+
// note: "bibcheck-allow: not-found (reason: 1680 pamphlet, Bodleian shelfmark X)"
|
|
48
|
+
//
|
|
49
|
+
// A single note may carry several directives (one per gateable finding). The
|
|
50
|
+
// finding-type token is matched against the known vocabulary; an unknown token
|
|
51
|
+
// is reported back so the caller can warn (a typo'd finding type must not
|
|
52
|
+
// silently suppress nothing AND must not crash).
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
const ALLOW_DIRECTIVE_RE = /bibcheck-allow:\s*([\w-]+)\s*(?:\(\s*reason:\s*([^)]*)\)\s*)?/gi;
|
|
55
|
+
/**
|
|
56
|
+
* Parse every `bibcheck-allow` directive out of one entry's CSL `note`.
|
|
57
|
+
*
|
|
58
|
+
* Pure. Returns both the valid allows (which may still have `reason: null` when
|
|
59
|
+
* the reason was omitted — the caller decides how to warn) and any directives
|
|
60
|
+
* whose finding-type token is unrecognised.
|
|
61
|
+
*/
|
|
62
|
+
export function parseAllows(citekey, note) {
|
|
63
|
+
const allows = [];
|
|
64
|
+
const unknownTypes = [];
|
|
65
|
+
if (note === undefined || note === '') {
|
|
66
|
+
return { allows, unknownTypes };
|
|
67
|
+
}
|
|
68
|
+
for (const m of note.matchAll(ALLOW_DIRECTIVE_RE)) {
|
|
69
|
+
const token = m[1];
|
|
70
|
+
if (token === undefined)
|
|
71
|
+
continue;
|
|
72
|
+
const raw = m[0].trim();
|
|
73
|
+
if (!isFindingType(token)) {
|
|
74
|
+
unknownTypes.push({ citekey, token, raw });
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
// m[2] is the reason capture (only present when `(reason: ...)` matched).
|
|
78
|
+
const reasonRaw = m[2];
|
|
79
|
+
const reason = reasonRaw !== undefined && reasonRaw.trim() !== '' ? reasonRaw.trim() : null;
|
|
80
|
+
allows.push({ citekey, findingType: token, reason, raw });
|
|
81
|
+
}
|
|
82
|
+
return { allows, unknownTypes };
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Parse allows across a whole bibliography. Convenience wrapper used by
|
|
86
|
+
* `check.ts`; aggregates the per-entry results.
|
|
87
|
+
*/
|
|
88
|
+
export function parseAllowsForBibliography(entries) {
|
|
89
|
+
const allows = [];
|
|
90
|
+
const unknownTypes = [];
|
|
91
|
+
for (const e of entries) {
|
|
92
|
+
const r = parseAllows(e.citekey, e.note);
|
|
93
|
+
allows.push(...r.allows);
|
|
94
|
+
unknownTypes.push(...r.unknownTypes);
|
|
95
|
+
}
|
|
96
|
+
return { allows, unknownTypes };
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Decide whether a single finding gates the build.
|
|
100
|
+
*
|
|
101
|
+
* Precedence (an explicit allow or a source-type exemption beats the default
|
|
102
|
+
* gate):
|
|
103
|
+
*
|
|
104
|
+
* 1. A valid per-entry allow (matching citekey + findingType, with a
|
|
105
|
+
* NON-EMPTY reason) → NOT gated, `reason: 'allow'`. Reason is mandatory:
|
|
106
|
+
* an allow with an empty/missing reason does NOT suppress (it is dropped
|
|
107
|
+
* here and warned about by the caller), so it falls through to the gate.
|
|
108
|
+
* 2. For `not-found` only, a source-type exemption
|
|
109
|
+
* (`[source_types] <cslType> = { gate_not_found = false }`) → NOT gated,
|
|
110
|
+
* `reason: 'source-type'`. Source-type rules govern only the not-found
|
|
111
|
+
* gate (no DOI was ever expected for the type); they do not exempt
|
|
112
|
+
* malformed identifiers, canonical issues, or metadata mismatches.
|
|
113
|
+
* 3. Otherwise the secure default applies → gated, `reason: 'default'`.
|
|
114
|
+
*
|
|
115
|
+
* Pure: depends only on its inputs.
|
|
116
|
+
*/
|
|
117
|
+
export function isGated(input) {
|
|
118
|
+
const { citekey, findingType, cslType, config, allows } = input;
|
|
119
|
+
// 1. Per-entry allow with a valid (non-empty) reason wins.
|
|
120
|
+
const hasValidAllow = allows.some((a) => a.citekey === citekey && a.findingType === findingType && a.reason !== null);
|
|
121
|
+
if (hasValidAllow) {
|
|
122
|
+
return { gated: false, reason: 'allow' };
|
|
123
|
+
}
|
|
124
|
+
// 2. Source-type exemption — applies to the not-found gate only.
|
|
125
|
+
if (findingType === 'not-found' && cslType !== undefined) {
|
|
126
|
+
const rule = config.source_types[cslType];
|
|
127
|
+
if (rule?.gate_not_found === false) {
|
|
128
|
+
return { gated: false, reason: 'source-type' };
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// 3. Secure default.
|
|
132
|
+
return { gated: true, reason: 'default' };
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=suppression.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suppression.js","sourceRoot":"","sources":["../src/suppression.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAIH,8EAA8E;AAC9E,0BAA0B;AAC1B,8EAA8E;AAE9E;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,WAAW;IACX,sBAAsB;IACtB,iBAAiB;IACjB,mBAAmB;CACX,CAAC;AAIX,SAAS,aAAa,CAAC,KAAa;IAClC,OAAQ,aAAmC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC9D,CAAC;AAsBD,8EAA8E;AAC9E,eAAe;AACf,EAAE;AACF,oDAAoD;AACpD,oFAAoF;AACpF,EAAE;AACF,6EAA6E;AAC7E,+EAA+E;AAC/E,0EAA0E;AAC1E,iDAAiD;AACjD,8EAA8E;AAE9E,MAAM,kBAAkB,GACtB,iEAAiE,CAAC;AAQpE;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,OAAe,EAAE,IAAwB;IACnE,MAAM,MAAM,GAAkB,EAAE,CAAC;IACjC,MAAM,YAAY,GAAsC,EAAE,CAAC;IAC3D,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;QACtC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;IAClC,CAAC;IAED,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC;QAClD,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACnB,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS;QAClC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACxB,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,YAAY,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;YAC3C,SAAS;QACX,CAAC;QACD,0EAA0E;QAC1E,MAAM,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACvB,MAAM,MAAM,GACV,SAAS,KAAK,SAAS,IAAI,SAAS,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAC/E,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;AAClC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,0BAA0B,CACxC,OAAyD;IAEzD,MAAM,MAAM,GAAkB,EAAE,CAAC;IACjC,MAAM,YAAY,GAAsC,EAAE,CAAC;IAC3D,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QACzC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;QACzB,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;AAClC,CAAC;AAqBD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,OAAO,CAAC,KAAuB;IAC7C,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IAEhE,2DAA2D;IAC3D,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,CAC/B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,IAAI,CAAC,CAAC,WAAW,KAAK,WAAW,IAAI,CAAC,CAAC,MAAM,KAAK,IAAI,CACnF,CAAC;IACF,IAAI,aAAa,EAAE,CAAC;QAClB,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IAC3C,CAAC;IAED,iEAAiE;IACjE,IAAI,WAAW,KAAK,WAAW,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QACzD,MAAM,IAAI,GAAG,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;QAC1C,IAAI,IAAI,EAAE,cAAc,KAAK,KAAK,EAAE,CAAC;YACnC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;QACjD,CAAC;IACH,CAAC;IAED,qBAAqB;IACrB,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AAC5C,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of the package version, read from the shipped package.json.
|
|
3
|
+
*
|
|
4
|
+
* `package.json` is always included in the npm tarball, and `createRequire`
|
|
5
|
+
* resolves it relative to this module (dist/version.js -> ../package.json),
|
|
6
|
+
* so this works both from the built package and from source under tests.
|
|
7
|
+
*/
|
|
8
|
+
export declare const VERSION: string;
|
|
9
|
+
/** Polite-pool User-Agent base, e.g. `bibcheck/0.1.0`. */
|
|
10
|
+
export declare const USER_AGENT_BASE: string;
|
|
11
|
+
//# sourceMappingURL=version.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,eAAO,MAAM,OAAO,EAAE,MAAoB,CAAC;AAE3C,0DAA0D;AAC1D,eAAO,MAAM,eAAe,QAAwB,CAAC"}
|
package/dist/version.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of the package version, read from the shipped package.json.
|
|
3
|
+
*
|
|
4
|
+
* `package.json` is always included in the npm tarball, and `createRequire`
|
|
5
|
+
* resolves it relative to this module (dist/version.js -> ../package.json),
|
|
6
|
+
* so this works both from the built package and from source under tests.
|
|
7
|
+
*/
|
|
8
|
+
import { createRequire } from 'node:module';
|
|
9
|
+
const require = createRequire(import.meta.url);
|
|
10
|
+
const pkg = require('../package.json');
|
|
11
|
+
export const VERSION = pkg.version;
|
|
12
|
+
/** Polite-pool User-Agent base, e.g. `bibcheck/0.1.0`. */
|
|
13
|
+
export const USER_AGENT_BASE = `bibcheck/${VERSION}`;
|
|
14
|
+
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,GAAG,GAAG,OAAO,CAAC,iBAAiB,CAAwB,CAAC;AAE9D,MAAM,CAAC,MAAM,OAAO,GAAW,GAAG,CAAC,OAAO,CAAC;AAE3C,0DAA0D;AAC1D,MAAM,CAAC,MAAM,eAAe,GAAG,YAAY,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bibcheck worklist subcommand.
|
|
3
|
+
*
|
|
4
|
+
* Generates the manual-triage worklist for Layer 2 / Layer 3 verification.
|
|
5
|
+
* Each citation in the prose is examined for four categories:
|
|
6
|
+
* A. direct-quotation
|
|
7
|
+
* B. paraphrase-with-page-ref
|
|
8
|
+
* C. contested-source-type
|
|
9
|
+
* D. non-canonical-edition
|
|
10
|
+
*
|
|
11
|
+
* Items are informational — they do NOT cause a non-zero exit code.
|
|
12
|
+
*/
|
|
13
|
+
import type { CslEntry } from './schema/csl.js';
|
|
14
|
+
import type { WorklistItem } from './schema/output.js';
|
|
15
|
+
import type { Config } from './config.js';
|
|
16
|
+
export interface RunWorklistDeps {
|
|
17
|
+
config: Config;
|
|
18
|
+
cwd: string;
|
|
19
|
+
bibliography: CslEntry[];
|
|
20
|
+
readFile: (path: string) => Promise<string>;
|
|
21
|
+
signal: AbortSignal;
|
|
22
|
+
}
|
|
23
|
+
export interface RunWorklistResult {
|
|
24
|
+
worklist: WorklistItem[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Returns the first author's family name (surname) from a CSL entry, or
|
|
28
|
+
* undefined if no author information is available.
|
|
29
|
+
*/
|
|
30
|
+
export declare function getFirstAuthorSurname(entry: CslEntry): string | undefined;
|
|
31
|
+
export declare function runWorklist(deps: RunWorklistDeps): Promise<RunWorklistResult>;
|
|
32
|
+
//# sourceMappingURL=worklist.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worklist.d.ts","sourceRoot":"","sources":["../src/worklist.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAU1C,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,QAAQ,EAAE,CAAC;IACzB,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5C,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,YAAY,EAAE,CAAC;CAC1B;AAuBD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,CAczE;AAyKD,wBAAsB,WAAW,CAC/B,IAAI,EAAE,eAAe,GACpB,OAAO,CAAC,iBAAiB,CAAC,CAsC5B"}
|
package/dist/worklist.js
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bibcheck worklist subcommand.
|
|
3
|
+
*
|
|
4
|
+
* Generates the manual-triage worklist for Layer 2 / Layer 3 verification.
|
|
5
|
+
* Each citation in the prose is examined for four categories:
|
|
6
|
+
* A. direct-quotation
|
|
7
|
+
* B. paraphrase-with-page-ref
|
|
8
|
+
* C. contested-source-type
|
|
9
|
+
* D. non-canonical-edition
|
|
10
|
+
*
|
|
11
|
+
* Items are informational — they do NOT cause a non-zero exit code.
|
|
12
|
+
*/
|
|
13
|
+
import { discoverDocs } from './markdown/glob.js';
|
|
14
|
+
import { extractCitekeys } from './markdown/citekeys.js';
|
|
15
|
+
import { extractBlockquotes, extractDirectQuotes } from './markdown/blocks.js';
|
|
16
|
+
import { extractProseLines } from './markdown/prose.js';
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// Constants
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
/** Regex matching page references like (p. 42), (pp. 42-44), (p.42), etc. */
|
|
21
|
+
const PAGE_REF_RE = /\(\s*pp?\.?\s*\d+(?:[-–]\d+)?\s*\)/;
|
|
22
|
+
/** Snippet target length in characters. */
|
|
23
|
+
const SNIPPET_TARGET = 80;
|
|
24
|
+
/** Source types treated as contested when warn_load_bearing is not explicitly false. */
|
|
25
|
+
const CONTESTED_TYPES = new Set(['webpage', 'blog', 'preprint', 'wikipedia']);
|
|
26
|
+
/** URL hosts that are considered archive/canonical for verificationUrl passthrough. */
|
|
27
|
+
const CANONICAL_URL_HOSTS_RE = /archive\.org|hathitrust\.org|oll\.libertyfund\.org|plato\.stanford\.edu/i;
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// Helpers
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
/**
|
|
32
|
+
* Returns the first author's family name (surname) from a CSL entry, or
|
|
33
|
+
* undefined if no author information is available.
|
|
34
|
+
*/
|
|
35
|
+
export function getFirstAuthorSurname(entry) {
|
|
36
|
+
const authors = entry.author;
|
|
37
|
+
if (!Array.isArray(authors) || authors.length === 0)
|
|
38
|
+
return undefined;
|
|
39
|
+
const first = authors[0];
|
|
40
|
+
if (first === undefined)
|
|
41
|
+
return undefined;
|
|
42
|
+
if (typeof first.family === 'string' && first.family.trim() !== '') {
|
|
43
|
+
return first.family.trim();
|
|
44
|
+
}
|
|
45
|
+
// Fall back to literal name — take the last word as surname
|
|
46
|
+
if (typeof first.literal === 'string' && first.literal.trim() !== '') {
|
|
47
|
+
const parts = first.literal.trim().split(/\s+/);
|
|
48
|
+
return parts[parts.length - 1];
|
|
49
|
+
}
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Build a ~80-char snippet of surrounding prose around the citation on the
|
|
54
|
+
* given line. Strips the leading markdown blockquote marker (`> `) if present.
|
|
55
|
+
*/
|
|
56
|
+
function buildSnippet(text, target = SNIPPET_TARGET) {
|
|
57
|
+
// Strip leading blockquote markers
|
|
58
|
+
const cleaned = text.replace(/^(?:>\s*)+/, '').trim();
|
|
59
|
+
if (cleaned.length <= target)
|
|
60
|
+
return cleaned;
|
|
61
|
+
// Trim to target length with ellipsis on the right
|
|
62
|
+
return cleaned.slice(0, target - 1) + '…';
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Returns a verification URL for the entry, or a Google Books fallback search
|
|
66
|
+
* URL using `quoteText` as the search term.
|
|
67
|
+
*/
|
|
68
|
+
function buildVerificationUrl(entry, quoteText) {
|
|
69
|
+
const entryUrl = entry.url;
|
|
70
|
+
if (typeof entryUrl === 'string' && entryUrl.trim() !== '') {
|
|
71
|
+
if (CANONICAL_URL_HOSTS_RE.test(entryUrl)) {
|
|
72
|
+
return entryUrl;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Build a Google Books search URL using the first ~10 chars of the quote text
|
|
76
|
+
const searchTerm = quoteText.slice(0, 60).trim();
|
|
77
|
+
if (searchTerm === '')
|
|
78
|
+
return null;
|
|
79
|
+
return `https://www.google.com/search?tbm=bks&q=${encodeURIComponent(searchTerm)}`;
|
|
80
|
+
}
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
// Per-file processing
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
function processFile(filePath, content, bibMap, config) {
|
|
85
|
+
const items = [];
|
|
86
|
+
const citekeys = extractCitekeys(content, filePath);
|
|
87
|
+
if (citekeys.length === 0)
|
|
88
|
+
return items;
|
|
89
|
+
// Build lookup structures for blockquote ranges and direct-quote lines
|
|
90
|
+
const blockquotes = extractBlockquotes(content);
|
|
91
|
+
const directQuotes = extractDirectQuotes(content);
|
|
92
|
+
const directQuoteLines = new Set(directQuotes.map((dq) => dq.line));
|
|
93
|
+
// Build a map from line number → prose text for snippet extraction
|
|
94
|
+
const proseLines = extractProseLines(content);
|
|
95
|
+
const proseMap = new Map(proseLines.map((pl) => [pl.line, pl.text]));
|
|
96
|
+
for (const ref of citekeys) {
|
|
97
|
+
const entry = bibMap.get(ref.citekey);
|
|
98
|
+
if (entry === undefined) {
|
|
99
|
+
// Not in bibliography — linkage handles this, skip
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const lineText = proseMap.get(ref.line) ?? '';
|
|
103
|
+
const snippet = buildSnippet(lineText);
|
|
104
|
+
// Determine if line is inside a blockquote
|
|
105
|
+
const inBlockquote = blockquotes.some((bq) => ref.line >= bq.startLine && ref.line <= bq.endLine);
|
|
106
|
+
// Determine if line has a direct typographic/quoted span
|
|
107
|
+
const inDirectQuote = directQuoteLines.has(ref.line);
|
|
108
|
+
let isDirectQuotation = false;
|
|
109
|
+
// --- A. direct-quotation ---
|
|
110
|
+
if (inBlockquote || inDirectQuote) {
|
|
111
|
+
isDirectQuotation = true;
|
|
112
|
+
// For the verification URL, try to use the first direct quote text on this
|
|
113
|
+
// line as the search term, otherwise use the snippet
|
|
114
|
+
const quoteText = directQuotes.find((dq) => dq.line === ref.line)?.text ?? snippet;
|
|
115
|
+
const verificationUrl = buildVerificationUrl(entry, quoteText);
|
|
116
|
+
items.push({
|
|
117
|
+
type: 'direct-quotation',
|
|
118
|
+
file: filePath,
|
|
119
|
+
line: ref.line,
|
|
120
|
+
citation: ref.citekey,
|
|
121
|
+
snippet,
|
|
122
|
+
verificationUrl,
|
|
123
|
+
recommendedAction: 'Verify quotation wording verbatim against the named edition.',
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
// --- B. paraphrase-with-page-ref ---
|
|
127
|
+
// Only emit if not already classified as a direct quotation. Fires when the
|
|
128
|
+
// prose line has a page reference OR the parsed citation carries a locator.
|
|
129
|
+
if (!isDirectQuotation && (ref.locator !== null || PAGE_REF_RE.test(lineText))) {
|
|
130
|
+
const pageMatch = PAGE_REF_RE.exec(lineText);
|
|
131
|
+
const locator = ref.locator ?? (pageMatch !== null ? pageMatch[0] : null);
|
|
132
|
+
const pageRef = locator ?? '';
|
|
133
|
+
const verificationUrl = buildVerificationUrl(entry, snippet);
|
|
134
|
+
items.push({
|
|
135
|
+
type: 'paraphrase-with-page-ref',
|
|
136
|
+
file: filePath,
|
|
137
|
+
line: ref.line,
|
|
138
|
+
citation: ref.citekey,
|
|
139
|
+
snippet,
|
|
140
|
+
verificationUrl,
|
|
141
|
+
locator,
|
|
142
|
+
recommendedAction: `Verify paraphrase against page ${pageRef} of the named edition.`,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
// --- C. contested-source-type ---
|
|
146
|
+
const entryType = entry.type ?? '';
|
|
147
|
+
if (CONTESTED_TYPES.has(entryType) &&
|
|
148
|
+
config.source_types[entryType]?.warn_load_bearing !== false) {
|
|
149
|
+
items.push({
|
|
150
|
+
type: 'contested-source-type',
|
|
151
|
+
file: filePath,
|
|
152
|
+
line: ref.line,
|
|
153
|
+
citation: ref.citekey,
|
|
154
|
+
snippet,
|
|
155
|
+
verificationUrl: entry.url ?? null,
|
|
156
|
+
recommendedAction: `${entryType} citation; confirm the claim is supported and the source is appropriate.`,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
// --- D. non-canonical-edition ---
|
|
160
|
+
const surname = getFirstAuthorSurname(entry);
|
|
161
|
+
if (surname !== undefined) {
|
|
162
|
+
const lowerSurname = surname.toLowerCase();
|
|
163
|
+
const canonicalEdition = config.edition_discipline[lowerSurname];
|
|
164
|
+
if (canonicalEdition !== undefined) {
|
|
165
|
+
const noteText = entry.note ?? '';
|
|
166
|
+
if (!noteText.toLowerCase().includes(canonicalEdition.toLowerCase())) {
|
|
167
|
+
items.push({
|
|
168
|
+
type: 'non-canonical-edition',
|
|
169
|
+
file: filePath,
|
|
170
|
+
line: ref.line,
|
|
171
|
+
citation: ref.citekey,
|
|
172
|
+
snippet,
|
|
173
|
+
verificationUrl: null,
|
|
174
|
+
recommendedAction: `Use the ${canonicalEdition} for this author.`,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return items;
|
|
181
|
+
}
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
// Main export
|
|
184
|
+
// ---------------------------------------------------------------------------
|
|
185
|
+
export async function runWorklist(deps) {
|
|
186
|
+
const { config, cwd, bibliography, readFile, signal } = deps;
|
|
187
|
+
if (signal.aborted) {
|
|
188
|
+
throw new Error('AbortSignal already aborted before runWorklist started');
|
|
189
|
+
}
|
|
190
|
+
// Build O(1) bibliography lookup map
|
|
191
|
+
const bibMap = new Map(bibliography.map((entry) => [entry.citekey, entry]));
|
|
192
|
+
// Discover markdown documents
|
|
193
|
+
const docs = await discoverDocs({
|
|
194
|
+
cwd,
|
|
195
|
+
include: config.docs.include,
|
|
196
|
+
exclude: config.docs.exclude,
|
|
197
|
+
});
|
|
198
|
+
const worklist = [];
|
|
199
|
+
for (const doc of docs) {
|
|
200
|
+
if (signal.aborted) {
|
|
201
|
+
throw new Error('Operation aborted');
|
|
202
|
+
}
|
|
203
|
+
const content = await readFile(doc.path);
|
|
204
|
+
const items = processFile(doc.relativePath, content, bibMap, config);
|
|
205
|
+
worklist.push(...items);
|
|
206
|
+
}
|
|
207
|
+
// Deterministic output order (file, line, type) — stable CI diffs.
|
|
208
|
+
worklist.sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line || a.type.localeCompare(b.type));
|
|
209
|
+
return { worklist };
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=worklist.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worklist.js","sourceRoot":"","sources":["../src/worklist.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@de-otio/bibcheck",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Humanities-aware citation verification for CSL-JSON bibliographies. Canonical-edition URL verification, Pandoc-citeproc-style linkage check, structured human-triage worklist generation, and an opt-in project-supplied phrase denylist.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"citations",
|
|
7
|
+
"bibliography",
|
|
8
|
+
"csl-json",
|
|
9
|
+
"verification",
|
|
10
|
+
"humanities",
|
|
11
|
+
"pandoc",
|
|
12
|
+
"research",
|
|
13
|
+
"academic",
|
|
14
|
+
"cli"
|
|
15
|
+
],
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"author": "Richard Myers <richard.myers@de-otio.org>",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/de-otio/bibcheck.git"
|
|
21
|
+
},
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/de-otio/bibcheck/issues"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/de-otio/bibcheck#readme",
|
|
26
|
+
"type": "module",
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"main": "./dist/index.js",
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"default": "./dist/index.js"
|
|
35
|
+
},
|
|
36
|
+
"./schema": {
|
|
37
|
+
"types": "./dist/schema/output.d.ts",
|
|
38
|
+
"default": "./dist/schema/output.js"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"bin": {
|
|
42
|
+
"bibcheck": "./dist/cli.js"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=20"
|
|
46
|
+
},
|
|
47
|
+
"files": [
|
|
48
|
+
"dist",
|
|
49
|
+
"README.md",
|
|
50
|
+
"LICENSE"
|
|
51
|
+
],
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "tsc && chmod +x dist/cli.js",
|
|
54
|
+
"prepublishOnly": "npm run typecheck && npm run build",
|
|
55
|
+
"pretest": "npm run build",
|
|
56
|
+
"test": "vitest run",
|
|
57
|
+
"test:watch": "vitest",
|
|
58
|
+
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.test.json"
|
|
59
|
+
},
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"commander": "^12.0.0",
|
|
62
|
+
"fastest-levenshtein": "^1.0.16",
|
|
63
|
+
"keyv": "^5.0.0",
|
|
64
|
+
"keyv-file": "^5.3.0",
|
|
65
|
+
"mdast-util-to-string": "^4.0.0",
|
|
66
|
+
"node-sarif-builder": "^4.0.0",
|
|
67
|
+
"p-queue": "^8.0.0",
|
|
68
|
+
"re2js": "^2.2.0",
|
|
69
|
+
"remark-parse": "^11.0.0",
|
|
70
|
+
"smol-toml": "^1.3.0",
|
|
71
|
+
"tinyglobby": "^0.2.0",
|
|
72
|
+
"unified": "^11.0.0",
|
|
73
|
+
"zod": "^3.23.0"
|
|
74
|
+
},
|
|
75
|
+
"devDependencies": {
|
|
76
|
+
"@types/node": "~20.14.0",
|
|
77
|
+
"@vitest/coverage-v8": "^1.6.0",
|
|
78
|
+
"ajv": "^8.17.0",
|
|
79
|
+
"typescript": "^5.4.0",
|
|
80
|
+
"vitest": "^1.6.0"
|
|
81
|
+
}
|
|
82
|
+
}
|