UNPKG

narou

Version:
1,373 lines (1,360 loc) 42.7 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.browser.ts var index_browser_exports = {}; __export(index_browser_exports, { BigGenre: () => BigGenre, BigGenreNotation: () => BigGenreNotation, BooleanNumber: () => BooleanNumber, BuntaiParam: () => BuntaiParam, DateParam: () => DateParam, End: () => End, Fields: () => Fields, Genre: () => Genre, GenreNotation: () => GenreNotation, NarouNovel: () => NarouNovel, NarouNovelJsonp: () => NarouNovelJsonp, NarouSearchResults: () => NarouSearchResults, NovelSearchBuilderBase: () => NovelSearchBuilderBase, NovelType: () => NovelType, NovelTypeParam: () => NovelTypeParam, OptionalFields: () => OptionalFields, Order: () => Order, R18Fields: () => R18Fields, R18Site: () => R18Site, R18SiteNotation: () => R18SiteNotation, RankingBuilder: () => RankingBuilder, RankingType: () => RankingType, SearchBuilder: () => SearchBuilder, SearchBuilderBase: () => SearchBuilderBase, SearchBuilderR18: () => SearchBuilderR18, StopParam: () => StopParam, UserFields: () => UserFields, UserOrder: () => UserOrder, default: () => index_browser_default, formatRankingHistory: () => formatRankingHistory, ranking: () => ranking, rankingHistory: () => rankingHistory, search: () => search, searchR18: () => searchR18, searchUser: () => searchUser }); module.exports = __toCommonJS(index_browser_exports); // src/narou-search-results.ts var NarouSearchResults = class { /** * @constractor * @private */ constructor([header, ...result], params) { const count2 = header.allcount; const limit = params.lim ?? 20; const start = params.st ?? 0; this.allcount = count2; this.limit = limit; this.start = start; this.page = start / limit; this.length = result.length; this.values = result; } }; var NovelType = { /** 連載 */ Rensai: 1, /** 短編 */ Tanpen: 2 }; var End = { /** 短編小説と完結済小説 */ KanketsuOrTanpen: 0, /** 連載中 */ Rensai: 1 }; // src/narou.ts var NarouNovel = class { /** * APIへの検索リクエストを実行する * @param params クエリパラメータ * @param endpoint APIエンドポイント * @returns 検索結果 */ async executeSearch(params, endpoint = "https://api.syosetu.com/novelapi/api/") { return new NarouSearchResults(await this.execute(params, endpoint), params); } /** * 小説APIへの検索リクエストを実行する * @param params クエリパラメータ * @returns 検索結果 * @see https://dev.syosetu.com/man/api/ */ async executeNovel(params) { return await this.executeSearch( params, "https://api.syosetu.com/novelapi/api/" ); } /** * R18小説APIへの検索リクエストを実行する * @param params クエリパラメータ * @returns 検索結果 * @see https://dev.syosetu.com/xman/api/ */ async executeNovel18(params) { return await this.executeSearch( params, "https://api.syosetu.com/novel18api/api/" ); } /** * ランキングAPIへのリクエストを実行する * @param params クエリパラメータ * @returns ランキング結果 * @see https://dev.syosetu.com/man/rankapi/ */ async executeRanking(params) { return await this.execute(params, "https://api.syosetu.com/rank/rankget/"); } /** * 殿堂入りAPiへのリクエストを実行する * @param params クエリパラメータ * @returns ランキング履歴結果 * @see https://dev.syosetu.com/man/rankinapi/ */ async executeRankingHistory(params) { return await this.execute(params, "https://api.syosetu.com/rank/rankin/"); } /** * ユーザー検索APIへのリクエストを実行する * @param params クエリパラメータ * @returns 検索結果 * @see https://dev.syosetu.com/man/userapi/ */ async executeUserSearch(params) { return new NarouSearchResults( await this.execute(params, "https://api.syosetu.com/userapi/api/"), params ); } }; // src/util/jsonp.ts var count = 0; var noop = function() { }; function jsonp(url, { prefix = "__jp", param = "callback", timeout = 15e3 } = {}) { return new Promise(function(resolve, reject) { const targetChild = document.getElementsByTagName("script").item(0); const target = targetChild?.parentNode ?? document.head; const id = `${prefix}${count++}`; const cleanup = function() { if (script && script.parentNode) { script.parentNode.removeChild(script); } window[id] = noop; if (timer) { clearTimeout(timer); } }; const timer = timeout > 0 ? setTimeout(() => { cleanup(); reject(new Error("Timeout")); }, timeout) : void 0; const callback = (data) => { cleanup(); resolve(data); }; window[id] = callback; const script = document.createElement("script"); const urlObj = new URL(url); urlObj.searchParams.set(param, id); script.setAttribute("src", urlObj.toString()); target.insertBefore(script, targetChild); }); } // src/narou-jsonp.ts var NarouNovelJsonp = class extends NarouNovel { async execute(params, endpoint) { const query = { ...params, out: "jsonp" }; query.gzip = 0; const url = new URL(endpoint); Object.entries(query).forEach(([key, value]) => { if (value !== void 0) { url.searchParams.append(key, value.toString()); } }); return await jsonp(url.toString()); } }; // src/params.ts var RankingType = { Daily: "d", Weekly: "w", Monthly: "m", Quarterly: "q" }; var BooleanNumber = { True: 1, False: 0 }; var Fields = { /** 小説名 */ title: "t", /** Nコード */ ncode: "n", /** 作者のユーザID(数値) */ userid: "u", /** 作者名 */ writer: "w", /** 小説のあらすじ */ story: "s", /** 大ジャンル */ biggenre: "bg", /** ジャンル */ genre: "g", /** キーワード */ keyword: "k", /** 初回掲載日 */ general_firstup: "gf", /** 最終掲載日 */ general_lastup: "gl", /** 連載の場合は1、短編の場合は2 */ noveltype: "nt", /** 短編小説と完結済小説は0となっています。連載中は1です。 */ end: "e", /** 全掲載部分数 */ general_all_no: "ga", /** 小説文字数 */ length: "l", /** 読了時間(分単位) */ time: "ti", /** 長期連載停止中 */ isstop: "i", /** 登録必須キーワードに「R15」が含まれる場合は1、それ以外は0です。 */ isr15: "isr", /** 登録必須キーワードに「ボーイズラブ」が含まれる場合は1、それ以外は0です。 */ isbl: "ibl", /** 登録必須キーワードに「ガールズラブ」が含まれる場合は1、それ以外は0です。 */ isgl: "igl", /** 登録必須キーワードに「残酷な描写あり」が含まれる場合は1、それ以外は0です。 */ iszankoku: "izk", /** 登録必須キーワードに「異世界転生」が含まれる場合は1、それ以外は0です。 */ istensei: "its", /** 登録必須キーワードに「異世界転移」が含まれる場合は1、それ以外は0です。 */ istenni: "iti", /** 総合評価ポイント */ global_point: "gp", /** 日間ポイント */ daily_point: "dp", /** 週間ポイント */ weekly_point: "wp", /** 月間ポイント */ monthly_point: "mp", /** 四半期ポイント */ quarter_point: "qp", /** 年間ポイント */ yearly_point: "yp", /** ブックマーク数 */ fav_novel_cnt: "f", /** 感想数 */ impression_cnt: "imp", /** レビュー数 */ review_cnt: "r", /** 評価ポイント */ all_point: "a", /** 評価者数 */ all_hyoka_cnt: "ah", /** 挿絵の数 */ sasie_cnt: "sa", /** 会話率 */ kaiwaritu: "ka", /** 小説の更新日時 */ novelupdated_at: "nu", /** * 最終更新日時 * システム用で小説更新時とは関係ありません */ updated_at: "ua" }; var R18Fields = { /** 小説名 */ title: "t", /** Nコード */ ncode: "n", /** 作者のユーザID(数値) */ userid: "u", /** 作者名 */ writer: "w", /** 小説のあらすじ */ story: "s", /** 掲載サイト */ nocgenre: "ng", /** キーワード */ keyword: "k", /** 初回掲載日 */ general_firstup: "gf", /** 最終掲載日 */ general_lastup: "gl", /** 連載の場合は1、短編の場合は2 */ noveltype: "nt", /** 短編小説と完結済小説は0となっています。連載中は1です。 */ end: "e", /** 全掲載部分数 */ general_all_no: "ga", /** 小説文字数 */ length: "l", /** 読了時間(分単位) */ time: "ti", /** 長期連載停止中 */ isstop: "i", /** 登録必須キーワードに「ボーイズラブ」が含まれる場合は1、それ以外は0です。 */ isbl: "ibl", /** 登録必須キーワードに「ガールズラブ」が含まれる場合は1、それ以外は0です。 */ isgl: "igl", /** 登録必須キーワードに「残酷な描写あり」が含まれる場合は1、それ以外は0です。 */ iszankoku: "izk", /** 登録必須キーワードに「異世界転生」が含まれる場合は1、それ以外は0です。 */ istensei: "its", /** 登録必須キーワードに「異世界転移」が含まれる場合は1、それ以外は0です。 */ istenni: "iti", /** 総合評価ポイント */ global_point: "gp", /** 日間ポイント */ daily_point: "dp", /** 週間ポイント */ weekly_point: "wp", /** 月間ポイント */ monthly_point: "mp", /** 四半期ポイント */ quarter_point: "qp", /** 年間ポイント */ yearly_point: "yp", /** R18ブックマーク数 */ fav_novel_cnt: "f", /** 感想数 */ impression_cnt: "imp", /** レビュー数 */ review_cnt: "r", /** 評価ポイント */ all_point: "a", /** 評価者数 */ all_hyoka_cnt: "ah", /** 挿絵の数 */ sasie_cnt: "sa", /** 会話率 */ kaiwaritu: "ka", /** 小説の更新日時 */ novelupdated_at: "nu", /** * 最終更新日時 * システム用で小説更新時とは関係ありません */ updated_at: "ua" }; var OptionalFields = { /** * 週間ユニークユーザ[項目名:weekly_unique]が追加されます。 * 週間ユニークユーザは前週の日曜日から土曜日分のユニークの合計です。 * 毎週火曜日早朝に更新されます。 */ weekly_unique: "weekly" }; var UserFields = { /** ユーザID */ userid: "u", /** ユーザ名 */ name: "n", /** ユーザ名のフリガナ */ yomikata: "y", /** ユーザ名のフリガナの頭文字 */ name1st: "1", /** 小説投稿数 */ novel_cnt: "nc", /** レビュー投稿数 */ review_cnt: "rc", /** 小説累計文字数 */ novel_length: "nl", /** 総合評価ポイントの合計 */ sum_global_point: "sg" }; var Order = { /** ブックマーク数の多い順 */ FavoriteNovelCount: "favnovelcnt", /** レビュー数の多い順 */ ReviewCount: "favnovelcnt", /** 総合ポイントの高い順 */ HyokaDesc: "hyoka", /** 総合ポイントの低い順 */ HyokaAsc: "hyokaasc", /** 感想の多い順 */ ImpressionCount: "impressioncnt", /** 評価者数の多い順 */ HyokaCountDesc: "hyokacnt", /** 評価者数の少ない順 */ HyokaCountAsc: "hyokacntasc", /** 週間ユニークユーザの多い順 */ Weekly: "weekly", /** 小説本文の文字数が多い順 */ LengthDesc: "lengthdesc", /** 小説本文の文字数が少ない順 */ LengthAsc: "lengthasc", /** Nコードが新しい順 */ NCodeDesc: "ncodedesc", /** 新着更新順 */ New: "new", /** 古い順 */ Old: "old", /** 日間ポイントの高い順 */ DailyPoint: "dailypoint", /** 週間ポイントの高い順 */ WeeklyPoint: "weeklypoint", /** 月間ポイントの高い順 */ MonthlyPoint: "monthlypoint", /** 四半期ポイントの高い順 */ QuarterPoint: "quarterpoint", /** 年間ポイントの高い順 */ YearlyPoint: "yearlypoint", /** 初回掲載順 */ GeneralFirstUp: "generalfirstup" }; var R18Site = { /** ノクターンノベルズ(男性向け) */ Nocturne: 1, /** ムーンライトノベルズ(女性向け) */ MoonLight: 2, /** ムーンライトノベルズ(BL) */ MoonLightBL: 3, /** ミッドナイトノベルズ(大人向け) */ Midnight: 4 }; var R18SiteNotation = { [R18Site.Nocturne]: "\u30CE\u30AF\u30BF\u30FC\u30F3\u30CE\u30D9\u30EB\u30BA(\u7537\u6027\u5411\u3051)", [R18Site.MoonLight]: "\u30E0\u30FC\u30F3\u30E9\u30A4\u30C8\u30CE\u30D9\u30EB\u30BA(\u5973\u6027\u5411\u3051)", [R18Site.MoonLightBL]: "\u30E0\u30FC\u30F3\u30E9\u30A4\u30C8\u30CE\u30D9\u30EB\u30BA(BL)", [R18Site.Midnight]: "\u30DF\u30C3\u30C9\u30CA\u30A4\u30C8\u30CE\u30D9\u30EB\u30BA(\u5927\u4EBA\u5411\u3051)" }; var BigGenre = { /** 恋愛 */ Renai: 1, /** ファンタジー */ Fantasy: 2, /** 文芸 */ Bungei: 3, /** SF */ Sf: 4, /** その他 */ Sonota: 99, /** ノンジャンル */ NonGenre: 98 }; var BigGenreNotation = { [BigGenre.Renai]: "\u604B\u611B", [BigGenre.Fantasy]: "\u30D5\u30A1\u30F3\u30BF\u30B8\u30FC", [BigGenre.Bungei]: "\u6587\u82B8", [BigGenre.Sf]: "SF", [BigGenre.Sonota]: "\u305D\u306E\u4ED6", [BigGenre.NonGenre]: "\u30CE\u30F3\u30B8\u30E3\u30F3\u30EB" }; var Genre = { /** 異世界〔恋愛〕*/ RenaiIsekai: 101, /** 現実世界〔恋愛〕*/ RenaiGenjitsusekai: 102, /** ハイファンタジー〔ファンタジー〕*/ FantasyHigh: 201, /** ローファンタジー〔ファンタジー〕*/ FantasyLow: 202, /** 純文学〔文芸〕*/ BungeiJyunbungei: 301, /** ヒューマンドラマ〔文芸〕*/ BungeiHumanDrama: 302, /** 歴史〔文芸〕*/ BungeiHistory: 303, /** 推理〔文芸〕*/ BungeiSuiri: 304, /** ホラー〔文芸〕*/ BungeiHorror: 305, /** アクション〔文芸〕*/ BungeiAction: 306, /** コメディー〔文芸〕*/ BungeiComedy: 307, /** VRゲーム〔SF〕*/ SfVrgame: 401, /** 宇宙〔SF〕*/ SfSpace: 402, /** 空想科学〔SF〕*/ SfKuusoukagaku: 403, /** パニック〔SF〕*/ SfPanic: 404, /** 童話〔その他〕*/ SonotaDouwa: 9901, /** 詩〔その他〕*/ SonotaShi: 9902, /** エッセイ〔その他〕*/ SonotaEssei: 9903, /** リプレイ〔その他〕*/ SonotaReplay: 9904, /** その他〔その他〕 */ SonotaSonota: 9999, /** ノンジャンル〔ノンジャンル〕*/ NonGenre: 9801 }; var GenreNotation = { [Genre.RenaiIsekai]: "\u7570\u4E16\u754C\u3014\u604B\u611B\u3015", [Genre.RenaiGenjitsusekai]: "\u73FE\u5B9F\u4E16\u754C\u3014\u604B\u611B\u3015", [Genre.FantasyHigh]: "\u30CF\u30A4\u30D5\u30A1\u30F3\u30BF\u30B8\u30FC\u3014\u30D5\u30A1\u30F3\u30BF\u30B8\u30FC\u3015", [Genre.FantasyLow]: "\u30ED\u30FC\u30D5\u30A1\u30F3\u30BF\u30B8\u30FC\u3014\u30D5\u30A1\u30F3\u30BF\u30B8\u30FC\u3015", [Genre.BungeiJyunbungei]: "\u7D14\u6587\u5B66\u3014\u6587\u82B8\u3015", [Genre.BungeiHumanDrama]: "\u30D2\u30E5\u30FC\u30DE\u30F3\u30C9\u30E9\u30DE\u3014\u6587\u82B8\u3015", [Genre.BungeiHistory]: "\u6B74\u53F2\u3014\u6587\u82B8\u3015", [Genre.BungeiSuiri]: "\u63A8\u7406\u3014\u6587\u82B8\u3015", [Genre.BungeiHorror]: "\u30DB\u30E9\u30FC\u3014\u6587\u82B8\u3015", [Genre.BungeiAction]: "\u30A2\u30AF\u30B7\u30E7\u30F3\u3014\u6587\u82B8\u3015", [Genre.BungeiComedy]: "\u30B3\u30E1\u30C7\u30A3\u30FC\u3014\u6587\u82B8\u3015", [Genre.SfVrgame]: "VR\u30B2\u30FC\u30E0\u3014SF\u3015", [Genre.SfSpace]: "\u5B87\u5B99\u3014SF\u3015", [Genre.SfKuusoukagaku]: "\u7A7A\u60F3\u79D1\u5B66\u3014SF\u3015", [Genre.SfPanic]: "\u30D1\u30CB\u30C3\u30AF\u3014SF\u3015", [Genre.SonotaDouwa]: "\u7AE5\u8A71\u3014\u305D\u306E\u4ED6\u3015", [Genre.SonotaShi]: "\u8A69\u3014\u305D\u306E\u4ED6\u3015", [Genre.SonotaEssei]: "\u30A8\u30C3\u30BB\u30A4\u3014\u305D\u306E\u4ED6\u3015", [Genre.SonotaReplay]: "\u30EA\u30D7\u30EC\u30A4\u3014\u305D\u306E\u4ED6\u3015", [Genre.SonotaSonota]: "\u305D\u306E\u4ED6\u3014\u305D\u306E\u4ED6\u3015", [Genre.NonGenre]: "\u30CE\u30F3\u30B8\u30E3\u30F3\u30EB\u3014\u30CE\u30F3\u30B8\u30E3\u30F3\u30EB\u3015" }; var BuntaiParam = { /** 字下げされておらず、連続改行が多い作品 */ NoJisageKaigyouOoi: 1, /** 字下げされていないが、改行数は平均な作品 */ NoJisageKaigyoHutsuu: 2, /** 字下げが適切だが、連続改行が多い作品 */ JisageKaigyoOoi: 4, /** 字下げが適切でかつ改行数も平均な作品 */ JisageKaigyoHutsuu: 6 }; var StopParam = { /** 長期連載停止中を除きます */ NoStopping: 1, /** 長期連載停止中のみ取得します */ Stopping: 2 }; var NovelTypeParam = { /** 短編 */ Short: "t", /** 連載中 */ RensaiNow: "r", /** 完結済連載小説 */ RensaiEnd: "er", /** すべての連載小説(連載中および完結済) */ Rensai: "re", /** 短編と完結済連載小説 */ ShortAndRensai: "ter" }; var DateParam = { ThisWeek: "thisweek", LastWeek: "lastweek", SevenDays: "sevenday", ThisMonth: "thismonth", LastMonth: "lastmonth" }; var UserOrder = { /** ユーザIDの新しい順 */ New: "new", /** 小説投稿数の多い順 */ NovelCount: "novelcnt", /** レビュー投稿数の多い順 */ ReviewCount: "reviewcnt", /** 小説累計文字数の多い順 */ NovelLength: "novellength", /** 総合評価ポイントの合計の多い順 */ SumGlobalPoint: "sumglobalpoint", /** ユーザIDの古い順 */ Old: "old" }; // src/search-builder.ts var SearchBuilderBase = class { /** * constructor * @private * @param params クエリパラメータ * @param api NarouNovel インスタンス */ constructor(params = {}, api) { this.params = params; this.api = api; } /** * 配列から重複を除去する * @protected * @static * @param array 配列 * @returns 重複を除去した配列 */ static distinct(array) { return Array.from(new Set(array)); } /** * 配列をハイフン区切りの文字列に変換する * @protected * @static * @param n 文字列または数値の配列、あるいは単一の文字列または数値 * @returns ハイフン区切りの文字列 */ static array2string(n) { if (Array.isArray(n)) { return this.distinct(n).join("-"); } else { return n.toString(); } } /** * 取得件数を指定する (lim) * @param num 取得件数 (1-500) * @return {this} */ limit(num) { this.set({ lim: num }); return this; } /** * 取得開始位置を指定する (st) * @param num 取得開始位置 (1-) * @return {this} */ start(num) { this.set({ st: num }); return this; } /** * ページ番号と1ページあたりの件数で取得範囲を指定する * @param no ページ番号 (0-) * @param count 1ページあたりの件数 (デフォルト: 20) * @return {this} */ page(no, count2 = 20) { return this.limit(count2).start(no * count2); } /** * 出力順序を指定する (order) * 指定しない場合は新着順となります。 * @param {TOrder} order 出力順序 * @return {this} */ order(order) { this.set({ order }); return this; } /** * gzip圧縮レベルを指定する (gzip) * * 転送量上限を減らすためにも推奨 * @param {GzipLevel} level gzip圧縮レベル(1~5) * @return {this} */ gzip(level) { this.set({ gzip: level }); return this; } /** * クエリパラメータをセットする * @protected * @param obj セットするパラメータ * @return {this} */ set(obj) { this.params = { ...this.params, ...obj }; return this; } /** * クエリパラメータを削除する * @protected * @param key 削除するパラメータのキー * @returns {this} */ unset(key) { delete this.params[key]; return this; } }; var NovelSearchBuilderBase = class _NovelSearchBuilderBase extends SearchBuilderBase { /** * 検索語を指定します (word)。 * 半角または全角スペースで区切るとAND抽出になります。部分一致でHITします。 * @param word 検索語 * @return {this} */ word(word) { this.set({ word }); return this; } /** * 除外したい単語を指定します (notword)。 * スペースで区切ることにより除外する単語を増やせます。部分一致で除外されます。 * @param word 除外語 * @return {this} */ notWord(word) { this.set({ notword: word }); return this; } /** * 検索対象を作品名に限定するかどうかを指定します (title)。 * @param bool trueの場合、作品名を検索対象とする (デフォルト: true) * @return {this} */ byTitle(bool = true) { this.set({ title: bool ? BooleanNumber.True : BooleanNumber.False }); return this; } /** * 検索対象をあらすじに限定するかどうかを指定します (ex)。 * @param bool trueの場合、あらすじを検索対象とする (デフォルト: true) * @return {this} */ byOutline(bool = true) { this.set({ ex: bool ? BooleanNumber.True : BooleanNumber.False }); return this; } /** * 検索対象をキーワードに限定するかどうかを指定します (keyword)。 * @param bool trueの場合、キーワードを検索対象とする (デフォルト: true) * @return {this} */ byKeyword(bool = true) { this.set({ keyword: bool ? BooleanNumber.True : BooleanNumber.False }); return this; } /** * 検索対象を作者名に限定するかどうかを指定します (wname)。 * @param bool trueの場合、作者名を検索対象とする (デフォルト: true) * @return {this} */ byAuthor(bool = true) { this.set({ wname: bool ? BooleanNumber.True : BooleanNumber.False }); return this; } /** * ボーイズラブ作品を抽出または除外します (isbl/notbl)。 * @param bool trueの場合、ボーイズラブ作品を抽出する (デフォルト: true)。falseの場合、除外する。 * @return {this} */ isBL(bool = true) { if (bool) { this.set({ isbl: BooleanNumber.True }); } else { this.set({ notbl: BooleanNumber.True }); } return this; } /** * ガールズラブ作品を抽出または除外します (isgl/notgl)。 * @param bool trueの場合、ガールズラブ作品を抽出する (デフォルト: true)。falseの場合、除外する。 * @return {this} */ isGL(bool = true) { if (bool) { this.set({ isgl: BooleanNumber.True }); } else { this.set({ notgl: BooleanNumber.True }); } return this; } /** * 残酷な描写あり作品を抽出または除外します (iszankoku/notzankoku)。 * @param bool trueの場合、残酷な描写あり作品を抽出する (デフォルト: true)。falseの場合、除外する。 * @return {this} */ isZankoku(bool = true) { if (bool) { this.set({ iszankoku: BooleanNumber.True }); } else { this.set({ notzankoku: BooleanNumber.True }); } return this; } /** * 異世界転生作品を抽出または除外します (istensei/nottensei)。 * @param bool trueの場合、異世界転生作品を抽出する (デフォルト: true)。falseの場合、除外する。 * @return {this} */ isTensei(bool = true) { if (bool) { this.set({ istensei: BooleanNumber.True }); } else { this.set({ nottensei: BooleanNumber.True }); } return this; } /** * 異世界転移作品を抽出または除外します (istenni/nottenni)。 * @param bool trueの場合、異世界転移作品を抽出する (デフォルト: true)。falseの場合、除外する。 * @return {this} */ isTenni(bool = true) { if (bool) { this.set({ istenni: BooleanNumber.True }); } else { this.set({ nottenni: BooleanNumber.True }); } return this; } /** * 異世界転生または異世界転移作品を抽出します (istt)。 * @return {this} */ isTT() { this.set({ istt: BooleanNumber.True }); return this; } /** * 抽出する作品の文字数を指定します (length)。 * 範囲指定する場合は、最小文字数と最大文字数をハイフン(-)記号で区切ってください。 * @param length 文字数、または[最小文字数, 最大文字数] * @return {this} */ length(length) { this.set({ length: _NovelSearchBuilderBase.array2string(length) }); return this; } kaiwaritu(min, max) { let n; if (max != null) { n = `${min}-${max}`; } else { n = min; } this.set({ kaiwaritu: n }); return this; } /** * 抽出する作品の挿絵数を指定します (sasie)。 * @param num 挿絵数、または[最小挿絵数, 最大挿絵数] * @return {this} */ sasie(num) { this.set({ sasie: _NovelSearchBuilderBase.array2string(num) }); return this; } /** * 抽出する作品の予想読了時間を分単位で指定します (time)。 * @param num 読了時間(分)、または[最小読了時間, 最大読了時間] * @return {this} */ time(num) { this.set({ time: _NovelSearchBuilderBase.array2string(num) }); return this; } /** * Nコードを指定して取得します (ncode)。 * @param ncodes Nコード、またはNコードの配列 * @return {this} */ ncode(ncodes) { this.set({ ncode: _NovelSearchBuilderBase.array2string(ncodes) }); return this; } /** * 抽出する小説タイプを指定します (type)。 * @param type 小説タイプ (t: 短編, r: 連載中, er: 完結済連載小説, ter: 短編と完結済連載小説, re: 連載中と完結済連載小説) * @return {this} */ type(type) { this.set({ type }); return this; } /** * 抽出する作品の文体を指定します (buntai)。 * 複数指定する場合はハイフン(-)で区切ってください。 * @param buntai 文体コード、または文体コードの配列 * @return {this} */ buntai(buntai) { this.set({ buntai: _NovelSearchBuilderBase.array2string(buntai) }); return this; } /** * 連載停止中作品に関する指定をします (stop)。 * @param bool trueの場合、長期連載停止中のみ取得する (デフォルト: true)。falseの場合、長期連載停止中を除外する。 * @return {this} */ isStop(bool = true) { this.set({ stop: bool ? StopParam.Stopping : StopParam.NoStopping }); return this; } /** * ピックアップ作品のみを取得します (ispickup)。 * @return {this} */ isPickup() { this.set({ ispickup: BooleanNumber.True }); return this; } lastUpdate(x, y) { let date; if (typeof x == "string") { date = x; } else if (x instanceof Date && y instanceof Date) { date = `${Math.floor(x.getTime() / 1e3)}-${Math.floor( y.getTime() / 1e3 )}`; } else { date = `${x}-${y}`; } this.set({ lastup: date }); return this; } lastNovelUpdate(x, y) { let date; if (typeof x == "string") { date = x; } else if (x instanceof Date && y instanceof Date) { date = `${Math.floor(x.getTime() / 1e3)}-${Math.floor( y.getTime() / 1e3 )}`; } else { date = `${x}-${y}`; } this.set({ lastupdate: date }); return this; } /** * なろう小説APIへの検索リクエストを実行する * @returns {Promise<NarouSearchResults>} 検索結果 */ execute() { return this.api.executeNovel(this.params); } }; var SearchBuilder = class _SearchBuilder extends NovelSearchBuilderBase { /** * 大ジャンルを指定して取得します (biggenre)。 * 複数指定する場合はハイフン(-)で区切ってください。 * @param genre 大ジャンルコード、または大ジャンルコードの配列 * @return {this} */ bigGenre(genre) { this.set({ biggenre: _SearchBuilder.array2string(genre) }); return this; } /** * 除外したい大ジャンルを指定します (notbiggenre)。 * 複数指定する場合はハイフン(-)で区切ってください。 * @param genre 除外する大ジャンルコード、または大ジャンルコードの配列 * @return {this} */ notBigGenre(genre) { this.set({ notbiggenre: _SearchBuilder.array2string(genre) }); return this; } /** * ジャンルを指定して取得します (genre)。 * 複数指定する場合はハイフン(-)で区切ってください。 * @param genre ジャンルコード、またはジャンルコードの配列 * @return {this} */ genre(genre) { this.set({ genre: _SearchBuilder.array2string(genre) }); return this; } /** * 除外したいジャンルを指定します (notgenre)。 * 複数指定する場合はハイフン(-)で区切ってください。 * @param genre 除外するジャンルコード、またはジャンルコードの配列 * @return {this} */ notGenre(genre) { this.set({ notgenre: _SearchBuilder.array2string(genre) }); return this; } /** * ユーザIDを指定して取得します (userid)。 * 複数指定する場合はハイフン(-)で区切ってください。 * @param ids ユーザID、またはユーザIDの配列 * @return {this} */ userId(ids) { this.set({ userid: _SearchBuilder.array2string(ids) }); return this; } /** * R15作品を抽出または除外します (isr15/notr15)。 * @param bool trueの場合、R15作品を抽出する (デフォルト: true)。falseの場合、除外する。 * @return {this} */ isR15(bool = true) { if (bool) { this.set({ isr15: 1 }); } else { this.set({ notr15: 1 }); } return this; } /** * 出力する項目を個別に指定します (of)。 * 未指定時は全項目出力されます。転送量軽減のため、このパラメータの使用が推奨されます。 * 複数項目を出力する場合はハイフン(-)記号で区切ってください。 * @param fields 出力するフィールド名、またはフィールド名の配列 * @return {SearchBuilder<SearchResultFields<TFields>, TOpt>} 型が更新されたビルダー */ fields(fields) { this.set({ of: _SearchBuilder.array2string(fields) }); return this; } /** * 出力オプション項目を指定します (opt)。 * 複数項目を出力する場合はハイフン(-)記号で区切ってください。 * @param option 出力するオプションフィールド名、またはオプションフィールド名の配列 * @return {SearchBuilder<T, SearchResultOptionalFields<TFields>>} 型が更新されたビルダー */ opt(option) { this.set({ opt: _SearchBuilder.array2string(option) }); return this; } }; // src/search-builder-r18.ts var SearchBuilderR18 = class extends NovelSearchBuilderBase { /** * なろう小説APIへの検索リクエストを実行する * @override * @returns {Promise<NarouSearchResults>} 検索結果 */ execute() { return this.api.executeNovel18(this.params); } /** * 抽出するR18サイトを指定します (nocgenre)。 * @param sites R18サイトコード、またはR18サイトコードの配列 (1: ノクターンノベルズ, 2: ムーンライトノベルズ(男性向け), 3: ムーンライトノベルズ(BL), 4: ミッドナイトノベルズ) * @return {this} */ r18Site(sites) { this.set({ nocgenre: NovelSearchBuilderBase.array2string(sites) }); return this; } /** * X-IDを指定して取得します (xid)。 * @param ids X-ID、またはX-IDの配列 * @return {this} */ xid(ids) { this.set({ xid: NovelSearchBuilderBase.array2string(ids) }); return this; } /** * 出力する項目を個別に指定します (of)。 * 未指定時は全項目出力されます。転送量軽減のため、このパラメータの使用が推奨されます。 * @param fields 出力するR18フィールド名、またはR18フィールド名の配列 * @return {SearchBuilderR18<SearchResultR18Fields<R18Fields>>} 型が更新されたビルダー */ fields(fields) { this.set({ of: NovelSearchBuilderBase.array2string(fields) }); return this; } /** * 出力オプション項目を指定します (opt)。 * @param option 出力するオプションフィールド名、またはオプションフィールド名の配列 * @return {SearchBuilderR18<T, SearchResultOptionalFields<TFields>>} 型が更新されたビルダー */ opt(option) { this.set({ opt: NovelSearchBuilderBase.array2string(option) }); return this; } }; // src/util/date.ts function parseDate(dateStr) { const year = parseInt(dateStr.substring(0, 4), 10); const month = parseInt(dateStr.substring(4, 6), 10) - 1; const day = parseInt(dateStr.substring(6, 8), 10); return new Date(year, month, day, 0, 0, 0, 0); } function formatDate(date) { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, "0"); const day = String(date.getDate()).padStart(2, "0"); return `${year}${month}${day}`; } function addDays(date, days) { const result = new Date(date); result.setDate(result.getDate() + days); return result; } // src/ranking.ts var RankingBuilder = class { /** * constructor * @param params - 初期クエリパラメータ * @param api - API実行クラスのインスタンス * @private */ constructor(params = {}, api) { this.params = params; this.api = api; this.date$ = addDays(/* @__PURE__ */ new Date(), -1); this.type$ = RankingType.Daily; } /** * ランキング集計対象の日付を指定します。 * * - 日間: 任意の日付 * - 週間: 火曜日の日付 * - 月間・四半期: 1日の日付 * * @param date 集計対象の日付 * @returns {RankingBuilder} this * @see https://dev.syosetu.com/man/rankapi/ */ date(date) { this.date$ = date; return this; } /** * ランキング種別を指定します。 * @param type ランキング種別 * @returns {RankingBuilder} this * @see https://dev.syosetu.com/man/rankapi/ */ type(type) { this.type$ = type; return this; } /** * gzip圧縮する。 * * 転送量上限を減らすためにも推奨 * @param {GzipLevel} level gzip圧縮レベル(1~5) * @return {RankingBuilder} this */ gzip(level) { this.set({ gzip: level }); return this; } /** * クエリパラメータを内部的にセットします。 * @param obj - セットするパラメータオブジェクト * @returns {RankingBuilder} this * @private */ set(obj) { Object.assign(this.params, obj); return this; } /** * 設定されたパラメータに基づき、なろう小説ランキングAPIへのリクエストを実行します。 * * 返される結果には、Nコード、ポイント、順位が含まれます。 * @returns {Promise<NarouRankingResult[]>} ランキング結果の配列 * @see https://dev.syosetu.com/man/rankapi/#output */ execute() { const date = formatDate(this.date$); this.set({ rtype: `${date}-${this.type$}` }); return this.api.executeRanking(this.params); } /** * ランキングAPIを実行し、取得したNコードを元になろう小説APIで詳細情報を取得して結合します。 * * @template TFields - 取得する小説情報のフィールド型 * @template TOpt - オプショナルな取得フィールドの型 * @param fields - 取得するフィールドの配列 (省略時はデフォルトフィールド) * @param opt - オプショナルな取得フィールド (`weekly` など) * @returns {Promise<RankingResult<SearchResultFields<TFields>>[]>} 詳細情報を含むランキング結果の配列 */ async executeWithFields(fields = [], opt) { const ranking2 = await this.execute(); const fields$ = Array.isArray(fields) ? fields.length == 0 ? [] : [...fields, Fields.ncode] : [fields, Fields.ncode]; const rankingNcodes = ranking2.map(({ ncode }) => ncode); const builder = new SearchBuilder({}, this.api); builder.fields(fields$); if (opt) { builder.opt(opt); } builder.ncode(rankingNcodes); builder.limit(ranking2.length); const result = await builder.execute(); return ranking2.map((r) => ({ ...r, // eslint-disable-next-line @typescript-eslint/no-explicit-any ...result.values.find((novel) => novel.ncode == r.ncode) })); } }; // src/ranking-history.ts function formatRankingHistory(rankin) { const { rtype, pt, rank } = rankin; const [_date, _type] = rtype.split("-"); const date = parseDate(_date); const type = _type; return { type, date, pt, rank }; } // src/user-search.ts var UserSearchBuilder = class _UserSearchBuilder extends SearchBuilderBase { /** * 単語を指定できます。 * 半角または全角スペースで区切るとAND抽出になります。 * 部分一致でHITします。検索の対象はユーザ名とユーザ名のフリガナです。 */ word(word) { this.set({ word }); return this; } /** * 含みたくない単語を指定できます。 * スペースで区切ることにより含ませない単語を増やせます。部分一致で除外されます。 * 除外の対象はユーザ名とユーザ名のフリガナです。 */ notWord(notword) { this.set({ notword }); return this; } /** * ユーザIDで抽出可能。 */ userId(userid) { this.set({ userid }); return this; } /** * 抽出するユーザのユーザ名のフリガナの頭文字を指定できます。 * 頭文字はユーザ名のフリガナをひらがなに変換し、最初の1文字が「ぁ」~「ん」の場合に対象となります。 * 「ぱ」や「ば」等の半濁音や濁音は清音として扱われます。 * 漢字や英数字が頭文字のユーザは対象外です。 */ name1st(name1st) { this.set({ name1st }); return this; } /** * 抽出するユーザの小説投稿数の下限を指定できます。 * 小説投稿件数が指定された数値以上のユーザを抽出します。 */ minNovel(minnovel) { this.set({ minnovel }); return this; } /** * 抽出するユーザの小説投稿数の上限を指定できます。 * 小説投稿件数が指定された数値以下のユーザを抽出します。 */ maxNovel(maxnovel) { this.set({ maxnovel }); return this; } /** * 抽出するユーザのレビュー投稿数の下限を指定できます。 * レビュー投稿件数が指定された数値以上のユーザを抽出します。 */ minReview(minreview) { this.set({ minreview }); return this; } /** * 抽出するユーザのレビュー投稿数の上限を指定できます。 * レビュー投稿件数が指定された数値以下のユーザを抽出します。 */ maxReview(maxreview) { this.set({ maxreview }); return this; } /** * 出力する項目を個別に指定できます。未指定時は全項目出力されます。転送量軽減のため、このパラメータの使用が推奨されます。 * @return {SearchBuilder} this */ fields(fields) { this.set({ of: _UserSearchBuilder.array2string(fields) }); return this; } /** * なろう小説APIへのリクエストを実行する * @returns ランキング */ execute() { return this.api.executeUserSearch(this.params); } }; // src/index.browser.ts var narouNovelJsonp = new NarouNovelJsonp(); function search(word = "", api = narouNovelJsonp) { const builder = new SearchBuilder({}, api); if (word != "") builder.word(word); return builder; } function searchR18(word = "", api = narouNovelJsonp) { const builder = new SearchBuilderR18({}, api); if (word != "") builder.word(word); return builder; } function searchUser(word = "", api = narouNovelJsonp) { const builder = new UserSearchBuilder({}, api); if (word != "") builder.word(word); return builder; } function ranking(api = narouNovelJsonp) { const builder = new RankingBuilder({}, api); return builder; } async function rankingHistory(ncode, api = narouNovelJsonp) { const result = await api.executeRankingHistory({ ncode }); if (Array.isArray(result)) { return result.map(formatRankingHistory); } else { throw new Error(result); } } var index_browser_default = { search, searchR18, searchUser, ranking, rankingHistory }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { BigGenre, BigGenreNotation, BooleanNumber, BuntaiParam, DateParam, End, Fields, Genre, GenreNotation, NarouNovel, NarouNovelJsonp, NarouSearchResults, NovelSearchBuilderBase, NovelType, NovelTypeParam, OptionalFields, Order, R18Fields, R18Site, R18SiteNotation, RankingBuilder, RankingType, SearchBuilder, SearchBuilderBase, SearchBuilderR18, StopParam, UserFields, UserOrder, formatRankingHistory, ranking, rankingHistory, search, searchR18, searchUser }); //# sourceMappingURL=index.browser.cjs.map