@de-otio/bibcheck 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@de-otio/bibcheck",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
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
5
|
"keywords": [
|
|
6
6
|
"citations",
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WorldCat ISBN lookup client (v0.1: OCLC Classify legacy endpoint).
|
|
3
|
-
*
|
|
4
|
-
* v0.1 uses the OCLC Classify API at:
|
|
5
|
-
* http://classify.oclc.org/classify2/api?isbn=<isbn>&summary=true
|
|
6
|
-
*
|
|
7
|
-
* IMPORTANT: This endpoint uses HTTP (not HTTPS). The response is bibliographic
|
|
8
|
-
* metadata only and contains no credentials, but response integrity is not
|
|
9
|
-
* guaranteed over a plaintext connection. This is an accepted v0.1 limitation.
|
|
10
|
-
* Migrate to the OCLC Discovery API (HTTPS + OAuth2) in v0.2 when apiKey
|
|
11
|
-
* support is implemented.
|
|
12
|
-
*
|
|
13
|
-
* The Classify API returns XML by default. We request JSON via Accept header.
|
|
14
|
-
* If the response is nonetheless XML (string), we perform a minimal regex parse
|
|
15
|
-
* for <work title="..." author="..."> — acceptable for v0.1 (documented kludge).
|
|
16
|
-
*/
|
|
17
|
-
import type { HttpClient } from '../http.js';
|
|
18
|
-
import type { Cache } from '../cache/fs-cache.js';
|
|
19
|
-
import type { DatabaseLookupResult, DatabaseClient } from './crossref.js';
|
|
20
|
-
export type { DatabaseLookupResult, DatabaseClient };
|
|
21
|
-
export interface WorldCatClientOptions {
|
|
22
|
-
http: HttpClient;
|
|
23
|
-
cache: Cache;
|
|
24
|
-
apiKey?: string | null;
|
|
25
|
-
/** Base URL for the Classify endpoint. Defaults to the public endpoint. */
|
|
26
|
-
baseUrl?: string;
|
|
27
|
-
}
|
|
28
|
-
export interface WorldCatClient extends DatabaseClient {
|
|
29
|
-
readonly name: 'worldcat';
|
|
30
|
-
lookupByIsbn(isbn: string, signal?: AbortSignal): Promise<DatabaseLookupResult>;
|
|
31
|
-
}
|
|
32
|
-
export declare function createWorldCatClient(opts: WorldCatClientOptions): WorldCatClient;
|
|
33
|
-
//# sourceMappingURL=worldcat.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"worldcat.d.ts","sourceRoot":"","sources":["../../src/databases/worldcat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAgB,MAAM,YAAY,CAAC;AAC3D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,KAAK,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAG1E,YAAY,EAAE,oBAAoB,EAAE,cAAc,EAAE,CAAC;AAQrD,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,KAAK,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAe,SAAQ,cAAc;IACpD,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;CACjF;AA6FD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,qBAAqB,GAAG,cAAc,CA2EhF"}
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WorldCat ISBN lookup client (v0.1: OCLC Classify legacy endpoint).
|
|
3
|
-
*
|
|
4
|
-
* v0.1 uses the OCLC Classify API at:
|
|
5
|
-
* http://classify.oclc.org/classify2/api?isbn=<isbn>&summary=true
|
|
6
|
-
*
|
|
7
|
-
* IMPORTANT: This endpoint uses HTTP (not HTTPS). The response is bibliographic
|
|
8
|
-
* metadata only and contains no credentials, but response integrity is not
|
|
9
|
-
* guaranteed over a plaintext connection. This is an accepted v0.1 limitation.
|
|
10
|
-
* Migrate to the OCLC Discovery API (HTTPS + OAuth2) in v0.2 when apiKey
|
|
11
|
-
* support is implemented.
|
|
12
|
-
*
|
|
13
|
-
* The Classify API returns XML by default. We request JSON via Accept header.
|
|
14
|
-
* If the response is nonetheless XML (string), we perform a minimal regex parse
|
|
15
|
-
* for <work title="..." author="..."> — acceptable for v0.1 (documented kludge).
|
|
16
|
-
*/
|
|
17
|
-
import { trimTrailingSlash } from './crossref.js';
|
|
18
|
-
const DEFAULT_WORLDCAT_BASE = 'http://classify.oclc.org';
|
|
19
|
-
// ---------------------------------------------------------------------------
|
|
20
|
-
// Helpers
|
|
21
|
-
// ---------------------------------------------------------------------------
|
|
22
|
-
function isClassifyJsonResponse(body) {
|
|
23
|
-
return typeof body === 'object' && body !== null;
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Minimal XML parse for OCLC Classify XML responses.
|
|
27
|
-
* Extracts title and author from the first <work title="..." author="..."> element.
|
|
28
|
-
* This is a v0.1 kludge; full XML parsing is deferred to v0.2.
|
|
29
|
-
*/
|
|
30
|
-
function parseClassifyXml(xml) {
|
|
31
|
-
// Match both <work ... title="..." author="..."> and with attributes in any order.
|
|
32
|
-
const workMatch = /<work\b([^>]*)>/i.exec(xml);
|
|
33
|
-
if (!workMatch?.[1])
|
|
34
|
-
return null;
|
|
35
|
-
const attrs = workMatch[1];
|
|
36
|
-
const titleMatch = /\btitle="([^"]*)"/.exec(attrs);
|
|
37
|
-
const authorMatch = /\bauthor="([^"]*)"/.exec(attrs);
|
|
38
|
-
if (!titleMatch && !authorMatch)
|
|
39
|
-
return null;
|
|
40
|
-
return {
|
|
41
|
-
title: titleMatch?.[1],
|
|
42
|
-
author: authorMatch?.[1],
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Determine whether there are zero works in a Classify JSON response.
|
|
47
|
-
* The Classify API returns an empty work list or a specific responseCode when
|
|
48
|
-
* no records are found.
|
|
49
|
-
*/
|
|
50
|
-
function hasClassifyWork(body) {
|
|
51
|
-
const classify = body.classify;
|
|
52
|
-
if (!classify)
|
|
53
|
-
return null;
|
|
54
|
-
// Single-work response.
|
|
55
|
-
if (classify.work && typeof classify.work === 'object') {
|
|
56
|
-
return classify.work;
|
|
57
|
-
}
|
|
58
|
-
// Multi-work summary: works.work may be an array or single object.
|
|
59
|
-
if (classify.works?.work) {
|
|
60
|
-
const w = classify.works.work;
|
|
61
|
-
if (Array.isArray(w)) {
|
|
62
|
-
return w[0] ?? null;
|
|
63
|
-
}
|
|
64
|
-
return w;
|
|
65
|
-
}
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
function mapClassifyMetadata(work, isbn) {
|
|
69
|
-
const authors = work.author ? [work.author] : undefined;
|
|
70
|
-
return {
|
|
71
|
-
title: work.title,
|
|
72
|
-
authors,
|
|
73
|
-
isbn,
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
// ---------------------------------------------------------------------------
|
|
77
|
-
// Factory
|
|
78
|
-
// ---------------------------------------------------------------------------
|
|
79
|
-
export function createWorldCatClient(opts) {
|
|
80
|
-
const { http, cache } = opts;
|
|
81
|
-
// apiKey is reserved for v0.2; ignored in v0.1.
|
|
82
|
-
const base = trimTrailingSlash(opts.baseUrl ?? DEFAULT_WORLDCAT_BASE);
|
|
83
|
-
async function lookupByIsbn(isbn, signal) {
|
|
84
|
-
const cacheKey = `worldcat:lookupByIsbn:${isbn}`;
|
|
85
|
-
const cached = await cache.get(cacheKey, signal);
|
|
86
|
-
if (cached !== null) {
|
|
87
|
-
return cached;
|
|
88
|
-
}
|
|
89
|
-
// v0.1: HTTP (not HTTPS) — legacy Classify endpoint does not support HTTPS.
|
|
90
|
-
const url = `${base}/classify2/api?isbn=${encodeURIComponent(isbn)}&summary=true`;
|
|
91
|
-
let response;
|
|
92
|
-
try {
|
|
93
|
-
response = await http.get(url, {
|
|
94
|
-
signal,
|
|
95
|
-
headers: { Accept: 'application/json' },
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
catch (err) {
|
|
99
|
-
throw err;
|
|
100
|
-
}
|
|
101
|
-
if (response.status === 404) {
|
|
102
|
-
return { found: false, metadata: null, raw: response.body };
|
|
103
|
-
}
|
|
104
|
-
// Handle XML fallback (response body is a string when Content-Type is not JSON).
|
|
105
|
-
if (typeof response.body === 'string') {
|
|
106
|
-
const parsed = parseClassifyXml(response.body);
|
|
107
|
-
if (!parsed || (!parsed.title && !parsed.author)) {
|
|
108
|
-
const result = {
|
|
109
|
-
found: false,
|
|
110
|
-
metadata: null,
|
|
111
|
-
raw: response.body,
|
|
112
|
-
};
|
|
113
|
-
await cache.set(cacheKey, result);
|
|
114
|
-
return result;
|
|
115
|
-
}
|
|
116
|
-
const metadata = mapClassifyMetadata(parsed, isbn);
|
|
117
|
-
const result = { found: true, metadata, raw: response.body };
|
|
118
|
-
await cache.set(cacheKey, result);
|
|
119
|
-
return result;
|
|
120
|
-
}
|
|
121
|
-
// JSON response path.
|
|
122
|
-
if (!isClassifyJsonResponse(response.body)) {
|
|
123
|
-
throw new Error(`WorldCat: unexpected response body for ISBN "${isbn}"`);
|
|
124
|
-
}
|
|
125
|
-
const work = hasClassifyWork(response.body);
|
|
126
|
-
if (!work) {
|
|
127
|
-
const result = {
|
|
128
|
-
found: false,
|
|
129
|
-
metadata: null,
|
|
130
|
-
raw: response.body,
|
|
131
|
-
};
|
|
132
|
-
await cache.set(cacheKey, result);
|
|
133
|
-
return result;
|
|
134
|
-
}
|
|
135
|
-
const metadata = mapClassifyMetadata(work, isbn);
|
|
136
|
-
const result = { found: true, metadata, raw: response.body };
|
|
137
|
-
await cache.set(cacheKey, result);
|
|
138
|
-
return result;
|
|
139
|
-
}
|
|
140
|
-
return {
|
|
141
|
-
name: 'worldcat',
|
|
142
|
-
lookupByIsbn,
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
//# sourceMappingURL=worldcat.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"worldcat.js","sourceRoot":"","sources":["../../src/databases/worldcat.ts"],"names":[],"mappings":"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"}
|