@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.
Files changed (127) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +147 -0
  3. package/dist/cache/fs-cache.d.ts +55 -0
  4. package/dist/cache/fs-cache.d.ts.map +1 -0
  5. package/dist/cache/fs-cache.js +264 -0
  6. package/dist/cache/fs-cache.js.map +1 -0
  7. package/dist/canonical.d.ts +29 -0
  8. package/dist/canonical.d.ts.map +1 -0
  9. package/dist/canonical.js +132 -0
  10. package/dist/canonical.js.map +1 -0
  11. package/dist/check.d.ts +140 -0
  12. package/dist/check.d.ts.map +1 -0
  13. package/dist/check.js +646 -0
  14. package/dist/check.js.map +1 -0
  15. package/dist/cli.d.ts +19 -0
  16. package/dist/cli.d.ts.map +1 -0
  17. package/dist/cli.js +357 -0
  18. package/dist/cli.js.map +1 -0
  19. package/dist/config.d.ts +175 -0
  20. package/dist/config.d.ts.map +1 -0
  21. package/dist/config.js +180 -0
  22. package/dist/config.js.map +1 -0
  23. package/dist/databases/crossref.d.ts +53 -0
  24. package/dist/databases/crossref.d.ts.map +1 -0
  25. package/dist/databases/crossref.js +138 -0
  26. package/dist/databases/crossref.js.map +1 -0
  27. package/dist/databases/index.d.ts +12 -0
  28. package/dist/databases/index.d.ts.map +1 -0
  29. package/dist/databases/index.js +9 -0
  30. package/dist/databases/index.js.map +1 -0
  31. package/dist/databases/openalex.d.ts +29 -0
  32. package/dist/databases/openalex.d.ts.map +1 -0
  33. package/dist/databases/openalex.js +117 -0
  34. package/dist/databases/openalex.js.map +1 -0
  35. package/dist/databases/openlibrary.d.ts +26 -0
  36. package/dist/databases/openlibrary.d.ts.map +1 -0
  37. package/dist/databases/openlibrary.js +79 -0
  38. package/dist/databases/openlibrary.js.map +1 -0
  39. package/dist/databases/worldcat.d.ts +33 -0
  40. package/dist/databases/worldcat.d.ts.map +1 -0
  41. package/dist/databases/worldcat.js +145 -0
  42. package/dist/databases/worldcat.js.map +1 -0
  43. package/dist/doctor.d.ts +44 -0
  44. package/dist/doctor.d.ts.map +1 -0
  45. package/dist/doctor.js +386 -0
  46. package/dist/doctor.js.map +1 -0
  47. package/dist/existence.d.ts +70 -0
  48. package/dist/existence.d.ts.map +1 -0
  49. package/dist/existence.js +308 -0
  50. package/dist/existence.js.map +1 -0
  51. package/dist/http.d.ts +97 -0
  52. package/dist/http.d.ts.map +1 -0
  53. package/dist/http.js +543 -0
  54. package/dist/http.js.map +1 -0
  55. package/dist/identifiers.d.ts +44 -0
  56. package/dist/identifiers.d.ts.map +1 -0
  57. package/dist/identifiers.js +111 -0
  58. package/dist/identifiers.js.map +1 -0
  59. package/dist/index.d.ts +9 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +8 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/linkage.d.ts +29 -0
  64. package/dist/linkage.d.ts.map +1 -0
  65. package/dist/linkage.js +73 -0
  66. package/dist/linkage.js.map +1 -0
  67. package/dist/markdown/blocks.d.ts +19 -0
  68. package/dist/markdown/blocks.d.ts.map +1 -0
  69. package/dist/markdown/blocks.js +69 -0
  70. package/dist/markdown/blocks.js.map +1 -0
  71. package/dist/markdown/citekeys.d.ts +22 -0
  72. package/dist/markdown/citekeys.d.ts.map +1 -0
  73. package/dist/markdown/citekeys.js +100 -0
  74. package/dist/markdown/citekeys.js.map +1 -0
  75. package/dist/markdown/glob.d.ts +18 -0
  76. package/dist/markdown/glob.d.ts.map +1 -0
  77. package/dist/markdown/glob.js +26 -0
  78. package/dist/markdown/glob.js.map +1 -0
  79. package/dist/markdown/prose.d.ts +19 -0
  80. package/dist/markdown/prose.d.ts.map +1 -0
  81. package/dist/markdown/prose.js +81 -0
  82. package/dist/markdown/prose.js.map +1 -0
  83. package/dist/output/json.d.ts +21 -0
  84. package/dist/output/json.d.ts.map +1 -0
  85. package/dist/output/json.js +24 -0
  86. package/dist/output/json.js.map +1 -0
  87. package/dist/output/markdown.d.ts +21 -0
  88. package/dist/output/markdown.d.ts.map +1 -0
  89. package/dist/output/markdown.js +194 -0
  90. package/dist/output/markdown.js.map +1 -0
  91. package/dist/output/sarif.d.ts +31 -0
  92. package/dist/output/sarif.d.ts.map +1 -0
  93. package/dist/output/sarif.js +322 -0
  94. package/dist/output/sarif.js.map +1 -0
  95. package/dist/output/text.d.ts +27 -0
  96. package/dist/output/text.d.ts.map +1 -0
  97. package/dist/output/text.js +212 -0
  98. package/dist/output/text.js.map +1 -0
  99. package/dist/phrases/load.d.ts +34 -0
  100. package/dist/phrases/load.d.ts.map +1 -0
  101. package/dist/phrases/load.js +148 -0
  102. package/dist/phrases/load.js.map +1 -0
  103. package/dist/phrases.d.ts +27 -0
  104. package/dist/phrases.d.ts.map +1 -0
  105. package/dist/phrases.js +116 -0
  106. package/dist/phrases.js.map +1 -0
  107. package/dist/schema/csl.d.ts +429 -0
  108. package/dist/schema/csl.d.ts.map +1 -0
  109. package/dist/schema/csl.js +101 -0
  110. package/dist/schema/csl.js.map +1 -0
  111. package/dist/schema/output.d.ts +1116 -0
  112. package/dist/schema/output.d.ts.map +1 -0
  113. package/dist/schema/output.js +419 -0
  114. package/dist/schema/output.js.map +1 -0
  115. package/dist/suppression.d.ts +106 -0
  116. package/dist/suppression.d.ts.map +1 -0
  117. package/dist/suppression.js +134 -0
  118. package/dist/suppression.js.map +1 -0
  119. package/dist/version.d.ts +11 -0
  120. package/dist/version.d.ts.map +1 -0
  121. package/dist/version.js +14 -0
  122. package/dist/version.js.map +1 -0
  123. package/dist/worklist.d.ts +32 -0
  124. package/dist/worklist.d.ts.map +1 -0
  125. package/dist/worklist.js +211 -0
  126. package/dist/worklist.js.map +1 -0
  127. 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":"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"}
@@ -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"}
@@ -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"}
@@ -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":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC/E,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAkBxD,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E,6EAA6E;AAC7E,MAAM,WAAW,GAAG,oCAAoC,CAAC;AAEzD,2CAA2C;AAC3C,MAAM,cAAc,GAAG,EAAE,CAAC;AAE1B,wFAAwF;AACxF,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC;AAE9E,uFAAuF;AACvF,MAAM,sBAAsB,GAC1B,0EAA0E,CAAC;AAE7E,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAe;IACnD,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC;IAC7B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACtE,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACzB,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC1C,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACnE,OAAO,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IAC7B,CAAC;IACD,4DAA4D;IAC5D,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACrE,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAChD,OAAO,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACjC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,SAAS,YAAY,CAAC,IAAY,EAAE,MAAM,GAAG,cAAc;IACzD,mCAAmC;IACnC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACtD,IAAI,OAAO,CAAC,MAAM,IAAI,MAAM;QAAE,OAAO,OAAO,CAAC;IAC7C,mDAAmD;IACnD,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC;AAC5C,CAAC;AAED;;;GAGG;AACH,SAAS,oBAAoB,CAC3B,KAAe,EACf,SAAiB;IAEjB,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC;IAC3B,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC3D,IAAI,sBAAsB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1C,OAAO,QAAQ,CAAC;QAClB,CAAC;IACH,CAAC;IACD,8EAA8E;IAC9E,MAAM,UAAU,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACjD,IAAI,UAAU,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IACnC,OAAO,2CAA2C,kBAAkB,CAAC,UAAU,CAAC,EAAE,CAAC;AACrF,CAAC;AAED,8EAA8E;AAC9E,sBAAsB;AACtB,8EAA8E;AAE9E,SAAS,WAAW,CAClB,QAAgB,EAChB,OAAe,EACf,MAA6B,EAC7B,MAAc;IAEd,MAAM,KAAK,GAAmB,EAAE,CAAC;IAEjC,MAAM,QAAQ,GAAG,eAAe,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACpD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAExC,uEAAuE;IACvE,MAAM,WAAW,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAChD,MAAM,YAAY,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAClD,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEpE,mEAAmE;IACnE,MAAM,UAAU,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAiB,UAAU,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAErF,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACtC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,mDAAmD;YACnD,SAAS;QACX,CAAC;QAED,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAC9C,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;QAEvC,2CAA2C;QAC3C,MAAM,YAAY,GAAG,WAAW,CAAC,IAAI,CACnC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,SAAS,IAAI,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,OAAO,CAC3D,CAAC;QAEF,yDAAyD;QACzD,MAAM,aAAa,GAAG,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAErD,IAAI,iBAAiB,GAAG,KAAK,CAAC;QAE9B,8BAA8B;QAC9B,IAAI,YAAY,IAAI,aAAa,EAAE,CAAC;YAClC,iBAAiB,GAAG,IAAI,CAAC;YAEzB,2EAA2E;YAC3E,qDAAqD;YACrD,MAAM,SAAS,GACb,YAAY,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,IAAI,OAAO,CAAC;YAEnE,MAAM,eAAe,GAAG,oBAAoB,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;YAE/D,KAAK,CAAC,IAAI,CAAC;gBACT,IAAI,EAAE,kBAAkB;gBACxB,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,QAAQ,EAAE,GAAG,CAAC,OAAO;gBACrB,OAAO;gBACP,eAAe;gBACf,iBAAiB,EACf,8DAA8D;aACjE,CAAC,CAAC;QACL,CAAC;QAED,sCAAsC;QACtC,4EAA4E;QAC5E,4EAA4E;QAC5E,IAAI,CAAC,iBAAiB,IAAI,CAAC,GAAG,CAAC,OAAO,KAAK,IAAI,IAAI,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;YAC/E,MAAM,SAAS,GAAG,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC7C,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC1E,MAAM,OAAO,GAAG,OAAO,IAAI,EAAE,CAAC;YAE9B,MAAM,eAAe,GAAG,oBAAoB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YAE7D,KAAK,CAAC,IAAI,CAAC;gBACT,IAAI,EAAE,0BAA0B;gBAChC,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,QAAQ,EAAE,GAAG,CAAC,OAAO;gBACrB,OAAO;gBACP,eAAe;gBACf,OAAO;gBACP,iBAAiB,EAAE,kCAAkC,OAAO,wBAAwB;aACrF,CAAC,CAAC;QACL,CAAC;QAED,mCAAmC;QACnC,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC;QACnC,IACE,eAAe,CAAC,GAAG,CAAC,SAAS,CAAC;YAC9B,MAAM,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,iBAAiB,KAAK,KAAK,EAC3D,CAAC;YACD,KAAK,CAAC,IAAI,CAAC;gBACT,IAAI,EAAE,uBAAuB;gBAC7B,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,QAAQ,EAAE,GAAG,CAAC,OAAO;gBACrB,OAAO;gBACP,eAAe,EAAE,KAAK,CAAC,GAAG,IAAI,IAAI;gBAClC,iBAAiB,EAAE,GAAG,SAAS,0EAA0E;aAC1G,CAAC,CAAC;QACL,CAAC;QAED,mCAAmC;QACnC,MAAM,OAAO,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;QAC7C,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,MAAM,YAAY,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;YAC3C,MAAM,gBAAgB,GAAG,MAAM,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;YACjE,IAAI,gBAAgB,KAAK,SAAS,EAAE,CAAC;gBACnC,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC;gBAClC,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,gBAAgB,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;oBACrE,KAAK,CAAC,IAAI,CAAC;wBACT,IAAI,EAAE,uBAAuB;wBAC7B,IAAI,EAAE,QAAQ;wBACd,IAAI,EAAE,GAAG,CAAC,IAAI;wBACd,QAAQ,EAAE,GAAG,CAAC,OAAO;wBACrB,OAAO;wBACP,eAAe,EAAE,IAAI;wBACrB,iBAAiB,EAAE,WAAW,gBAAgB,mBAAmB;qBAClE,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,8EAA8E;AAC9E,cAAc;AACd,8EAA8E;AAE9E,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,IAAqB;IAErB,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAE7D,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;IAC5E,CAAC;IAED,qCAAqC;IACrC,MAAM,MAAM,GAAG,IAAI,GAAG,CACpB,YAAY,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CACpD,CAAC;IAEF,8BAA8B;IAC9B,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC;QAC9B,GAAG;QACH,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO;QAC5B,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO;KAC7B,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAmB,EAAE,CAAC;IAEpC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACvC,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACzC,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QACrE,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC;IAC1B,CAAC;IAED,mEAAmE;IACnE,QAAQ,CAAC,IAAI,CACX,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACP,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAClF,CAAC;IAEF,OAAO,EAAE,QAAQ,EAAE,CAAC;AACtB,CAAC"}
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
+ }