@prose-reader/metadata-fetcher 1.344.0 → 1.345.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/dist/fetchMetadata.d.ts +2 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +14 -1
- package/dist/index.js +515 -232
- package/dist/index.js.map +1 -1
- package/dist/providers/googleBooks/createGoogleBooksProvider.d.ts +17 -0
- package/dist/providers/googleBooks/createGoogleBooksProvider.test.d.ts +1 -0
- package/dist/providers/googleBooks/identifier.d.ts +14 -0
- package/dist/providers/googleBooks/identifier.test.d.ts +1 -0
- package/dist/providers/googleBooks/parse.d.ts +44 -0
- package/dist/providers/googleBooks/resolve.d.ts +41 -0
- package/dist/providers/googleBooks/resolve.test.d.ts +1 -0
- package/dist/providers/googleBooks/search.d.ts +12 -0
- package/dist/providers/openLibrary/resolve.d.ts +6 -9
- package/dist/providers/projectGutenberg/identifier.d.ts +2 -1
- package/dist/providers/projectGutenberg/resolve.d.ts +2 -5
- package/dist/types/fetchMetadataInput.d.ts +2 -9
- package/dist/types/provider.d.ts +4 -1
- package/dist/utils/identifierValues.d.ts +5 -0
- package/dist/utils/identifierValues.test.d.ts +1 -0
- package/dist/utils/retryWithBackoff.d.ts +20 -0
- package/dist/utils/retryWithBackoff.test.d.ts +1 -0
- package/dist/utils/toIsbn13.d.ts +4 -4
- package/package.json +4 -4
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { MetadataIdentifier } from '@prose-reader/archive-reader';
|
|
2
|
+
import { FetchMetadataInput } from '../../types/fetchMetadataInput.ts';
|
|
3
|
+
export declare const GOOGLE_BOOKS_IDENTIFIER_SCHEME = "GoogleBooks";
|
|
4
|
+
export type GoogleBooksLookup = {
|
|
5
|
+
readonly id: string;
|
|
6
|
+
/** The book's exact identifier spelling, echoed only after Google confirms it. */
|
|
7
|
+
readonly identifier: MetadataIdentifier;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Recognizes an authored Google Books volume id and the official Google Books
|
|
11
|
+
* website/API URL forms. Arbitrary scheme-less strings are not reinterpreted
|
|
12
|
+
* as ids: callers with a raw id should label it `GoogleBooks`.
|
|
13
|
+
*/
|
|
14
|
+
export declare const googleBooksLookupFromInput: (input: FetchMetadataInput) => GoogleBooksLookup | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export type GoogleBooksIndustryIdentifier = {
|
|
2
|
+
readonly type?: string;
|
|
3
|
+
readonly identifier: string;
|
|
4
|
+
};
|
|
5
|
+
export type GoogleBooksImageLinks = {
|
|
6
|
+
readonly smallThumbnail?: string;
|
|
7
|
+
readonly thumbnail?: string;
|
|
8
|
+
readonly small?: string;
|
|
9
|
+
readonly medium?: string;
|
|
10
|
+
readonly large?: string;
|
|
11
|
+
readonly extraLarge?: string;
|
|
12
|
+
};
|
|
13
|
+
export type GoogleBooksSeriesInfo = {
|
|
14
|
+
readonly bookDisplayNumber?: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* The subset of a Google Books `volumeInfo` record that has a normalized home.
|
|
18
|
+
* Every field remains optional because the public catalog is sparse in practice.
|
|
19
|
+
*/
|
|
20
|
+
export type GoogleBooksVolumeInfo = {
|
|
21
|
+
readonly title?: string;
|
|
22
|
+
readonly subtitle?: string;
|
|
23
|
+
readonly authors?: ReadonlyArray<string>;
|
|
24
|
+
readonly publisher?: string;
|
|
25
|
+
readonly publishedDate?: string;
|
|
26
|
+
readonly description?: string;
|
|
27
|
+
readonly industryIdentifiers?: ReadonlyArray<GoogleBooksIndustryIdentifier>;
|
|
28
|
+
readonly pageCount?: number;
|
|
29
|
+
readonly categories?: ReadonlyArray<string>;
|
|
30
|
+
readonly language?: string;
|
|
31
|
+
readonly imageLinks?: GoogleBooksImageLinks;
|
|
32
|
+
readonly infoLink?: string;
|
|
33
|
+
readonly canonicalVolumeLink?: string;
|
|
34
|
+
readonly seriesInfo?: GoogleBooksSeriesInfo;
|
|
35
|
+
};
|
|
36
|
+
/** One Google Books volume, retaining the source's nested field names. */
|
|
37
|
+
export type GoogleBooksVolume = {
|
|
38
|
+
readonly id?: string;
|
|
39
|
+
readonly volumeInfo?: GoogleBooksVolumeInfo;
|
|
40
|
+
};
|
|
41
|
+
/** Reads one public Google Books Volume resource from untrusted JSON. */
|
|
42
|
+
export declare const parseGoogleBooksVolume: (payload: unknown) => GoogleBooksVolume | undefined;
|
|
43
|
+
/** Reads the `items` array of a Google Books volumes search response. */
|
|
44
|
+
export declare const parseGoogleBooksVolumesResponse: (payload: unknown) => ReadonlyArray<GoogleBooksVolume>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { MetadataIdentifier, ResolvedMetadata } from '@prose-reader/archive-reader';
|
|
2
|
+
import { GoogleBooksImageLinks, GoogleBooksVolume } from './parse.ts';
|
|
3
|
+
export declare const GOOGLE_BOOKS_MAX_SUBJECTS = 25;
|
|
4
|
+
/** Compile-enforced homes for the top-level Volume resource fields. */
|
|
5
|
+
export declare const googleBooksVolumeMetadataHomes: {
|
|
6
|
+
id: "identifiers";
|
|
7
|
+
volumeInfo: "metadata";
|
|
8
|
+
};
|
|
9
|
+
/** Compile-enforced map from every parsed Google Books field to its home. */
|
|
10
|
+
export declare const googleBooksVolumeInfoMetadataHomes: {
|
|
11
|
+
title: "title";
|
|
12
|
+
subtitle: "title";
|
|
13
|
+
authors: "contributors";
|
|
14
|
+
publisher: "publication.edition.publisher";
|
|
15
|
+
publishedDate: "publication.edition.date";
|
|
16
|
+
description: "description";
|
|
17
|
+
industryIdentifiers: "identifiers";
|
|
18
|
+
pageCount: "numberOfPages";
|
|
19
|
+
categories: "subjects";
|
|
20
|
+
language: "languages";
|
|
21
|
+
imageLinks: "cover";
|
|
22
|
+
infoLink: "candidate.url";
|
|
23
|
+
canonicalVolumeLink: "candidate.url";
|
|
24
|
+
seriesInfo: "title";
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Prefer the largest cover Google announced and force HTTPS. Preserve the
|
|
28
|
+
* query exactly: changing Google's `zoom`, `edge` or signed `imgtk` parameters
|
|
29
|
+
* can turn a valid cover into its "image not available" placeholder.
|
|
30
|
+
*/
|
|
31
|
+
export declare const googleBooksCoverUrl: (imageLinks: GoogleBooksImageLinks | undefined) => string | undefined;
|
|
32
|
+
export type ResolveGoogleBooksVolumeOptions = {
|
|
33
|
+
/** Exact Google Books identifier that this response confirmed. */
|
|
34
|
+
readonly matchedIdentifier?: MetadataIdentifier;
|
|
35
|
+
/** ISBN query that this response confirmed, even if the payload omits it. */
|
|
36
|
+
readonly matchedIsbn?: string;
|
|
37
|
+
};
|
|
38
|
+
/** Normalizes one Google Books volume into the cross-format vocabulary. */
|
|
39
|
+
export declare const resolveGoogleBooksVolume: (volume: GoogleBooksVolume, options?: ResolveGoogleBooksVolumeOptions) => ResolvedMetadata;
|
|
40
|
+
/** The human-facing Google Books page for a candidate. */
|
|
41
|
+
export declare const googleBooksVolumeUrl: (volume: GoogleBooksVolume) => string | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { MetadataProvider } from '../../types/provider.ts';
|
|
2
|
+
type GoogleBooksSearchOptions = {
|
|
3
|
+
readonly apiKey: string;
|
|
4
|
+
readonly baseUrl: string;
|
|
5
|
+
readonly fetch: typeof globalThis.fetch;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Creates the Google Books lookup pipeline. Lookup order is an exact volume
|
|
9
|
+
* identifier, ISBN, then title plus first author with a title-only fallback.
|
|
10
|
+
*/
|
|
11
|
+
export declare const createGoogleBooksSearch: (options: GoogleBooksSearchOptions) => MetadataProvider["search"];
|
|
12
|
+
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ResolvedMetadata } from '@prose-reader/archive-reader';
|
|
1
|
+
import { MetadataIdentifier, ResolvedMetadata } from '@prose-reader/archive-reader';
|
|
2
2
|
import { OpenLibraryDoc } from './parse.ts';
|
|
3
3
|
/**
|
|
4
4
|
* A popular work carries hundreds of subject headings, most of them long-tail
|
|
@@ -32,18 +32,15 @@ export declare const OPEN_LIBRARY_IDENTIFIER_SCHEME = "OpenLibrary";
|
|
|
32
32
|
* - **`title` folds in `subtitle`** (`Dune: Messiah`): the vocabulary has one
|
|
33
33
|
* title field, and an OPF `dc:title` normally carries the subtitle too, so
|
|
34
34
|
* comparing a bare title against a full one would cost match score.
|
|
35
|
-
* -
|
|
36
|
-
* answered "this work has that ISBN", a fact about the
|
|
37
|
-
* describes a *work*, whose editions each have
|
|
38
|
-
*
|
|
35
|
+
* - **An ISBN identifier is added only for an ISBN lookup**, using the queried
|
|
36
|
+
* value — the API answered "this work has that ISBN", a fact about the
|
|
37
|
+
* record. A title-search hit describes a *work*, whose editions each have
|
|
38
|
+
* their own ISBN, so picking one would be fabrication.
|
|
39
39
|
*/
|
|
40
40
|
export declare const resolveOpenLibraryDoc: (doc: OpenLibraryDoc, options: {
|
|
41
41
|
readonly coversBaseUrl: string;
|
|
42
42
|
/** The ISBN this record was looked up by, when it was. */
|
|
43
43
|
readonly isbn?: string;
|
|
44
44
|
/** The source identifier that an exact Gutenberg-id lookup confirmed. */
|
|
45
|
-
readonly matchedProjectGutenbergIdentifier?:
|
|
46
|
-
readonly value: string;
|
|
47
|
-
readonly scheme?: string;
|
|
48
|
-
};
|
|
45
|
+
readonly matchedProjectGutenbergIdentifier?: MetadataIdentifier;
|
|
49
46
|
}) => ResolvedMetadata;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { MetadataIdentifier } from '@prose-reader/archive-reader';
|
|
2
|
+
import { FetchMetadataInput } from '../../types/fetchMetadataInput.ts';
|
|
2
3
|
export declare const PROJECT_GUTENBERG_IDENTIFIER_SCHEME = "ProjectGutenberg";
|
|
3
4
|
export type ProjectGutenbergLookup = {
|
|
4
5
|
readonly id: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ResolvedMetadata } from '@prose-reader/archive-reader';
|
|
1
|
+
import { MetadataIdentifier, ResolvedMetadata } from '@prose-reader/archive-reader';
|
|
2
2
|
import { ProjectGutenbergRecord } from './parse.ts';
|
|
3
3
|
export declare const PROJECT_GUTENBERG_MAX_SUBJECTS = 25;
|
|
4
4
|
/** Compile-enforced map from every parsed RDF field to its resolved home. */
|
|
@@ -19,8 +19,5 @@ export declare const projectGutenbergMetadataHomes: {
|
|
|
19
19
|
};
|
|
20
20
|
export declare const resolveProjectGutenbergRecord: (record: ProjectGutenbergRecord, options: {
|
|
21
21
|
readonly baseUrl: string;
|
|
22
|
-
readonly matchedIdentifier:
|
|
23
|
-
readonly value: string;
|
|
24
|
-
readonly scheme?: string;
|
|
25
|
-
};
|
|
22
|
+
readonly matchedIdentifier: MetadataIdentifier;
|
|
26
23
|
}) => ResolvedMetadata;
|
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
export type MetadataIdentifier = {
|
|
3
|
-
/** Identifier exactly as announced by the source. */
|
|
4
|
-
readonly value: string;
|
|
5
|
-
/** Identifier namespace, such as `DOI`, `URL` or `ProjectGutenberg`. */
|
|
6
|
-
readonly scheme?: string;
|
|
7
|
-
};
|
|
1
|
+
import { MetadataIdentifier } from '@prose-reader/archive-reader';
|
|
8
2
|
/**
|
|
9
3
|
* The compact description of a publication that metadata providers understand
|
|
10
4
|
* for lookup and matching. Rich catalog-only values belong on returned
|
|
@@ -15,8 +9,7 @@ export type FetchMetadataInput = {
|
|
|
15
9
|
readonly title?: string;
|
|
16
10
|
/** Author names, lead author first. */
|
|
17
11
|
readonly authors?: ReadonlyArray<string>;
|
|
18
|
-
|
|
19
|
-
readonly gtin?: string;
|
|
12
|
+
/** Scheme-scoped publication and catalog identifiers. */
|
|
20
13
|
readonly identifiers?: ReadonlyArray<MetadataIdentifier>;
|
|
21
14
|
/** Series name, when it helps identify or disambiguate the publication. */
|
|
22
15
|
readonly series?: string;
|
package/dist/types/provider.d.ts
CHANGED
|
@@ -53,7 +53,10 @@ export type MetadataCandidate = {
|
|
|
53
53
|
* id: result.id,
|
|
54
54
|
* url: `https://example.com/book/${result.id}`,
|
|
55
55
|
* raw: result,
|
|
56
|
-
* metadata: {
|
|
56
|
+
* metadata: {
|
|
57
|
+
* title: result.name,
|
|
58
|
+
* identifiers: [{ value: result.isbn, scheme: "ISBN" }],
|
|
59
|
+
* },
|
|
57
60
|
* }))
|
|
58
61
|
* },
|
|
59
62
|
* }
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { MetadataIdentifier } from '@prose-reader/archive-reader';
|
|
2
|
+
/** ISBNs may arrive through ComicInfo's broader GTIN field. */
|
|
3
|
+
export declare const isbnIdentifierValue: (identifiers: ReadonlyArray<MetadataIdentifier> | undefined) => string | undefined;
|
|
4
|
+
/** ISBN-13 is also a GTIN-13, so both announced schemes are accepted. */
|
|
5
|
+
export declare const gtinIdentifierValue: (identifiers: ReadonlyArray<MetadataIdentifier> | undefined) => string | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type RetryWithBackoffOptions = {
|
|
2
|
+
/** Total calls, including the initial one. Must be a positive integer. */
|
|
3
|
+
readonly attempts: number;
|
|
4
|
+
/** Delay before the second call. Later delays double. */
|
|
5
|
+
readonly initialDelayMs: number;
|
|
6
|
+
/** Upper bound for one delay, including jitter. */
|
|
7
|
+
readonly maxDelayMs?: number;
|
|
8
|
+
/** Adds up to one base delay of randomness. Defaults to `true`. */
|
|
9
|
+
readonly jitter?: boolean;
|
|
10
|
+
/** Decides whether a failed call is safe and useful to repeat. */
|
|
11
|
+
readonly shouldRetry: (error: unknown, attempt: number) => boolean;
|
|
12
|
+
/** Stops future attempts and cancels a wait between attempts. */
|
|
13
|
+
readonly signal?: AbortSignal;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Repeats a failing asynchronous operation with abortable exponential backoff.
|
|
17
|
+
* `attempts` is the total call count, not the number of retries. The original
|
|
18
|
+
* error is rethrown when it is not retryable or the final attempt fails.
|
|
19
|
+
*/
|
|
20
|
+
export declare const retryWithBackoff: <Value>(operation: (attempt: number) => Promise<Value>, options: RetryWithBackoffOptions) => Promise<Value>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/utils/toIsbn13.d.ts
CHANGED
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
* books", which — ISBN being a decisive match signal — is the most damaging
|
|
6
6
|
* false negative the matcher can produce.
|
|
7
7
|
*
|
|
8
|
-
* Input is put through `normalizeIsbn`, the same
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* Input is put through `normalizeIsbn`, the same normalization used for ISBN
|
|
9
|
+
* identifiers, so the hyphenated and prefixed forms a human types
|
|
10
|
+
* (`978-0-441-01359-3`, `ISBN 0-441-01359-7`) are recognized rather than
|
|
11
|
+
* silently dropped.
|
|
12
12
|
*
|
|
13
13
|
* Returns `undefined` when nothing ISBN-shaped can be recovered (check digits
|
|
14
14
|
* are not verified: a wrong one still identifies the intended book).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prose-reader/metadata-fetcher",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.345.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
"test:watch": "vitest watch"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@prose-reader/archive-reader": "^1.
|
|
33
|
-
"@prose-reader/shared": "^1.
|
|
32
|
+
"@prose-reader/archive-reader": "^1.345.0",
|
|
33
|
+
"@prose-reader/shared": "^1.345.0",
|
|
34
34
|
"xmldoc": "^2.0.0"
|
|
35
35
|
},
|
|
36
|
-
"gitHead": "
|
|
36
|
+
"gitHead": "f2f6e09d604f1f8e539d01ddd70c6a799c9d82cd"
|
|
37
37
|
}
|