-
Notifications
You must be signed in to change notification settings - Fork 0
Remove unsupported fetch option note for browser #211
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds support for passing custom fetch options (like headers) to all API methods in the library. The changes enable users to configure fetch behavior when making API calls, such as adding custom user-agent headers or other RequestInit options. This functionality works in Node.js (fetch implementation) while being gracefully ignored in browser environments (JSONP implementation).
Key Changes:
- Added optional
RequestInit fetchOptionsparameter to all builderexecute()methods - Implemented complex overload resolution for
RankingBuilder.executeWithFields()to support backwards-compatible API with new fetchOptions parameter - Added test coverage for basic fetchOptions functionality in ranking and low-level fetch implementation
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/narou.ts | Added fetchOptions parameter to all internal execute methods (executeNovel, executeRanking, executeUserSearch, etc.) |
| src/narou-fetch.ts | Updated fetch implementation to pass fetchOptions to the underlying fetch call |
| src/narou-jsonp.ts | Added fetchOptions parameter (intentionally ignored with void pattern for browser compatibility) |
| src/search-builder.ts | Added fetchOptions parameter to NovelSearchBuilderBase.execute() method |
| src/search-builder-r18.ts | Added fetchOptions parameter to SearchBuilderR18.execute() method |
| src/user-search.ts | Added fetchOptions parameter to UserSearchBuilder.execute() method |
| src/ranking.ts | Added fetchOptions parameter with complex overload resolution for executeWithFields, including runtime type guard |
| test/ranking.test.ts | Added tests for passing fetchOptions to execute() and executeWithFields() methods |
| test/narou-fetch.test.ts | Added test verifying fetchOptions are passed to the underlying fetch function |
| README.md | Updated examples to demonstrate fetchOptions usage with custom headers |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| execute(fetchOptions?: RequestInit): Promise<NarouSearchResults<NarouSearchResult, T>> { | ||
| return this.api.executeNovel(this.params, fetchOptions); | ||
| } |
Copilot
AI
Dec 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing test coverage for the new fetchOptions parameter added to the execute() method. Consider adding a test similar to the one in test/ranking.test.ts:32-54 that verifies fetchOptions (e.g., custom headers) are properly passed through to the underlying API call.
| execute( | ||
| fetchOptions?: RequestInit | ||
| ): Promise<NarouSearchResults<NarouSearchResult, T | TOpt>> { | ||
| return this.api.executeNovel18(this.params, fetchOptions); |
Copilot
AI
Dec 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing test coverage for the new fetchOptions parameter added to the execute() method. Consider adding a test similar to the one in test/ranking.test.ts:32-54 that verifies fetchOptions (e.g., custom headers) are properly passed through to the underlying API call.
| execute( | ||
| fetchOptions?: RequestInit | ||
| ): Promise<NarouSearchResults<UserSearchResult, TField>> { | ||
| return this.api.executeUserSearch( | ||
| this.params as UserSearchParams, | ||
| fetchOptions | ||
| ); | ||
| } |
Copilot
AI
Dec 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing test coverage for the new fetchOptions parameter added to the execute() method. Consider adding a test similar to the one in test/ranking.test.ts:32-54 that verifies fetchOptions (e.g., custom headers) are properly passed through to the underlying API call.
| import { addDays, formatDate } from "./util/date.js"; | ||
|
|
||
| function isRequestInit(value: unknown): value is RequestInit { | ||
| return typeof value == "object" && value !== null && !Array.isArray(value); |
Copilot
AI
Dec 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use strict equality (===) instead of loose equality (==) for type checking. While this works correctly in this case, using === is a best practice in TypeScript/JavaScript to avoid unexpected type coercion.
| return typeof value == "object" && value !== null && !Array.isArray(value); | |
| return typeof value === "object" && value !== null && !Array.isArray(value); |
| * @param fetchOptions fetch のオプション | ||
| * @returns {Promise<RankingResult<SearchResultFields<TFields>>[]>} 詳細情報を含むランキング結果の配列 | ||
| */ |
Copilot
AI
Dec 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent JSDoc indentation. The @param fetchOptions and @returns tags should be aligned with the other JSDoc tags (no leading spaces). The extra leading spaces before these lines break the standard JSDoc formatting convention.
| * @param fetchOptions fetch のオプション | |
| * @returns {Promise<RankingResult<SearchResultFields<TFields>>[]>} 詳細情報を含むランキング結果の配列 | |
| */ | |
| * @param fetchOptions fetch のオプション | |
| * @returns {Promise<RankingResult<SearchResultFields<TFields>>[]>} 詳細情報を含むランキング結果の配列 | |
| */ |
| test("fetchOptions を executeWithFields の両 API に渡せる", async () => { | ||
| const rankingHeaderMock = vi.fn(); | ||
| const detailHeaderMock = vi.fn(); | ||
|
|
||
| server.use( | ||
| http.get("https://api.syosetu.com/rank/rankget/", ({ request }) => { | ||
| const url = new URL(request.url); | ||
| rankingHeaderMock(request.headers.get("x-test-header")); | ||
| const response: NarouRankingResult[] = [ | ||
| { ncode: "N0000AA", rank: 1, pt: 1000 }, | ||
| ]; | ||
| return responseGzipOrJson(response, url); | ||
| }), | ||
| http.get("https://api.syosetu.com/novelapi/api/", ({ request }) => { | ||
| const url = new URL(request.url); | ||
| detailHeaderMock(request.headers.get("x-test-header")); | ||
| const response = [ | ||
| { allcount: 1 }, | ||
| { | ||
| ncode: "N0000AA", | ||
| title: "タイトル", | ||
| writer: "作者", | ||
| story: "あらすじ", | ||
| biggenre: 1, | ||
| genre: 101, | ||
| keyword: ["キーワード"], | ||
| general_firstup: "2021-01-01 00:00:00", | ||
| general_lastup: "2021-01-01 00:00:00", | ||
| novel_type: 1, | ||
| end: 0, | ||
| general_all_no: 100, | ||
| length: 100, | ||
| time: 100, | ||
| isstop: 0, | ||
| isr15: 0, | ||
| isbl: 0, | ||
| isgl: 0, | ||
| iszankoku: 0, | ||
| istensei: 0, | ||
| istenni: 0, | ||
| global_point: 100, | ||
| fav_novel_cnt: 100, | ||
| review_cnt: 100, | ||
| all_point: 100, | ||
| all_hyoka_cnt: 100, | ||
| sasie_cnt: 100, | ||
| kaiwaritu: 100, | ||
| novelupdated_at: "2021-01-01 00:00:00", | ||
| updated_at: "2021-01-01 00:00:00", | ||
| }, | ||
| ]; | ||
| return responseGzipOrJson(response, url); | ||
| }) | ||
| ); | ||
|
|
||
| const result = await ranking().executeWithFields({ | ||
| headers: { "x-test-header": "ranking-detail-header" }, | ||
| }); | ||
|
|
||
| expect(result).toHaveLength(1); | ||
| expect(rankingHeaderMock).toHaveBeenCalledWith("ranking-detail-header"); | ||
| expect(detailHeaderMock).toHaveBeenCalledWith("ranking-detail-header"); | ||
| }); |
Copilot
AI
Dec 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing test coverage for executeWithFields overloads that pass both fields and fetchOptions. The current tests only cover:
executeWithFields()- no parametersexecuteWithFields(fetchOptions)- only fetchOptionsexecuteWithFields([Fields.title])- only fields
Consider adding tests for:
executeWithFields([Fields.title], fetchOptions)- fields + fetchOptionsexecuteWithFields([], OptionalFields.weekly, fetchOptions)- fields + opt + fetchOptionsexecuteWithFields([Fields.title], OptionalFields.weekly, fetchOptions)- all parameters
This will ensure the parameter detection logic in lines 187-194 works correctly for all overload combinations.
Summary
Testing
Codex Task