{"version":3,"sources":["../src/narou-fetch.ts","../src/util/unzipp.ts","../src/narou-search-results.ts","../src/narou.ts"],"sourcesContent":["import { unzipp } from \"./util/unzipp.js\";\nimport NarouNovel from \"./narou.js\";\nimport type { NarouParams } from \"./narou.js\";\n\ntype Fetch = typeof fetch;\n\n/**\n * なろう小説APIへのリクエストを実行する\n */\nexport default class NarouNovelFetch extends NarouNovel {\n  /**\n   * コンストラクタ\n   * @param fetch fetch関数（デフォルトはネイティブのfetch）\n   */\n  constructor(private fetch?: Fetch) {\n    super();\n  }\n\n  protected async execute<T>(\n    params: NarouParams,\n    endpoint: string\n  ): Promise<T> {\n    const query = { ...params, out: \"json\" };\n\n    if (query.gzip === undefined) {\n      query.gzip = 5;\n    }\n    if (query.gzip === 0) {\n      delete query.gzip;\n    }\n    const url = new URL(endpoint);\n\n    Object.entries(query).forEach(([key, value]) => {\n      if (value !== undefined) {\n        url.searchParams.append(key, value.toString());\n      }\n    });\n\n    const res = await (this.fetch ?? fetch)(url);\n\n    if (!query.gzip) {\n      return (await res.json()) as T;\n    }\n\n    const buffer = await res.arrayBuffer();\n    return await unzipp(buffer);\n  }\n}\n","import { gunzip, InputType } from \"zlib\";\nimport { promisify } from \"util\";\n\nconst gunzipAsync = promisify<InputType, Buffer>(gunzip);\n\nconst decoder = new TextDecoder()\n/**\n * 圧縮されたJSONデータを解凍して解析します。\n * \n * @param data - ArrayBuffer形式の圧縮データ\n * @returns 解凍されたデータからパースされたJSONオブジェクト\n * @throws {string} データが解凍できない、または解凍されたデータが有効なJSONでない場合、\n *                  解凍されたデータの文字列表現をスローします。\n * @throws {string} 解凍中にエラーが発生した場合、元のデータの文字列表現をスローします。\n */\nexport async function unzipp(data: ArrayBuffer) {\n  try {\n    const buffer = await gunzipAsync(data);\n    try {\n      return JSON.parse(decoder.decode(buffer));\n    } catch {\n      throw decoder.decode(buffer);\n    }\n  } catch (e) {\n    if (typeof e === \"string\") throw e;\n    throw decoder.decode(data);\n  }\n}\n","import type {\n  BooleanNumber as BooleanNumber,\n  Genre,\n  R18Site,\n  SearchParams,\n  Fields,\n  BigGenre,\n  R18Fields,\n  OptionalFields,\n  UserFields,\n  UserSearchParams,\n} from \"./params.js\";\n\n/**\n * なろう小説API検索結果\n */\nexport default class NarouSearchResults<T, TKey extends keyof T> {\n  /**\n   * 検索結果数\n   */\n  allcount: number;\n  /**\n   * 結果表示上限数\n   */\n  limit: number;\n  /**\n   * 結果表示開始数\n   */\n  start: number;\n  /**\n   * 結果表示の現在ページ(=start/limit)\n   */\n  page: number;\n  /**\n   * 今回取得できた検索結果の数\n   */\n  length: number;\n  /**\n   * 検索結果\n   */\n  values: readonly Pick<T, TKey>[];\n\n  /**\n   * @constractor\n   * @private\n   */\n  constructor(\n    [header, ...result]: [{ allcount: number }, ...Pick<T, TKey>[]],\n    params: SearchParams | UserSearchParams\n  ) {\n    const count = header.allcount;\n    const limit = params.lim ?? 20;\n    const start = params.st ?? 0;\n\n    this.allcount = count;\n    this.limit = limit;\n    this.start = start;\n    this.page = start / limit;\n    this.length = result.length;\n    this.values = result;\n  }\n}\n\n/**\n * 小説情報\n * @see https://dev.syosetu.com/man/api/#output\n * @see https://dev.syosetu.com/xman/api/#output\n */\nexport interface NarouSearchResult {\n  /** 小説名 */\n  title: string;\n  /** Nコード */\n  ncode: string;\n  /** 作者のユーザID(数値) */\n  userid: number;\n  /** 作者名 */\n  writer: string;\n  /** 小説のあらすじ */\n  story: string;\n  /** 掲載サイト */\n  nocgenre: R18Site;\n  /** 大ジャンル */\n  biggenre: BigGenre;\n  /** ジャンル */\n  genre: Genre;\n  /** キーワード */\n  keyword: string;\n  /** 初回掲載日 YYYY-MM-DD HH:MM:SSの形式 */\n  general_firstup: string;\n  /** 最終掲載日 YYYY-MM-DD HH:MM:SSの形式 */\n  general_lastup: string;\n  /** 連載の場合は1、短編の場合は2 */\n  novel_type: NovelType;\n  /** 連載の場合は1、短編の場合は2 */\n  noveltype: NovelType;\n  /** 短編小説と完結済小説は0となっています。連載中は1です。 */\n  end: End;\n  /** 全掲載話数です。短編の場合は1です。 */\n  general_all_no: number;\n  /** 小説文字数です。スペースや改行は文字数としてカウントしません。 */\n  length: number;\n  /** 読了時間(分単位)です。読了時間は小説文字数÷500を切り上げした数値です。 */\n  time: number;\n  /** 長期連載中は1、それ以外は0です。 */\n  isstop: BooleanNumber;\n  /** 登録必須キーワードに「R15」が含まれる場合は1、それ以外は0です。 */\n  isr15: BooleanNumber;\n  /** 登録必須キーワードに「ボーイズラブ」が含まれる場合は1、それ以外は0です。 */\n  isbl: BooleanNumber;\n  /** 登録必須キーワードに「ガールズラブ」が含まれる場合は1、それ以外は0です。 */\n  isgl: BooleanNumber;\n  /** 登録必須キーワードに「残酷な描写あり」が含まれる場合は1、それ以外は0です。 */\n  iszankoku: BooleanNumber;\n  /** 登録必須キーワードに「異世界転生」が含まれる場合は1、それ以外は0です。 */\n  istensei: BooleanNumber;\n  /** 登録必須キーワードに「異世界転移」が含まれる場合は1、それ以外は0です。 */\n  istenni: BooleanNumber;\n  /** 総合得点(=(ブックマーク数×2)+評価点) */\n  global_point: number;\n  /**\n   * 日間ポイント\n   * ランキング集計時点から過去24時間以内で新たに登録されたブックマークや評価が対象\n   */\n  daily_point: number;\n  /**\n   * 週間ポイント\n   * ランキング集計時点から過去7日以内で新たに登録されたブックマークや評価が対象\n   */\n  weekly_point: number;\n  /**\n   * 月間ポイント\n   * ランキング集計時点から過去30日以内で新たに登録されたブックマークや評価が対象\n   */\n  monthly_point: number;\n  /**\n   * 四半期ポイント\n   * ランキング集計時点から過去90日以内で新たに登録されたブックマークや評価が対象\n   */\n  quarter_point: number;\n  /**\n   * 年間ポイント\n   * ランキング集計時点から過去365日以内で新たに登録されたブックマークや評価が対象\n   */\n  yearly_point: number;\n  /** ブックマーク数 */\n  fav_novel_cnt: number;\n  /** 感想数 */\n  impression_cnt: number;\n  /** レビュー数 */\n  review_cnt: number;\n  /** 評価ポイント */\n  all_point: number;\n  /** 評価者数 */\n  all_hyoka_cnt: number;\n  /** 挿絵の数 */\n  sasie_cnt: number;\n  /**\n   * 会話率\n   * @see https://dev.syosetu.com/man/kaiwa/\n   */\n  kaiwaritu: number;\n  /**\n   * 小説の更新日時\n   */\n  novelupdated_at: string;\n  /**\n   * 最終更新日時\n   * システム用で小説更新時とは関係ありません\n   */\n  updated_at: string;\n  /** 週間ユニークユーザー数 */\n  weekly_unique: number;\n}\n\n/**\n * ユーザ情報\n * @see https://dev.syosetu.com/man/userapi/#output\n */\nexport interface UserSearchResult {\n  /** ユーザID */\n  userid: number;\n  /** ユーザ名 */\n  name: string;\n  /** ユーザ名のフリガナ */\n  yomikata: string;\n  /**\n   * ユーザ名のフリガナの頭文字\n   * ひらがな以外の場合はnullまたは空文字となります。\n   */\n  name1st: string;\n  /** 小説投稿数 */\n  novel_cnt: number;\n  /** レビュー投稿数 */\n  review_cnt: number;\n  /**\n   * 小説累計文字数\n   * スペースや改行は文字数としてカウントしません。\n   */\n  novel_length: number;\n  /**\n   * 総合評価ポイントの合計\n   * 投稿済小説でそれぞれ獲得した総合評価ポイントの合計です。\n   */\n  sum_global_point: number;\n}\n\n/**\n * noveltype/novel_typeの値ヘルパー\n */\nexport const NovelType = {\n  /** 連載 */\n  Rensai: 1,\n  /** 短編 */\n  Tanpen: 2,\n} as const;\nexport type NovelType = typeof NovelType[keyof typeof NovelType];\n\n/**\n * endの値ヘルパー\n */\nexport const End = {\n  /** 短編小説と完結済小説 */\n  KanketsuOrTanpen: 0,\n  /** 連載中 */\n  Rensai: 1,\n} as const;\nexport type End = typeof End[keyof typeof End];\n\nexport type SearchResultFields<T extends Fields> = {\n  [K in keyof typeof Fields]: typeof Fields[K] extends T ? K : never;\n}[keyof typeof Fields];\n\nexport type SearchResultOptionalFields<T extends OptionalFields> = {\n  [K in keyof typeof OptionalFields]: typeof OptionalFields[K] extends T\n  ? K\n  : never;\n}[keyof typeof OptionalFields];\n\nexport type SearchResultR18Fields<T extends R18Fields> = {\n  [K in keyof typeof R18Fields]: typeof R18Fields[K] extends T ? K : never;\n}[keyof typeof R18Fields];\n\nexport type UserSearchResultFields<T extends UserFields> = {\n  [K in keyof typeof UserFields]: typeof UserFields[K] extends T ? K : never;\n}[keyof typeof UserFields];\n\nexport type PickedNarouSearchResult<T extends keyof NarouSearchResult> = Pick<\n  NarouSearchResult,\n  T\n>;\n","import type { NarouRankingResult } from \"./narou-ranking-results.js\";\nimport NarouSearchResults from \"./narou-search-results.js\";\nimport type {\n  NarouSearchResult,\n  UserSearchResult,\n} from \"./narou-search-results.js\";\nimport type {\n  RankingHistoryParams,\n  RankingParams,\n  SearchParams,\n  UserSearchParams,\n} from \"./params.js\";\nimport type { RankingHistoryRawResult } from \"./ranking-history.js\";\n\n/**\n * なろう小説APIへのリクエストパラメータ\n */\nexport type NarouParams =\n  | SearchParams\n  | RankingParams\n  | RankingHistoryParams\n  | UserSearchParams;\n\n/**\n * なろう小説APIへのリクエストを実行する\n * @class NarouNovel\n * @private\n */\nexport default abstract class NarouNovel {\n  /**\n   * なろうAPIへのAPIリクエストを実行する\n   * @param params クエリパラメータ\n   * @param endpoint APIエンドポイント\n   * @returns 実行結果\n   */\n  protected abstract execute<T>(\n    params: NarouParams,\n    endpoint: string\n  ): Promise<T>;\n\n  /**\n   * APIへの検索リクエストを実行する\n   * @param params クエリパラメータ\n   * @param endpoint APIエンドポイント\n   * @returns 検索結果\n   */\n  protected async executeSearch<T extends keyof NarouSearchResult>(\n    params: SearchParams,\n    endpoint = \"https://api.syosetu.com/novelapi/api/\"\n  ): Promise<NarouSearchResults<NarouSearchResult, T>> {\n    return new NarouSearchResults(await this.execute(params, endpoint), params);\n  }\n\n  /**\n   * 小説APIへの検索リクエストを実行する\n   * @param params クエリパラメータ\n   * @returns 検索結果\n   * @see https://dev.syosetu.com/man/api/\n   */\n  async executeNovel<T extends keyof NarouSearchResult>(\n    params: SearchParams\n  ): Promise<NarouSearchResults<NarouSearchResult, T>> {\n    return await this.executeSearch(\n      params,\n      \"https://api.syosetu.com/novelapi/api/\"\n    );\n  }\n\n  /**\n   * R18小説APIへの検索リクエストを実行する\n   * @param params クエリパラメータ\n   * @returns 検索結果\n   * @see https://dev.syosetu.com/xman/api/\n   */\n  async executeNovel18<T extends keyof NarouSearchResult>(\n    params: SearchParams\n  ): Promise<NarouSearchResults<NarouSearchResult, T>> {\n    return await this.executeSearch(\n      params,\n      \"https://api.syosetu.com/novel18api/api/\"\n    );\n  }\n\n  /**\n   * ランキングAPIへのリクエストを実行する\n   * @param params クエリパラメータ\n   * @returns ランキング結果\n   * @see https://dev.syosetu.com/man/rankapi/\n   */\n  async executeRanking(params: RankingParams): Promise<NarouRankingResult[]> {\n    return await this.execute(params, \"https://api.syosetu.com/rank/rankget/\");\n  }\n\n  /**\n   * 殿堂入りAPiへのリクエストを実行する\n   * @param params クエリパラメータ\n   * @returns ランキング履歴結果\n   * @see https://dev.syosetu.com/man/rankinapi/\n   */\n  async executeRankingHistory(\n    params: RankingHistoryParams\n  ): Promise<RankingHistoryRawResult[]> {\n    return await this.execute(params, \"https://api.syosetu.com/rank/rankin/\");\n  }\n\n  /**\n   * ユーザー検索APIへのリクエストを実行する\n   * @param params クエリパラメータ\n   * @returns 検索結果\n   * @see https://dev.syosetu.com/man/userapi/\n   */\n  async executeUserSearch<T extends keyof UserSearchResult>(\n    params: UserSearchParams\n  ): Promise<NarouSearchResults<UserSearchResult, T>> {\n    return new NarouSearchResults<UserSearchResult, T>(\n      await this.execute(params, \"https://api.syosetu.com/userapi/api/\"),\n      params\n    );\n  }\n}\n"],"mappings":"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","names":["fetch"]}