{"version":3,"sources":["../src/narou-jsonp.ts","../src/narou-search-results.ts","../src/narou.ts","../src/util/jsonp.ts"],"sourcesContent":["import NarouNovel from \"./narou.js\";\nimport type { NarouParams } from \"./narou.js\";\nimport { jsonp } from \"./util/jsonp.js\";\n\n/**\n * なろう小説APIへのリクエストを実行する\n */\nexport default class NarouNovelJsonp extends NarouNovel {\n  protected async execute<T>(\n    params: NarouParams,\n    endpoint: string\n  ): Promise<T> {\n    const query = { ...params, out: \"jsonp\" };\n    query.gzip = 0;\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    return await jsonp(url.toString());\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","/**\n * MIT license\n */\n\n// Callback index.\nlet count = 0;\n\ntype CallbackId<Prefix extends string = string> = `${Prefix}${number}`;\n\ndeclare global {\n  interface Window {\n    // eslint-disable-next-line @typescript-eslint/no-explicit-any\n    [key: CallbackId]: (data: any) => void;\n  }\n}\n\n/**\n * JSONP呼び出しのオプション設定\n */\nexport type JsonpOption = {\n  /**\n   * コールバック関数名のプレフィックス\n   * @default \"__jp\"\n   */\n  prefix?: string;\n  \n  /**\n   * コールバック関数名を指定するURLパラメータ名\n   * @default \"callback\"\n   */\n  param?: string;\n  \n  /**\n   * タイムアウト時間（ミリ秒）\n   * @default 15000\n   */\n  timeout?: number;\n};\n\nconst noop = function () { };\n\n/**\n * JSONPリクエストを実行してデータを取得します。\n * \n * @param url - リクエスト先のURL\n * @param options - JSONP呼び出しのオプション設定\n * @returns JSONPリクエストの結果をPromiseで返します\n * @throws {Error} タイムアウトが発生した場合、\"Timeout\"メッセージのエラーをスローします\n * \n * @example\n * ```typescript\n * // 基本的な使用方法\n * const data = await jsonp<ResponseType>('https://example.com/api');\n * \n * // オプション指定\n * const data = await jsonp<ResponseType>('https://example.com/api', {\n *   prefix: 'customPrefix',\n *   param: 'callbackParam',\n *   timeout: 10000\n * });\n * ```\n */\nexport function jsonp<T>(\n  url: string,\n  { prefix = \"__jp\", param = \"callback\", timeout = 15000 }: JsonpOption = {}\n): Promise<T> {\n  return new Promise(function (resolve, reject) {\n    // 最初のscriptタグを取得し、そのタグの直前に新しいscriptタグを挿入するための参照を取得\n    // これにより、ページの構造を大きく変えることなくscriptを追加できる\n    const targetChild = document.getElementsByTagName(\"script\").item(0);\n    const target = targetChild?.parentNode ?? document.head;\n\n    // ユニークなコールバック関数名を生成\n    const id: CallbackId = `${prefix}${count++}`;\n    \n    // リソース解放用の関数を定義\n    // スクリプトタグの削除、コールバック関数のクリーンアップ、タイマーのクリアを行う\n    const cleanup = function () {\n      // Remove the script tag.\n      if (script && script.parentNode) {\n        script.parentNode.removeChild(script);\n      }\n\n      // コールバック関数を空の関数に置き換えてメモリリークを防止\n      window[id] = noop;\n\n      if (timer) {\n        clearTimeout(timer);\n      }\n    };\n\n    // タイムアウト処理の設定\n    // 指定された時間内にレスポンスがない場合はエラーとして処理\n    const timer =\n      timeout > 0\n        ? setTimeout(() => {\n          cleanup();\n          reject(new Error(\"Timeout\"));\n        }, timeout)\n        : undefined;\n\n    // サーバーからのレスポンスを処理するコールバック関数\n    const callback = (data: T) => {\n      cleanup();\n      resolve(data);\n    };\n    \n    // グローバルスコープにコールバック関数を登録\n    // これによりJSONPのレスポンスから関数が呼び出せるようになる\n    window[id] = callback;\n\n    // JSONPリクエスト用のscriptタグを作成\n    const script = document.createElement(\"script\");\n    const urlObj = new URL(url);\n    \n    // URLにコールバック関数名をパラメータとして追加\n    urlObj.searchParams.set(param, id);\n    script.setAttribute(\"src\", urlObj.toString());\n    \n    // DOMにscriptタグを挿入し、リクエストを開始\n    target.insertBefore(script, targetChild);\n  });\n}\n"],"mappings":"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","names":["count"]}