@library-pals/isbn 1.6.0 → 2.0.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/README.md CHANGED
@@ -48,6 +48,25 @@ try {
48
48
  }
49
49
  ```
50
50
 
51
+ ### Using a Google Books API key
52
+
53
+ The Google Books API has a shared anonymous quota that can be exhausted. To
54
+ avoid `429` errors, pass your API key via `params`:
55
+
56
+ ```javascript
57
+ import Isbn from "@library-pals/isbn";
58
+
59
+ try {
60
+ const isbn = new Isbn();
61
+ const book = await isbn.resolve("9780374104092", {
62
+ params: { key: process.env.GOOGLE_BOOKS_API_KEY },
63
+ });
64
+ console.log("Book found %j", book);
65
+ } catch (err) {
66
+ console.log("Book not found", err);
67
+ }
68
+ ```
69
+
51
70
  ### Response
52
71
 
53
72
  Response follows the same schema, but some fields could depend on the service
@@ -69,12 +88,14 @@ information.
69
88
  "Fiction",
70
89
  "Fiction / General",
71
90
  "Fiction / Fantasy / General",
72
- "Fiction / Horror",
91
+ "Fiction / Horror / General",
73
92
  "Fiction / Literary",
74
93
  "Fiction / Science Fiction / General",
75
94
  "Fiction / Science Fiction / Action & Adventure",
95
+ "Fiction / Science Fiction / Apocalyptic & Post-Apocalyptic",
76
96
  "Fiction / Thrillers / Suspense",
77
- "Fiction / Dystopian"
97
+ "Fiction / Dystopian",
98
+ "Fiction / Nature & the Environment"
78
99
  ],
79
100
  "thumbnail": "http://books.google.com/books/publisher/content?id=2cl7AgAAQBAJ&printsec=frontcover&img=1&zoom=6&edge=curl&source=gbs_api",
80
101
  "link": "https://books.google.com/books/about/Annihilation.html?hl=&id=2cl7AgAAQBAJ",
@@ -141,10 +162,10 @@ information.
141
162
  "Ps3572.a4284 a84 2014",
142
163
  "813/.54"
143
164
  ],
144
- "thumbnail": "https://covers.openlibrary.org/b/id/10520611-L.jpg",
145
- "link": "https://openlibrary.org/books/OL31444108M",
146
- "publisher": "Farrar, Straus and Giroux",
147
- "publishedDate": "2014",
165
+ "thumbnail": "https://covers.openlibrary.org/b/id/12900600-L.jpg",
166
+ "link": "https://openlibrary.org/books/OL25841044M",
167
+ "publisher": "Farrar, Strauss and Giroux",
168
+ "publishedDate": "February 2, 2014",
148
169
  "language": "en",
149
170
  "isbn": "9780374104092",
150
171
  "bookProvider": "Open Library"
@@ -176,7 +197,7 @@ import Isbn from "@library-pals/isbn";
176
197
  try {
177
198
  const isbn = new Isbn();
178
199
  // This request will search ONLY in the Google Books API
179
- isbn.provider( "google"]);
200
+ isbn.provider(["google"]);
180
201
  const book = await isbn.resolve("9780374104092");
181
202
  console.log("Book isbn:" + input + " found %j", book);
182
203
  } catch (err) {
@@ -209,3 +230,26 @@ try {
209
230
  See also
210
231
  [Google Books API Terms of Service](https://developers.google.com/books/terms),
211
232
  [Open Library Licensing](https://openlibrary.org/developers/licensing)
233
+
234
+ ## Development
235
+
236
+ ### Running tests
237
+
238
+ Unit tests run without any API keys:
239
+
240
+ ```bash
241
+ npm test
242
+ ```
243
+
244
+ The end-to-end tests call the real Google Books API, which requires an API key
245
+ to avoid `429` quota errors. Set `GOOGLE_BOOKS_API_KEY` before running:
246
+
247
+ ```bash
248
+ GOOGLE_BOOKS_API_KEY=your_key npm test
249
+ ```
250
+
251
+ To update snapshots after expected output changes:
252
+
253
+ ```bash
254
+ GOOGLE_BOOKS_API_KEY=your_key npm test -- -u
255
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@library-pals/isbn",
3
- "version": "1.6.0",
3
+ "version": "2.0.0",
4
4
  "description": "Find books by ISBN",
5
5
  "exports": {
6
6
  ".": {
@@ -11,10 +11,10 @@
11
11
  "types": "./types/index.d.ts",
12
12
  "type": "module",
13
13
  "engines": {
14
- "node": ">=20.0.0"
14
+ "node": ">=22.0.0"
15
15
  },
16
16
  "scripts": {
17
- "build": "tsc src/**/*.js --declaration --allowJs --emitDeclarationOnly --outDir types",
17
+ "build": "tsc",
18
18
  "prepack": "npm run build",
19
19
  "lint": "eslint .",
20
20
  "pretest": "npm run build && npm run lint",
@@ -36,19 +36,20 @@
36
36
  "author": "Katy DeCorah <@katydecorah>",
37
37
  "license": "AGPL-3.0-or-later",
38
38
  "devDependencies": {
39
- "@eslint/js": "^9.36.0",
40
- "eslint": "^9.36.0",
41
- "eslint-plugin-jest": "^29.0.1",
42
- "eslint-plugin-jsdoc": "^60.4.1",
43
- "eslint-plugin-unicorn": "^61.0.2",
44
- "globals": "^16.4.0",
45
- "jest": "^30.1.3",
46
- "prettier": "^3.6.2",
39
+ "@eslint/js": "^10.0.1",
40
+ "baseline-browser-mapping": "^2.10.40",
41
+ "eslint": "^10.6.0",
42
+ "eslint-plugin-jest": "^29.15.3",
43
+ "eslint-plugin-jsdoc": "^63.0.10",
44
+ "eslint-plugin-unicorn": "^69.0.0",
45
+ "globals": "^17.7.0",
46
+ "jest": "^30.4.2",
47
+ "prettier": "^3.9.3",
47
48
  "prettier-2": "npm:prettier@^2",
48
- "typescript": "^5.9.2"
49
+ "typescript": "^6.0.3"
49
50
  },
50
51
  "dependencies": {
51
- "axios": "^1.12.2"
52
+ "axios": "^1.18.1"
52
53
  },
53
54
  "bugs": {
54
55
  "url": "https://github.com/library-pals/isbn/issues"
package/src/index.js CHANGED
@@ -31,7 +31,7 @@ export default class Isbn {
31
31
  /**
32
32
  * @type {Providers}
33
33
  */
34
- _providers = DEFAULT_PROVIDERS;
34
+ #providers = DEFAULT_PROVIDERS;
35
35
 
36
36
  constructor() {
37
37
  this.PROVIDER_NAMES = PROVIDER_NAMES;
@@ -62,7 +62,7 @@ export default class Isbn {
62
62
  );
63
63
  }
64
64
 
65
- this._providers = [...new Set(providers)];
65
+ this.#providers = [...new Set(providers)];
66
66
  return this;
67
67
  }
68
68
 
@@ -75,7 +75,7 @@ export default class Isbn {
75
75
  */
76
76
  async resolve(isbn, options = {}) {
77
77
  const messages = [];
78
- for (const provider of this._providers) {
78
+ for (const provider of this.#providers) {
79
79
  try {
80
80
  return await PROVIDER_RESOLVERS[provider](isbn, options);
81
81
  } catch (error) {
@@ -21,6 +21,7 @@ export const GOOGLE_BOOKS_API_BOOK = "/books/v1/volumes";
21
21
 
22
22
  export const OPENLIBRARY_API_BASE = "https://openlibrary.org";
23
23
  export const OPENLIBRARY_API_BOOK = "/isbn";
24
+ export const OPENLIBRARY_API_SEARCH = "/search.json";
24
25
 
25
26
  export const LIBROFM_API_BASE = "https://libro.fm";
26
27
  export const LIBROFM_API_BOOK = "/audiobooks";
@@ -38,9 +38,9 @@ export async function resolveGoogle(isbn, options) {
38
38
  throw new Error(`No volume info found for book with isbn: ${isbn}`);
39
39
  }
40
40
  const book = books.items[0];
41
- return await standardize(book.volumeInfo, book.id, isbn);
41
+ return await standardize(book.volumeInfo, book.id, isbn, requestOptions);
42
42
  } catch (error) {
43
- throw new Error(error.message);
43
+ throw new Error(error.message, { cause: error });
44
44
  }
45
45
  }
46
46
 
@@ -88,11 +88,12 @@ export async function resolveGoogle(isbn, options) {
88
88
  * @param {GoogleBook} book - The book object to be standardized.
89
89
  * @param {string} id - The book id.
90
90
  * @param {string} isbn - The book's ISBN.
91
+ * @param {AxiosRequestConfig} [options] - Additional options for the API request.
91
92
  * @returns {Promise<Book>} The standardized book object.
92
93
  */
93
- export async function standardize(book, id, isbn) {
94
+ export async function standardize(book, id, isbn, options = {}) {
94
95
  const { imageLinks = book.imageLinks, categories = book.categories } =
95
- await getVolume(id);
96
+ await getVolume(id, options);
96
97
 
97
98
  const standardBook = {
98
99
  title: book.title,
@@ -116,13 +117,14 @@ export async function standardize(book, id, isbn) {
116
117
  /**
117
118
  * Retrieves the volume information for a book.
118
119
  * @param {string} id - The book id.
120
+ * @param {AxiosRequestConfig} [options] - Additional options for the API request.
119
121
  * @returns {Promise<{imageLinks?: ImageLinks, categories?: string[]}>} - A promise that resolves to an array of author names.
120
122
  * @throws {Error} - If there is an error retrieving the author information.
121
123
  */
122
- export async function getVolume(id) {
124
+ export async function getVolume(id, options = {}) {
123
125
  try {
124
126
  const url = `${GOOGLE_BOOKS_API_BASE}${GOOGLE_BOOKS_API_BOOK}/${id}`;
125
- const response = await axios.get(url);
127
+ const response = await axios.get(url, options);
126
128
 
127
129
  if (response.status !== 200) {
128
130
  throw new Error(`Unable to get volume ${id}: ${response.status}`);
@@ -131,7 +133,7 @@ export async function getVolume(id) {
131
133
  ...response.data.volumeInfo,
132
134
  };
133
135
  } catch (error) {
134
- throw new Error(error.message);
136
+ throw new Error(error.message, { cause: error });
135
137
  }
136
138
  }
137
139
 
@@ -141,6 +143,8 @@ export async function getVolume(id) {
141
143
  * @returns {string|undefined} The URL of the largest thumbnail, or undefined if not found.
142
144
  */
143
145
  function getLargestThumbnail(imageLinks) {
146
+ if (!imageLinks) return;
147
+
144
148
  const sizes = [
145
149
  "extraLarge",
146
150
  "smallThumbnail",
@@ -150,9 +154,7 @@ function getLargestThumbnail(imageLinks) {
150
154
  "thumbnail",
151
155
  ];
152
156
 
153
- if (!imageLinks) return;
154
-
155
- const size = sizes.find((size) => size in imageLinks);
157
+ const size = sizes.find((size) => Object.hasOwn(imageLinks, size));
156
158
 
157
159
  // @ts-ignore
158
160
  return removeQueryParameter(imageLinks[size], "imgtk");
@@ -167,7 +169,7 @@ function getLargestThumbnail(imageLinks) {
167
169
  function removeQueryParameter(url, parameter) {
168
170
  const urlObject = new URL(url);
169
171
  urlObject.searchParams.delete(parameter);
170
- return urlObject.toString();
172
+ return urlObject.href;
171
173
  }
172
174
 
173
175
  /**
@@ -180,6 +182,6 @@ function formatCategories(categories) {
180
182
 
181
183
  const [firstCategory] = categories;
182
184
  return firstCategory.includes("/")
183
- ? [firstCategory.split("/")[0].trim(), ...categories]
185
+ ? [firstCategory.split("/", 1)[0].trim(), ...categories]
184
186
  : categories;
185
187
  }
@@ -32,7 +32,7 @@ export async function resolveLibroFm(isbn, options) {
32
32
  }
33
33
  return standardize(response.data, isbn, url);
34
34
  } catch (error) {
35
- throw new Error(error.message);
35
+ throw new Error(error.message, { cause: error });
36
36
  }
37
37
  }
38
38
 
@@ -2,7 +2,7 @@ import axios from "axios";
2
2
  import {
3
3
  defaultOptions,
4
4
  OPENLIBRARY_API_BASE,
5
- OPENLIBRARY_API_BOOK,
5
+ OPENLIBRARY_API_SEARCH,
6
6
  } from "../provider-resolvers.js";
7
7
 
8
8
  /**
@@ -10,8 +10,24 @@ import {
10
10
  * @typedef {import('axios').AxiosRequestConfig} AxiosRequestConfig
11
11
  */
12
12
 
13
+ const SEARCH_FIELDS = [
14
+ "title",
15
+ "author_name",
16
+ "number_of_pages_median",
17
+ "subject",
18
+ "cover_i",
19
+ "key",
20
+ "editions",
21
+ "editions.key",
22
+ "editions.title",
23
+ "editions.cover_i",
24
+ "editions.publisher",
25
+ "editions.publish_date",
26
+ "editions.language",
27
+ ].join(",");
28
+
13
29
  /**
14
- * Resolves a book from the Open Library API using the provided ISBN.
30
+ * Resolves a book from the Open Library Search API using the provided ISBN.
15
31
  * @param {string} isbn - The ISBN of the book.
16
32
  * @param {AxiosRequestConfig} options - Additional options for the request.
17
33
  * @returns {Promise<Book>} A promise that resolves to the standardized book object.
@@ -22,234 +38,128 @@ export async function resolveOpenLibrary(isbn, options) {
22
38
  ...defaultOptions,
23
39
  ...options,
24
40
  };
25
- const url = `${OPENLIBRARY_API_BASE}${OPENLIBRARY_API_BOOK}/${isbn}.json`;
41
+ const url = `${OPENLIBRARY_API_BASE}${OPENLIBRARY_API_SEARCH}`;
26
42
 
27
43
  try {
28
- const response = await axios.get(url, requestOptions);
44
+ const response = await axios.get(url, {
45
+ timeout: requestOptions.timeout,
46
+ params: { isbn, fields: SEARCH_FIELDS, limit: 1 },
47
+ });
29
48
  if (response.status !== 200) {
30
49
  throw new Error(`Wrong response code: ${response.status}`);
31
50
  }
32
- const book = response.data;
33
- if (!book || Object.keys(book).length === 0) {
51
+ const { docs } = response.data;
52
+ if (!docs || docs.length === 0) {
34
53
  throw new Error(`No books found with ISBN: ${isbn}`);
35
54
  }
36
- return await standardize(book, isbn);
55
+ const document = docs[0];
56
+ const description = document.key
57
+ ? await getDescription(document.key, requestOptions.timeout)
58
+ : "";
59
+ return standardize(document, isbn, description);
37
60
  } catch (error) {
38
- throw new Error(error.message);
61
+ throw new Error(error.message, { cause: error });
39
62
  }
40
63
  }
41
64
 
42
65
  /**
43
- * @typedef {object} Author
44
- * @property {string} key - The key of the author.
45
- */
46
-
47
- /**
48
- * @typedef {object} Language
49
- * @property {string} key - The key of the language.
50
- */
51
-
52
- /**
53
- * @typedef {object} Type
54
- * @property {string} key - The key of the type.
55
- */
56
-
57
- /**
58
- * @typedef {object} FirstSentence
59
- * @property {string} type - The type of the first sentence.
60
- * @property {string} value - The value of the first sentence.
61
- */
62
-
63
- /**
64
- * @typedef {object} Work
65
- * @property {string} key - The key of the work.
66
- */
67
-
68
- /**
69
- * @typedef {object} DateTime
70
- * @property {string} type - The type of the datetime.
71
- * @property {string} value - The value of the datetime.
66
+ * @typedef {object} OpenLibraryEdition
67
+ * @property {string} key - Edition key.
68
+ * @property {string} [title] - Edition title.
69
+ * @property {number} [cover_i] - Edition cover image ID.
70
+ * @property {string[]} [publisher] - Publishers.
71
+ * @property {string[]} [publish_date] - Publish dates.
72
+ * @property {string[]} [language] - ISO 639-2 language codes.
72
73
  */
73
74
 
74
75
  /**
75
- * @typedef {object} OpenLibraryBook
76
- * @property {object} identifiers - The identifiers of the book.
77
- * @property {string} title - The title of the book.
78
- * @property {Author[]} authors - The authors of the book.
79
- * @property {string} publish_date - The publish date of the book.
80
- * @property {string[]} publishers - The publishers of the book.
81
- * @property {number[]} covers - The covers of the book.
82
- * @property {string[]} contributions - The contributions to the book.
83
- * @property {Language[]} languages - The languages of the book.
84
- * @property {string[]} source_records - The source records of the book.
85
- * @property {string[]} local_id - The local IDs of the book.
86
- * @property {Type} type - The type of the book.
87
- * @property {FirstSentence} first_sentence - The first sentence of the book.
88
- * @property {string} key - The key of the book.
89
- * @property {number} number_of_pages - The number of pages in the book.
90
- * @property {Work[]} works - The works related to the book.
91
- * @property {object} classifications - The classifications of the book.
92
- * @property {string} ocaid - The Open Content Alliance ID of the book.
93
- * @property {string[]} isbn_10 - The ISBN-10 of the book.
94
- * @property {string[]} isbn_13 - The ISBN-13 of the book.
95
- * @property {number} latest_revision - The latest revision of the book.
96
- * @property {number} revision - The revision of the book.
97
- * @property {DateTime} created - The creation datetime of the book.
98
- * @property {DateTime} last_modified - The last modified datetime of the book.
76
+ * @typedef {object} OpenLibrarySearchDoc
77
+ * @property {string} title - Work title.
78
+ * @property {string[]} [author_name] - Author names.
79
+ * @property {number} [number_of_pages_median] - Median page count.
80
+ * @property {string[]} [subject] - Subjects/categories.
81
+ * @property {number} [cover_i] - Work cover image ID.
82
+ * @property {string} key - Work key.
83
+ * @property {{docs: OpenLibraryEdition[]}} [editions] - Matched edition data.
99
84
  */
100
85
 
101
86
  /**
102
- * Standardizes a book object by extracting relevant information from the provided book object.
103
- * @param {OpenLibraryBook} book - The book object to be standardized.
104
- * @param {string} isbn - The book's isbn.
105
- * @returns {Promise<Book>} - The standardized book object.
87
+ * Standardizes a search result doc into a Book object.
88
+ * @param {OpenLibrarySearchDoc} document - The search result doc.
89
+ * @param {string} isbn - The book's ISBN.
90
+ * @param {string} [description] - The book's description.
91
+ * @returns {Book} The standardized book object.
106
92
  */
107
- export async function standardize(book, isbn) {
108
- const { description, subjects, rawAuthors } = await getWorks(book);
109
- const authors = await getAuthors(rawAuthors);
110
- const standardBook = {
111
- title: book.title,
112
- authors,
113
- description: handleDescription(description),
114
- pageCount: book.number_of_pages,
93
+ export function standardize(document, isbn, description = "") {
94
+ const edition = document.editions?.docs?.[0];
95
+ const coverId = edition?.cover_i || document.cover_i;
96
+ return {
97
+ title: edition?.title || document.title,
98
+ authors: document.author_name || [],
99
+ description,
100
+ pageCount: document.number_of_pages_median,
115
101
  format: "book",
116
- categories: subjects,
117
- thumbnail: `https://covers.openlibrary.org/b/id/${book.covers[0]}-L.jpg`,
118
- link: book.key
119
- ? `${OPENLIBRARY_API_BASE}${book.key}`
120
- : `${OPENLIBRARY_API_BASE}${OPENLIBRARY_API_BOOK}/${isbn}`,
121
- publisher: book.publishers?.join(", "),
122
- publishedDate: book.publish_date,
123
- language: formatLanguage(book.languages),
102
+ categories: document.subject || [],
103
+ thumbnail: coverId
104
+ ? `https://covers.openlibrary.org/b/id/${coverId}-L.jpg`
105
+ : undefined,
106
+ link: edition?.key
107
+ ? `${OPENLIBRARY_API_BASE}${edition.key}`
108
+ : (document.key
109
+ ? `${OPENLIBRARY_API_BASE}${document.key}`
110
+ : `${OPENLIBRARY_API_BASE}/isbn/${isbn}`),
111
+ publisher: edition?.publisher?.[0],
112
+ publishedDate: edition?.publish_date?.[0],
113
+ language: formatLanguage(edition?.language),
124
114
  isbn,
125
115
  bookProvider: "Open Library",
126
116
  };
127
-
128
- return standardBook;
129
- }
130
-
131
- /**
132
- * Handles the description of the book.
133
- * @param {string|object} description - The description of the book.
134
- * @returns {string} - The processed description.
135
- */
136
- function handleDescription(description) {
137
- if (typeof description === "string") {
138
- return description;
139
- }
140
- return description.value;
141
- }
142
-
143
- /**
144
- * Retrieves the author names from OpenLibrary.
145
- * @param {{key: string}[]} rawAuthors - List of author keys.
146
- * @returns {Promise<string[]>} - List of author names.
147
- */
148
- export async function getAuthors(rawAuthors) {
149
- const promises = rawAuthors
150
- .filter((author) => author && author.key)
151
- .map((author) =>
152
- axios
153
- .get(`https://openlibrary.org/${author.key}.json`)
154
- .then((response) => {
155
- if (response.status !== 200) {
156
- throw new Error(
157
- `Unable to get author ${author.key}: ${response.status}`,
158
- );
159
- }
160
- return response.data && response.data.name;
161
- }),
162
- );
163
-
164
- try {
165
- return await Promise.all(promises);
166
- } catch (error) {
167
- throw new Error(error.message);
168
- }
169
117
  }
170
118
 
171
119
  /**
172
- * @typedef {object} OpenLibraryResponse
173
- * @property {string} description - The description of the book.
174
- * @property {string[]} subjects - The subjects of the book.
175
- * @property {{author: {key: string}}[]} authors - The authors of the book.
120
+ * Fetches the description for a work from the Open Library works endpoint.
121
+ * @param {string} workKey - The work key (e.g. "/works/OL45804W").
122
+ * @param {number} [timeout] - Request timeout in milliseconds.
123
+ * @returns {Promise<string>} The description, or an empty string if unavailable.
176
124
  */
177
-
178
- /**
179
- * Retrieves the description of the book from OpenLibrary.
180
- * @param {OpenLibraryBook} book - The book object from OpenLibrary.
181
- * @returns {Promise<{description: string, subjects: string[], rawAuthors: {key: string}[]}>} - Description of the book.
182
- */
183
- export async function getWorks(book) {
184
- const defaultResponse = {
185
- description: "",
186
- subjects: [],
187
- rawAuthors: [],
188
- };
189
-
190
- if (!book.works) {
191
- return defaultResponse;
192
- }
193
-
194
- const [work] = book.works;
195
-
196
- if (!work || !work.key) {
197
- return defaultResponse;
198
- }
199
-
125
+ export async function getDescription(workKey, timeout) {
200
126
  try {
201
- const response = await axios.get(
202
- `https://openlibrary.org/${work.key}.json`,
203
- );
204
-
205
- if (response.status !== 200) {
206
- throw new Error(`Unable to get ${work.key}: ${response.status}`);
207
- }
208
-
209
- /** @type {OpenLibraryResponse} */
210
- const data = response.data;
211
-
212
- return {
213
- description: data.description || "",
214
- subjects: data.subjects || [],
215
- rawAuthors: data.authors?.map((a) => a.author) || [],
216
- };
217
- } catch (error) {
218
- throw new Error(error.message);
127
+ const response = await axios.get(`${OPENLIBRARY_API_BASE}${workKey}.json`, {
128
+ timeout,
129
+ });
130
+ if (response.status !== 200) return "";
131
+ const { description } = response.data;
132
+ if (!description) return "";
133
+ if (typeof description === "string") return description;
134
+ return description.value || "";
135
+ } catch {
136
+ return "";
219
137
  }
220
138
  }
221
139
 
222
140
  /**
223
- * Formats the language codes from Open Library API to their corresponding ISO 639-1 codes.
224
- * @param {Language[]} languages - An array of language codes from Open Library API.
225
- * @returns {string | undefined} - A new language map object with ISO 639-1 codes as keys and language codes as values.
141
+ * Formats ISO 639-2 language codes to ISO 639-1.
142
+ * @param {string[]} [languages] - Array of ISO 639-2 language codes.
143
+ * @returns {string | undefined} ISO 639-1 code, or undefined if not mapped.
226
144
  */
227
145
  function formatLanguage(languages) {
228
- if (!languages || languages.length === 0) {
229
- return;
230
- }
231
- /**
232
- * Mapping of Open Library language codes to their corresponding language names.
233
- * https://openlibrary.org/languages.json
234
- * @type {{ [key: string]: string } } - A new language map object with ISO 639-1 codes as keys and language codes as values.
235
- */
236
- const newLanguageMap = {
237
- "/languages/eng": "en",
238
- "/languages/spa": "es",
239
- "/languages/fre": "fr",
240
- "/languages/ger": "de",
241
- "/languages/rus": "ru",
242
- "/languages/ita": "it",
243
- "/languages/chi": "zh",
244
- "/languages/jpn": "ja",
245
- "/languages/por": "pt",
246
- "/languages/ara": "ar",
247
- "/languages/heb": "he",
248
- "/languages/kor": "ko",
249
- "/languages/pol": "pl",
250
- "/languages/dut": "nl",
251
- "/languages/lat": "la",
146
+ if (!languages || languages.length === 0) return;
147
+ const languageMap = {
148
+ eng: "en",
149
+ spa: "es",
150
+ fre: "fr",
151
+ ger: "de",
152
+ rus: "ru",
153
+ ita: "it",
154
+ chi: "zh",
155
+ jpn: "ja",
156
+ por: "pt",
157
+ ara: "ar",
158
+ heb: "he",
159
+ kor: "ko",
160
+ pol: "pl",
161
+ dut: "nl",
162
+ lat: "la",
252
163
  };
253
-
254
- return newLanguageMap[languages[0].key] || undefined;
164
+ return languageMap[languages[0]] || undefined;
255
165
  }
package/types/index.d.ts CHANGED
@@ -20,10 +20,6 @@
20
20
  * @typedef {import('axios').AxiosRequestConfig} AxiosRequestConfig
21
21
  */
22
22
  export default class Isbn {
23
- /**
24
- * @type {Providers}
25
- */
26
- _providers: Providers;
27
23
  PROVIDER_NAMES: {
28
24
  GOOGLE: string;
29
25
  OPENLIBRARY: string;
@@ -45,6 +41,7 @@ export default class Isbn {
45
41
  * @throws {Error} - If an error occurs while resolving the book information.
46
42
  */
47
43
  resolve(isbn: string, options?: AxiosRequestConfig): Promise<Book>;
44
+ #private;
48
45
  }
49
46
  export type Book = {
50
47
  /**
@@ -66,7 +63,7 @@ export type Book = {
66
63
  /**
67
64
  * - The number of pages in the book.
68
65
  */
69
- pageCount?: number;
66
+ pageCount?: number | undefined;
70
67
  /**
71
68
  * - The format of the book.
72
69
  */
@@ -94,7 +91,7 @@ export type Book = {
94
91
  /**
95
92
  * - The link of the book.
96
93
  */
97
- link?: string;
94
+ link?: string | undefined;
98
95
  /**
99
96
  * - The provider of the book information.
100
97
  */
@@ -102,7 +99,8 @@ export type Book = {
102
99
  /**
103
100
  * - The duration of the audiobook.
104
101
  */
105
- duration?: string;
102
+ duration?: string | undefined;
106
103
  };
107
104
  export type Providers = import("./provider-resolvers.js").Providers;
108
105
  export type AxiosRequestConfig = import("axios").AxiosRequestConfig;
106
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;GAgBG;AAEH;;;GAGG;AAEH;IAOI;;;;MAAoC;IAGtC;;;;;;OAMG;IACH,oBALW,MAAM,EAAE,GACN,MAAM,CAwBlB;IAED;;;;;;OAMG;IACH,cALW,MAAM,YACN,kBAAkB,GAChB,OAAO,CAAC,IAAI,CAAC,CAkBzB;;CACF;;;;;UAnFa,MAAM;;;;WACN,MAAM;;;;aACN,MAAM,EAAE;;;;iBACR,MAAM;;;;;;;;YAEN,MAAM;;;;gBACN,MAAM,EAAE;;;;eACR,MAAM;;;;mBACN,MAAM;;;;eACN,MAAM,GAAG,SAAS;;;;gBAClB,MAAM,GAAG,SAAS;;;;;;;;kBAElB,MAAM;;;;;;wBAKP,OAAO,yBAAyB,EAAE,SAAS;iCAC3C,OAAO,OAAO,EAAE,kBAAkB"}
@@ -12,6 +12,7 @@ export const GOOGLE_BOOKS_API_BASE: "https://www.googleapis.com";
12
12
  export const GOOGLE_BOOKS_API_BOOK: "/books/v1/volumes";
13
13
  export const OPENLIBRARY_API_BASE: "https://openlibrary.org";
14
14
  export const OPENLIBRARY_API_BOOK: "/isbn";
15
+ export const OPENLIBRARY_API_SEARCH: "/search.json";
15
16
  export const LIBROFM_API_BASE: "https://libro.fm";
16
17
  export const LIBROFM_API_BOOK: "/audiobooks";
17
18
  export namespace PROVIDER_NAMES {
@@ -34,3 +35,4 @@ export type AxiosRequestConfig = import("axios").AxiosRequestConfig;
34
35
  import { resolveGoogle } from "./providers/google.js";
35
36
  import { resolveOpenLibrary } from "./providers/open-library.js";
36
37
  import { resolveLibroFm } from "./providers/librofm.js";
38
+ //# sourceMappingURL=provider-resolvers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-resolvers.d.ts","sourceRoot":"","sources":["../src/provider-resolvers.js"],"names":[],"mappings":"AAIA;;;GAGG;AAEH;;;;GAIG;AACH,6BAHU,kBAAkB,CAK1B;AAEF,oCAAqC,4BAA4B,CAAC;AAClE,oCAAqC,mBAAmB,CAAC;AAEzD,mCAAoC,yBAAyB,CAAC;AAC9D,mCAAoC,OAAO,CAAC;AAC5C,qCAAsC,cAAc,CAAC;AAErD,+BAAgC,kBAAkB,CAAC;AACnD,+BAAgC,aAAa,CAAC;;;;;;AAQ9C;;;GAGG;AACH,gCAFU,SAAS,CAMjB;AAEF;IACE,CAAC,cAAc,CAAC,MAAM,CAAC,uBAAe;IACtC,CAAC,cAAc,CAAC,WAAW,CAAC,4BAAoB;IAChD,CAAC,cAAc,CAAC,OAAO,CAAC,wBAAgB;EACxC;wBA3CW,MAAM,EAAE;iCACR,OAAO,OAAO,EAAE,kBAAkB;8BANjB,uBAAuB;mCAClB,6BAA6B;+BACjC,wBAAwB"}
@@ -52,16 +52,18 @@ export function resolveGoogle(isbn: string, options: AxiosRequestConfig): Promis
52
52
  * @param {GoogleBook} book - The book object to be standardized.
53
53
  * @param {string} id - The book id.
54
54
  * @param {string} isbn - The book's ISBN.
55
+ * @param {AxiosRequestConfig} [options] - Additional options for the API request.
55
56
  * @returns {Promise<Book>} The standardized book object.
56
57
  */
57
- export function standardize(book: GoogleBook, id: string, isbn: string): Promise<Book>;
58
+ export function standardize(book: GoogleBook, id: string, isbn: string, options?: AxiosRequestConfig): Promise<Book>;
58
59
  /**
59
60
  * Retrieves the volume information for a book.
60
61
  * @param {string} id - The book id.
62
+ * @param {AxiosRequestConfig} [options] - Additional options for the API request.
61
63
  * @returns {Promise<{imageLinks?: ImageLinks, categories?: string[]}>} - A promise that resolves to an array of author names.
62
64
  * @throws {Error} - If there is an error retrieving the author information.
63
65
  */
64
- export function getVolume(id: string): Promise<{
66
+ export function getVolume(id: string, options?: AxiosRequestConfig): Promise<{
65
67
  imageLinks?: ImageLinks;
66
68
  categories?: string[];
67
69
  }>;
@@ -71,27 +73,27 @@ export type ImageLinks = {
71
73
  /**
72
74
  * - extraLarge
73
75
  */
74
- extraLarge?: string;
76
+ extraLarge?: string | undefined;
75
77
  /**
76
78
  * - large
77
79
  */
78
- large?: string;
80
+ large?: string | undefined;
79
81
  /**
80
82
  * - medium
81
83
  */
82
- medium?: string;
84
+ medium?: string | undefined;
83
85
  /**
84
86
  * - small
85
87
  */
86
- small?: string;
88
+ small?: string | undefined;
87
89
  /**
88
90
  * - thumbnail
89
91
  */
90
- thumbnail?: string;
92
+ thumbnail?: string | undefined;
91
93
  /**
92
94
  * - smallThumbnail
93
95
  */
94
- smallThumbnail?: string;
96
+ smallThumbnail?: string | undefined;
95
97
  };
96
98
  export type GoogleBook = {
97
99
  /**
@@ -165,7 +167,7 @@ export type GoogleBook = {
165
167
  /**
166
168
  * - The image links of the book.
167
169
  */
168
- imageLinks?: ImageLinks;
170
+ imageLinks?: ImageLinks | undefined;
169
171
  /**
170
172
  * - The language of the book.
171
173
  */
@@ -195,3 +197,4 @@ export type GoogleBook = {
195
197
  */
196
198
  searchInfo: object;
197
199
  };
200
+ //# sourceMappingURL=google.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google.d.ts","sourceRoot":"","sources":["../../src/providers/google.js"],"names":[],"mappings":"AAOA;;;GAGG;AAEH;;;;;;GAMG;AACH,oCALW,MAAM,WACN,kBAAkB,GAChB,OAAO,CAAC,IAAI,CAAC,CA4BzB;AAED;;;;;;;;GAQG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH;;;;;;;GAOG;AACH,kCANW,UAAU,MACV,MAAM,QACN,MAAM,YACN,kBAAkB,GAChB,OAAO,CAAC,IAAI,CAAC,CAuBzB;AAED;;;;;;GAMG;AACH,8BALW,MAAM,YACN,kBAAkB,GAChB,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,UAAU,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;CAAC,CAAC,CAiBrE;mBAjIY,OAAO,aAAa,EAAE,IAAI;iCAC1B,OAAO,OAAO,EAAE,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAiDjC,MAAM;;;;cACN,MAAM;;;;aACN,MAAM,EAAE;;;;eACR,MAAM;;;;mBACN,MAAM;;;;iBACN,MAAM;;;;yBACN,MAAM,EAAE;;;;kBACR,MAAM;;;;eACN,MAAM;;;;eACN,MAAM;;;;gBACN,MAAM,EAAE;;;;mBACR,MAAM;;;;kBACN,MAAM;;;;oBACN,MAAM;;;;sBACN,OAAO;;;;oBACP,MAAM;;;;yBACN,MAAM;;;;;;;;cAEN,MAAM;;;;iBACN,MAAM;;;;cACN,MAAM;;;;yBACN,MAAM;;;;cACN,MAAM;;;;gBACN,MAAM;;;;gBACN,MAAM"}
@@ -121,3 +121,4 @@ export type Audiobook = {
121
121
  */
122
122
  workExample: object;
123
123
  };
124
+ //# sourceMappingURL=librofm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"librofm.d.ts","sourceRoot":"","sources":["../../src/providers/librofm.js"],"names":[],"mappings":"AAOA;;;GAGG;AAEH;;;;;;GAMG;AACH,qCALW,MAAM,WACN,kBAAkB,GAChB,OAAO,CAAC,IAAI,CAAC,CAoBzB;AAED;;;;;;GAMG;AACH,kCALW,MAAM,QACN,MAAM,OACN,MAAM,GACJ,OAAO,CAAC,IAAI,CAAC,CAiCzB;AAED;;;GAGG;AACH;;;;;;;;;;;;;;;;;;GAkBG;AAEH;;;;GAIG;AACH,+CAHW,MAAM,GACJ,MAAM,CAiBlB;mBAlHY,OAAO,aAAa,EAAE,IAAI;iCAC1B,OAAO,OAAO,EAAE,kBAAkB;;;;;UAuEjC,MAAM;;;;;;SAIN,MAAM;;;;gBACN,MAAM;;;;UACN,MAAM;;;;iBACN,MAAM;;;;UACN,MAAM;;;;WACN,MAAM;;;;cACN,MAAM;;;;YACN,MAAM,EAAE;;;;YACR,MAAM,EAAE;;;;eACR,MAAM;;;;mBACN,MAAM;;;;gBACN,MAAM;;;;cACN,MAAM;;;;oBACN,MAAM,EAAE;;;;YACR,MAAM;;;;iBACN,MAAM"}
@@ -1,9 +1,5 @@
1
1
  /**
2
- * @typedef {import('../index.js').Book} Book
3
- * @typedef {import('axios').AxiosRequestConfig} AxiosRequestConfig
4
- */
5
- /**
6
- * Resolves a book from the Open Library API using the provided ISBN.
2
+ * Resolves a book from the Open Library Search API using the provided ISBN.
7
3
  * @param {string} isbn - The ISBN of the book.
8
4
  * @param {AxiosRequestConfig} options - Additional options for the request.
9
5
  * @returns {Promise<Book>} A promise that resolves to the standardized book object.
@@ -11,245 +7,97 @@
11
7
  */
12
8
  export function resolveOpenLibrary(isbn: string, options: AxiosRequestConfig): Promise<Book>;
13
9
  /**
14
- * @typedef {object} Author
15
- * @property {string} key - The key of the author.
16
- */
17
- /**
18
- * @typedef {object} Language
19
- * @property {string} key - The key of the language.
20
- */
21
- /**
22
- * @typedef {object} Type
23
- * @property {string} key - The key of the type.
24
- */
25
- /**
26
- * @typedef {object} FirstSentence
27
- * @property {string} type - The type of the first sentence.
28
- * @property {string} value - The value of the first sentence.
29
- */
30
- /**
31
- * @typedef {object} Work
32
- * @property {string} key - The key of the work.
33
- */
34
- /**
35
- * @typedef {object} DateTime
36
- * @property {string} type - The type of the datetime.
37
- * @property {string} value - The value of the datetime.
10
+ * @typedef {object} OpenLibraryEdition
11
+ * @property {string} key - Edition key.
12
+ * @property {string} [title] - Edition title.
13
+ * @property {number} [cover_i] - Edition cover image ID.
14
+ * @property {string[]} [publisher] - Publishers.
15
+ * @property {string[]} [publish_date] - Publish dates.
16
+ * @property {string[]} [language] - ISO 639-2 language codes.
38
17
  */
39
18
  /**
40
- * @typedef {object} OpenLibraryBook
41
- * @property {object} identifiers - The identifiers of the book.
42
- * @property {string} title - The title of the book.
43
- * @property {Author[]} authors - The authors of the book.
44
- * @property {string} publish_date - The publish date of the book.
45
- * @property {string[]} publishers - The publishers of the book.
46
- * @property {number[]} covers - The covers of the book.
47
- * @property {string[]} contributions - The contributions to the book.
48
- * @property {Language[]} languages - The languages of the book.
49
- * @property {string[]} source_records - The source records of the book.
50
- * @property {string[]} local_id - The local IDs of the book.
51
- * @property {Type} type - The type of the book.
52
- * @property {FirstSentence} first_sentence - The first sentence of the book.
53
- * @property {string} key - The key of the book.
54
- * @property {number} number_of_pages - The number of pages in the book.
55
- * @property {Work[]} works - The works related to the book.
56
- * @property {object} classifications - The classifications of the book.
57
- * @property {string} ocaid - The Open Content Alliance ID of the book.
58
- * @property {string[]} isbn_10 - The ISBN-10 of the book.
59
- * @property {string[]} isbn_13 - The ISBN-13 of the book.
60
- * @property {number} latest_revision - The latest revision of the book.
61
- * @property {number} revision - The revision of the book.
62
- * @property {DateTime} created - The creation datetime of the book.
63
- * @property {DateTime} last_modified - The last modified datetime of the book.
19
+ * @typedef {object} OpenLibrarySearchDoc
20
+ * @property {string} title - Work title.
21
+ * @property {string[]} [author_name] - Author names.
22
+ * @property {number} [number_of_pages_median] - Median page count.
23
+ * @property {string[]} [subject] - Subjects/categories.
24
+ * @property {number} [cover_i] - Work cover image ID.
25
+ * @property {string} key - Work key.
26
+ * @property {{docs: OpenLibraryEdition[]}} [editions] - Matched edition data.
64
27
  */
65
28
  /**
66
- * Standardizes a book object by extracting relevant information from the provided book object.
67
- * @param {OpenLibraryBook} book - The book object to be standardized.
68
- * @param {string} isbn - The book's isbn.
69
- * @returns {Promise<Book>} - The standardized book object.
29
+ * Standardizes a search result doc into a Book object.
30
+ * @param {OpenLibrarySearchDoc} document - The search result doc.
31
+ * @param {string} isbn - The book's ISBN.
32
+ * @param {string} [description] - The book's description.
33
+ * @returns {Book} The standardized book object.
70
34
  */
71
- export function standardize(book: OpenLibraryBook, isbn: string): Promise<Book>;
35
+ export function standardize(document: OpenLibrarySearchDoc, isbn: string, description?: string): Book;
72
36
  /**
73
- * Retrieves the author names from OpenLibrary.
74
- * @param {{key: string}[]} rawAuthors - List of author keys.
75
- * @returns {Promise<string[]>} - List of author names.
37
+ * Fetches the description for a work from the Open Library works endpoint.
38
+ * @param {string} workKey - The work key (e.g. "/works/OL45804W").
39
+ * @param {number} [timeout] - Request timeout in milliseconds.
40
+ * @returns {Promise<string>} The description, or an empty string if unavailable.
76
41
  */
77
- export function getAuthors(rawAuthors: {
78
- key: string;
79
- }[]): Promise<string[]>;
80
- /**
81
- * @typedef {object} OpenLibraryResponse
82
- * @property {string} description - The description of the book.
83
- * @property {string[]} subjects - The subjects of the book.
84
- * @property {{author: {key: string}}[]} authors - The authors of the book.
85
- */
86
- /**
87
- * Retrieves the description of the book from OpenLibrary.
88
- * @param {OpenLibraryBook} book - The book object from OpenLibrary.
89
- * @returns {Promise<{description: string, subjects: string[], rawAuthors: {key: string}[]}>} - Description of the book.
90
- */
91
- export function getWorks(book: OpenLibraryBook): Promise<{
92
- description: string;
93
- subjects: string[];
94
- rawAuthors: {
95
- key: string;
96
- }[];
97
- }>;
98
- export type Book = import("../index.js").Book;
99
- export type AxiosRequestConfig = import("axios").AxiosRequestConfig;
100
- export type Author = {
101
- /**
102
- * - The key of the author.
103
- */
104
- key: string;
105
- };
106
- export type Language = {
107
- /**
108
- * - The key of the language.
109
- */
110
- key: string;
111
- };
112
- export type Type = {
42
+ export function getDescription(workKey: string, timeout?: number): Promise<string>;
43
+ export type OpenLibraryEdition = {
113
44
  /**
114
- * - The key of the type.
45
+ * - Edition key.
115
46
  */
116
47
  key: string;
117
- };
118
- export type FirstSentence = {
119
48
  /**
120
- * - The type of the first sentence.
49
+ * - Edition title.
121
50
  */
122
- type: string;
51
+ title?: string | undefined;
123
52
  /**
124
- * - The value of the first sentence.
53
+ * - Edition cover image ID.
125
54
  */
126
- value: string;
127
- };
128
- export type Work = {
55
+ cover_i?: number | undefined;
129
56
  /**
130
- * - The key of the work.
57
+ * - Publishers.
131
58
  */
132
- key: string;
133
- };
134
- export type DateTime = {
59
+ publisher?: string[] | undefined;
135
60
  /**
136
- * - The type of the datetime.
61
+ * - Publish dates.
137
62
  */
138
- type: string;
63
+ publish_date?: string[] | undefined;
139
64
  /**
140
- * - The value of the datetime.
65
+ * - ISO 639-2 language codes.
141
66
  */
142
- value: string;
67
+ language?: string[] | undefined;
143
68
  };
144
- export type OpenLibraryBook = {
69
+ export type OpenLibrarySearchDoc = {
145
70
  /**
146
- * - The identifiers of the book.
147
- */
148
- identifiers: object;
149
- /**
150
- * - The title of the book.
71
+ * - Work title.
151
72
  */
152
73
  title: string;
153
74
  /**
154
- * - The authors of the book.
155
- */
156
- authors: Author[];
157
- /**
158
- * - The publish date of the book.
159
- */
160
- publish_date: string;
161
- /**
162
- * - The publishers of the book.
163
- */
164
- publishers: string[];
165
- /**
166
- * - The covers of the book.
167
- */
168
- covers: number[];
169
- /**
170
- * - The contributions to the book.
171
- */
172
- contributions: string[];
173
- /**
174
- * - The languages of the book.
175
- */
176
- languages: Language[];
177
- /**
178
- * - The source records of the book.
75
+ * - Author names.
179
76
  */
180
- source_records: string[];
77
+ author_name?: string[] | undefined;
181
78
  /**
182
- * - The local IDs of the book.
79
+ * - Median page count.
183
80
  */
184
- local_id: string[];
81
+ number_of_pages_median?: number | undefined;
185
82
  /**
186
- * - The type of the book.
83
+ * - Subjects/categories.
187
84
  */
188
- type: Type;
85
+ subject?: string[] | undefined;
189
86
  /**
190
- * - The first sentence of the book.
87
+ * - Work cover image ID.
191
88
  */
192
- first_sentence: FirstSentence;
89
+ cover_i?: number | undefined;
193
90
  /**
194
- * - The key of the book.
91
+ * - Work key.
195
92
  */
196
93
  key: string;
197
94
  /**
198
- * - The number of pages in the book.
199
- */
200
- number_of_pages: number;
201
- /**
202
- * - The works related to the book.
203
- */
204
- works: Work[];
205
- /**
206
- * - The classifications of the book.
207
- */
208
- classifications: object;
209
- /**
210
- * - The Open Content Alliance ID of the book.
211
- */
212
- ocaid: string;
213
- /**
214
- * - The ISBN-10 of the book.
215
- */
216
- isbn_10: string[];
217
- /**
218
- * - The ISBN-13 of the book.
219
- */
220
- isbn_13: string[];
221
- /**
222
- * - The latest revision of the book.
223
- */
224
- latest_revision: number;
225
- /**
226
- * - The revision of the book.
227
- */
228
- revision: number;
229
- /**
230
- * - The creation datetime of the book.
231
- */
232
- created: DateTime;
233
- /**
234
- * - The last modified datetime of the book.
95
+ * - Matched edition data.
235
96
  */
236
- last_modified: DateTime;
237
- };
238
- export type OpenLibraryResponse = {
239
- /**
240
- * - The description of the book.
241
- */
242
- description: string;
243
- /**
244
- * - The subjects of the book.
245
- */
246
- subjects: string[];
247
- /**
248
- * - The authors of the book.
249
- */
250
- authors: {
251
- author: {
252
- key: string;
253
- };
254
- }[];
97
+ editions?: {
98
+ docs: OpenLibraryEdition[];
99
+ } | undefined;
255
100
  };
101
+ export type Book = import("../index.js").Book;
102
+ export type AxiosRequestConfig = import("axios").AxiosRequestConfig;
103
+ //# sourceMappingURL=open-library.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open-library.d.ts","sourceRoot":"","sources":["../../src/providers/open-library.js"],"names":[],"mappings":"AA4BA;;;;;;GAMG;AACH,yCALW,MAAM,WACN,kBAAkB,GAChB,OAAO,CAAC,IAAI,CAAC,CA8BzB;AAED;;;;;;;;GAQG;AAEH;;;;;;;;;GASG;AAEH;;;;;;GAMG;AACH,sCALW,oBAAoB,QACpB,MAAM,gBACN,MAAM,GACJ,IAAI,CA0BhB;AAED;;;;;GAKG;AACH,wCAJW,MAAM,YACN,MAAM,GACJ,OAAO,CAAC,MAAM,CAAC,CAe3B;;;;;SAvEa,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;WAUN,MAAM;;;;;;;;;;;;;;;;;;;;SAKN,MAAM;;;;;cACC,kBAAkB,EAAE;;;mBA1E5B,OAAO,aAAa,EAAE,IAAI;iCAC1B,OAAO,OAAO,EAAE,kBAAkB"}