@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 @@
1
+ {"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/schema/output.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,419 @@
1
+ /**
2
+ * bibcheck output JSON schema — the contract consumers (LLM agents, CI tools,
3
+ * editor integrations) read.
4
+ *
5
+ * FROZEN. This schema is the contract every renderer and consumer reads;
6
+ * changes require a surfaced design decision (see tmp/design-review/) and a
7
+ * `SCHEMA_VERSION` bump. Do not edit ad hoc.
8
+ *
9
+ * Versioned independently of the package version via `SCHEMA_VERSION` below.
10
+ * Bumping rules:
11
+ * - Additive changes (new optional fields, new enum members on otherwise-open
12
+ * types) bump the minor part and remain backward-compatible.
13
+ * - Renames, removals, or changed semantics bump the major part; consumers
14
+ * pinning a major version are insulated.
15
+ *
16
+ * 0.3.0 (H2 — reverse-linkage / orphan detection): added the `'orphan'`
17
+ * member to `LinkageStatusSchema` (a bibliography citekey never referenced in
18
+ * any doc — the inverse of `'unresolved'`) and the OPTIONAL `orphanedEntries`
19
+ * summary counter. Orphans are INFORMATIONAL: they are surfaced but MUST NOT
20
+ * gate `bibcheck check` (an uncited bibliography entry is a smell, not proof of
21
+ * fabrication; gating would train users to disable checks). The change is
22
+ * purely additive — the `linkageFailures` invariant counts only `'unresolved'`
23
+ * entries, so orphans (status `'orphan'`) are naturally excluded — and the new
24
+ * summary counter is optional so existing Output builders that do not populate
25
+ * it keep emitting valid documents.
26
+ *
27
+ * 0.2.0 (Phase 5 — hallucination-hardening): added the existence evidence
28
+ * vocabulary (`ExistenceEvidenceSchema`) and verification-boundary fields
29
+ * (`evidence` / `checkedFor` / `notCheckedFor` / `error` on the existence
30
+ * layer); the per-entry `IdentifiersLayerSchema` (local DOI/ISBN/URL
31
+ * well-formedness); the `notFoundInDatabases` + `malformedIdentifiers`
32
+ * summary counters with existence-bucket reconciliation; and the optional
33
+ * `locator` / `authorSuppressed` linkage/worklist fields for the
34
+ * citation-parser swap. The new per-entry/summary fields are presently
35
+ * OPTIONAL so that the v0.1 producer (which does not yet populate them) keeps
36
+ * emitting valid documents; T21/T22/T25 populate them and a follow-up tightens
37
+ * them to required (see the `TODO(T22)` markers below).
38
+ *
39
+ * Authoritative documentation is the Zod schemas in this file. The published
40
+ * JSON Schema in `docs/output-schema.md` (when generated) is derived from
41
+ * these definitions, not maintained by hand.
42
+ */
43
+ import { z } from 'zod';
44
+ /** Current bibcheck output schema version. Independent of the package version. */
45
+ export const SCHEMA_VERSION = '0.3.0';
46
+ /**
47
+ * Accepts any URL whose scheme is http or https. Rejects non-web schemes
48
+ * such as `javascript:`, `file://`, `data:`, and `ftp://`.
49
+ */
50
+ const httpUrl = () => z.string().url().refine((u) => /^https?:\/\//i.test(u), {
51
+ message: 'Only http/https URLs are accepted',
52
+ });
53
+ // ---------------------------------------------------------------------------
54
+ // Tool info
55
+ // ---------------------------------------------------------------------------
56
+ export const ToolInfoSchema = z.object({
57
+ name: z.literal('bibcheck'),
58
+ version: z.string().min(1),
59
+ });
60
+ // ---------------------------------------------------------------------------
61
+ // Summary counts
62
+ // ---------------------------------------------------------------------------
63
+ export const SummarySchema = z.object({
64
+ totalEntries: z.number().int().nonnegative(),
65
+ verified: z.number().int().nonnegative(),
66
+ metadataMismatches: z.number().int().nonnegative(),
67
+ /**
68
+ * Entries confirmed absent from every applicable database (a fabrication
69
+ * signal; gates by default per Q1). NEW in 0.2.0.
70
+ */
71
+ notFoundInDatabases: z.number().int().nonnegative(),
72
+ /**
73
+ * Entries with at least one malformed/bad-checksum identifier in the
74
+ * `identifiers` layer (a cheap fabrication signal; gates by default). NEW
75
+ * in 0.2.0.
76
+ */
77
+ malformedIdentifiers: z.number().int().nonnegative(),
78
+ unverifiable: z.number().int().nonnegative(),
79
+ canonicalIssues: z.number().int().nonnegative(),
80
+ linkageFailures: z.number().int().nonnegative(),
81
+ phraseFlags: z.number().int().nonnegative(),
82
+ worklistItems: z.number().int().nonnegative(),
83
+ /**
84
+ * Bibliography entries whose citekey is never referenced in any markdown doc
85
+ * (reverse linkage; the inverse of `linkageFailures`). INFORMATIONAL — an
86
+ * uncited entry is a smell (e.g. an LLM-padded reference list), not proof of
87
+ * fabrication, so it does NOT gate `bibcheck check`. OPTIONAL so the counter
88
+ * is additive: Output builders predating 0.3.0 that omit it stay valid. NEW
89
+ * in 0.3.0.
90
+ */
91
+ orphanedEntries: z.number().int().nonnegative().optional(),
92
+ });
93
+ // ---------------------------------------------------------------------------
94
+ // Layer 1: existence (commodity convenience layer)
95
+ //
96
+ // DOI / ISBN / title-search lookup against CrossRef, OpenAlex, OpenLibrary.
97
+ // A thin direct-fetch wrapper, not a heavyweight resolver. (WorldCat / OCLC
98
+ // Classify was removed in 0.2.0 — the Classify endpoint was decommissioned in
99
+ // 2019; see tmp/design-review/worldcat.md. ISBN existence is covered by
100
+ // OpenLibrary.)
101
+ // ---------------------------------------------------------------------------
102
+ export const ExistenceCheckSourceSchema = z.enum([
103
+ 'crossref',
104
+ 'openalex',
105
+ 'openlibrary',
106
+ ]);
107
+ export const ExistenceCheckResultSchema = z.enum([
108
+ 'no-doi',
109
+ 'found',
110
+ 'not-found',
111
+ 'metadata-mismatch',
112
+ 'error',
113
+ ]);
114
+ export const ExistenceCheckSchema = z.object({
115
+ source: ExistenceCheckSourceSchema,
116
+ result: ExistenceCheckResultSchema,
117
+ /** Source-specific evidence. Shape varies per source; not strictly typed. */
118
+ evidence: z.unknown().nullable(),
119
+ });
120
+ export const ExistenceStatusSchema = z.enum([
121
+ 'verified',
122
+ 'metadata-mismatch',
123
+ 'not-found-in-databases',
124
+ 'unverifiable',
125
+ ]);
126
+ /**
127
+ * Defined per-entry evidence vocabulary, distinct from the bare `status`
128
+ * rollup, so an LLM-agent consumer cannot read `verified` as "the citation's
129
+ * claim is sound" (Q2). Deliberately discrete — there is NO numeric confidence
130
+ * score anywhere in this schema (barred by the project's "no uncalibrated
131
+ * confidence scores" design default); the vocabulary is the calibrated
132
+ * alternative.
133
+ */
134
+ export const ExistenceEvidenceSchema = z.enum([
135
+ 'exists-metadata-match', // found + metadata agrees
136
+ 'exists-metadata-mismatch', // found, but metadata differs
137
+ 'absent', // confirmed not-found in all applicable databases
138
+ 'unverifiable', // no applicable identifier, or all sources errored
139
+ ]);
140
+ /**
141
+ * The verification dimensions bibcheck can report on. Used by the existence
142
+ * layer's `checkedFor` / `notCheckedFor` arrays to state explicitly what was
143
+ * and was NOT checked. `claim-support` (does the source actually support the
144
+ * prose's claim?) is never checked automatically — it is the manual worklist's
145
+ * job — so it always appears in `notCheckedFor` for v0.1.
146
+ */
147
+ export const CheckDimensionSchema = z.enum([
148
+ 'existence',
149
+ 'metadata',
150
+ 'canonical-url',
151
+ 'claim-support',
152
+ ]);
153
+ export const ExistenceLayerSchema = z.object({
154
+ status: ExistenceStatusSchema,
155
+ /** Defined evidence vocabulary (Q2). NEW in 0.2.0. */
156
+ evidence: ExistenceEvidenceSchema,
157
+ /** Dimensions that were checked, e.g. ['existence','metadata']. NEW in 0.2.0. */
158
+ checkedFor: z.array(CheckDimensionSchema),
159
+ /**
160
+ * Dimensions that were NOT checked. Always includes 'claim-support' for
161
+ * v0.1 (bibcheck never verifies whether the source supports the prose's
162
+ * claim; that is the manual worklist's job). NEW in 0.2.0.
163
+ */
164
+ notCheckedFor: z.array(CheckDimensionSchema),
165
+ checks: z.array(ExistenceCheckSchema),
166
+ /**
167
+ * Set when the layer crashed (vs. a clean unverifiable result), so consumers
168
+ * can distinguish "we ran and found nothing applicable" from "we failed to
169
+ * run." NEW in 0.2.0 (also addresses S1). Null when the layer ran cleanly.
170
+ */
171
+ error: z.string().nullable(),
172
+ });
173
+ // ---------------------------------------------------------------------------
174
+ // Layer 0: identifiers (local, pre-network well-formedness)
175
+ //
176
+ // A pure, local check of DOI/ISBN/URL well-formedness, run BEFORE any network
177
+ // existence lookup (Q5 / T21). Kept as its own per-entry layer — sibling to
178
+ // `existence` and `canonical` — so "is the identifier well-formed" stays
179
+ // cleanly distinct from "does the work exist," and so sibling bibliography-
180
+ // hygiene checks can grow into the same space later. A malformed identifier is
181
+ // a strong, cheap fabrication signal and gates by default (consistent with the
182
+ // Q1 secure default).
183
+ // ---------------------------------------------------------------------------
184
+ export const IdentifierStatusSchema = z.enum([
185
+ 'ok',
186
+ 'malformed',
187
+ 'bad-checksum',
188
+ 'not-applicable',
189
+ ]);
190
+ export const IdentifiersLayerSchema = z.object({
191
+ /** 'malformed' if the DOI fails `^10\.\d{4,}/\S+$`. */
192
+ doi: IdentifierStatusSchema,
193
+ /** 'bad-checksum' for a failed ISBN-10/13 check digit; 'malformed' for bad shape. */
194
+ isbn: IdentifierStatusSchema,
195
+ /** 'malformed' if the URL is not a well-formed http/https URL. */
196
+ url: IdentifierStatusSchema,
197
+ });
198
+ // ---------------------------------------------------------------------------
199
+ // Layer 1: canonical-edition URL verification (differentiated)
200
+ //
201
+ // For pre-DOI primary sources, checks that each entry's `url:` field points
202
+ // to a trusted canonical-edition host (HathiTrust, Internet Archive, Liberty
203
+ // Fund OLL, plato.stanford.edu/archives, PhilPapers, national-library
204
+ // catalogues, project-extensible) and that the URL is live.
205
+ // ---------------------------------------------------------------------------
206
+ export const CanonicalStatusSchema = z.enum([
207
+ 'verified-canonical',
208
+ 'wrong-host',
209
+ 'dead-url',
210
+ 'live-url-not-archived-snapshot',
211
+ 'no-url-on-pre-doi-entry',
212
+ 'not-applicable',
213
+ ]);
214
+ export const CanonicalLayerSchema = z.object({
215
+ status: CanonicalStatusSchema,
216
+ url: httpUrl().nullable(),
217
+ /** Redirect chain, in order, if HEAD followed redirects. */
218
+ redirectChain: z.array(httpUrl()).optional(),
219
+ });
220
+ // ---------------------------------------------------------------------------
221
+ // Per-entry record
222
+ // ---------------------------------------------------------------------------
223
+ export const EntrySchema = z.object({
224
+ citekey: z.string().min(1),
225
+ /**
226
+ * Layer 0 local identifier well-formedness (pre-network). Null when not run.
227
+ * NEW in 0.2.0 (Q5 / T21).
228
+ */
229
+ identifiers: IdentifiersLayerSchema.nullable(),
230
+ /** Layer 1 existence findings (commodity layer). Null when not run. */
231
+ existence: ExistenceLayerSchema.nullable(),
232
+ /** Layer 1 canonical-edition findings (differentiated layer). Null when not run. */
233
+ canonical: CanonicalLayerSchema.nullable(),
234
+ });
235
+ // ---------------------------------------------------------------------------
236
+ // Layer 1 (structural): linkage
237
+ //
238
+ // Every `@citekey` reference in the docs has an entry in the bibliography.
239
+ // Deterministic equivalent of pandoc-citeproc's render-time warning.
240
+ // ---------------------------------------------------------------------------
241
+ /**
242
+ * 'resolved' — citekey appears in prose AND in the bibliography.
243
+ * 'unresolved' — citekey appears in prose but NOT in the bibliography (gates).
244
+ * 'orphan' — citekey appears in the bibliography but is NOT referenced in
245
+ * any prose doc (reverse linkage). INFORMATIONAL; never gates.
246
+ * NEW in 0.3.0.
247
+ */
248
+ export const LinkageStatusSchema = z.enum(['resolved', 'unresolved', 'orphan']);
249
+ export const LinkageReferenceSchema = z.object({
250
+ file: z.string().min(1),
251
+ line: z.number().int().positive(),
252
+ /** Citation locator, e.g. 'p. 42', 'pp. 33-35'. NEW in 0.2.0 (T25 populates). */
253
+ locator: z.string().nullable().optional(),
254
+ /** True when the citation suppressed the author, e.g. '-@key'. NEW in 0.2.0 (T25). */
255
+ authorSuppressed: z.boolean().optional(),
256
+ });
257
+ export const LinkageEntrySchema = z.object({
258
+ citekey: z.string().min(1),
259
+ status: LinkageStatusSchema,
260
+ references: z.array(LinkageReferenceSchema),
261
+ });
262
+ // ---------------------------------------------------------------------------
263
+ // Phrase denylist matches
264
+ //
265
+ // Regex pass over prose against a project-supplied phrase denylist (loaded
266
+ // via `[phrases] file = "..."` in bibcheck.toml). bibcheck does not ship a
267
+ // baseline; the feature is a configurable lint, not curated guidance.
268
+ //
269
+ // A match without an explicit `<!-- bibcheck-allow: <key> -->` acknowledgement
270
+ // is reported as `flagged`; matches with the acknowledgement are reported as
271
+ // `acknowledged` (informational).
272
+ // ---------------------------------------------------------------------------
273
+ export const PhraseFlagStatusSchema = z.enum([
274
+ 'flagged',
275
+ 'acknowledged',
276
+ ]);
277
+ export const PhraseFlagSchema = z.object({
278
+ status: PhraseFlagStatusSchema,
279
+ /** Stable key naming the denylist pattern that matched. */
280
+ patternKey: z.string().min(1),
281
+ /** Optional project-supplied URL — typically a doc explaining why this phrase is denylisted. */
282
+ referenceUrl: httpUrl().nullable(),
283
+ file: z.string().min(1),
284
+ line: z.number().int().positive(),
285
+ /** The substring of the prose that matched the pattern. */
286
+ matchedText: z.string().min(1),
287
+ });
288
+ // ---------------------------------------------------------------------------
289
+ // Layer 2 / Layer 3: human-triage worklist
290
+ //
291
+ // Items emitted by bibcheck for manual verification. The tool does not
292
+ // attempt to resolve these; the worklist is the bridge between automated
293
+ // and manual layers of the policy.
294
+ // ---------------------------------------------------------------------------
295
+ export const WorklistItemTypeSchema = z.enum([
296
+ /** A direct quotation needs to be verified verbatim against the source. */
297
+ 'direct-quotation',
298
+ /** A paraphrase attached to a page or section reference needs to be checked. */
299
+ 'paraphrase-with-page-ref',
300
+ /** Cited source is on a contested-coverage source-type (Wikipedia, blog, preprint). */
301
+ 'contested-source-type',
302
+ /** Citation references a non-canonical edition where a canonical one exists. */
303
+ 'non-canonical-edition',
304
+ ]);
305
+ export const WorklistItemSchema = z.object({
306
+ type: WorklistItemTypeSchema,
307
+ file: z.string().min(1),
308
+ line: z.number().int().positive(),
309
+ /** The citation invocation in the prose, e.g., `@mill1859onliberty`. */
310
+ citation: z.string().min(1),
311
+ /** Excerpt of prose around the citation, for context. */
312
+ snippet: z.string().min(1),
313
+ /** Pre-filled URL the human can use to perform the manual check. */
314
+ verificationUrl: httpUrl().nullable(),
315
+ /** Human-readable description of what the manual check should establish. */
316
+ recommendedAction: z.string().min(1),
317
+ /** Citation locator, e.g. 'p. 42', 'pp. 33-35'. NEW in 0.2.0 (T25 populates). */
318
+ locator: z.string().nullable().optional(),
319
+ });
320
+ // ---------------------------------------------------------------------------
321
+ // Top-level output
322
+ //
323
+ // schemaVersion: Validation accepts any `0.x.y` document; the runtime tool
324
+ // emits the current `SCHEMA_VERSION`. Bumping rules: additive minor bump
325
+ // remains backward-compatible (consumers continue to validate); a major bump
326
+ // (1.x.y) breaks pinned consumers.
327
+ // ---------------------------------------------------------------------------
328
+ export const OutputSchema = z
329
+ .object({
330
+ schemaVersion: z.string().regex(/^0\.\d+\.\d+$/, 'Major version 0 expected'),
331
+ tool: ToolInfoSchema,
332
+ summary: SummarySchema,
333
+ entries: z.array(EntrySchema),
334
+ linkage: z.array(LinkageEntrySchema),
335
+ phraseFlags: z.array(PhraseFlagSchema),
336
+ worklist: z.array(WorklistItemSchema),
337
+ })
338
+ .superRefine((o, ctx) => {
339
+ if (o.summary.verified > o.summary.totalEntries) {
340
+ ctx.addIssue({
341
+ code: z.ZodIssueCode.custom,
342
+ path: ['summary', 'verified'],
343
+ message: `verified (${o.summary.verified}) cannot exceed totalEntries (${o.summary.totalEntries})`,
344
+ });
345
+ }
346
+ if (o.summary.metadataMismatches > o.summary.totalEntries) {
347
+ ctx.addIssue({
348
+ code: z.ZodIssueCode.custom,
349
+ path: ['summary', 'metadataMismatches'],
350
+ message: `metadataMismatches (${o.summary.metadataMismatches}) cannot exceed totalEntries (${o.summary.totalEntries})`,
351
+ });
352
+ }
353
+ if (o.summary.unverifiable > o.summary.totalEntries) {
354
+ ctx.addIssue({
355
+ code: z.ZodIssueCode.custom,
356
+ path: ['summary', 'unverifiable'],
357
+ message: `unverifiable (${o.summary.unverifiable}) cannot exceed totalEntries (${o.summary.totalEntries})`,
358
+ });
359
+ }
360
+ if (o.summary.canonicalIssues > o.summary.totalEntries) {
361
+ ctx.addIssue({
362
+ code: z.ZodIssueCode.custom,
363
+ path: ['summary', 'canonicalIssues'],
364
+ message: `canonicalIssues (${o.summary.canonicalIssues}) cannot exceed totalEntries (${o.summary.totalEntries})`,
365
+ });
366
+ }
367
+ // 0.2.0 counters (required since T22).
368
+ if (o.summary.notFoundInDatabases > o.summary.totalEntries) {
369
+ ctx.addIssue({
370
+ code: z.ZodIssueCode.custom,
371
+ path: ['summary', 'notFoundInDatabases'],
372
+ message: `notFoundInDatabases (${o.summary.notFoundInDatabases}) cannot exceed totalEntries (${o.summary.totalEntries})`,
373
+ });
374
+ }
375
+ if (o.summary.malformedIdentifiers > o.summary.totalEntries) {
376
+ ctx.addIssue({
377
+ code: z.ZodIssueCode.custom,
378
+ path: ['summary', 'malformedIdentifiers'],
379
+ message: `malformedIdentifiers (${o.summary.malformedIdentifiers}) cannot exceed totalEntries (${o.summary.totalEntries})`,
380
+ });
381
+ }
382
+ // Existence-bucket reconciliation: every entry lands in exactly one of the
383
+ // four existence buckets, which must sum to totalEntries.
384
+ {
385
+ const bucketSum = o.summary.verified +
386
+ o.summary.metadataMismatches +
387
+ o.summary.notFoundInDatabases +
388
+ o.summary.unverifiable;
389
+ if (bucketSum !== o.summary.totalEntries) {
390
+ ctx.addIssue({
391
+ code: z.ZodIssueCode.custom,
392
+ path: ['summary'],
393
+ message: `existence buckets (verified + metadataMismatches + notFoundInDatabases + unverifiable = ${bucketSum}) must sum to totalEntries (${o.summary.totalEntries})`,
394
+ });
395
+ }
396
+ }
397
+ if (o.summary.phraseFlags !== o.phraseFlags.filter(f => f.status === 'flagged').length) {
398
+ ctx.addIssue({
399
+ code: z.ZodIssueCode.custom,
400
+ path: ['summary', 'phraseFlags'],
401
+ message: `summary.phraseFlags (${o.summary.phraseFlags}) must equal the count of flagged (not acknowledged) entries in phraseFlags array`,
402
+ });
403
+ }
404
+ if (o.summary.worklistItems !== o.worklist.length) {
405
+ ctx.addIssue({
406
+ code: z.ZodIssueCode.custom,
407
+ path: ['summary', 'worklistItems'],
408
+ message: `summary.worklistItems must equal worklist.length`,
409
+ });
410
+ }
411
+ if (o.summary.linkageFailures !== o.linkage.filter(l => l.status === 'unresolved').length) {
412
+ ctx.addIssue({
413
+ code: z.ZodIssueCode.custom,
414
+ path: ['summary', 'linkageFailures'],
415
+ message: `summary.linkageFailures must equal the count of unresolved linkage entries`,
416
+ });
417
+ }
418
+ });
419
+ //# sourceMappingURL=output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/schema/output.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,106 @@
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
+ import type { Config } from './config.js';
24
+ /**
25
+ * The gating finding kinds a suppression can target. Mirrors the gateable
26
+ * reasons in `check.ts` (`CHECK_NON_ZERO_REASON`):
27
+ * - 'not-found' → existence.status === 'not-found-in-databases'
28
+ * - 'malformed-identifier' → summary.malformedIdentifiers (per entry)
29
+ * - 'canonical-issue' → canonical.status in the problem set
30
+ * - 'metadata-mismatch' → existence.status === 'metadata-mismatch'
31
+ */
32
+ export declare const FINDING_TYPES: readonly ["not-found", "malformed-identifier", "canonical-issue", "metadata-mismatch"];
33
+ export type FindingType = (typeof FINDING_TYPES)[number];
34
+ /**
35
+ * A single parsed `bibcheck-allow` directive from an entry's CSL `note`.
36
+ *
37
+ * `reason` is the trimmed text inside `(reason: ...)`. An allow with an empty
38
+ * or missing reason is NOT a valid suppression — it is captured here with
39
+ * `reason: null` so the caller can emit a warning and decline to suppress
40
+ * (reason is MANDATORY; see `parseAllows` / `isGated`).
41
+ */
42
+ export interface ParsedAllow {
43
+ citekey: string;
44
+ findingType: FindingType;
45
+ reason: string | null;
46
+ /** The raw directive text, for diagnostics. */
47
+ raw: string;
48
+ }
49
+ export interface ParseAllowsResult {
50
+ allows: ParsedAllow[];
51
+ /** Directives whose finding-type token is not a known FindingType. */
52
+ unknownTypes: {
53
+ citekey: string;
54
+ token: string;
55
+ raw: string;
56
+ }[];
57
+ }
58
+ /**
59
+ * Parse every `bibcheck-allow` directive out of one entry's CSL `note`.
60
+ *
61
+ * Pure. Returns both the valid allows (which may still have `reason: null` when
62
+ * the reason was omitted — the caller decides how to warn) and any directives
63
+ * whose finding-type token is unrecognised.
64
+ */
65
+ export declare function parseAllows(citekey: string, note: string | undefined): ParseAllowsResult;
66
+ /**
67
+ * Parse allows across a whole bibliography. Convenience wrapper used by
68
+ * `check.ts`; aggregates the per-entry results.
69
+ */
70
+ export declare function parseAllowsForBibliography(entries: {
71
+ citekey: string;
72
+ note?: string | undefined;
73
+ }[]): ParseAllowsResult;
74
+ export interface SuppressionInput {
75
+ citekey: string;
76
+ findingType: FindingType;
77
+ cslType: string | undefined;
78
+ config: Config;
79
+ allows: ParsedAllow[];
80
+ }
81
+ export type GateReason = 'default' | 'source-type' | 'allow';
82
+ export interface GateResult {
83
+ gated: boolean;
84
+ reason: GateReason;
85
+ }
86
+ /**
87
+ * Decide whether a single finding gates the build.
88
+ *
89
+ * Precedence (an explicit allow or a source-type exemption beats the default
90
+ * gate):
91
+ *
92
+ * 1. A valid per-entry allow (matching citekey + findingType, with a
93
+ * NON-EMPTY reason) → NOT gated, `reason: 'allow'`. Reason is mandatory:
94
+ * an allow with an empty/missing reason does NOT suppress (it is dropped
95
+ * here and warned about by the caller), so it falls through to the gate.
96
+ * 2. For `not-found` only, a source-type exemption
97
+ * (`[source_types] <cslType> = { gate_not_found = false }`) → NOT gated,
98
+ * `reason: 'source-type'`. Source-type rules govern only the not-found
99
+ * gate (no DOI was ever expected for the type); they do not exempt
100
+ * malformed identifiers, canonical issues, or metadata mismatches.
101
+ * 3. Otherwise the secure default applies → gated, `reason: 'default'`.
102
+ *
103
+ * Pure: depends only on its inputs.
104
+ */
105
+ export declare function isGated(input: SuppressionInput): GateResult;
106
+ //# sourceMappingURL=suppression.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suppression.d.ts","sourceRoot":"","sources":["../src/suppression.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAM1C;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,wFAKhB,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAUzD;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,WAAW,CAAC;IACzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,+CAA+C;IAC/C,GAAG,EAAE,MAAM,CAAC;CACb;AAiBD,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,sEAAsE;IACtE,YAAY,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACjE;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,iBAAiB,CAuBxF;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,EAAE,GACxD,iBAAiB,CASnB;AAMD,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,WAAW,CAAC;IACzB,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB;AAED,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,aAAa,GAAG,OAAO,CAAC;AAE7D,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,UAAU,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,gBAAgB,GAAG,UAAU,CAqB3D"}