@zivue/zuuid 0.1.0 → 0.2.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/CHANGELOG.md +10 -1
- package/README.md +210 -227
- package/dist/client.d.ts +16 -3
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +22 -0
- package/dist/entity.d.ts.map +1 -1
- package/dist/entity.js +9 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -0
- package/dist/providers/comicvine/character.d.ts +2 -0
- package/dist/providers/comicvine/character.d.ts.map +1 -0
- package/dist/providers/comicvine/character.js +1 -0
- package/dist/providers/comicvine/constants.d.ts +8 -0
- package/dist/providers/comicvine/constants.d.ts.map +1 -0
- package/dist/providers/comicvine/constants.js +7 -0
- package/dist/providers/comicvine/index.d.ts +9 -0
- package/dist/providers/comicvine/index.d.ts.map +1 -0
- package/dist/providers/comicvine/index.js +8 -0
- package/dist/providers/comicvine/issue.d.ts +2 -0
- package/dist/providers/comicvine/issue.d.ts.map +1 -0
- package/dist/providers/comicvine/issue.js +1 -0
- package/dist/providers/comicvine/person.d.ts +2 -0
- package/dist/providers/comicvine/person.d.ts.map +1 -0
- package/dist/providers/comicvine/person.js +1 -0
- package/dist/providers/comicvine/publisher.d.ts +2 -0
- package/dist/providers/comicvine/publisher.d.ts.map +1 -0
- package/dist/providers/comicvine/publisher.js +1 -0
- package/dist/providers/comicvine/story-arc.d.ts +2 -0
- package/dist/providers/comicvine/story-arc.d.ts.map +1 -0
- package/dist/providers/comicvine/story-arc.js +1 -0
- package/dist/providers/comicvine/transform.d.ts +4 -0
- package/dist/providers/comicvine/transform.d.ts.map +1 -0
- package/dist/providers/comicvine/transform.js +63 -0
- package/dist/providers/comicvine/volume.d.ts +2 -0
- package/dist/providers/comicvine/volume.d.ts.map +1 -0
- package/dist/providers/comicvine/volume.js +1 -0
- package/dist/providers/common.d.ts +23 -0
- package/dist/providers/common.d.ts.map +1 -0
- package/dist/providers/common.js +132 -0
- package/dist/providers/gamesdb/constants.d.ts +4 -0
- package/dist/providers/gamesdb/constants.d.ts.map +1 -0
- package/dist/providers/gamesdb/constants.js +3 -0
- package/dist/providers/gamesdb/game.d.ts +7 -0
- package/dist/providers/gamesdb/game.d.ts.map +1 -0
- package/dist/providers/gamesdb/game.js +59 -0
- package/dist/providers/gamesdb/index.d.ts +4 -0
- package/dist/providers/gamesdb/index.d.ts.map +1 -0
- package/dist/providers/gamesdb/index.js +3 -0
- package/dist/providers/gamesdb/platform.d.ts +4 -0
- package/dist/providers/gamesdb/platform.d.ts.map +1 -0
- package/dist/providers/gamesdb/platform.js +23 -0
- package/dist/providers/jikan/anime.d.ts +2 -0
- package/dist/providers/jikan/anime.d.ts.map +1 -0
- package/dist/providers/jikan/anime.js +1 -0
- package/dist/providers/jikan/character.d.ts +2 -0
- package/dist/providers/jikan/character.d.ts.map +1 -0
- package/dist/providers/jikan/character.js +1 -0
- package/dist/providers/jikan/constants.d.ts +8 -0
- package/dist/providers/jikan/constants.d.ts.map +1 -0
- package/dist/providers/jikan/constants.js +7 -0
- package/dist/providers/jikan/index.d.ts +9 -0
- package/dist/providers/jikan/index.d.ts.map +1 -0
- package/dist/providers/jikan/index.js +8 -0
- package/dist/providers/jikan/magazine.d.ts +2 -0
- package/dist/providers/jikan/magazine.d.ts.map +1 -0
- package/dist/providers/jikan/magazine.js +1 -0
- package/dist/providers/jikan/manga.d.ts +2 -0
- package/dist/providers/jikan/manga.d.ts.map +1 -0
- package/dist/providers/jikan/manga.js +1 -0
- package/dist/providers/jikan/person.d.ts +2 -0
- package/dist/providers/jikan/person.d.ts.map +1 -0
- package/dist/providers/jikan/person.js +1 -0
- package/dist/providers/jikan/producer.d.ts +2 -0
- package/dist/providers/jikan/producer.d.ts.map +1 -0
- package/dist/providers/jikan/producer.js +1 -0
- package/dist/providers/jikan/transform.d.ts +8 -0
- package/dist/providers/jikan/transform.d.ts.map +1 -0
- package/dist/providers/jikan/transform.js +125 -0
- package/dist/providers/musicbrainz/artist.d.ts +4 -0
- package/dist/providers/musicbrainz/artist.d.ts.map +1 -0
- package/dist/providers/musicbrainz/artist.js +28 -0
- package/dist/providers/musicbrainz/constants.d.ts +10 -0
- package/dist/providers/musicbrainz/constants.d.ts.map +1 -0
- package/dist/providers/musicbrainz/constants.js +9 -0
- package/dist/providers/musicbrainz/helpers.d.ts +15 -0
- package/dist/providers/musicbrainz/helpers.d.ts.map +1 -0
- package/dist/providers/musicbrainz/helpers.js +71 -0
- package/dist/providers/musicbrainz/index.d.ts +8 -0
- package/dist/providers/musicbrainz/index.d.ts.map +1 -0
- package/dist/providers/musicbrainz/index.js +7 -0
- package/dist/providers/musicbrainz/label.d.ts +4 -0
- package/dist/providers/musicbrainz/label.d.ts.map +1 -0
- package/dist/providers/musicbrainz/label.js +28 -0
- package/dist/providers/musicbrainz/recording.d.ts +4 -0
- package/dist/providers/musicbrainz/recording.d.ts.map +1 -0
- package/dist/providers/musicbrainz/recording.js +41 -0
- package/dist/providers/musicbrainz/release-group.d.ts +7 -0
- package/dist/providers/musicbrainz/release-group.d.ts.map +1 -0
- package/dist/providers/musicbrainz/release-group.js +28 -0
- package/dist/providers/musicbrainz/release.d.ts +7 -0
- package/dist/providers/musicbrainz/release.d.ts.map +1 -0
- package/dist/providers/musicbrainz/release.js +50 -0
- package/dist/providers/musicbrainz/work.d.ts +4 -0
- package/dist/providers/musicbrainz/work.d.ts.map +1 -0
- package/dist/providers/musicbrainz/work.js +40 -0
- package/dist/providers/openfoodfacts/constants.d.ts +3 -0
- package/dist/providers/openfoodfacts/constants.d.ts.map +1 -0
- package/dist/providers/openfoodfacts/constants.js +2 -0
- package/dist/providers/openfoodfacts/index.d.ts +3 -0
- package/dist/providers/openfoodfacts/index.d.ts.map +1 -0
- package/dist/providers/openfoodfacts/index.js +2 -0
- package/dist/providers/openfoodfacts/product.d.ts +4 -0
- package/dist/providers/openfoodfacts/product.d.ts.map +1 -0
- package/dist/providers/openfoodfacts/product.js +29 -0
- package/dist/providers/openlibrary/author.d.ts +65 -0
- package/dist/providers/openlibrary/author.d.ts.map +1 -0
- package/dist/providers/openlibrary/author.js +228 -0
- package/dist/providers/openlibrary/book.d.ts +138 -0
- package/dist/providers/openlibrary/book.d.ts.map +1 -0
- package/dist/providers/openlibrary/book.js +535 -0
- package/dist/providers/openlibrary/client.d.ts +22 -0
- package/dist/providers/openlibrary/client.d.ts.map +1 -0
- package/dist/providers/openlibrary/client.js +61 -0
- package/dist/providers/openlibrary/constants.d.ts +4 -0
- package/dist/providers/openlibrary/constants.d.ts.map +1 -0
- package/dist/providers/openlibrary/constants.js +3 -0
- package/dist/providers/openlibrary/index.d.ts +6 -0
- package/dist/providers/openlibrary/index.d.ts.map +1 -0
- package/dist/providers/openlibrary/index.js +5 -0
- package/dist/providers/openlibrary/types.d.ts +23 -0
- package/dist/providers/openlibrary/types.d.ts.map +1 -0
- package/dist/providers/openlibrary/types.js +1 -0
- package/dist/providers/openstreetmap/constants.d.ts +6 -0
- package/dist/providers/openstreetmap/constants.d.ts.map +1 -0
- package/dist/providers/openstreetmap/constants.js +5 -0
- package/dist/providers/openstreetmap/index.d.ts +4 -0
- package/dist/providers/openstreetmap/index.d.ts.map +1 -0
- package/dist/providers/openstreetmap/index.js +3 -0
- package/dist/providers/openstreetmap/place.d.ts +4 -0
- package/dist/providers/openstreetmap/place.d.ts.map +1 -0
- package/dist/providers/openstreetmap/place.js +65 -0
- package/dist/providers/openstreetmap/venue.d.ts +2 -0
- package/dist/providers/openstreetmap/venue.d.ts.map +1 -0
- package/dist/providers/openstreetmap/venue.js +1 -0
- package/dist/providers/podcast/constants.d.ts +3 -0
- package/dist/providers/podcast/constants.d.ts.map +1 -0
- package/dist/providers/podcast/constants.js +2 -0
- package/dist/providers/podcast/index.d.ts +3 -0
- package/dist/providers/podcast/index.d.ts.map +1 -0
- package/dist/providers/podcast/index.js +2 -0
- package/dist/providers/podcast/podcast.d.ts +4 -0
- package/dist/providers/podcast/podcast.d.ts.map +1 -0
- package/dist/providers/podcast/podcast.js +30 -0
- package/dist/providers/setlistfm/artist.d.ts +2 -0
- package/dist/providers/setlistfm/artist.d.ts.map +1 -0
- package/dist/providers/setlistfm/artist.js +1 -0
- package/dist/providers/setlistfm/constants.d.ts +5 -0
- package/dist/providers/setlistfm/constants.d.ts.map +1 -0
- package/dist/providers/setlistfm/constants.js +4 -0
- package/dist/providers/setlistfm/index.d.ts +6 -0
- package/dist/providers/setlistfm/index.d.ts.map +1 -0
- package/dist/providers/setlistfm/index.js +5 -0
- package/dist/providers/setlistfm/setlist.d.ts +2 -0
- package/dist/providers/setlistfm/setlist.d.ts.map +1 -0
- package/dist/providers/setlistfm/setlist.js +1 -0
- package/dist/providers/setlistfm/transform.d.ts +7 -0
- package/dist/providers/setlistfm/transform.d.ts.map +1 -0
- package/dist/providers/setlistfm/transform.js +122 -0
- package/dist/providers/setlistfm/venue.d.ts +2 -0
- package/dist/providers/setlistfm/venue.d.ts.map +1 -0
- package/dist/providers/setlistfm/venue.js +1 -0
- package/dist/providers/ticketmaster/attraction.d.ts +2 -0
- package/dist/providers/ticketmaster/attraction.d.ts.map +1 -0
- package/dist/providers/ticketmaster/attraction.js +1 -0
- package/dist/providers/ticketmaster/constants.d.ts +5 -0
- package/dist/providers/ticketmaster/constants.d.ts.map +1 -0
- package/dist/providers/ticketmaster/constants.js +4 -0
- package/dist/providers/ticketmaster/event.d.ts +2 -0
- package/dist/providers/ticketmaster/event.d.ts.map +1 -0
- package/dist/providers/ticketmaster/event.js +1 -0
- package/dist/providers/ticketmaster/index.d.ts +6 -0
- package/dist/providers/ticketmaster/index.d.ts.map +1 -0
- package/dist/providers/ticketmaster/index.js +5 -0
- package/dist/providers/ticketmaster/transform.d.ts +7 -0
- package/dist/providers/ticketmaster/transform.d.ts.map +1 -0
- package/dist/providers/ticketmaster/transform.js +89 -0
- package/dist/providers/ticketmaster/venue.d.ts +2 -0
- package/dist/providers/ticketmaster/venue.d.ts.map +1 -0
- package/dist/providers/ticketmaster/venue.js +1 -0
- package/dist/providers/wger/constants.d.ts +4 -0
- package/dist/providers/wger/constants.d.ts.map +1 -0
- package/dist/providers/wger/constants.js +3 -0
- package/dist/providers/wger/equipment.d.ts +4 -0
- package/dist/providers/wger/equipment.d.ts.map +1 -0
- package/dist/providers/wger/equipment.js +22 -0
- package/dist/providers/wger/exercise.d.ts +4 -0
- package/dist/providers/wger/exercise.d.ts.map +1 -0
- package/dist/providers/wger/exercise.js +42 -0
- package/dist/providers/wger/index.d.ts +4 -0
- package/dist/providers/wger/index.d.ts.map +1 -0
- package/dist/providers/wger/index.js +3 -0
- package/package.json +201 -5
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
import { createZuuidData } from "../../entity.js";
|
|
2
|
+
import { providerZuuid } from "../../identity.js";
|
|
3
|
+
import { attachSourceMetadata, createSourceRecord } from "../../source.js";
|
|
4
|
+
import { OPEN_LIBRARY_COVER_BASE_URL, OPEN_LIBRARY_PROVIDER } from "./constants.js";
|
|
5
|
+
export const OPEN_LIBRARY_BOOK_CATEGORY = "book";
|
|
6
|
+
export async function fetchOpenLibraryBookSourceRecord(provider, input) {
|
|
7
|
+
const id = normalizeOpenLibraryBookId(input.id);
|
|
8
|
+
const work = await provider.getJson(`/works/${id}.json`, {});
|
|
9
|
+
if (!work) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
const search = await fetchOpenLibraryBookSearchDoc(provider, id, work);
|
|
13
|
+
const editions = await provider.getJson(`/works/${id}/editions.json`, {
|
|
14
|
+
limit: String(input.editionLimit ?? 50)
|
|
15
|
+
});
|
|
16
|
+
const ratings = await provider.getJson(`/works/${id}/ratings.json`, {});
|
|
17
|
+
const authors = await fetchOpenLibraryAuthors(provider, work);
|
|
18
|
+
const payload = {
|
|
19
|
+
work,
|
|
20
|
+
...(search ? { search } : {}),
|
|
21
|
+
...(editions ? { editions: { entries: editions.entries ?? [], size: editions.size } } : {}),
|
|
22
|
+
...(authors.length ? { authors } : {}),
|
|
23
|
+
...(ratings ? { ratings } : {})
|
|
24
|
+
};
|
|
25
|
+
return createSourceRecord({
|
|
26
|
+
source: { provider: OPEN_LIBRARY_PROVIDER, category: OPEN_LIBRARY_BOOK_CATEGORY, externalId: id },
|
|
27
|
+
payload: payload
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
export async function searchOpenLibraryBookSourceRecords(provider, input) {
|
|
31
|
+
const payload = await fetchOpenLibraryBookSearchResults(provider, input);
|
|
32
|
+
return {
|
|
33
|
+
results: await sourceRecordsFromSearchResults(payload.docs),
|
|
34
|
+
pagination: paginationFromOpenLibrarySearchResponse(payload, input)
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export async function searchOpenLibraryBooks(provider, input, options = {}) {
|
|
38
|
+
const payload = await fetchOpenLibraryBookSearchResults(provider, input);
|
|
39
|
+
const results = [];
|
|
40
|
+
for (const item of payload.docs ?? []) {
|
|
41
|
+
const id = openLibraryWorkIdFromKey(item.key);
|
|
42
|
+
const title = stringField(item.title);
|
|
43
|
+
if (!id || !title) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const zuuid = await providerZuuid({ provider: OPEN_LIBRARY_PROVIDER, category: OPEN_LIBRARY_BOOK_CATEGORY, externalId: id });
|
|
47
|
+
const rating = typeof item.ratings_average === "number" ? item.ratings_average : null;
|
|
48
|
+
results.push({
|
|
49
|
+
id: zuuid,
|
|
50
|
+
zuuid,
|
|
51
|
+
category: OPEN_LIBRARY_BOOK_CATEGORY,
|
|
52
|
+
title,
|
|
53
|
+
date: item.first_publish_year === undefined ? stringField(item.first_publish_date) ?? null : String(item.first_publish_year),
|
|
54
|
+
cover: coverUrl(item.cover_i, options.coverBaseUrl ?? OPEN_LIBRARY_COVER_BASE_URL),
|
|
55
|
+
rating,
|
|
56
|
+
weight: typeof item.edition_count === "number" ? item.edition_count : rating,
|
|
57
|
+
relationType: null,
|
|
58
|
+
attribute: item.author_name?.filter(Boolean).join(", ") || null,
|
|
59
|
+
order: null,
|
|
60
|
+
source: { source: OPEN_LIBRARY_PROVIDER, category: OPEN_LIBRARY_BOOK_CATEGORY, value: id }
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
results,
|
|
65
|
+
pagination: paginationFromOpenLibrarySearchResponse(payload, input)
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
export async function transformOpenLibraryBook(source, options = {}) {
|
|
69
|
+
if (source.source.provider !== OPEN_LIBRARY_PROVIDER || source.source.category !== OPEN_LIBRARY_BOOK_CATEGORY) {
|
|
70
|
+
throw new Error(`unsupported Open Library source: ${source.source.provider}:${source.source.category}`);
|
|
71
|
+
}
|
|
72
|
+
const payload = openLibraryBookPayload(source.payload);
|
|
73
|
+
const work = payload.work;
|
|
74
|
+
const id = normalizeOpenLibraryBookId(source.source.externalId || work.key || "");
|
|
75
|
+
const title = stringField(work.title);
|
|
76
|
+
if (!title) {
|
|
77
|
+
throw new Error("missing required Open Library book field: title");
|
|
78
|
+
}
|
|
79
|
+
const zuuid = await providerZuuid({ provider: OPEN_LIBRARY_PROVIDER, category: OPEN_LIBRARY_BOOK_CATEGORY, externalId: id });
|
|
80
|
+
const data = createZuuidData({ zuuid, category: OPEN_LIBRARY_BOOK_CATEGORY, primaryTitle: title });
|
|
81
|
+
const preferredEdition = preferredOpenLibraryEdition(payload);
|
|
82
|
+
const rating = numberField(payload.ratings?.summary?.average ?? payload.ratings?.summary?.sortable ?? payload.search?.ratings_average);
|
|
83
|
+
if (rating !== undefined) {
|
|
84
|
+
data.rating = rating;
|
|
85
|
+
data.details.push({ key: "rating_average", value: rating, source: OPEN_LIBRARY_PROVIDER });
|
|
86
|
+
}
|
|
87
|
+
const ratingCount = numberField(payload.ratings?.summary?.count);
|
|
88
|
+
if (ratingCount !== undefined) {
|
|
89
|
+
data.details.push({ key: "rating_count", value: ratingCount, source: OPEN_LIBRARY_PROVIDER });
|
|
90
|
+
}
|
|
91
|
+
const firstPublishYear = numberField(payload.search?.first_publish_year);
|
|
92
|
+
const firstPublishDate = stringField(work.first_publish_date) ?? stringField(payload.search?.first_publish_date);
|
|
93
|
+
if (firstPublishYear !== undefined) {
|
|
94
|
+
data.primaryDate = String(firstPublishYear);
|
|
95
|
+
data.details.push({ key: "first_publish_year", value: firstPublishYear, source: OPEN_LIBRARY_PROVIDER });
|
|
96
|
+
if (firstPublishDate && firstPublishDate !== String(firstPublishYear)) {
|
|
97
|
+
data.details.push({ key: "work_first_publish_date", value: firstPublishDate, source: OPEN_LIBRARY_PROVIDER });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
else if (firstPublishDate) {
|
|
101
|
+
data.primaryDate = firstPublishDate;
|
|
102
|
+
data.details.push({ key: "first_publish_date", value: firstPublishDate, source: OPEN_LIBRARY_PROVIDER });
|
|
103
|
+
}
|
|
104
|
+
const description = descriptionValue(work.description);
|
|
105
|
+
if (description) {
|
|
106
|
+
data.descriptions.push({ value: description, source: OPEN_LIBRARY_PROVIDER });
|
|
107
|
+
}
|
|
108
|
+
for (const edition of payload.editions?.entries ?? []) {
|
|
109
|
+
const editionDescription = descriptionValue(edition.description);
|
|
110
|
+
if (editionDescription && !data.descriptions.some((item) => item.value === editionDescription)) {
|
|
111
|
+
data.descriptions.push({ value: editionDescription, source: OPEN_LIBRARY_PROVIDER });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const primaryCover = work.covers?.find((cover) => typeof cover === "number") ??
|
|
115
|
+
preferredEdition?.covers?.find((cover) => typeof cover === "number") ??
|
|
116
|
+
payload.search?.cover_i;
|
|
117
|
+
const primaryCoverUrl = coverUrl(primaryCover, options.coverBaseUrl ?? OPEN_LIBRARY_COVER_BASE_URL);
|
|
118
|
+
if (primaryCoverUrl) {
|
|
119
|
+
data.cover = primaryCoverUrl;
|
|
120
|
+
data.media.push({
|
|
121
|
+
url: primaryCoverUrl,
|
|
122
|
+
mediaType: "image",
|
|
123
|
+
mediaCategory: "cover",
|
|
124
|
+
isPrimary: true,
|
|
125
|
+
source: OPEN_LIBRARY_PROVIDER
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
addArrayDetail(data, "subjects", work.subjects);
|
|
129
|
+
addArrayDetail(data, "subject_places", work.subject_places);
|
|
130
|
+
addArrayDetail(data, "subject_people", work.subject_people);
|
|
131
|
+
addArrayDetail(data, "subject_times", work.subject_times);
|
|
132
|
+
addStructuredDetail(data, "links", work.links);
|
|
133
|
+
addStructuredDetail(data, "excerpts", work.excerpts);
|
|
134
|
+
addStructuredDetail(data, "series", work.series);
|
|
135
|
+
addStructuredDetail(data, "cover_edition", work.cover_edition);
|
|
136
|
+
addOpenLibraryBookAggregateDetails(data, payload, preferredEdition);
|
|
137
|
+
if (typeof payload.editions?.size === "number") {
|
|
138
|
+
data.details.push({ key: "edition_count", value: payload.editions.size, source: OPEN_LIBRARY_PROVIDER });
|
|
139
|
+
}
|
|
140
|
+
else if (typeof payload.search?.edition_count === "number") {
|
|
141
|
+
data.details.push({ key: "edition_count", value: payload.search.edition_count, source: OPEN_LIBRARY_PROVIDER });
|
|
142
|
+
}
|
|
143
|
+
for (const subject of work.subjects ?? []) {
|
|
144
|
+
addTag(data, subject);
|
|
145
|
+
}
|
|
146
|
+
await addAuthorRelations(data, payload);
|
|
147
|
+
return attachSourceMetadata(data, source);
|
|
148
|
+
}
|
|
149
|
+
async function fetchOpenLibraryBookSearchDoc(provider, id, work) {
|
|
150
|
+
const byId = await fetchOpenLibraryBookSearchDocByQuery(provider, id, id, 5);
|
|
151
|
+
if (byId) {
|
|
152
|
+
return byId;
|
|
153
|
+
}
|
|
154
|
+
const title = stringField(work.title);
|
|
155
|
+
return title ? fetchOpenLibraryBookSearchDocByQuery(provider, id, title, 20) : undefined;
|
|
156
|
+
}
|
|
157
|
+
async function fetchOpenLibraryBookSearchDocByQuery(provider, id, query, limit) {
|
|
158
|
+
const payload = await provider.getJson("/search.json", {
|
|
159
|
+
q: query,
|
|
160
|
+
limit: String(limit),
|
|
161
|
+
fields: openLibraryBookSearchFields().join(",")
|
|
162
|
+
});
|
|
163
|
+
return payload?.docs?.find((doc) => openLibraryWorkIdFromKey(doc.key) === id);
|
|
164
|
+
}
|
|
165
|
+
async function fetchOpenLibraryAuthors(provider, work) {
|
|
166
|
+
const authors = [];
|
|
167
|
+
for (const ref of work.authors ?? []) {
|
|
168
|
+
const id = openLibraryAuthorIdFromKey(ref.author?.key);
|
|
169
|
+
if (!id) {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
const author = await provider.getJson(`/authors/${id}.json`, {});
|
|
173
|
+
if (author) {
|
|
174
|
+
authors.push(author);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return authors;
|
|
178
|
+
}
|
|
179
|
+
async function fetchOpenLibraryBookSearchResults(provider, input) {
|
|
180
|
+
const query = searchQuery(input.query);
|
|
181
|
+
const fields = input.fields ?? openLibraryBookSearchFields();
|
|
182
|
+
const payload = await provider.getJson("/search.json", {
|
|
183
|
+
q: query,
|
|
184
|
+
page: String(input.page ?? 1),
|
|
185
|
+
limit: String(input.limit ?? 20),
|
|
186
|
+
fields: fields.join(","),
|
|
187
|
+
...(input.language ? { language: input.language } : {})
|
|
188
|
+
});
|
|
189
|
+
return payload ?? {};
|
|
190
|
+
}
|
|
191
|
+
function openLibraryBookSearchFields() {
|
|
192
|
+
return [
|
|
193
|
+
"key",
|
|
194
|
+
"title",
|
|
195
|
+
"author_name",
|
|
196
|
+
"author_key",
|
|
197
|
+
"first_publish_year",
|
|
198
|
+
"first_publish_date",
|
|
199
|
+
"cover_i",
|
|
200
|
+
"ratings_average",
|
|
201
|
+
"edition_count",
|
|
202
|
+
"language",
|
|
203
|
+
"isbn",
|
|
204
|
+
"ia",
|
|
205
|
+
"subject",
|
|
206
|
+
"ddc",
|
|
207
|
+
"lcc",
|
|
208
|
+
"lccn"
|
|
209
|
+
];
|
|
210
|
+
}
|
|
211
|
+
async function sourceRecordsFromSearchResults(results) {
|
|
212
|
+
const records = [];
|
|
213
|
+
for (const result of results ?? []) {
|
|
214
|
+
const id = openLibraryWorkIdFromKey(result.key);
|
|
215
|
+
if (!id) {
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
records.push(await createSourceRecord({
|
|
219
|
+
source: { provider: OPEN_LIBRARY_PROVIDER, category: OPEN_LIBRARY_BOOK_CATEGORY, externalId: id },
|
|
220
|
+
payload: result
|
|
221
|
+
}));
|
|
222
|
+
}
|
|
223
|
+
return records;
|
|
224
|
+
}
|
|
225
|
+
async function addAuthorRelations(data, payload) {
|
|
226
|
+
for (const [index, ref] of (payload.work.authors ?? []).entries()) {
|
|
227
|
+
const id = openLibraryAuthorIdFromKey(ref.author?.key);
|
|
228
|
+
if (!id) {
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
const author = payload.authors?.find((item) => openLibraryAuthorIdFromKey(item.key) === id);
|
|
232
|
+
const zuuid = await providerZuuid({ provider: OPEN_LIBRARY_PROVIDER, category: "author", externalId: id });
|
|
233
|
+
data.relations.push({
|
|
234
|
+
id: zuuid,
|
|
235
|
+
zuuid,
|
|
236
|
+
category: "author",
|
|
237
|
+
title: stringField(author?.name) ?? id,
|
|
238
|
+
date: stringField(author?.birth_date) ?? null,
|
|
239
|
+
cover: coverUrl(author?.photos?.find((photo) => typeof photo === "number"), OPEN_LIBRARY_COVER_BASE_URL),
|
|
240
|
+
rating: null,
|
|
241
|
+
weight: null,
|
|
242
|
+
relationType: "authored_by",
|
|
243
|
+
direction: "outgoing",
|
|
244
|
+
attribute: null,
|
|
245
|
+
order: index,
|
|
246
|
+
source: OPEN_LIBRARY_PROVIDER,
|
|
247
|
+
externalId: id
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
function addOpenLibraryBookAggregateDetails(data, payload, preferredEdition) {
|
|
252
|
+
const editions = payload.editions?.entries ?? [];
|
|
253
|
+
if (!editions.length && !payload.search) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
addArrayDetail(data, "languages", uniqueStrings([...(payload.search?.language ?? []), ...editions.flatMap((edition) => languageKeys(edition.languages))]));
|
|
257
|
+
addArrayDetail(data, "isbn_10", uniqueStrings(editions.flatMap((edition) => edition.isbn_10 ?? [])));
|
|
258
|
+
addArrayDetail(data, "isbn_13", uniqueStrings(editions.flatMap((edition) => edition.isbn_13 ?? [])));
|
|
259
|
+
addArrayDetail(data, "isbn", uniqueStrings([...(payload.search?.isbn ?? []), ...editions.flatMap((edition) => [...(edition.isbn_10 ?? []), ...(edition.isbn_13 ?? [])])]));
|
|
260
|
+
addArrayDetail(data, "ia", payload.search?.ia);
|
|
261
|
+
addArrayDetail(data, "publishers", uniqueStrings(editions.flatMap((edition) => edition.publishers ?? [])));
|
|
262
|
+
addArrayDetail(data, "source_records", uniqueStrings(editions.flatMap((edition) => edition.source_records ?? [])));
|
|
263
|
+
addArrayDetail(data, "local_id", uniqueStrings(editions.flatMap((edition) => edition.local_id ?? [])));
|
|
264
|
+
addArrayDetail(data, "dewey_decimal_class", uniqueStrings([...(payload.search?.ddc ?? []), ...classificationSubjects(payload.work.subjects, "dewey")]));
|
|
265
|
+
addArrayDetail(data, "lc_classifications", uniqueStrings([...(payload.search?.lcc ?? []), ...classificationSubjects(payload.work.subjects, "lc")]));
|
|
266
|
+
addArrayDetail(data, "lccn", payload.search?.lccn);
|
|
267
|
+
addArrayDetail(data, "translated_from", uniqueStrings(editions.flatMap((edition) => languageKeys(edition.translated_from))));
|
|
268
|
+
addArrayDetail(data, "translation_of", uniqueStrings(editions.map((edition) => stringField(edition.translation_of)).filter((item) => Boolean(item))));
|
|
269
|
+
const contributors = uniqueObjects(editions.flatMap((edition) => (edition.contributors ?? [])
|
|
270
|
+
.map((contributor) => ({
|
|
271
|
+
role: stringField(contributor.role),
|
|
272
|
+
name: stringField(contributor.name)
|
|
273
|
+
}))
|
|
274
|
+
.filter((contributor) => contributor.name)));
|
|
275
|
+
addStructuredDetail(data, "contributors", contributors);
|
|
276
|
+
const classifications = combineEditionRecords(editions, "classifications");
|
|
277
|
+
addStructuredDetail(data, "classifications", classifications);
|
|
278
|
+
const identifiers = combineEditionRecords(editions, "identifiers");
|
|
279
|
+
addStructuredDetail(data, "identifiers", identifiers);
|
|
280
|
+
if (preferredEdition) {
|
|
281
|
+
const preferred = editionSummary(preferredEdition);
|
|
282
|
+
addStructuredDetail(data, "preferred_edition", preferred);
|
|
283
|
+
addDetail(data, "publish_date", preferredEdition.publish_date);
|
|
284
|
+
addDetail(data, "edition_name", preferredEdition.edition_name);
|
|
285
|
+
addDetail(data, "full_title", preferredEdition.full_title);
|
|
286
|
+
addDetail(data, "subtitle", preferredEdition.subtitle);
|
|
287
|
+
if (typeof preferredEdition.number_of_pages === "number") {
|
|
288
|
+
data.details.push({ key: "number_of_pages", value: preferredEdition.number_of_pages, source: OPEN_LIBRARY_PROVIDER });
|
|
289
|
+
}
|
|
290
|
+
addDetail(data, "physical_format", preferredEdition.physical_format);
|
|
291
|
+
}
|
|
292
|
+
const summaries = editions.map((edition) => editionSummary(edition)).filter((edition) => Object.keys(edition).length > 0);
|
|
293
|
+
addStructuredDetail(data, "editions", summaries);
|
|
294
|
+
}
|
|
295
|
+
function preferredOpenLibraryEdition(payload) {
|
|
296
|
+
const editions = payload.editions?.entries ?? [];
|
|
297
|
+
if (!editions.length) {
|
|
298
|
+
return undefined;
|
|
299
|
+
}
|
|
300
|
+
const coverEditionId = openLibraryEditionIdFromKey(payload.work.cover_edition?.key);
|
|
301
|
+
const coverEdition = editions.find((edition) => openLibraryEditionIdFromKey(edition.key) === coverEditionId);
|
|
302
|
+
if (coverEdition) {
|
|
303
|
+
return coverEdition;
|
|
304
|
+
}
|
|
305
|
+
return [...editions].sort((a, b) => editionScore(b, payload) - editionScore(a, payload))[0];
|
|
306
|
+
}
|
|
307
|
+
function editionScore(edition, payload) {
|
|
308
|
+
let score = 0;
|
|
309
|
+
const title = stringField(edition.title)?.toLowerCase();
|
|
310
|
+
const workTitle = stringField(payload.work.title)?.toLowerCase();
|
|
311
|
+
const languages = languageKeys(edition.languages);
|
|
312
|
+
if (title && workTitle && title === workTitle) {
|
|
313
|
+
score += 20;
|
|
314
|
+
}
|
|
315
|
+
if (languages.includes("eng")) {
|
|
316
|
+
score += 15;
|
|
317
|
+
}
|
|
318
|
+
if (edition.isbn_13?.length) {
|
|
319
|
+
score += 8;
|
|
320
|
+
}
|
|
321
|
+
if (edition.isbn_10?.length) {
|
|
322
|
+
score += 6;
|
|
323
|
+
}
|
|
324
|
+
if (edition.covers?.length) {
|
|
325
|
+
score += 5;
|
|
326
|
+
}
|
|
327
|
+
if (edition.publish_date) {
|
|
328
|
+
score += 4;
|
|
329
|
+
}
|
|
330
|
+
if (edition.publishers?.length) {
|
|
331
|
+
score += 3;
|
|
332
|
+
}
|
|
333
|
+
if (typeof edition.number_of_pages === "number") {
|
|
334
|
+
score += 2;
|
|
335
|
+
}
|
|
336
|
+
return score;
|
|
337
|
+
}
|
|
338
|
+
function editionSummary(edition) {
|
|
339
|
+
const summary = {};
|
|
340
|
+
addSummaryString(summary, "id", openLibraryEditionIdFromKey(edition.key));
|
|
341
|
+
addSummaryString(summary, "title", edition.title);
|
|
342
|
+
addSummaryString(summary, "subtitle", edition.subtitle);
|
|
343
|
+
addSummaryString(summary, "fullTitle", edition.full_title);
|
|
344
|
+
addSummaryString(summary, "editionName", edition.edition_name);
|
|
345
|
+
addSummaryString(summary, "publishDate", edition.publish_date);
|
|
346
|
+
addSummaryString(summary, "physicalFormat", edition.physical_format);
|
|
347
|
+
addSummaryNumber(summary, "numberOfPages", edition.number_of_pages);
|
|
348
|
+
addSummaryArray(summary, "publishers", edition.publishers);
|
|
349
|
+
addSummaryArray(summary, "isbn10", edition.isbn_10);
|
|
350
|
+
addSummaryArray(summary, "isbn13", edition.isbn_13);
|
|
351
|
+
addSummaryArray(summary, "languages", languageKeys(edition.languages));
|
|
352
|
+
addSummaryArray(summary, "translatedFrom", languageKeys(edition.translated_from));
|
|
353
|
+
addSummaryString(summary, "translationOf", edition.translation_of);
|
|
354
|
+
addSummaryArray(summary, "covers", edition.covers);
|
|
355
|
+
addSummaryArray(summary, "sourceRecords", edition.source_records);
|
|
356
|
+
addSummaryArray(summary, "localId", edition.local_id);
|
|
357
|
+
if (edition.contributors?.length) {
|
|
358
|
+
summary.contributors = edition.contributors;
|
|
359
|
+
}
|
|
360
|
+
if (edition.identifiers && Object.keys(edition.identifiers).length) {
|
|
361
|
+
summary.identifiers = edition.identifiers;
|
|
362
|
+
}
|
|
363
|
+
if (edition.classifications && Object.keys(edition.classifications).length) {
|
|
364
|
+
summary.classifications = edition.classifications;
|
|
365
|
+
}
|
|
366
|
+
return summary;
|
|
367
|
+
}
|
|
368
|
+
function addSummaryString(summary, key, value) {
|
|
369
|
+
const clean = stringField(value);
|
|
370
|
+
if (clean) {
|
|
371
|
+
summary[key] = clean;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
function addSummaryNumber(summary, key, value) {
|
|
375
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
376
|
+
summary[key] = value;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
function addSummaryArray(summary, key, value) {
|
|
380
|
+
if (value?.length) {
|
|
381
|
+
summary[key] = value;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
function languageKeys(value) {
|
|
385
|
+
return uniqueStrings((value ?? [])
|
|
386
|
+
.map((item) => stringField(item.key)?.replace(/^\/languages\//, ""))
|
|
387
|
+
.filter((item) => Boolean(item)));
|
|
388
|
+
}
|
|
389
|
+
function combineEditionRecords(editions, key) {
|
|
390
|
+
const combined = {};
|
|
391
|
+
for (const edition of editions) {
|
|
392
|
+
const record = edition[key];
|
|
393
|
+
if (!record) {
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
for (const [recordKey, value] of Object.entries(record)) {
|
|
397
|
+
if (value === undefined || value === null || (Array.isArray(value) && value.length === 0)) {
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
400
|
+
const values = Array.isArray(value) ? value : [value];
|
|
401
|
+
for (const item of values) {
|
|
402
|
+
combined[recordKey] = combined[recordKey] ?? [];
|
|
403
|
+
if (!combined[recordKey].some((existing) => JSON.stringify(existing) === JSON.stringify(item))) {
|
|
404
|
+
combined[recordKey].push(item);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
return Object.keys(combined).length ? combined : undefined;
|
|
410
|
+
}
|
|
411
|
+
function classificationSubjects(subjects, type) {
|
|
412
|
+
const values = [];
|
|
413
|
+
for (const subject of subjects ?? []) {
|
|
414
|
+
const clean = stringField(subject);
|
|
415
|
+
if (!clean) {
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
if (type === "dewey" && /^\d{3}(?:\/|\.)/.test(clean)) {
|
|
419
|
+
values.push(clean);
|
|
420
|
+
}
|
|
421
|
+
if (type === "lc" && /^[A-Za-z]{1,3}\d{1,5}(?:\.\w+)?(?:\s+\w+)*$/.test(clean) && !/^\d/.test(clean)) {
|
|
422
|
+
values.push(clean);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
return uniqueStrings(values);
|
|
426
|
+
}
|
|
427
|
+
function uniqueStrings(values) {
|
|
428
|
+
const seen = new Set();
|
|
429
|
+
const result = [];
|
|
430
|
+
for (const value of values ?? []) {
|
|
431
|
+
const clean = stringField(value);
|
|
432
|
+
if (clean && !seen.has(clean)) {
|
|
433
|
+
seen.add(clean);
|
|
434
|
+
result.push(clean);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
return result;
|
|
438
|
+
}
|
|
439
|
+
function uniqueObjects(values) {
|
|
440
|
+
const seen = new Set();
|
|
441
|
+
const result = [];
|
|
442
|
+
for (const value of values) {
|
|
443
|
+
const key = JSON.stringify(value);
|
|
444
|
+
if (!seen.has(key)) {
|
|
445
|
+
seen.add(key);
|
|
446
|
+
result.push(value);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
return result;
|
|
450
|
+
}
|
|
451
|
+
function addTag(data, value) {
|
|
452
|
+
const tag = stringField(value)?.toLowerCase();
|
|
453
|
+
if (tag && !data.tags.includes(tag)) {
|
|
454
|
+
data.tags.push(tag);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
function addArrayDetail(data, key, value) {
|
|
458
|
+
const clean = value?.map((item) => stringField(item)).filter((item) => Boolean(item));
|
|
459
|
+
if (clean?.length) {
|
|
460
|
+
data.details.push({ key, value: clean, source: OPEN_LIBRARY_PROVIDER });
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
function addDetail(data, key, value) {
|
|
464
|
+
const clean = stringField(value);
|
|
465
|
+
if (clean) {
|
|
466
|
+
data.details.push({ key, value: clean, source: OPEN_LIBRARY_PROVIDER });
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
function addStructuredDetail(data, key, value) {
|
|
470
|
+
if (value === undefined || value === null || (Array.isArray(value) && value.length === 0)) {
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
data.details.push({ key, value, source: OPEN_LIBRARY_PROVIDER });
|
|
474
|
+
}
|
|
475
|
+
function paginationFromOpenLibrarySearchResponse(payload, input) {
|
|
476
|
+
const totalResults = payload.numFound ?? payload.num_found ?? 0;
|
|
477
|
+
const limit = input.limit ?? 20;
|
|
478
|
+
return {
|
|
479
|
+
page: input.page ?? 1,
|
|
480
|
+
totalPages: limit > 0 ? Math.ceil(totalResults / limit) : 0,
|
|
481
|
+
totalResults
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
function normalizeOpenLibraryBookId(value) {
|
|
485
|
+
const normalized = stringField(value)?.replace(/^\/works\//, "").replace(/\.json$/, "");
|
|
486
|
+
if (!normalized) {
|
|
487
|
+
throw new Error("Open Library book id must not be empty");
|
|
488
|
+
}
|
|
489
|
+
if (!/^OL\d+W$/i.test(normalized)) {
|
|
490
|
+
throw new Error(`Open Library book id must be a work id like OL82563W: ${value}`);
|
|
491
|
+
}
|
|
492
|
+
return normalized.toUpperCase();
|
|
493
|
+
}
|
|
494
|
+
function openLibraryWorkIdFromKey(value) {
|
|
495
|
+
const normalized = stringField(value)?.replace(/^\/works\//, "");
|
|
496
|
+
return normalized && /^OL\d+W$/i.test(normalized) ? normalized.toUpperCase() : undefined;
|
|
497
|
+
}
|
|
498
|
+
function openLibraryEditionIdFromKey(value) {
|
|
499
|
+
const normalized = stringField(value)?.replace(/^\/books\//, "");
|
|
500
|
+
return normalized && /^OL\d+M$/i.test(normalized) ? normalized.toUpperCase() : undefined;
|
|
501
|
+
}
|
|
502
|
+
function openLibraryAuthorIdFromKey(value) {
|
|
503
|
+
const normalized = stringField(value)?.replace(/^\/authors\//, "");
|
|
504
|
+
return normalized && /^OL\d+A$/i.test(normalized) ? normalized.toUpperCase() : undefined;
|
|
505
|
+
}
|
|
506
|
+
function coverUrl(coverId, baseUrl) {
|
|
507
|
+
if (typeof coverId !== "number" || !baseUrl) {
|
|
508
|
+
return null;
|
|
509
|
+
}
|
|
510
|
+
return `${baseUrl.replace(/\/$/, "")}/${coverId}-L.jpg`;
|
|
511
|
+
}
|
|
512
|
+
function descriptionValue(value) {
|
|
513
|
+
return typeof value === "string" ? stringField(value) : stringField(value?.value);
|
|
514
|
+
}
|
|
515
|
+
function openLibraryBookPayload(payload) {
|
|
516
|
+
const value = payload;
|
|
517
|
+
if ("work" in value && value.work) {
|
|
518
|
+
return value;
|
|
519
|
+
}
|
|
520
|
+
return { work: value };
|
|
521
|
+
}
|
|
522
|
+
function searchQuery(value) {
|
|
523
|
+
const query = stringField(value);
|
|
524
|
+
if (!query) {
|
|
525
|
+
throw new Error("Open Library search query must not be empty");
|
|
526
|
+
}
|
|
527
|
+
return query;
|
|
528
|
+
}
|
|
529
|
+
function stringField(value) {
|
|
530
|
+
const normalized = value?.trim();
|
|
531
|
+
return normalized ? normalized : undefined;
|
|
532
|
+
}
|
|
533
|
+
function numberField(value) {
|
|
534
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
535
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { SearchResponse, ZuuidData, ZuuidSearchResult } from "../../entity.js";
|
|
2
|
+
import type { SourceRecord } from "../../source.js";
|
|
3
|
+
import { type FetchOpenLibraryAuthorInput } from "./author.js";
|
|
4
|
+
import { type FetchOpenLibraryBookInput } from "./book.js";
|
|
5
|
+
import type { OpenLibraryProviderOptions, OpenLibrarySearchInput, OpenLibraryTransformOptions } from "./types.js";
|
|
6
|
+
export declare class OpenLibraryProvider {
|
|
7
|
+
readonly apiBase: string;
|
|
8
|
+
readonly coverBaseUrl: string | null;
|
|
9
|
+
private readonly fetchImpl;
|
|
10
|
+
constructor(options?: OpenLibraryProviderOptions);
|
|
11
|
+
getJson<T>(path: string, params: Record<string, string>): Promise<T | undefined>;
|
|
12
|
+
fetchBookSourceRecord(input: FetchOpenLibraryBookInput): Promise<SourceRecord | undefined>;
|
|
13
|
+
fetchBook(input: FetchOpenLibraryBookInput): Promise<ZuuidData | undefined>;
|
|
14
|
+
fetchAuthorSourceRecord(input: FetchOpenLibraryAuthorInput): Promise<SourceRecord | undefined>;
|
|
15
|
+
fetchAuthor(input: FetchOpenLibraryAuthorInput): Promise<ZuuidData | undefined>;
|
|
16
|
+
searchBookSourceRecords(input: OpenLibrarySearchInput): Promise<SearchResponse<SourceRecord>>;
|
|
17
|
+
searchBooks(input: OpenLibrarySearchInput): Promise<SearchResponse<ZuuidSearchResult>>;
|
|
18
|
+
searchAuthorSourceRecords(input: OpenLibrarySearchInput): Promise<SearchResponse<SourceRecord>>;
|
|
19
|
+
searchAuthors(input: OpenLibrarySearchInput): Promise<SearchResponse<ZuuidSearchResult>>;
|
|
20
|
+
transformOptions(): OpenLibraryTransformOptions;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/providers/openlibrary/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAKL,KAAK,2BAA2B,EACjC,MAAM,aAAa,CAAC;AACrB,OAAO,EAKL,KAAK,yBAAyB,EAC/B,MAAM,WAAW,CAAC;AAEnB,OAAO,KAAK,EAAwB,0BAA0B,EAAE,sBAAsB,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC;AAExI,qBAAa,mBAAmB;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAErC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAuB;gBAErC,OAAO,GAAE,0BAA+B;IAM9C,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAqBhF,qBAAqB,CAAC,KAAK,EAAE,yBAAyB,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC;IAI1F,SAAS,CAAC,KAAK,EAAE,yBAAyB,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAK3E,uBAAuB,CAAC,KAAK,EAAE,2BAA2B,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC;IAI9F,WAAW,CAAC,KAAK,EAAE,2BAA2B,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAK/E,uBAAuB,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC;IAI7F,WAAW,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC;IAItF,yBAAyB,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC;IAI/F,aAAa,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC;IAI9F,gBAAgB,IAAI,2BAA2B;CAKhD"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { fetchOpenLibraryAuthorSourceRecord, searchOpenLibraryAuthors, searchOpenLibraryAuthorSourceRecords, transformOpenLibraryAuthor } from "./author.js";
|
|
2
|
+
import { fetchOpenLibraryBookSourceRecord, searchOpenLibraryBooks, searchOpenLibraryBookSourceRecords, transformOpenLibraryBook } from "./book.js";
|
|
3
|
+
import { OPEN_LIBRARY_API_BASE, OPEN_LIBRARY_COVER_BASE_URL } from "./constants.js";
|
|
4
|
+
export class OpenLibraryProvider {
|
|
5
|
+
apiBase;
|
|
6
|
+
coverBaseUrl;
|
|
7
|
+
fetchImpl;
|
|
8
|
+
constructor(options = {}) {
|
|
9
|
+
this.apiBase = options.apiBase ?? OPEN_LIBRARY_API_BASE;
|
|
10
|
+
this.coverBaseUrl = options.coverBaseUrl === undefined ? OPEN_LIBRARY_COVER_BASE_URL : options.coverBaseUrl;
|
|
11
|
+
this.fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
12
|
+
}
|
|
13
|
+
async getJson(path, params) {
|
|
14
|
+
const url = new URL(`${this.apiBase.replace(/\/$/, "")}/${path.replace(/^\//, "")}`);
|
|
15
|
+
for (const [key, value] of Object.entries(params)) {
|
|
16
|
+
url.searchParams.set(key, value);
|
|
17
|
+
}
|
|
18
|
+
const headers = new Headers();
|
|
19
|
+
headers.set("accept", "application/json");
|
|
20
|
+
const response = await this.fetchImpl(url, { headers });
|
|
21
|
+
if (response.status === 404) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
if (!response.ok) {
|
|
25
|
+
const body = await response.text().catch(() => "");
|
|
26
|
+
throw new Error(`Open Library API returned ${response.status}: ${body}`);
|
|
27
|
+
}
|
|
28
|
+
return response.json();
|
|
29
|
+
}
|
|
30
|
+
async fetchBookSourceRecord(input) {
|
|
31
|
+
return fetchOpenLibraryBookSourceRecord(this, input);
|
|
32
|
+
}
|
|
33
|
+
async fetchBook(input) {
|
|
34
|
+
const source = await this.fetchBookSourceRecord(input);
|
|
35
|
+
return source ? transformOpenLibraryBook(source, this.transformOptions()) : undefined;
|
|
36
|
+
}
|
|
37
|
+
async fetchAuthorSourceRecord(input) {
|
|
38
|
+
return fetchOpenLibraryAuthorSourceRecord(this, input);
|
|
39
|
+
}
|
|
40
|
+
async fetchAuthor(input) {
|
|
41
|
+
const source = await this.fetchAuthorSourceRecord(input);
|
|
42
|
+
return source ? transformOpenLibraryAuthor(source, this.transformOptions()) : undefined;
|
|
43
|
+
}
|
|
44
|
+
async searchBookSourceRecords(input) {
|
|
45
|
+
return searchOpenLibraryBookSourceRecords(this, input);
|
|
46
|
+
}
|
|
47
|
+
async searchBooks(input) {
|
|
48
|
+
return searchOpenLibraryBooks(this, input, this.transformOptions());
|
|
49
|
+
}
|
|
50
|
+
async searchAuthorSourceRecords(input) {
|
|
51
|
+
return searchOpenLibraryAuthorSourceRecords(this, input);
|
|
52
|
+
}
|
|
53
|
+
async searchAuthors(input) {
|
|
54
|
+
return searchOpenLibraryAuthors(this, input);
|
|
55
|
+
}
|
|
56
|
+
transformOptions() {
|
|
57
|
+
return {
|
|
58
|
+
coverBaseUrl: this.coverBaseUrl
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/providers/openlibrary/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,qBAAqB,gBAAgB,CAAC;AACnD,eAAO,MAAM,qBAAqB,4BAA4B,CAAC;AAC/D,eAAO,MAAM,2BAA2B,wCAAwC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/providers/openlibrary/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type OpenLibraryFetchLike = (input: string | URL, init?: RequestInit) => Promise<Response>;
|
|
2
|
+
export type OpenLibraryProviderOptions = {
|
|
3
|
+
apiBase?: string;
|
|
4
|
+
coverBaseUrl?: string | null;
|
|
5
|
+
fetch?: OpenLibraryFetchLike;
|
|
6
|
+
};
|
|
7
|
+
export type OpenLibraryTransformOptions = {
|
|
8
|
+
coverBaseUrl?: string | null;
|
|
9
|
+
};
|
|
10
|
+
export type OpenLibrarySearchInput = {
|
|
11
|
+
query: string;
|
|
12
|
+
page?: number;
|
|
13
|
+
limit?: number;
|
|
14
|
+
language?: string;
|
|
15
|
+
fields?: string[];
|
|
16
|
+
};
|
|
17
|
+
export type OpenLibrarySearchResponse<T> = {
|
|
18
|
+
start?: number;
|
|
19
|
+
numFound?: number;
|
|
20
|
+
num_found?: number;
|
|
21
|
+
docs?: T[];
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=types.d.ts.map
|