@zonuexe/techbook-mcp 0.2.3 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/CHANGELOG.md +52 -1
  2. package/README.md +54 -22
  3. package/dist/adapters/http/fetch-client.d.ts.map +1 -1
  4. package/dist/adapters/http/fetch-client.js +18 -1
  5. package/dist/adapters/http/fetch-client.js.map +1 -1
  6. package/dist/adapters/openbd.d.ts.map +1 -1
  7. package/dist/adapters/openbd.js +18 -5
  8. package/dist/adapters/openbd.js.map +1 -1
  9. package/dist/adapters/publishers/base.d.ts +2 -1
  10. package/dist/adapters/publishers/base.d.ts.map +1 -1
  11. package/dist/adapters/publishers/base.js +8 -3
  12. package/dist/adapters/publishers/base.js.map +1 -1
  13. package/dist/adapters/publishers/cq-publishing.d.ts +3 -0
  14. package/dist/adapters/publishers/cq-publishing.d.ts.map +1 -0
  15. package/dist/adapters/publishers/cq-publishing.js +120 -0
  16. package/dist/adapters/publishers/cq-publishing.js.map +1 -0
  17. package/dist/adapters/publishers/google-books.d.ts +4 -0
  18. package/dist/adapters/publishers/google-books.d.ts.map +1 -0
  19. package/dist/adapters/publishers/google-books.js +76 -0
  20. package/dist/adapters/publishers/google-books.js.map +1 -0
  21. package/dist/adapters/publishers/isbn-publisher-codes.d.ts +21 -0
  22. package/dist/adapters/publishers/isbn-publisher-codes.d.ts.map +1 -0
  23. package/dist/adapters/publishers/isbn-publisher-codes.js +49 -0
  24. package/dist/adapters/publishers/isbn-publisher-codes.js.map +1 -0
  25. package/dist/adapters/publishers/juse-p.d.ts +3 -0
  26. package/dist/adapters/publishers/juse-p.d.ts.map +1 -0
  27. package/dist/adapters/publishers/juse-p.js +110 -0
  28. package/dist/adapters/publishers/juse-p.js.map +1 -0
  29. package/dist/adapters/publishers/leanpub.d.ts +3 -0
  30. package/dist/adapters/publishers/leanpub.d.ts.map +1 -0
  31. package/dist/adapters/publishers/leanpub.js +96 -0
  32. package/dist/adapters/publishers/leanpub.js.map +1 -0
  33. package/dist/adapters/publishers/oreilly-japan.d.ts.map +1 -1
  34. package/dist/adapters/publishers/oreilly-japan.js +8 -2
  35. package/dist/adapters/publishers/oreilly-japan.js.map +1 -1
  36. package/dist/adapters/publishers/peaks.d.ts.map +1 -1
  37. package/dist/adapters/publishers/peaks.js +3 -2
  38. package/dist/adapters/publishers/peaks.js.map +1 -1
  39. package/dist/adapters/publishers/personal-media.d.ts.map +1 -1
  40. package/dist/adapters/publishers/personal-media.js +3 -2
  41. package/dist/adapters/publishers/personal-media.js.map +1 -1
  42. package/dist/adapters/publishers/pragprog.d.ts +3 -0
  43. package/dist/adapters/publishers/pragprog.d.ts.map +1 -0
  44. package/dist/adapters/publishers/pragprog.js +120 -0
  45. package/dist/adapters/publishers/pragprog.js.map +1 -0
  46. package/dist/adapters/publishers/registry.d.ts.map +1 -1
  47. package/dist/adapters/publishers/registry.js +10 -0
  48. package/dist/adapters/publishers/registry.js.map +1 -1
  49. package/dist/adapters/publishers/techbookfest.d.ts.map +1 -1
  50. package/dist/adapters/publishers/techbookfest.js +2 -1
  51. package/dist/adapters/publishers/techbookfest.js.map +1 -1
  52. package/dist/application/concurrency.d.ts +16 -0
  53. package/dist/application/concurrency.d.ts.map +1 -0
  54. package/dist/application/concurrency.js +42 -0
  55. package/dist/application/concurrency.js.map +1 -0
  56. package/dist/application/get-book-by-isbn.d.ts +0 -8
  57. package/dist/application/get-book-by-isbn.d.ts.map +1 -1
  58. package/dist/application/get-book-by-isbn.js +64 -7
  59. package/dist/application/get-book-by-isbn.js.map +1 -1
  60. package/dist/application/get-book-detail.d.ts.map +1 -1
  61. package/dist/application/get-book-detail.js +3 -0
  62. package/dist/application/get-book-detail.js.map +1 -1
  63. package/dist/application/search-books.d.ts +16 -5
  64. package/dist/application/search-books.d.ts.map +1 -1
  65. package/dist/application/search-books.js +46 -9
  66. package/dist/application/search-books.js.map +1 -1
  67. package/dist/config/credentials.d.ts +8 -0
  68. package/dist/config/credentials.d.ts.map +1 -0
  69. package/dist/config/credentials.js +32 -0
  70. package/dist/config/credentials.js.map +1 -0
  71. package/dist/domain/authors.d.ts +7 -0
  72. package/dist/domain/authors.d.ts.map +1 -0
  73. package/dist/domain/authors.js +22 -0
  74. package/dist/domain/authors.js.map +1 -0
  75. package/dist/domain/book.d.ts +2 -0
  76. package/dist/domain/book.d.ts.map +1 -1
  77. package/dist/domain/isbn.d.ts +8 -0
  78. package/dist/domain/isbn.d.ts.map +1 -0
  79. package/dist/domain/isbn.js +16 -0
  80. package/dist/domain/isbn.js.map +1 -0
  81. package/dist/domain/publisher.d.ts +16 -0
  82. package/dist/domain/publisher.d.ts.map +1 -1
  83. package/dist/domain/text-match.d.ts +32 -0
  84. package/dist/domain/text-match.d.ts.map +1 -0
  85. package/dist/domain/text-match.js +84 -0
  86. package/dist/domain/text-match.js.map +1 -0
  87. package/dist/main.js +15 -1
  88. package/dist/main.js.map +1 -1
  89. package/dist/mcp/server.d.ts +6 -0
  90. package/dist/mcp/server.d.ts.map +1 -1
  91. package/dist/mcp/server.js +40 -4
  92. package/dist/mcp/server.js.map +1 -1
  93. package/dist/mcp/tools.d.ts.map +1 -1
  94. package/dist/mcp/tools.js +9 -1
  95. package/dist/mcp/tools.js.map +1 -1
  96. package/dist/setup.d.ts +2 -0
  97. package/dist/setup.d.ts.map +1 -0
  98. package/dist/setup.js +43 -0
  99. package/dist/setup.js.map +1 -0
  100. package/dist/version.d.ts +9 -0
  101. package/dist/version.d.ts.map +1 -0
  102. package/dist/version.js +9 -0
  103. package/dist/version.js.map +1 -0
  104. package/docs/design-doc.md +127 -7
  105. package/package.json +14 -15
  106. package/.claude/settings.local.json +0 -38
  107. package/.codex/skills/techbook-mcp-release-prep/SKILL.md +0 -105
  108. package/.github/workflows/test.yml +0 -72
  109. package/.oxlintrc.json +0 -12
  110. package/AGENTS.md +0 -100
  111. package/deno.json +0 -3
  112. package/src/adapters/cache/memory-cache.ts +0 -31
  113. package/src/adapters/cache/null-cache.ts +0 -8
  114. package/src/adapters/calil.ts +0 -57
  115. package/src/adapters/html/cheerio-parser.ts +0 -50
  116. package/src/adapters/http/fetch-client.ts +0 -47
  117. package/src/adapters/http/mock-client.ts +0 -77
  118. package/src/adapters/openbd.ts +0 -142
  119. package/src/adapters/publishers/base.ts +0 -279
  120. package/src/adapters/publishers/book-tech.ts +0 -117
  121. package/src/adapters/publishers/born-digital.ts +0 -143
  122. package/src/adapters/publishers/coronasha.ts +0 -139
  123. package/src/adapters/publishers/gihyo.ts +0 -120
  124. package/src/adapters/publishers/impress.ts +0 -103
  125. package/src/adapters/publishers/lambdanote.ts +0 -146
  126. package/src/adapters/publishers/manatee.ts +0 -113
  127. package/src/adapters/publishers/maruzen-publishing.ts +0 -129
  128. package/src/adapters/publishers/optronics.ts +0 -113
  129. package/src/adapters/publishers/oreilly-japan.ts +0 -133
  130. package/src/adapters/publishers/peaks.ts +0 -98
  131. package/src/adapters/publishers/personal-media.ts +0 -168
  132. package/src/adapters/publishers/registry.ts +0 -38
  133. package/src/adapters/publishers/rutles.ts +0 -149
  134. package/src/adapters/publishers/saiensu.ts +0 -136
  135. package/src/adapters/publishers/seshop.ts +0 -121
  136. package/src/adapters/publishers/tatsu-zine.ts +0 -142
  137. package/src/adapters/publishers/techbookfest.ts +0 -179
  138. package/src/application/get-book-by-isbn.ts +0 -50
  139. package/src/application/get-book-detail.ts +0 -40
  140. package/src/application/search-books.ts +0 -64
  141. package/src/domain/book.ts +0 -35
  142. package/src/domain/publisher.ts +0 -18
  143. package/src/main.ts +0 -14
  144. package/src/mcp/server.ts +0 -113
  145. package/src/mcp/tools.ts +0 -71
  146. package/src/ports/cache.ts +0 -5
  147. package/src/ports/html-parser.ts +0 -15
  148. package/src/ports/http.ts +0 -17
  149. package/tests/fixtures/book-tech-detail.html +0 -51
  150. package/tests/fixtures/book-tech-search.html +0 -91
  151. package/tests/fixtures/born-digital-detail.html +0 -62
  152. package/tests/fixtures/born-digital-search.html +0 -51
  153. package/tests/fixtures/calil-book.html +0 -987
  154. package/tests/fixtures/coronasha-detail.html +0 -41
  155. package/tests/fixtures/coronasha-search.html +0 -61
  156. package/tests/fixtures/gihyo-detail.html +0 -42
  157. package/tests/fixtures/gihyo-search.json +0 -54
  158. package/tests/fixtures/impress-detail-epub.html +0 -746
  159. package/tests/fixtures/impress-detail-social.html +0 -689
  160. package/tests/fixtures/lambdanote-search.html +0 -66
  161. package/tests/fixtures/manatee-detail.html +0 -53
  162. package/tests/fixtures/manatee-search.html +0 -59
  163. package/tests/fixtures/maruzen-detail.html +0 -51
  164. package/tests/fixtures/maruzen-search.html +0 -60
  165. package/tests/fixtures/openbd-response.json +0 -110
  166. package/tests/fixtures/optronics-detail.html +0 -30
  167. package/tests/fixtures/optronics-search.html +0 -75
  168. package/tests/fixtures/oreilly-detail.html +0 -52
  169. package/tests/fixtures/oreilly-ebook-list.html +0 -53
  170. package/tests/fixtures/peaks-detail.html +0 -39
  171. package/tests/fixtures/peaks-top.html +0 -50
  172. package/tests/fixtures/personal-media-detail.html +0 -32
  173. package/tests/fixtures/personal-media-search.html +0 -39
  174. package/tests/fixtures/rutles-detail.html +0 -32
  175. package/tests/fixtures/rutles-search.html +0 -62
  176. package/tests/fixtures/saiensu-detail.html +0 -41
  177. package/tests/fixtures/saiensu-search.html +0 -65
  178. package/tests/fixtures/seshop-detail.html +0 -45
  179. package/tests/fixtures/seshop-search.html +0 -58
  180. package/tests/fixtures/tatsu-zine-detail-free.html +0 -24
  181. package/tests/fixtures/tatsu-zine-search.html +0 -40
  182. package/tests/fixtures/techbookfest-search.json +0 -73
  183. package/tests/unit/adapters/base.test.ts +0 -441
  184. package/tests/unit/adapters/calil.test.ts +0 -69
  185. package/tests/unit/adapters/openbd.test.ts +0 -185
  186. package/tests/unit/adapters/publishers/book-tech.test.ts +0 -186
  187. package/tests/unit/adapters/publishers/born-digital.test.ts +0 -194
  188. package/tests/unit/adapters/publishers/coronasha.test.ts +0 -207
  189. package/tests/unit/adapters/publishers/gihyo.test.ts +0 -137
  190. package/tests/unit/adapters/publishers/impress.test.ts +0 -129
  191. package/tests/unit/adapters/publishers/lambdanote.test.ts +0 -85
  192. package/tests/unit/adapters/publishers/manatee.test.ts +0 -165
  193. package/tests/unit/adapters/publishers/maruzen-publishing.test.ts +0 -179
  194. package/tests/unit/adapters/publishers/optronics.test.ts +0 -208
  195. package/tests/unit/adapters/publishers/oreilly-japan.test.ts +0 -194
  196. package/tests/unit/adapters/publishers/peaks.test.ts +0 -177
  197. package/tests/unit/adapters/publishers/personal-media.test.ts +0 -199
  198. package/tests/unit/adapters/publishers/rutles.test.ts +0 -173
  199. package/tests/unit/adapters/publishers/saiensu.test.ts +0 -169
  200. package/tests/unit/adapters/publishers/seshop.test.ts +0 -174
  201. package/tests/unit/adapters/publishers/tatsu-zine.test.ts +0 -172
  202. package/tests/unit/adapters/publishers/techbookfest.test.ts +0 -94
  203. package/tests/unit/adapters/registry.test.ts +0 -37
  204. package/tests/unit/application/get-book-by-isbn.test.ts +0 -176
  205. package/tests/unit/application/get-book-detail.test.ts +0 -102
  206. package/tests/unit/application/search-books.test.ts +0 -137
  207. package/tsconfig.json +0 -17
@@ -1,186 +0,0 @@
1
- import { describe, it } from "node:test";
2
- import assert from "node:assert/strict";
3
- import { readFile } from "node:fs/promises";
4
- import { join } from "node:path";
5
- import { bookTechAdapter } from "../../../../src/adapters/publishers/book-tech.js";
6
- import { MockHttpClient } from "../../../../src/adapters/http/mock-client.js";
7
- import { CheerioHtmlParser } from "../../../../src/adapters/html/cheerio-parser.js";
8
- import { NullCacheStore } from "../../../../src/adapters/cache/null-cache.js";
9
-
10
- const FIXTURES_DIR = join(import.meta.dirname, "../../../fixtures");
11
-
12
- function makeDeps(http: MockHttpClient) {
13
- return { http, parser: new CheerioHtmlParser(), cache: new NullCacheStore() };
14
- }
15
-
16
- async function loadFixture(name: string): Promise<string> {
17
- return readFile(join(FIXTURES_DIR, name), "utf-8");
18
- }
19
-
20
- describe("bookTechAdapter", () => {
21
- describe("search()", () => {
22
- it("BookRecord[] を返す", async () => {
23
- const body = await loadFixture("book-tech-search.html");
24
- const http = new MockHttpClient().addResponse(
25
- "https://book-tech.com/books",
26
- { status: 200, body },
27
- );
28
-
29
- const results = await bookTechAdapter.search({ title: "TypeScript" }, makeDeps(http));
30
-
31
- assert.strictEqual(results.length, 2);
32
- assert.partialDeepStrictEqual(results[0], {
33
- title: "次のステップへ!React実践開発 サクサク作って学ぶ UI/テスト/デプロイ",
34
- publisher: "インプレス NextPublishing",
35
- url: "https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
36
- price: 2178,
37
- publishedAt: "2026-02-20",
38
- });
39
- });
40
-
41
- it("著者名から役割語を除去する", async () => {
42
- const body = await loadFixture("book-tech-search.html");
43
- const http = new MockHttpClient().addResponse(
44
- "https://book-tech.com/books",
45
- { status: 200, body },
46
- );
47
-
48
- const results = await bookTechAdapter.search({ title: "TypeScript" }, makeDeps(http));
49
-
50
- assert.deepStrictEqual(results[0].authors, ["philosophy"]);
51
- assert.deepStrictEqual(results[1].authors, ["井手 優太"]);
52
- });
53
-
54
- it("coverImageUrl が設定される", async () => {
55
- const body = await loadFixture("book-tech-search.html");
56
- const http = new MockHttpClient().addResponse(
57
- "https://book-tech.com/books",
58
- { status: 200, body },
59
- );
60
-
61
- const results = await bookTechAdapter.search({ title: "TypeScript" }, makeDeps(http));
62
-
63
- assert.strictEqual(
64
- results[0].coverImageUrl,
65
- "https://booktech-share.s3-ap-northeast-1.amazonaws.com/books/d80ffe3d.webp",
66
- );
67
- });
68
-
69
- it("ebookStores に BOOK TECH (social DRM) が含まれる", async () => {
70
- const body = await loadFixture("book-tech-search.html");
71
- const http = new MockHttpClient().addResponse(
72
- "https://book-tech.com/books",
73
- { status: 200, body },
74
- );
75
-
76
- const results = await bookTechAdapter.search({ title: "TypeScript" }, makeDeps(http));
77
-
78
- assert.deepStrictEqual(results[0].ebookStores, [
79
- {
80
- name: "BOOK TECH",
81
- url: "https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
82
- drm: "social",
83
- },
84
- ]);
85
- });
86
-
87
- it("title も author も空の場合は [] を返しHTTPを呼ばない", async () => {
88
- const http = new MockHttpClient();
89
-
90
- const results = await bookTechAdapter.search({}, makeDeps(http));
91
-
92
- assert.deepStrictEqual(results, []);
93
- assert.strictEqual(http.calls.length, 0);
94
- });
95
-
96
- it("検索リクエストに q[...] パラメータが含まれる", async () => {
97
- const body = await loadFixture("book-tech-search.html");
98
- const http = new MockHttpClient().addResponse(
99
- "https://book-tech.com/books",
100
- { status: 200, body },
101
- );
102
-
103
- await bookTechAdapter.search({ title: "TypeScript" }, makeDeps(http));
104
-
105
- assert.ok(http.calls[0].includes("TypeScript"));
106
- assert.ok(http.calls[0].includes("title_or_overview"));
107
- });
108
- });
109
-
110
- describe("getDetail()", () => {
111
- it("詳細情報を返す", async () => {
112
- const body = await loadFixture("book-tech-detail.html");
113
- const http = new MockHttpClient().addResponse(
114
- "https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
115
- { status: 200, body },
116
- );
117
-
118
- const book = await bookTechAdapter.getDetail(
119
- "https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
120
- makeDeps(http),
121
- );
122
-
123
- assert.partialDeepStrictEqual(book, {
124
- title: "次のステップへ!React実践開発 サクサク作って学ぶ UI/テスト/デプロイ",
125
- publisher: "インプレス NextPublishing",
126
- isbn: "9784295604136",
127
- price: 2178,
128
- publishedAt: "2026-02-20",
129
- });
130
- });
131
-
132
- it("著者名から役割語を除去する", async () => {
133
- const body = await loadFixture("book-tech-detail.html");
134
- const http = new MockHttpClient().addResponse(
135
- "https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
136
- { status: 200, body },
137
- );
138
-
139
- const book = await bookTechAdapter.getDetail(
140
- "https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
141
- makeDeps(http),
142
- );
143
-
144
- assert.deepStrictEqual(book.authors, ["philosophy"]);
145
- });
146
-
147
- it("coverImageUrl が設定される", async () => {
148
- const body = await loadFixture("book-tech-detail.html");
149
- const http = new MockHttpClient().addResponse(
150
- "https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
151
- { status: 200, body },
152
- );
153
-
154
- const book = await bookTechAdapter.getDetail(
155
- "https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
156
- makeDeps(http),
157
- );
158
-
159
- assert.strictEqual(
160
- book.coverImageUrl,
161
- "https://booktech-share.s3-ap-northeast-1.amazonaws.com/books/d80ffe3d.webp",
162
- );
163
- });
164
-
165
- it("ebookStores に BOOK TECH (social DRM) が含まれる", async () => {
166
- const body = await loadFixture("book-tech-detail.html");
167
- const http = new MockHttpClient().addResponse(
168
- "https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
169
- { status: 200, body },
170
- );
171
-
172
- const book = await bookTechAdapter.getDetail(
173
- "https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
174
- makeDeps(http),
175
- );
176
-
177
- assert.deepStrictEqual(book.ebookStores, [
178
- {
179
- name: "BOOK TECH",
180
- url: "https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
181
- drm: "social",
182
- },
183
- ]);
184
- });
185
- });
186
- });
@@ -1,194 +0,0 @@
1
- import { describe, it } from "node:test";
2
- import assert from "node:assert/strict";
3
- import { readFile } from "node:fs/promises";
4
- import { join } from "node:path";
5
- import { bornDigitalAdapter } from "../../../../src/adapters/publishers/born-digital.js";
6
- import { MockHttpClient } from "../../../../src/adapters/http/mock-client.js";
7
- import { CheerioHtmlParser } from "../../../../src/adapters/html/cheerio-parser.js";
8
- import { NullCacheStore } from "../../../../src/adapters/cache/null-cache.js";
9
-
10
- const FIXTURES_DIR = join(import.meta.dirname, "../../../fixtures");
11
-
12
- function makeDeps(http: MockHttpClient) {
13
- return { http, parser: new CheerioHtmlParser(), cache: new NullCacheStore() };
14
- }
15
-
16
- async function loadFixture(name: string): Promise<string> {
17
- return readFile(join(FIXTURES_DIR, name), "utf-8");
18
- }
19
-
20
- describe("bornDigitalAdapter", () => {
21
- describe("search()", () => {
22
- it("電子書籍のみ BookRecord[] を返す(紙書籍は除外)", async () => {
23
- const body = await loadFixture("born-digital-search.html");
24
- const http = new MockHttpClient().addResponse(
25
- "https://wgn-obs.shop-pro.jp/",
26
- { status: 200, body },
27
- );
28
-
29
- const results = await bornDigitalAdapter.search({ title: "HTML" }, makeDeps(http));
30
-
31
- // フィクスチャには電子2件・紙1件あり、電子のみ返す
32
- assert.strictEqual(results.length, 2);
33
- assert.partialDeepStrictEqual(results[0], {
34
- title: "【PDFダウンロード版】HTML解体新書 ー仕様から紐解く本格入門",
35
- publisher: "ボーンデジタル",
36
- url: "https://wgn-obs.shop-pro.jp/?pid=167400957",
37
- price: 3520,
38
- });
39
- assert.partialDeepStrictEqual(results[1], {
40
- title: "【電子書籍版】インクルーシブHTML+CSS & JavaScript",
41
- publisher: "ボーンデジタル",
42
- url: "https://wgn-obs.shop-pro.jp/?pid=144269584",
43
- price: 2640,
44
- });
45
- });
46
-
47
- it("coverImageUrl が設定される", async () => {
48
- const body = await loadFixture("born-digital-search.html");
49
- const http = new MockHttpClient().addResponse(
50
- "https://wgn-obs.shop-pro.jp/",
51
- { status: 200, body },
52
- );
53
-
54
- const results = await bornDigitalAdapter.search({ title: "HTML" }, makeDeps(http));
55
-
56
- assert.strictEqual(
57
- results[0].coverImageUrl,
58
- "https://img07.shop-pro.jp/PA01427/945/product/167400957_th.png?cmsp_timestamp=20220328140327",
59
- );
60
- });
61
-
62
- it("ebookStores に ボーンデジタル (social DRM) が含まれる", async () => {
63
- const body = await loadFixture("born-digital-search.html");
64
- const http = new MockHttpClient().addResponse(
65
- "https://wgn-obs.shop-pro.jp/",
66
- { status: 200, body },
67
- );
68
-
69
- const results = await bornDigitalAdapter.search({ title: "HTML" }, makeDeps(http));
70
-
71
- assert.deepStrictEqual(results[0].ebookStores, [
72
- {
73
- name: "ボーンデジタル",
74
- url: "https://wgn-obs.shop-pro.jp/?pid=167400957",
75
- drm: "social",
76
- },
77
- ]);
78
- });
79
-
80
- it("title も author も空の場合は [] を返しHTTPを呼ばない", async () => {
81
- const http = new MockHttpClient();
82
-
83
- const results = await bornDigitalAdapter.search({}, makeDeps(http));
84
-
85
- assert.deepStrictEqual(results, []);
86
- assert.strictEqual(http.calls.length, 0);
87
- });
88
-
89
- it("検索リクエストに EUC-JP エンコードされた keyword が含まれる", async () => {
90
- const body = await loadFixture("born-digital-search.html");
91
- const http = new MockHttpClient().addResponse(
92
- "https://wgn-obs.shop-pro.jp/",
93
- { status: 200, body },
94
- );
95
-
96
- await bornDigitalAdapter.search({ title: "HTML" }, makeDeps(http));
97
-
98
- assert.ok(http.calls[0].includes("mode=srh"));
99
- // ASCII は EUC-JP でも同じバイト列だがパーセントエンコードされる
100
- assert.ok(http.calls[0].includes("keyword=%48%54%4D%4C"));
101
- });
102
- });
103
-
104
- describe("getDetail()", () => {
105
- it("詳細情報を返す", async () => {
106
- const body = await loadFixture("born-digital-detail.html");
107
- const http = new MockHttpClient().addResponse(
108
- "https://wgn-obs.shop-pro.jp/?pid=167400957",
109
- { status: 200, body },
110
- );
111
-
112
- const book = await bornDigitalAdapter.getDetail(
113
- "https://wgn-obs.shop-pro.jp/?pid=167400957",
114
- makeDeps(http),
115
- );
116
-
117
- assert.partialDeepStrictEqual(book, {
118
- title: "【PDFダウンロード版】HTML解体新書 ー仕様から紐解く本格入門",
119
- publisher: "ボーンデジタル",
120
- price: 3520,
121
- publishedAt: "2022-04-07",
122
- });
123
- });
124
-
125
- it("著者を取得する", async () => {
126
- const body = await loadFixture("born-digital-detail.html");
127
- const http = new MockHttpClient().addResponse(
128
- "https://wgn-obs.shop-pro.jp/?pid=167400957",
129
- { status: 200, body },
130
- );
131
-
132
- const book = await bornDigitalAdapter.getDetail(
133
- "https://wgn-obs.shop-pro.jp/?pid=167400957",
134
- makeDeps(http),
135
- );
136
-
137
- assert.deepStrictEqual(book.authors, ["太田 良典", "中村 直樹"]);
138
- });
139
-
140
- it("Colorme JSON から価格を取得する", async () => {
141
- const body = await loadFixture("born-digital-detail.html");
142
- const http = new MockHttpClient().addResponse(
143
- "https://wgn-obs.shop-pro.jp/?pid=167400957",
144
- { status: 200, body },
145
- );
146
-
147
- const book = await bornDigitalAdapter.getDetail(
148
- "https://wgn-obs.shop-pro.jp/?pid=167400957",
149
- makeDeps(http),
150
- );
151
-
152
- assert.strictEqual(book.price, 3520);
153
- });
154
-
155
- it("coverImageUrl が設定される", async () => {
156
- const body = await loadFixture("born-digital-detail.html");
157
- const http = new MockHttpClient().addResponse(
158
- "https://wgn-obs.shop-pro.jp/?pid=167400957",
159
- { status: 200, body },
160
- );
161
-
162
- const book = await bornDigitalAdapter.getDetail(
163
- "https://wgn-obs.shop-pro.jp/?pid=167400957",
164
- makeDeps(http),
165
- );
166
-
167
- assert.strictEqual(
168
- book.coverImageUrl,
169
- "https://img07.shop-pro.jp/PA01427/945/product/167400957.png?cmsp_timestamp=20220328140327",
170
- );
171
- });
172
-
173
- it("ebookStores に ボーンデジタル (social DRM) が含まれる", async () => {
174
- const body = await loadFixture("born-digital-detail.html");
175
- const http = new MockHttpClient().addResponse(
176
- "https://wgn-obs.shop-pro.jp/?pid=167400957",
177
- { status: 200, body },
178
- );
179
-
180
- const book = await bornDigitalAdapter.getDetail(
181
- "https://wgn-obs.shop-pro.jp/?pid=167400957",
182
- makeDeps(http),
183
- );
184
-
185
- assert.deepStrictEqual(book.ebookStores, [
186
- {
187
- name: "ボーンデジタル",
188
- url: "https://wgn-obs.shop-pro.jp/?pid=167400957",
189
- drm: "social",
190
- },
191
- ]);
192
- });
193
- });
194
- });
@@ -1,207 +0,0 @@
1
- import { describe, it } from "node:test";
2
- import assert from "node:assert/strict";
3
- import { readFile } from "node:fs/promises";
4
- import { join } from "node:path";
5
- import { coronashaAdapter } from "../../../../src/adapters/publishers/coronasha.js";
6
- import { MockHttpClient } from "../../../../src/adapters/http/mock-client.js";
7
- import { CheerioHtmlParser } from "../../../../src/adapters/html/cheerio-parser.js";
8
- import { NullCacheStore } from "../../../../src/adapters/cache/null-cache.js";
9
-
10
- const FIXTURES_DIR = join(import.meta.dirname, "../../../fixtures");
11
-
12
- function makeDeps(http: MockHttpClient) {
13
- return { http, parser: new CheerioHtmlParser(), cache: new NullCacheStore() };
14
- }
15
-
16
- async function loadFixture(name: string): Promise<string> {
17
- return readFile(join(FIXTURES_DIR, name), "utf-8");
18
- }
19
-
20
- describe("coronashaAdapter", () => {
21
- describe("search()", () => {
22
- it("電子版ありの書籍のみ返す(電子版なしは除外)", async () => {
23
- const body = await loadFixture("coronasha-search.html");
24
- const http = new MockHttpClient().addResponse(
25
- "https://www.coronasha.co.jp/np/result.html",
26
- { status: 200, body },
27
- );
28
-
29
- const results = await coronashaAdapter.search({ title: "Julia" }, makeDeps(http));
30
-
31
- // フィクスチャには電子版あり1件・電子版なし1件あり、電子版のみ返す
32
- assert.strictEqual(results.length, 1);
33
- });
34
-
35
- it("タイトルを tunogaki + book-title で組み立てる", async () => {
36
- const body = await loadFixture("coronasha-search.html");
37
- const http = new MockHttpClient().addResponse(
38
- "https://www.coronasha.co.jp/np/result.html",
39
- { status: 200, body },
40
- );
41
-
42
- const results = await coronashaAdapter.search({ title: "Julia" }, makeDeps(http));
43
-
44
- assert.strictEqual(results[0].title, "1から始める Juliaプログラミング大全");
45
- });
46
-
47
- it("著者一覧を返す", async () => {
48
- const body = await loadFixture("coronasha-search.html");
49
- const http = new MockHttpClient().addResponse(
50
- "https://www.coronasha.co.jp/np/result.html",
51
- { status: 200, body },
52
- );
53
-
54
- const results = await coronashaAdapter.search({ title: "Julia" }, makeDeps(http));
55
-
56
- assert.deepStrictEqual(results[0].authors, ["進藤 裕之", "佐藤 建太"]);
57
- });
58
-
59
- it("価格・ISBN・発行日を返す", async () => {
60
- const body = await loadFixture("coronasha-search.html");
61
- const http = new MockHttpClient().addResponse(
62
- "https://www.coronasha.co.jp/np/result.html",
63
- { status: 200, body },
64
- );
65
-
66
- const results = await coronashaAdapter.search({ title: "Julia" }, makeDeps(http));
67
-
68
- assert.partialDeepStrictEqual(results[0], {
69
- price: 3630,
70
- isbn: "9784339029345",
71
- publishedAt: "2023-05-01",
72
- });
73
- });
74
-
75
- it("coverImageUrl が絶対URLになる", async () => {
76
- const body = await loadFixture("coronasha-search.html");
77
- const http = new MockHttpClient().addResponse(
78
- "https://www.coronasha.co.jp/np/result.html",
79
- { status: 200, body },
80
- );
81
-
82
- const results = await coronashaAdapter.search({ title: "Julia" }, makeDeps(http));
83
-
84
- assert.strictEqual(
85
- results[0].coverImageUrl,
86
- "https://www.coronasha.co.jp/np/images/isbn/9784339029345_main.jpg",
87
- );
88
- });
89
-
90
- it("publisher が コロナ社 になる", async () => {
91
- const body = await loadFixture("coronasha-search.html");
92
- const http = new MockHttpClient().addResponse(
93
- "https://www.coronasha.co.jp/np/result.html",
94
- { status: 200, body },
95
- );
96
-
97
- const results = await coronashaAdapter.search({ title: "Julia" }, makeDeps(http));
98
-
99
- assert.strictEqual(results[0].publisher, "コロナ社");
100
- });
101
-
102
- it("title も author も空の場合は [] を返しHTTPを呼ばない", async () => {
103
- const http = new MockHttpClient();
104
-
105
- const results = await coronashaAdapter.search({}, makeDeps(http));
106
-
107
- assert.deepStrictEqual(results, []);
108
- assert.strictEqual(http.calls.length, 0);
109
- });
110
- });
111
-
112
- describe("getDetail()", () => {
113
- it("詳細情報を返す", async () => {
114
- const body = await loadFixture("coronasha-detail.html");
115
- const http = new MockHttpClient().addResponse(
116
- "https://www.coronasha.co.jp/np/isbn/9784339029345/",
117
- { status: 200, body },
118
- );
119
-
120
- const book = await coronashaAdapter.getDetail(
121
- "https://www.coronasha.co.jp/np/isbn/9784339029345/",
122
- makeDeps(http),
123
- );
124
-
125
- assert.partialDeepStrictEqual(book, {
126
- title: "1から始める Juliaプログラミング大全",
127
- publisher: "コロナ社",
128
- price: 3630,
129
- publishedAt: "2023-05-01",
130
- isbn: "9784339029345",
131
- });
132
- });
133
-
134
- it("著者一覧を返す", async () => {
135
- const body = await loadFixture("coronasha-detail.html");
136
- const http = new MockHttpClient().addResponse(
137
- "https://www.coronasha.co.jp/np/isbn/9784339029345/",
138
- { status: 200, body },
139
- );
140
-
141
- const book = await coronashaAdapter.getDetail(
142
- "https://www.coronasha.co.jp/np/isbn/9784339029345/",
143
- makeDeps(http),
144
- );
145
-
146
- assert.deepStrictEqual(book.authors, ["進藤 裕之", "佐藤 建太"]);
147
- });
148
-
149
- it("ebookStores に Kindle・Kinoppy・VarsityWave eBooks が含まれる", async () => {
150
- const body = await loadFixture("coronasha-detail.html");
151
- const http = new MockHttpClient().addResponse(
152
- "https://www.coronasha.co.jp/np/isbn/9784339029345/",
153
- { status: 200, body },
154
- );
155
-
156
- const book = await coronashaAdapter.getDetail(
157
- "https://www.coronasha.co.jp/np/isbn/9784339029345/",
158
- makeDeps(http),
159
- );
160
-
161
- for (const { name, drm } of [
162
- { name: "Kindle", drm: "drm" },
163
- { name: "Kinoppy", drm: "drm" },
164
- { name: "VarsityWave eBooks", drm: "drm" },
165
- ]) {
166
- assert.ok(
167
- book.ebookStores.some(s => s.name === name && s.drm === drm),
168
- `Expected store: ${name} (${drm})`,
169
- );
170
- }
171
- });
172
-
173
- it("Knowledge Worker (kw.maruzen.co.jp) は ebookStores に含まれない", async () => {
174
- const body = await loadFixture("coronasha-detail.html");
175
- const http = new MockHttpClient().addResponse(
176
- "https://www.coronasha.co.jp/np/isbn/9784339029345/",
177
- { status: 200, body },
178
- );
179
-
180
- const book = await coronashaAdapter.getDetail(
181
- "https://www.coronasha.co.jp/np/isbn/9784339029345/",
182
- makeDeps(http),
183
- );
184
-
185
- const names = book.ebookStores.map(s => s.name);
186
- assert.ok(!names.includes("Knowledge Worker"));
187
- });
188
-
189
- it("coverImageUrl が絶対URLになる", async () => {
190
- const body = await loadFixture("coronasha-detail.html");
191
- const http = new MockHttpClient().addResponse(
192
- "https://www.coronasha.co.jp/np/isbn/9784339029345/",
193
- { status: 200, body },
194
- );
195
-
196
- const book = await coronashaAdapter.getDetail(
197
- "https://www.coronasha.co.jp/np/isbn/9784339029345/",
198
- makeDeps(http),
199
- );
200
-
201
- assert.strictEqual(
202
- book.coverImageUrl,
203
- "https://www.coronasha.co.jp/np/images/isbn/9784339029345_main.jpg",
204
- );
205
- });
206
- });
207
- });