@zonuexe/techbook-mcp 0.2.4 → 0.3.2
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 +45 -1
- package/README.md +16 -2
- package/dist/adapters/http/fetch-client.d.ts.map +1 -1
- package/dist/adapters/http/fetch-client.js +18 -1
- package/dist/adapters/http/fetch-client.js.map +1 -1
- package/dist/adapters/openbd.d.ts.map +1 -1
- package/dist/adapters/openbd.js +18 -5
- package/dist/adapters/openbd.js.map +1 -1
- package/dist/adapters/publishers/base.d.ts +2 -1
- package/dist/adapters/publishers/base.d.ts.map +1 -1
- package/dist/adapters/publishers/base.js +8 -3
- package/dist/adapters/publishers/base.js.map +1 -1
- package/dist/adapters/publishers/cq-publishing.d.ts +3 -0
- package/dist/adapters/publishers/cq-publishing.d.ts.map +1 -0
- package/dist/adapters/publishers/cq-publishing.js +120 -0
- package/dist/adapters/publishers/cq-publishing.js.map +1 -0
- package/dist/adapters/publishers/google-books.d.ts.map +1 -1
- package/dist/adapters/publishers/google-books.js +1 -0
- package/dist/adapters/publishers/google-books.js.map +1 -1
- package/dist/adapters/publishers/isbn-publisher-codes.js +1 -1
- package/dist/adapters/publishers/isbn-publisher-codes.js.map +1 -1
- package/dist/adapters/publishers/juse-p.js +2 -2
- package/dist/adapters/publishers/juse-p.js.map +1 -1
- package/dist/adapters/publishers/leanpub.d.ts +3 -0
- package/dist/adapters/publishers/leanpub.d.ts.map +1 -0
- package/dist/adapters/publishers/leanpub.js +96 -0
- package/dist/adapters/publishers/leanpub.js.map +1 -0
- package/dist/adapters/publishers/oreilly-japan.d.ts.map +1 -1
- package/dist/adapters/publishers/oreilly-japan.js +8 -2
- package/dist/adapters/publishers/oreilly-japan.js.map +1 -1
- package/dist/adapters/publishers/peaks.d.ts.map +1 -1
- package/dist/adapters/publishers/peaks.js +3 -2
- package/dist/adapters/publishers/peaks.js.map +1 -1
- package/dist/adapters/publishers/personal-media.d.ts.map +1 -1
- package/dist/adapters/publishers/personal-media.js +3 -2
- package/dist/adapters/publishers/personal-media.js.map +1 -1
- package/dist/adapters/publishers/pragprog.d.ts +3 -0
- package/dist/adapters/publishers/pragprog.d.ts.map +1 -0
- package/dist/adapters/publishers/pragprog.js +120 -0
- package/dist/adapters/publishers/pragprog.js.map +1 -0
- package/dist/adapters/publishers/registry.d.ts.map +1 -1
- package/dist/adapters/publishers/registry.js +6 -0
- package/dist/adapters/publishers/registry.js.map +1 -1
- package/dist/adapters/publishers/seshop.d.ts.map +1 -1
- package/dist/adapters/publishers/seshop.js +58 -32
- package/dist/adapters/publishers/seshop.js.map +1 -1
- package/dist/adapters/publishers/techbookfest.d.ts.map +1 -1
- package/dist/adapters/publishers/techbookfest.js +2 -1
- package/dist/adapters/publishers/techbookfest.js.map +1 -1
- package/dist/application/concurrency.d.ts +16 -0
- package/dist/application/concurrency.d.ts.map +1 -0
- package/dist/application/concurrency.js +42 -0
- package/dist/application/concurrency.js.map +1 -0
- package/dist/application/get-book-by-isbn.d.ts +0 -9
- package/dist/application/get-book-by-isbn.d.ts.map +1 -1
- package/dist/application/get-book-by-isbn.js +44 -6
- package/dist/application/get-book-by-isbn.js.map +1 -1
- package/dist/application/get-book-detail.d.ts.map +1 -1
- package/dist/application/get-book-detail.js +3 -0
- package/dist/application/get-book-detail.js.map +1 -1
- package/dist/application/search-books.d.ts +16 -5
- package/dist/application/search-books.d.ts.map +1 -1
- package/dist/application/search-books.js +46 -9
- package/dist/application/search-books.js.map +1 -1
- package/dist/domain/authors.d.ts +7 -0
- package/dist/domain/authors.d.ts.map +1 -0
- package/dist/domain/authors.js +22 -0
- package/dist/domain/authors.js.map +1 -0
- package/dist/domain/book.d.ts +2 -0
- package/dist/domain/book.d.ts.map +1 -1
- package/dist/domain/isbn.d.ts +8 -0
- package/dist/domain/isbn.d.ts.map +1 -0
- package/dist/domain/isbn.js +16 -0
- package/dist/domain/isbn.js.map +1 -0
- package/dist/domain/publisher.d.ts +16 -0
- package/dist/domain/publisher.d.ts.map +1 -1
- package/dist/domain/text-match.d.ts +32 -0
- package/dist/domain/text-match.d.ts.map +1 -0
- package/dist/domain/text-match.js +84 -0
- package/dist/domain/text-match.js.map +1 -0
- package/dist/main.js +0 -0
- package/dist/mcp/server.d.ts +6 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +40 -4
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +9 -1
- package/dist/mcp/tools.js.map +1 -1
- package/dist/version.d.ts +9 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +9 -0
- package/dist/version.js.map +1 -0
- package/docs/design-doc.md +127 -7
- package/package.json +14 -15
- package/flake.lock +0 -61
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"techbookfest.js","sourceRoot":"","sources":["../../../src/adapters/publishers/techbookfest.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"techbookfest.js","sourceRoot":"","sources":["../../../src/adapters/publishers/techbookfest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** withTimeout が時間切れで投げる専用エラー */
|
|
2
|
+
export declare class TimeoutError extends Error {
|
|
3
|
+
constructor(ms: number);
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* promise が ms 以内に解決しなければ TimeoutError で reject する。
|
|
7
|
+
* 元の fetch 自体はキャンセルされない(解決すればキャッシュに載るので無駄にならない)。
|
|
8
|
+
*/
|
|
9
|
+
export declare function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T>;
|
|
10
|
+
/**
|
|
11
|
+
* items を最大 limit 並列で fn に通し、入力順に揃えた結果を返す。
|
|
12
|
+
* 各要素は fulfilled / rejected に振り分けられ、1件の失敗が全体を止めない。
|
|
13
|
+
* (JS は単一スレッドなのでインデックス採番に競合はない)
|
|
14
|
+
*/
|
|
15
|
+
export declare function mapWithConcurrency<T, R>(items: readonly T[], limit: number, fn: (item: T, index: number) => Promise<R>): Promise<PromiseSettledResult<R>[]>;
|
|
16
|
+
//# sourceMappingURL=concurrency.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"concurrency.d.ts","sourceRoot":"","sources":["../../src/application/concurrency.ts"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,qBAAa,YAAa,SAAQ,KAAK;gBACzB,EAAE,EAAE,MAAM;CAIvB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAM1E;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAC3C,KAAK,EAAE,SAAS,CAAC,EAAE,EACnB,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,GACzC,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAAC,EAAE,CAAC,CAgBpC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** withTimeout が時間切れで投げる専用エラー */
|
|
2
|
+
export class TimeoutError extends Error {
|
|
3
|
+
constructor(ms) {
|
|
4
|
+
super(`タイムアウト (${ms}ms)`);
|
|
5
|
+
this.name = "TimeoutError";
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* promise が ms 以内に解決しなければ TimeoutError で reject する。
|
|
10
|
+
* 元の fetch 自体はキャンセルされない(解決すればキャッシュに載るので無駄にならない)。
|
|
11
|
+
*/
|
|
12
|
+
export function withTimeout(promise, ms) {
|
|
13
|
+
let timer;
|
|
14
|
+
const timeout = new Promise((_, reject) => {
|
|
15
|
+
timer = setTimeout(() => reject(new TimeoutError(ms)), ms);
|
|
16
|
+
});
|
|
17
|
+
return Promise.race([promise, timeout]).finally(() => clearTimeout(timer));
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* items を最大 limit 並列で fn に通し、入力順に揃えた結果を返す。
|
|
21
|
+
* 各要素は fulfilled / rejected に振り分けられ、1件の失敗が全体を止めない。
|
|
22
|
+
* (JS は単一スレッドなのでインデックス採番に競合はない)
|
|
23
|
+
*/
|
|
24
|
+
export async function mapWithConcurrency(items, limit, fn) {
|
|
25
|
+
const results = [];
|
|
26
|
+
let next = 0;
|
|
27
|
+
const worker = async () => {
|
|
28
|
+
while (next < items.length) {
|
|
29
|
+
const i = next++;
|
|
30
|
+
try {
|
|
31
|
+
results[i] = { status: "fulfilled", value: await fn(items[i], i) };
|
|
32
|
+
}
|
|
33
|
+
catch (reason) {
|
|
34
|
+
results[i] = { status: "rejected", reason };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const workerCount = Math.max(1, Math.min(limit, items.length));
|
|
39
|
+
await Promise.all(Array.from({ length: workerCount }, worker));
|
|
40
|
+
return results;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=concurrency.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"concurrency.js","sourceRoot":"","sources":["../../src/application/concurrency.ts"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,MAAM,OAAO,YAAa,SAAQ,KAAK;IACrC,YAAY,EAAU;QACpB,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAI,OAAmB,EAAE,EAAU;IAC5D,IAAI,KAAoC,CAAC;IACzC,MAAM,OAAO,GAAG,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;QAC/C,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,YAAY,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAe,CAAC;AAC3F,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAAmB,EACnB,KAAa,EACb,EAA0C;IAE1C,MAAM,OAAO,GAA8B,EAAE,CAAC;IAC9C,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,MAAM,MAAM,GAAG,KAAK,IAAmB,EAAE;QACvC,OAAO,IAAI,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;YAC3B,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;YACjB,IAAI,CAAC;gBACH,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;YACrE,CAAC;YAAC,OAAO,MAAM,EAAE,CAAC;gBAChB,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;YAC9C,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IACF,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAC/D,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;IAC/D,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
import type { BookRecord } from "../domain/book.js";
|
|
2
2
|
import type { PublisherAdapter, PublisherDeps } from "../domain/publisher.js";
|
|
3
|
-
/**
|
|
4
|
-
* ISBNから書籍情報を取得する。
|
|
5
|
-
*
|
|
6
|
-
* 1. openBD で書誌情報と出版社ストアリンクを取得する
|
|
7
|
-
* 2. ストアリンクが既知アダプターと一致する場合は出版社サイトから詳細取得を試みる
|
|
8
|
-
* 3. ISBN出版者記号から対応アダプターを特定し、出版社サイトで検索して詳細取得を試みる
|
|
9
|
-
* 4. 取得できない場合は openBD データをそのまま返す
|
|
10
|
-
* 5. openBD にも存在しない場合はカーリルから書誌情報を取得する(廃業出版社など)
|
|
11
|
-
*/
|
|
12
3
|
export declare function getBookByIsbn(isbn: string, publishers: readonly PublisherAdapter[], deps: PublisherDeps): Promise<BookRecord>;
|
|
13
4
|
//# sourceMappingURL=get-book-by-isbn.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-book-by-isbn.d.ts","sourceRoot":"","sources":["../../src/application/get-book-by-isbn.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"get-book-by-isbn.d.ts","sourceRoot":"","sources":["../../src/application/get-book-by-isbn.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AA8C9E,wBAAsB,aAAa,CACjC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,SAAS,gBAAgB,EAAE,EACvC,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,UAAU,CAAC,CAyDrB"}
|
|
@@ -2,6 +2,7 @@ import { checkRobotsTxt } from "../adapters/publishers/base.js";
|
|
|
2
2
|
import { fetchOpenBDBooks, openBDEntryToBookRecord } from "../adapters/openbd.js";
|
|
3
3
|
import { fetchCalilBook } from "../adapters/calil.js";
|
|
4
4
|
import { findAdapterIdByIsbn } from "../adapters/publishers/isbn-publisher-codes.js";
|
|
5
|
+
import { dedupeAuthors } from "../domain/authors.js";
|
|
5
6
|
/**
|
|
6
7
|
* ISBNから書籍情報を取得する。
|
|
7
8
|
*
|
|
@@ -9,17 +10,54 @@ import { findAdapterIdByIsbn } from "../adapters/publishers/isbn-publisher-codes
|
|
|
9
10
|
* 2. ストアリンクが既知アダプターと一致する場合は出版社サイトから詳細取得を試みる
|
|
10
11
|
* 3. ISBN出版者記号から対応アダプターを特定し、出版社サイトで検索して詳細取得を試みる
|
|
11
12
|
* 4. 取得できない場合は openBD データをそのまま返す
|
|
12
|
-
* 5. openBD
|
|
13
|
+
* 5. openBD にも存在しない場合:
|
|
14
|
+
* a. ISBN から詳細URLを決定的に引けるアダプター(detailUrlForIsbn)で詳細取得を試みる
|
|
15
|
+
* (O'Reilly の電子書籍専売・販売終了の旧刊救済。openBD/カーリル未収録のため)
|
|
16
|
+
* b. それも不可ならカーリルから書誌情報を取得する(廃業出版社など)
|
|
13
17
|
*/
|
|
18
|
+
/**
|
|
19
|
+
* ISBN出版者記号からアダプターを特定し、detailUrlForIsbn で構成した詳細ページから直接取得する。
|
|
20
|
+
* openBD・カーリル・検索一覧いずれにも出ない旧刊(販売終了・電子書籍専売)の救済経路。
|
|
21
|
+
* 取得できなければ undefined を返す。
|
|
22
|
+
*/
|
|
23
|
+
async function fetchDetailByIsbnCode(isbn, publishers, deps) {
|
|
24
|
+
const adapterId = findAdapterIdByIsbn(isbn);
|
|
25
|
+
if (!adapterId)
|
|
26
|
+
return undefined;
|
|
27
|
+
const publisher = publishers.find(p => p.id === adapterId);
|
|
28
|
+
const url = publisher?.detailUrlForIsbn?.(isbn);
|
|
29
|
+
if (!publisher || !url)
|
|
30
|
+
return undefined;
|
|
31
|
+
if (!(await checkRobotsTxt(url, deps)))
|
|
32
|
+
return undefined;
|
|
33
|
+
try {
|
|
34
|
+
const book = await publisher.getDetail(url, deps);
|
|
35
|
+
book.language ??= publisher.language ?? "ja";
|
|
36
|
+
return book;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
14
42
|
export async function getBookByIsbn(isbn, publishers, deps) {
|
|
15
43
|
const normalizedIsbn = isbn.replace(/-/g, "");
|
|
44
|
+
// 言語の既定を刻み、著者の重複を除く(openBD/カーリル/国内出版社はいずれも日本語)
|
|
45
|
+
const stamp = (book, lang = "ja") => {
|
|
46
|
+
book.language ??= lang;
|
|
47
|
+
book.authors = dedupeAuthors(book.authors);
|
|
48
|
+
return book;
|
|
49
|
+
};
|
|
16
50
|
const openBDMap = await fetchOpenBDBooks([normalizedIsbn], deps);
|
|
17
51
|
const entry = openBDMap.get(normalizedIsbn);
|
|
18
52
|
if (!entry) {
|
|
19
|
-
// openBD
|
|
53
|
+
// openBD 未収録でも、ISBN から詳細URLを決定的に引けるアダプター(O'Reilly 旧刊等)を先に試す
|
|
54
|
+
const byAdapter = await fetchDetailByIsbnCode(normalizedIsbn, publishers, deps);
|
|
55
|
+
if (byAdapter)
|
|
56
|
+
return stamp(byAdapter);
|
|
57
|
+
// それも不可ならカーリルをフォールバックとして試みる(廃業出版社など)
|
|
20
58
|
const calilBook = await fetchCalilBook(normalizedIsbn, deps);
|
|
21
59
|
if (calilBook)
|
|
22
|
-
return calilBook;
|
|
60
|
+
return stamp(calilBook);
|
|
23
61
|
throw new Error(`書誌情報が見つかりません: ${isbn}`);
|
|
24
62
|
}
|
|
25
63
|
// hanmoto.storelink が既知アダプターの baseUrl と前方一致する場合は
|
|
@@ -31,7 +69,7 @@ export async function getBookByIsbn(isbn, publishers, deps) {
|
|
|
31
69
|
const allowed = await checkRobotsTxt(storelink, deps);
|
|
32
70
|
if (allowed) {
|
|
33
71
|
try {
|
|
34
|
-
return await publisher.getDetail(storelink, deps);
|
|
72
|
+
return stamp(await publisher.getDetail(storelink, deps), publisher.language ?? "ja");
|
|
35
73
|
}
|
|
36
74
|
catch {
|
|
37
75
|
// 出版社サイトからの取得失敗は無視して次のフォールバックへ
|
|
@@ -49,13 +87,13 @@ export async function getBookByIsbn(isbn, publishers, deps) {
|
|
|
49
87
|
const matched = results.find(r => r.isbn && r.isbn.replace(/-/g, "") === normalizedIsbn)
|
|
50
88
|
?? results[0];
|
|
51
89
|
if (matched)
|
|
52
|
-
return matched;
|
|
90
|
+
return stamp(matched, publisher.language ?? "ja");
|
|
53
91
|
}
|
|
54
92
|
catch {
|
|
55
93
|
// 出版社サイトからの取得失敗は無視して openBD データで返す
|
|
56
94
|
}
|
|
57
95
|
}
|
|
58
96
|
}
|
|
59
|
-
return openBDEntryToBookRecord(entry);
|
|
97
|
+
return stamp(openBDEntryToBookRecord(entry));
|
|
60
98
|
}
|
|
61
99
|
//# sourceMappingURL=get-book-by-isbn.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-book-by-isbn.js","sourceRoot":"","sources":["../../src/application/get-book-by-isbn.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAClF,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gDAAgD,CAAC;
|
|
1
|
+
{"version":3,"file":"get-book-by-isbn.js","sourceRoot":"","sources":["../../src/application/get-book-by-isbn.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-book-detail.d.ts","sourceRoot":"","sources":["../../src/application/get-book-detail.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"get-book-detail.d.ts","sourceRoot":"","sources":["../../src/application/get-book-detail.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAK9E,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,SAAS,gBAAgB,EAAE,EACvC,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,UAAU,CAAC,CAgCrB"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { checkRobotsTxt } from "../adapters/publishers/base.js";
|
|
2
2
|
import { fetchOpenBDBooks, enrichWithOpenBD } from "../adapters/openbd.js";
|
|
3
|
+
import { dedupeAuthors } from "../domain/authors.js";
|
|
3
4
|
export async function getBookDetail(url, publishers, deps) {
|
|
4
5
|
const publisher = publishers.find(p => url.startsWith(p.baseUrl));
|
|
5
6
|
if (!publisher) {
|
|
@@ -11,6 +12,8 @@ export async function getBookDetail(url, publishers, deps) {
|
|
|
11
12
|
throw new Error(`robots.txt によりアクセスが禁止されています: ${url}`);
|
|
12
13
|
}
|
|
13
14
|
const book = await publisher.getDetail(url, deps);
|
|
15
|
+
book.language ??= publisher.language ?? "ja";
|
|
16
|
+
book.authors = dedupeAuthors(book.authors);
|
|
14
17
|
// ISBNが特定できる場合はopenBDで欠損フィールドを補完
|
|
15
18
|
if (book.isbn !== undefined) {
|
|
16
19
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-book-detail.js","sourceRoot":"","sources":["../../src/application/get-book-detail.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"get-book-detail.js","sourceRoot":"","sources":["../../src/application/get-book-detail.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC3E,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,GAAW,EACX,UAAuC,EACvC,IAAmB;IAEnB,MAAM,SAAS,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAClE,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CACb,6BAA6B,GAAG,IAAI;YACpC,UAAU,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACtD,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAChD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,gCAAgC,GAAG,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAClD,IAAI,CAAC,QAAQ,KAAK,SAAS,CAAC,QAAQ,IAAI,IAAI,CAAC;IAC7C,IAAI,CAAC,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAE3C,iCAAiC;IACjC,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC5B,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,MAAM,gBAAgB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;YAC5D,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACvC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,OAAO,gBAAgB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YACvC,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,mCAAmC;QACrC,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
import type { BookRecord, SearchQuery } from "../domain/book.js";
|
|
2
2
|
import type { PublisherAdapter, PublisherDeps } from "../domain/publisher.js";
|
|
3
|
+
/** 1出版社あたりの検索タイムアウト。遅い1社が全体をブロックしないための上限 */
|
|
4
|
+
export declare const SEARCH_TIMEOUT_MS = 12000;
|
|
5
|
+
/** 同時に叩く出版社サイト数の上限(サイト負荷・レート制限への配慮) */
|
|
6
|
+
export declare const SEARCH_CONCURRENCY = 6;
|
|
7
|
+
/** 検索結果の書籍。クエリとの一致度 matchScore(0..1)付き */
|
|
8
|
+
export type ScoredBook = BookRecord & {
|
|
9
|
+
matchScore: number;
|
|
10
|
+
};
|
|
11
|
+
export type SearchErrorType = "robots" | "timeout" | "http" | "other";
|
|
12
|
+
export interface SearchError {
|
|
13
|
+
publisherId: string;
|
|
14
|
+
type: SearchErrorType;
|
|
15
|
+
message: string;
|
|
16
|
+
}
|
|
3
17
|
export interface SearchBooksResult {
|
|
4
|
-
books:
|
|
5
|
-
errors:
|
|
6
|
-
publisherId: string;
|
|
7
|
-
message: string;
|
|
8
|
-
}>;
|
|
18
|
+
books: ScoredBook[];
|
|
19
|
+
errors: SearchError[];
|
|
9
20
|
}
|
|
10
21
|
export declare function searchBooks(query: SearchQuery, publishers: readonly PublisherAdapter[], deps: PublisherDeps): Promise<SearchBooksResult>;
|
|
11
22
|
//# sourceMappingURL=search-books.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search-books.d.ts","sourceRoot":"","sources":["../../src/application/search-books.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"search-books.d.ts","sourceRoot":"","sources":["../../src/application/search-books.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAO9E,4CAA4C;AAC5C,eAAO,MAAM,iBAAiB,QAAS,CAAC;AACxC,uCAAuC;AACvC,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC,0CAA0C;AAC1C,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7D,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;AAEtE,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,eAAe,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB;AAWD,wBAAsB,WAAW,CAC/B,KAAK,EAAE,WAAW,EAClB,UAAU,EAAE,SAAS,gBAAgB,EAAE,EACvC,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,iBAAiB,CAAC,CAoE5B"}
|
|
@@ -1,28 +1,53 @@
|
|
|
1
1
|
import { checkRobotsTxt } from "../adapters/publishers/base.js";
|
|
2
2
|
import { fetchOpenBDBooks, enrichWithOpenBD } from "../adapters/openbd.js";
|
|
3
|
+
import { matchScore } from "../domain/text-match.js";
|
|
4
|
+
import { dedupeAuthors } from "../domain/authors.js";
|
|
5
|
+
import { mapWithConcurrency, withTimeout, TimeoutError } from "./concurrency.js";
|
|
6
|
+
/** 1出版社あたりの検索タイムアウト。遅い1社が全体をブロックしないための上限 */
|
|
7
|
+
export const SEARCH_TIMEOUT_MS = 12_000;
|
|
8
|
+
/** 同時に叩く出版社サイト数の上限(サイト負荷・レート制限への配慮) */
|
|
9
|
+
export const SEARCH_CONCURRENCY = 6;
|
|
10
|
+
/** 失敗理由を種別に分類する(errors の静音化・集約のため) */
|
|
11
|
+
function classifyError(reason) {
|
|
12
|
+
if (reason instanceof TimeoutError)
|
|
13
|
+
return { type: "timeout", message: reason.message };
|
|
14
|
+
const message = reason instanceof Error ? reason.message : String(reason);
|
|
15
|
+
if (message.includes("robots.txt"))
|
|
16
|
+
return { type: "robots", message };
|
|
17
|
+
if (/^HTTP \d/.test(message))
|
|
18
|
+
return { type: "http", message };
|
|
19
|
+
return { type: "other", message };
|
|
20
|
+
}
|
|
3
21
|
export async function searchBooks(query, publishers, deps) {
|
|
4
|
-
const
|
|
22
|
+
const matched = query.publisherId
|
|
5
23
|
? publishers.filter(p => p.id === query.publisherId)
|
|
6
24
|
: publishers;
|
|
7
|
-
|
|
25
|
+
// 大規模出版社を先に、小規模・専門サイト (scale: "minor") を後に回す。
|
|
26
|
+
// 並列度が限られるなか、ヒット率の高い大手にスロットを優先的に割り当てる。
|
|
27
|
+
const targets = [
|
|
28
|
+
...matched.filter(p => p.scale !== "minor"),
|
|
29
|
+
...matched.filter(p => p.scale === "minor"),
|
|
30
|
+
];
|
|
31
|
+
const results = await mapWithConcurrency(targets, SEARCH_CONCURRENCY, async (p) => {
|
|
8
32
|
const allowed = await checkRobotsTxt(p.baseUrl, deps);
|
|
9
33
|
if (!allowed)
|
|
10
34
|
throw new Error(`robots.txt によりアクセスが禁止されています: ${p.baseUrl}`);
|
|
11
|
-
return p.search(query, deps);
|
|
12
|
-
})
|
|
35
|
+
return withTimeout(p.search(query, deps), SEARCH_TIMEOUT_MS);
|
|
36
|
+
});
|
|
13
37
|
const books = [];
|
|
14
38
|
const errors = [];
|
|
15
39
|
for (let i = 0; i < results.length; i++) {
|
|
16
40
|
const result = results[i];
|
|
17
41
|
const publisher = targets[i];
|
|
18
42
|
if (result.status === "fulfilled") {
|
|
43
|
+
// 言語が未設定の書籍に出版社の既定言語(省略時 "ja")を刻む
|
|
44
|
+
for (const book of result.value) {
|
|
45
|
+
book.language ??= publisher.language ?? "ja";
|
|
46
|
+
}
|
|
19
47
|
books.push(...result.value);
|
|
20
48
|
}
|
|
21
49
|
else {
|
|
22
|
-
|
|
23
|
-
? result.reason.message
|
|
24
|
-
: String(result.reason);
|
|
25
|
-
errors.push({ publisherId: publisher.id, message });
|
|
50
|
+
errors.push({ publisherId: publisher.id, ...classifyError(result.reason) });
|
|
26
51
|
}
|
|
27
52
|
}
|
|
28
53
|
// ISBNが特定できる書籍をopenBDで一括補完
|
|
@@ -44,6 +69,18 @@ export async function searchBooks(query, publishers, deps) {
|
|
|
44
69
|
// openBD の取得失敗は無視して出版社から取得できた情報を返す
|
|
45
70
|
}
|
|
46
71
|
}
|
|
47
|
-
|
|
72
|
+
// クエリとの一致度を付与し、著者の重複を除いて、ベストマッチ順に並べる
|
|
73
|
+
const hasQuery = Boolean(query.title || query.author);
|
|
74
|
+
const scored = books
|
|
75
|
+
.map(book => ({
|
|
76
|
+
...book,
|
|
77
|
+
authors: dedupeAuthors(book.authors),
|
|
78
|
+
matchScore: Math.round(matchScore(query, book) * 1000) / 1000,
|
|
79
|
+
}))
|
|
80
|
+
// クエリ語があるのに一致度ゼロ=検索サイトが返す新着フォールバック等の無関係本。
|
|
81
|
+
// 「該当なし」と「誤ヒット」を呼び出し側が区別できるよう、ここで除外して空にする。
|
|
82
|
+
.filter(book => !hasQuery || book.matchScore > 0)
|
|
83
|
+
.sort((a, b) => b.matchScore - a.matchScore);
|
|
84
|
+
return { books: scored, errors };
|
|
48
85
|
}
|
|
49
86
|
//# sourceMappingURL=search-books.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search-books.js","sourceRoot":"","sources":["../../src/application/search-books.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"search-books.js","sourceRoot":"","sources":["../../src/application/search-books.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authors.d.ts","sourceRoot":"","sources":["../../src/domain/authors.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAYlE"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { normalizeForMatch } from "./text-match.js";
|
|
2
|
+
/**
|
|
3
|
+
* 著者配列から重複を除く(出現順を保持)。
|
|
4
|
+
* 比較キーは normalizeForMatch なので "吉川 邦夫" と "吉川邦夫" のような表記ゆれも同一視する。
|
|
5
|
+
* 表示は最初に現れた表記を採用する。空文字は除去する。
|
|
6
|
+
*/
|
|
7
|
+
export function dedupeAuthors(authors) {
|
|
8
|
+
const seen = new Set();
|
|
9
|
+
const out = [];
|
|
10
|
+
for (const author of authors) {
|
|
11
|
+
const name = author.trim();
|
|
12
|
+
if (!name)
|
|
13
|
+
continue;
|
|
14
|
+
const key = normalizeForMatch(name);
|
|
15
|
+
if (key === "" || seen.has(key))
|
|
16
|
+
continue;
|
|
17
|
+
seen.add(key);
|
|
18
|
+
out.push(name);
|
|
19
|
+
}
|
|
20
|
+
return out;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=authors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authors.js","sourceRoot":"","sources":["../../src/domain/authors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEpD;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,OAA0B;IACtD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;QAC3B,IAAI,CAAC,IAAI;YAAE,SAAS;QACpB,MAAM,GAAG,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,GAAG,KAAK,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QAC1C,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACd,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/dist/domain/book.d.ts
CHANGED
|
@@ -15,10 +15,12 @@ export interface BookRecord {
|
|
|
15
15
|
authors: string[];
|
|
16
16
|
publisher: string;
|
|
17
17
|
publishedAt?: string;
|
|
18
|
+
language?: string;
|
|
18
19
|
isbn?: string;
|
|
19
20
|
asin?: string;
|
|
20
21
|
url: string;
|
|
21
22
|
price?: number;
|
|
23
|
+
currency?: string;
|
|
22
24
|
coverImageUrl?: string;
|
|
23
25
|
description?: string;
|
|
24
26
|
tags?: string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"book.d.ts","sourceRoot":"","sources":["../../src/domain/book.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,cAAc,GAAG,KAAK,CAAC;AAEjE,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,OAAO,CAAC;CACd;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"}
|
|
1
|
+
{"version":3,"file":"book.d.ts","sourceRoot":"","sources":["../../src/domain/book.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,cAAc,GAAG,KAAK,CAAC;AAEjE,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,OAAO,CAAC;CACd;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** ハイフン・空白・"ISBN" 接頭辞を除去して数字列(ISBN-10 は末尾 X 可)に正規化する */
|
|
2
|
+
export declare function normalizeIsbn(text: string): string;
|
|
3
|
+
/**
|
|
4
|
+
* 文字列が ISBN-10 / ISBN-13 として妥当な形か判定する(桁数・接頭辞のみ。チェックディジットは検証しない)。
|
|
5
|
+
* search_books の title に ISBN が入力されたケースを ISBN 経路へ振り分けるために使う。
|
|
6
|
+
*/
|
|
7
|
+
export declare function looksLikeIsbn(text: string): boolean;
|
|
8
|
+
//# sourceMappingURL=isbn.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isbn.d.ts","sourceRoot":"","sources":["../../src/domain/isbn.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKlD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGnD"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** ハイフン・空白・"ISBN" 接頭辞を除去して数字列(ISBN-10 は末尾 X 可)に正規化する */
|
|
2
|
+
export function normalizeIsbn(text) {
|
|
3
|
+
return text
|
|
4
|
+
.replace(/isbn/i, "")
|
|
5
|
+
.replace(/[\s-]/g, "")
|
|
6
|
+
.toUpperCase();
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* 文字列が ISBN-10 / ISBN-13 として妥当な形か判定する(桁数・接頭辞のみ。チェックディジットは検証しない)。
|
|
10
|
+
* search_books の title に ISBN が入力されたケースを ISBN 経路へ振り分けるために使う。
|
|
11
|
+
*/
|
|
12
|
+
export function looksLikeIsbn(text) {
|
|
13
|
+
const s = normalizeIsbn(text);
|
|
14
|
+
return /^(?:97[89]\d{10}|\d{9}[\dX])$/.test(s);
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=isbn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isbn.js","sourceRoot":"","sources":["../../src/domain/isbn.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,IAAI;SACR,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;SACpB,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;SACrB,WAAW,EAAE,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,MAAM,CAAC,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IAC9B,OAAO,+BAA+B,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACjD,CAAC"}
|
|
@@ -11,7 +11,23 @@ export interface PublisherAdapter {
|
|
|
11
11
|
readonly id: string;
|
|
12
12
|
readonly name: string;
|
|
13
13
|
readonly baseUrl: string;
|
|
14
|
+
/** この出版社の書籍の既定言語(ISO 639-1)。省略時はアプリ層で "ja" とみなす */
|
|
15
|
+
readonly language?: string;
|
|
16
|
+
/**
|
|
17
|
+
* 検索時のスケジューリング/キャッシュ戦略のヒント。
|
|
18
|
+
* - 省略 = 大規模出版社(優先的にスケジュールし、通常 TTL でキャッシュ)
|
|
19
|
+
* - "minor" = 小規模・専門/ローカルフィルタ型(大規模の後に回し、カタログを長 TTL で全キャッシュ)
|
|
20
|
+
*/
|
|
21
|
+
readonly scale?: "minor";
|
|
14
22
|
search(query: SearchQuery, deps: PublisherDeps): Promise<BookRecord[]>;
|
|
15
23
|
getDetail(url: string, deps: PublisherDeps): Promise<BookRecord>;
|
|
24
|
+
/**
|
|
25
|
+
* ISBN から詳細ページの URL を決定的に構成できるアダプター向け(任意)。
|
|
26
|
+
* 詳細ページが ISBN ベースの安定 URL を持つサイト(例 O'Reilly の `/books/{isbn}/`)で実装する。
|
|
27
|
+
* openBD・カーリル未収録かつ検索一覧にも出ない旧刊(販売終了・電子書籍専売)を
|
|
28
|
+
* `get_book_by_isbn` のフォールバックで直接引くために使う([[docs/design-doc.md「カバレッジの制約」]])。
|
|
29
|
+
* URL を構成できない ISBN は undefined を返す。
|
|
30
|
+
*/
|
|
31
|
+
detailUrlForIsbn?(isbn: string): string | undefined;
|
|
16
32
|
}
|
|
17
33
|
//# sourceMappingURL=publisher.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publisher.d.ts","sourceRoot":"","sources":["../../src/domain/publisher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,UAAU,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;IACvE,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"publisher.d.ts","sourceRoot":"","sources":["../../src/domain/publisher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,UAAU,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,mDAAmD;IACnD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,MAAM,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;IACvE,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACjE;;;;;;OAMG;IACH,gBAAgB,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACrD"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { BookRecord, SearchQuery } from "./book.js";
|
|
2
|
+
/**
|
|
3
|
+
* 照合用にテキストを正規化する。
|
|
4
|
+
*
|
|
5
|
+
* - NFKC で全角・半角を統一("Rust" → "Rust"、"(" → "(")
|
|
6
|
+
* - 小文字化
|
|
7
|
+
* - 装飾・区切り記号(【】〔〕()()[]、・ー― ~: 空白など)を除去
|
|
8
|
+
*
|
|
9
|
+
* PDF奥付から抜いた表記ゆれのある title/author を、出版社サイトの
|
|
10
|
+
* 表記とできるだけ一致させるための前処理。
|
|
11
|
+
*/
|
|
12
|
+
export declare function normalizeForMatch(text: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* クエリ書名と候補書名の一致度(0..1)。
|
|
15
|
+
*
|
|
16
|
+
* 正規化後に完全一致すれば 1。そうでなければクエリを空白でトークン分割し、
|
|
17
|
+
* 候補書名に含まれるトークンの割合でスコアを付ける(完全一致を上回らないよう 0.9 で頭打ち)。
|
|
18
|
+
* トークン方式により純日本語の部分文字列("メール技術 教科書" → "メール技術の教科書")も拾える。
|
|
19
|
+
* どのトークンも含まれなければ 0 = 無関係(フォールバック書籍の除外に使う)。
|
|
20
|
+
*/
|
|
21
|
+
export declare function titleMatchScore(query: string, candidate: string): number;
|
|
22
|
+
/** クエリ著者名と候補著者リストの一致度(0..1)。最も一致する1名を採用 */
|
|
23
|
+
export declare function authorMatchScore(query: string, candidates: readonly string[]): number;
|
|
24
|
+
/**
|
|
25
|
+
* クエリと書籍レコードの総合一致度(0..1)。
|
|
26
|
+
*
|
|
27
|
+
* - title・author 両方が指定された場合は平均
|
|
28
|
+
* - 片方のみ指定ならそのスコア
|
|
29
|
+
* - どちらも未指定なら 0(スコア付けの意味がない)
|
|
30
|
+
*/
|
|
31
|
+
export declare function matchScore(query: SearchQuery, book: BookRecord): number;
|
|
32
|
+
//# sourceMappingURL=text-match.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-match.d.ts","sourceRoot":"","sources":["../../src/domain/text-match.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAEzD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMtD;AAcD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAWxE;AAED,2CAA2C;AAC3C,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CASrF;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,CAMvE"}
|