@wzyjs/utils 0.3.31 → 0.3.36

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.
package/dist/node.esm.js CHANGED
@@ -10240,6 +10240,12 @@ var replaceByVariables = (prompt, variables) => {
10240
10240
  return variables[p1] ?? match;
10241
10241
  });
10242
10242
  };
10243
+ var getSafeCallbackPath = (value, fallback) => {
10244
+ if (!value || !value.startsWith("/") || value.startsWith("//")) {
10245
+ return fallback;
10246
+ }
10247
+ return value;
10248
+ };
10243
10249
  var getType = (value) => {
10244
10250
  return Object.prototype.toString.call(value).slice(8, -1);
10245
10251
  };
@@ -10553,6 +10559,36 @@ var Timezone;
10553
10559
  Timezone2["EuropeBerlin"] = "Europe/Berlin";
10554
10560
  })(Timezone ||= {});
10555
10561
  var dayjs_default = dayjs;
10562
+ // src/node/cors.ts
10563
+ var exports_cors = {};
10564
+ __export(exports_cors, {
10565
+ createCorsPreflightResponse: () => createCorsPreflightResponse,
10566
+ createCorsHeaders: () => createCorsHeaders,
10567
+ applyCorsHeaders: () => applyCorsHeaders
10568
+ });
10569
+ var DEFAULT_ALLOW_HEADERS = ["Authorization", "Content-Type", "X-TRPC-Source"];
10570
+ var DEFAULT_ALLOW_METHODS = ["GET", "POST", "OPTIONS"];
10571
+ var DEFAULT_MAX_AGE = "86400";
10572
+ var createCorsHeaders = (request, options = {}) => {
10573
+ const origin = request.headers.get("origin") ?? "*";
10574
+ return {
10575
+ "Access-Control-Allow-Headers": (options.allowHeaders ?? DEFAULT_ALLOW_HEADERS).join(", "),
10576
+ "Access-Control-Allow-Methods": (options.allowMethods ?? DEFAULT_ALLOW_METHODS).join(", "),
10577
+ "Access-Control-Allow-Origin": origin,
10578
+ "Access-Control-Max-Age": options.maxAge ?? DEFAULT_MAX_AGE,
10579
+ Vary: "Origin"
10580
+ };
10581
+ };
10582
+ var applyCorsHeaders = (request, response, options) => {
10583
+ Object.entries(createCorsHeaders(request, options)).forEach(([key2, value]) => {
10584
+ response.headers.set(key2, value);
10585
+ });
10586
+ return response;
10587
+ };
10588
+ var createCorsPreflightResponse = (request, options) => new Response(null, {
10589
+ headers: createCorsHeaders(request, options),
10590
+ status: 204
10591
+ });
10556
10592
  // src/node/oss/index.ts
10557
10593
  var exports_oss = {};
10558
10594
  __export(exports_oss, {
@@ -10721,7 +10757,11 @@ var exports_file = {};
10721
10757
  __export(exports_file, {
10722
10758
  replaceContentInFile: () => replaceContentInFile,
10723
10759
  processFile: () => processFile,
10760
+ parseBase64File: () => parseBase64File,
10761
+ normalizeDataUrlBase64: () => normalizeDataUrlBase64,
10724
10762
  getMimeType: () => getMimeType,
10763
+ getExtFromMimeType: () => getExtFromMimeType,
10764
+ getExtFromFileName: () => getExtFromFileName,
10725
10765
  downloadFile: () => downloadFile
10726
10766
  });
10727
10767
  import fs from "fs-extra";
@@ -10729,53 +10769,62 @@ import axios6 from "axios";
10729
10769
  import url2 from "url";
10730
10770
  import * as path2 from "path";
10731
10771
  import crypto2 from "crypto";
10772
+ var mimeExtMap = {
10773
+ "image/png": ["png"],
10774
+ "image/jpeg": ["jpg", "jpeg"],
10775
+ "image/gif": ["gif"],
10776
+ "image/webp": ["webp"],
10777
+ "image/svg+xml": ["svg"],
10778
+ "video/mp4": ["mp4"],
10779
+ "audio/mpeg": ["mp3"],
10780
+ "application/pdf": ["pdf"],
10781
+ "application/msword": ["doc"],
10782
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document": ["docx"],
10783
+ "application/vnd.ms-excel": ["xls"],
10784
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": ["xlsx"],
10785
+ "application/vnd.ms-powerpoint": ["ppt"],
10786
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation": ["pptx"],
10787
+ "text/plain": ["txt"],
10788
+ "text/html": ["html"],
10789
+ "text/css": ["css"],
10790
+ "application/javascript": ["js"],
10791
+ "application/json": ["json"],
10792
+ "application/xml": ["xml"]
10793
+ };
10794
+ var getExtFromFileName = (fileName) => {
10795
+ return fileName ? path2.extname(fileName).toLowerCase() : "";
10796
+ };
10797
+ var getExtFromMimeType = (mimeType) => {
10798
+ const ext = mimeType ? mimeExtMap[mimeType]?.[0] : "";
10799
+ return ext ? `.${ext}` : "";
10800
+ };
10732
10801
  var getMimeType = (fileName) => {
10733
- const ext = fileName.split(".").pop()?.toLowerCase();
10734
- switch (ext) {
10735
- case "png":
10736
- return "image/png";
10737
- case "jpg":
10738
- case "jpeg":
10739
- return "image/jpeg";
10740
- case "gif":
10741
- return "image/gif";
10742
- case "webp":
10743
- return "image/webp";
10744
- case "svg":
10745
- return "image/svg+xml";
10746
- case "mp4":
10747
- return "video/mp4";
10748
- case "mp3":
10749
- return "audio/mpeg";
10750
- case "pdf":
10751
- return "application/pdf";
10752
- case "doc":
10753
- return "application/msword";
10754
- case "docx":
10755
- return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
10756
- case "xls":
10757
- return "application/vnd.ms-excel";
10758
- case "xlsx":
10759
- return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
10760
- case "ppt":
10761
- return "application/vnd.ms-powerpoint";
10762
- case "pptx":
10763
- return "application/vnd.openxmlformats-officedocument.presentationml.presentation";
10764
- case "txt":
10765
- return "text/plain";
10766
- case "html":
10767
- return "text/html";
10768
- case "css":
10769
- return "text/css";
10770
- case "js":
10771
- return "application/javascript";
10772
- case "json":
10773
- return "application/json";
10774
- case "xml":
10775
- return "application/xml";
10776
- default:
10777
- return "application/octet-stream";
10778
- }
10802
+ const ext = getExtFromFileName(fileName).replace(/^\./, "");
10803
+ const matched = Object.entries(mimeExtMap).find(([, extensions]) => extensions.includes(ext));
10804
+ return matched?.[0] || "application/octet-stream";
10805
+ };
10806
+ var generateUniqueFileName = (originalName = "") => {
10807
+ const ext = path2.extname(originalName);
10808
+ const nameWithoutExt = path2.basename(originalName, ext);
10809
+ const finalFileName = `${nameWithoutExt ? `${nameWithoutExt}_` : ""}${Date.now()}_${Math.random().toString(36).slice(2)}${ext}`;
10810
+ return {
10811
+ finalFileName,
10812
+ ext: ext.toLowerCase()
10813
+ };
10814
+ };
10815
+ var normalizeDataUrlBase64 = (value) => {
10816
+ const matched = value.match(/^data:([^;]+);base64,(.*)$/);
10817
+ return {
10818
+ base64: matched?.[2] || value,
10819
+ mimeType: matched?.[1]
10820
+ };
10821
+ };
10822
+ var parseBase64File = (input) => {
10823
+ const parsed = normalizeDataUrlBase64(input.fileBase64);
10824
+ return {
10825
+ buffer: Buffer.from(parsed.base64, "base64"),
10826
+ mimeType: input.mimeType || parsed.mimeType
10827
+ };
10779
10828
  };
10780
10829
  var replaceContentInFile = async (filePath, targetContent, replacement) => {
10781
10830
  const data = await fs.readFile(filePath, "utf8");
@@ -10789,8 +10838,8 @@ var replaceContentInFile = async (filePath, targetContent, replacement) => {
10789
10838
  };
10790
10839
  var downloadFile = async (httpUrl, outputPath) => {
10791
10840
  try {
10792
- let parsedUrl = url2.parse(httpUrl);
10793
- let fileName = path2.basename(parsedUrl.pathname);
10841
+ const parsedUrl = url2.parse(httpUrl);
10842
+ const fileName = path2.basename(parsedUrl.pathname);
10794
10843
  if (!outputPath) {
10795
10844
  if (!fs.existsSync(".tmp")) {
10796
10845
  fs.mkdirSync(".tmp", { recursive: true });
@@ -10814,15 +10863,6 @@ var downloadFile = async (httpUrl, outputPath) => {
10814
10863
  throw error;
10815
10864
  }
10816
10865
  };
10817
- var generateUniqueFileName = (originalName = "") => {
10818
- const ext = path2.extname(originalName);
10819
- const nameWithoutExt = path2.basename(originalName, ext);
10820
- const finalFileName = `${nameWithoutExt ? `${nameWithoutExt}_` : ""}${Date.now()}_${Math.random().toString(36).slice(2)}${ext}`;
10821
- return {
10822
- finalFileName,
10823
- ext: ext.toLowerCase()
10824
- };
10825
- };
10826
10866
  var processFile = async (params) => {
10827
10867
  const { file, type } = params;
10828
10868
  let buffer;
@@ -10885,7 +10925,1033 @@ var processFile = async (params) => {
10885
10925
  ext
10886
10926
  };
10887
10927
  };
10928
+ // src/node/crawl/index.ts
10929
+ var exports_crawl = {};
10930
+ __export(exports_crawl, {
10931
+ fanqie: () => exports_fanqie
10932
+ });
10933
+
10934
+ // src/node/crawl/fanqie/index.ts
10935
+ var exports_fanqie = {};
10936
+ __export(exports_fanqie, {
10937
+ fanqieContent: () => exports_content,
10938
+ fanqieChapter: () => exports_chapter,
10939
+ fanqieBook: () => exports_book
10940
+ });
10941
+
10942
+ // src/node/crawl/fanqie/book/index.ts
10943
+ var exports_book = {};
10944
+ __export(exports_book, {
10945
+ searchFanqieBooks: () => searchFanqieBooks,
10946
+ resolveFanqieLibraryCategoryId: () => resolveFanqieLibraryCategoryId,
10947
+ normalizeFanqieSearchResults: () => normalizeFanqieSearchResults,
10948
+ normalizeFanqieOfficialLibraryItem: () => normalizeFanqieOfficialLibraryItem,
10949
+ normalizeFanqieBookTags: () => normalizeFanqieBookTags,
10950
+ normalizeFanqieBookStatus: () => normalizeFanqieBookStatus,
10951
+ normalizeFanqieBookSearchItem: () => normalizeFanqieBookSearchItem,
10952
+ normalizeFanqieBookDetailItem: () => normalizeFanqieBookDetailItem,
10953
+ listFanqieLibraryBooks: () => listFanqieLibraryBooks,
10954
+ getFanqieLibraryFilters: () => getFanqieLibraryFilters,
10955
+ getFanqieBookDetailById: () => getFanqieBookDetailById,
10956
+ createFanqieSearchUrl: () => createFanqieSearchUrl
10957
+ });
10958
+
10959
+ // src/node/crawl/fanqie/utils/consts.ts
10960
+ var FANQIE_LIBRARY_LIST_URL = "https://fanqienovel.com/api/author/library/book_list/v0/";
10961
+ var FANQIE_LIBRARY_CATEGORY_URL = "https://fanqienovel.com/api/author/book/category_list/v0/";
10962
+ var FANQIE_BOOK_SOURCE_SEARCH_PATH = "/search";
10963
+ var FANQIE_BOOK_SOURCE_SEARCH_DEFAULT_SOURCE = "番茄";
10964
+ var FANQIE_BOOK_SOURCE_SEARCH_DEFAULT_TAB = "小说";
10965
+ var FANQIE_BOOK_SOURCE_SEARCH_DEFAULT_DISABLED_SOURCES = "0";
10966
+ var FANQIE_BOOK_SOURCE_CATALOG_PATH = "/catalog";
10967
+ var FANQIE_BOOK_SOURCE_CATALOG_SOURCE = "番茄";
10968
+ var FANQIE_BOOK_SOURCE_CATALOG_TAB = "小说";
10969
+ var FANQIE_BOOK_SOURCE_CATALOG_VARIABLE = { custom: "" };
10970
+ var FANQIE_BOOK_SOURCE_DETAIL_PATH = "/detail";
10971
+ var FANQIE_BOOK_SOURCE_DETAIL_SOURCE = "番茄";
10972
+ var FANQIE_BOOK_SOURCE_DETAIL_TAB = "小说";
10973
+ var FANQIE_BOOK_SOURCE_DETAIL_VARIABLE = { custom: "" };
10974
+ var DEFAULT_FANQIE_BOOK_SOURCE_BASE_URLS = [
10975
+ "https://api.langge.cf",
10976
+ "https://v2.czyl.cf",
10977
+ "http://219.154.201.122:5006"
10978
+ ];
10979
+ var FANQIE_PAGE_HEADERS = {
10980
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
10981
+ Referer: "https://fanqienovel.com/"
10982
+ };
10983
+ var FANQIE_BOOK_SOURCE_SEARCH_HEADERS = {
10984
+ "user-agent": "Mozilla/5.0"
10985
+ };
10986
+ var FANQIE_BOOK_SOURCE_CATALOG_HEADERS = {
10987
+ "content-type": "application/json",
10988
+ "user-agent": "Mozilla/5.0"
10989
+ };
10990
+ var FANQIE_BOOK_SOURCE_DETAIL_HEADERS = {
10991
+ "content-type": "application/json",
10992
+ "user-agent": "Mozilla/5.0"
10993
+ };
10994
+ var DEFAULT_FANQIE_OFFICIAL_SIDECAR_BASE_URL = "http://127.0.0.1:18423";
10995
+ var DEFAULT_FANQIE_OFFICIAL_SIDECAR_CONTAINER = "tomato-novel-webui";
10996
+ var DEFAULT_FANQIE_OFFICIAL_SIDECAR_JOB_TIMEOUT_MS = 5 * 60 * 1000;
10997
+ var DEFAULT_FANQIE_OFFICIAL_SIDECAR_JOB_POLL_INTERVAL_MS = 1500;
10998
+ var DEFAULT_FANQIE_OFFICIAL_SIDECAR_STATUS_TIMEOUT_MS = 15 * 1000;
10999
+ var DEFAULT_FANQIE_OFFICIAL_SIDECAR_STATUS_POLL_INTERVAL_MS = 1000;
11000
+
11001
+ // src/node/crawl/fanqie/utils/http.ts
11002
+ var fetchFanqieJson = async (url3, init) => {
11003
+ const response = await fetch(url3, {
11004
+ ...init,
11005
+ cache: "no-store"
11006
+ });
11007
+ if (!response.ok) {
11008
+ throw new Error(`番茄请求失败: ${response.status} ${url3}`);
11009
+ }
11010
+ return await response.json();
11011
+ };
11012
+
11013
+ // src/node/crawl/fanqie/utils/font-map.ts
11014
+ var FANQIE_OBFUSCATED_FONT_MAP = {
11015
+ "": "D",
11016
+ "": "在",
11017
+ "": "主",
11018
+ "": "特",
11019
+ "": "家",
11020
+ "": "军",
11021
+ "": "然",
11022
+ "": "表",
11023
+ "": "场",
11024
+ "": "4",
11025
+ "": "要",
11026
+ "": "只",
11027
+ "": "v",
11028
+ "": "和",
11029
+ "": "6",
11030
+ "": "别",
11031
+ "": "还",
11032
+ "": "g",
11033
+ "": "现",
11034
+ "": "儿",
11035
+ "": "岁",
11036
+ "": "此",
11037
+ "": "象",
11038
+ "": "月",
11039
+ "": "3",
11040
+ "": "出",
11041
+ "": "战",
11042
+ "": "工",
11043
+ "": "相",
11044
+ "": "o",
11045
+ "": "男",
11046
+ "": "直",
11047
+ "": "失",
11048
+ "": "世",
11049
+ "": "F",
11050
+ "": "都",
11051
+ "": "平",
11052
+ "": "文",
11053
+ "": "什",
11054
+ "": "V",
11055
+ "": "O",
11056
+ "": "将",
11057
+ "": "真",
11058
+ "": "T",
11059
+ "": "那",
11060
+ "": "当",
11061
+ "": "会",
11062
+ "": "立",
11063
+ "": "些",
11064
+ "": "u",
11065
+ "": "是",
11066
+ "": "十",
11067
+ "": "张",
11068
+ "": "学",
11069
+ "": "气",
11070
+ "": "大",
11071
+ "": "爱",
11072
+ "": "两",
11073
+ "": "命",
11074
+ "": "全",
11075
+ "": "后",
11076
+ "": "东",
11077
+ "": "性",
11078
+ "": "通",
11079
+ "": "被",
11080
+ "": "1",
11081
+ "": "它",
11082
+ "": "乐",
11083
+ "": "接",
11084
+ "": "而",
11085
+ "": "感",
11086
+ "": "车",
11087
+ "": "山",
11088
+ "": "公",
11089
+ "": "了",
11090
+ "": "常",
11091
+ "": "以",
11092
+ "": "何",
11093
+ "": "可",
11094
+ "": "话",
11095
+ "": "先",
11096
+ "": "p",
11097
+ "": "i",
11098
+ "": "叫",
11099
+ "": "轻",
11100
+ "": "M",
11101
+ "": "士",
11102
+ "": "w",
11103
+ "": "着",
11104
+ "": "变",
11105
+ "": "尔",
11106
+ "": "快",
11107
+ "": "l",
11108
+ "": "个",
11109
+ "": "说",
11110
+ "": "少",
11111
+ "": "色",
11112
+ "": "里",
11113
+ "": "安",
11114
+ "": "花",
11115
+ "": "远",
11116
+ "": "7",
11117
+ "": "难",
11118
+ "": "师",
11119
+ "": "放",
11120
+ "": "t",
11121
+ "": "报",
11122
+ "": "认",
11123
+ "": "面",
11124
+ "": "道",
11125
+ "": "S",
11126
+ "": "克",
11127
+ "": "地",
11128
+ "": "度",
11129
+ "": "I",
11130
+ "": "好",
11131
+ "": "机",
11132
+ "": "U",
11133
+ "": "民",
11134
+ "": "写",
11135
+ "": "把",
11136
+ "": "万",
11137
+ "": "同",
11138
+ "": "水",
11139
+ "": "新",
11140
+ "": "没",
11141
+ "": "书",
11142
+ "": "电",
11143
+ "": "吃",
11144
+ "": "像",
11145
+ "": "斯",
11146
+ "": "5",
11147
+ "": "为",
11148
+ "": "y",
11149
+ "": "白",
11150
+ "": "几",
11151
+ "": "日",
11152
+ "": "教",
11153
+ "": "看",
11154
+ "": "但",
11155
+ "": "第",
11156
+ "": "加",
11157
+ "": "候",
11158
+ "": "作",
11159
+ "": "上",
11160
+ "": "拉",
11161
+ "": "住",
11162
+ "": "有",
11163
+ "": "法",
11164
+ "": "r",
11165
+ "": "事",
11166
+ "": "应",
11167
+ "": "位",
11168
+ "": "利",
11169
+ "": "你",
11170
+ "": "声",
11171
+ "": "身",
11172
+ "": "国",
11173
+ "": "问",
11174
+ "": "马",
11175
+ "": "女",
11176
+ "": "他",
11177
+ "": "Y",
11178
+ "": "比",
11179
+ "": "父",
11180
+ "": "x",
11181
+ "": "A",
11182
+ "": "H",
11183
+ "": "N",
11184
+ "": "s",
11185
+ "": "X",
11186
+ "": "边",
11187
+ "": "美",
11188
+ "": "对",
11189
+ "": "所",
11190
+ "": "金",
11191
+ "": "活",
11192
+ "": "回",
11193
+ "": "意",
11194
+ "": "到",
11195
+ "": "z",
11196
+ "": "从",
11197
+ "": "j",
11198
+ "": "知",
11199
+ "": "又",
11200
+ "": "内",
11201
+ "": "因",
11202
+ "": "点",
11203
+ "": "Q",
11204
+ "": "三",
11205
+ "": "定",
11206
+ "": "8",
11207
+ "": "R",
11208
+ "": "b",
11209
+ "": "正",
11210
+ "": "或",
11211
+ "": "夫",
11212
+ "": "向",
11213
+ "": "德",
11214
+ "": "听",
11215
+ "": "更",
11216
+ "": "得",
11217
+ "": "告",
11218
+ "": "并",
11219
+ "": "本",
11220
+ "": "q",
11221
+ "": "过",
11222
+ "": "记",
11223
+ "": "L",
11224
+ "": "让",
11225
+ "": "打",
11226
+ "": "f",
11227
+ "": "人",
11228
+ "": "就",
11229
+ "": "者",
11230
+ "": "去",
11231
+ "": "原",
11232
+ "": "满",
11233
+ "": "体",
11234
+ "": "做",
11235
+ "": "经",
11236
+ "": "K",
11237
+ "": "走",
11238
+ "": "如",
11239
+ "": "孩",
11240
+ "": "c",
11241
+ "": "G",
11242
+ "": "给",
11243
+ "": "使",
11244
+ "": "物",
11245
+ "": "最",
11246
+ "": "笑",
11247
+ "": "部",
11248
+ "": "员",
11249
+ "": "等",
11250
+ "": "受",
11251
+ "": "k",
11252
+ "": "行",
11253
+ "": "一",
11254
+ "": "条",
11255
+ "": "果",
11256
+ "": "动",
11257
+ "": "光",
11258
+ "": "门",
11259
+ "": "头",
11260
+ "": "见",
11261
+ "": "往",
11262
+ "": "自",
11263
+ "": "解",
11264
+ "": "成",
11265
+ "": "处",
11266
+ "": "天",
11267
+ "": "能",
11268
+ "": "于",
11269
+ "": "名",
11270
+ "": "其",
11271
+ "": "发",
11272
+ "": "总",
11273
+ "": "母",
11274
+ "": "的",
11275
+ "": "死",
11276
+ "": "手",
11277
+ "": "入",
11278
+ "": "路",
11279
+ "": "进",
11280
+ "": "心",
11281
+ "": "来",
11282
+ "": "h",
11283
+ "": "时",
11284
+ "": "力",
11285
+ "": "多",
11286
+ "": "开",
11287
+ "": "已",
11288
+ "": "许",
11289
+ "": "d",
11290
+ "": "至",
11291
+ "": "由",
11292
+ "": "很",
11293
+ "": "界",
11294
+ "": "n",
11295
+ "": "小",
11296
+ "": "与",
11297
+ "": "Z",
11298
+ "": "想",
11299
+ "": "代",
11300
+ "": "么",
11301
+ "": "分",
11302
+ "": "生",
11303
+ "": "口",
11304
+ "": "再",
11305
+ "": "妈",
11306
+ "": "望",
11307
+ "": "次",
11308
+ "": "西",
11309
+ "": "风",
11310
+ "": "种",
11311
+ "": "带",
11312
+ "": "J",
11313
+ "": "实",
11314
+ "": "情",
11315
+ "": "才",
11316
+ "": "这",
11317
+ "": "E",
11318
+ "": "我",
11319
+ "": "神",
11320
+ "": "格",
11321
+ "": "长",
11322
+ "": "觉",
11323
+ "": "间",
11324
+ "": "年",
11325
+ "": "眼",
11326
+ "": "无",
11327
+ "": "不",
11328
+ "": "亲",
11329
+ "": "关",
11330
+ "": "结",
11331
+ "": "0",
11332
+ "": "友",
11333
+ "": "信",
11334
+ "": "下",
11335
+ "": "却",
11336
+ "": "重",
11337
+ "": "己",
11338
+ "": "老",
11339
+ "": "2",
11340
+ "": "音",
11341
+ "": "字",
11342
+ "": "m",
11343
+ "": "呢",
11344
+ "": "明",
11345
+ "": "之",
11346
+ "": "前",
11347
+ "": "高",
11348
+ "": "P",
11349
+ "": "B",
11350
+ "": "目",
11351
+ "": "太",
11352
+ "": "e",
11353
+ "": "9",
11354
+ "": "起",
11355
+ "": "稜",
11356
+ "": "她",
11357
+ "": "也",
11358
+ "": "W",
11359
+ "": "用",
11360
+ "": "方",
11361
+ "": "子",
11362
+ "": "英",
11363
+ "": "每",
11364
+ "": "理",
11365
+ "": "便",
11366
+ "": "四",
11367
+ "": "数",
11368
+ "": "期",
11369
+ "": "中",
11370
+ "": "C",
11371
+ "": "外",
11372
+ "": "样",
11373
+ "": "a",
11374
+ "": "海",
11375
+ "": "们",
11376
+ "": "任"
11377
+ };
11378
+ var restoreFanqieObfuscatedText = (content) => {
11379
+ return Array.from(content).map((char) => FANQIE_OBFUSCATED_FONT_MAP[char] || char).join("");
11380
+ };
11381
+
11382
+ // src/node/crawl/fanqie/utils/normalize.ts
11383
+ var normalizeFanqieText = (value) => {
11384
+ if (value === null || value === undefined) {
11385
+ return null;
11386
+ }
11387
+ const normalized = restoreFanqieObfuscatedText(String(value)).trim();
11388
+ return normalized || null;
11389
+ };
11390
+ var normalizeFanqieCoverUrl = (value) => {
11391
+ const url3 = value ? String(value).trim() : "";
11392
+ if (!url3) {
11393
+ return null;
11394
+ }
11395
+ return url3.replace(/^http:\/\//, "https://");
11396
+ };
11397
+
11398
+ // src/node/crawl/fanqie/book/categories.ts
11399
+ var getCategoryGroupKey = (label) => {
11400
+ if (label === "主题") {
11401
+ return "topic";
11402
+ }
11403
+ if (label === "角色") {
11404
+ return "role";
11405
+ }
11406
+ if (label === "情节") {
11407
+ return "plot";
11408
+ }
11409
+ return null;
11410
+ };
11411
+ var createEmptyLibraryFilters = () => ({
11412
+ topic: [],
11413
+ role: [],
11414
+ plot: []
11415
+ });
11416
+ var normalizeFanqieLibraryFilters = (payload) => {
11417
+ const list = Array.isArray(payload) ? payload : [];
11418
+ const groups = createEmptyLibraryFilters();
11419
+ list.forEach((item) => {
11420
+ const label = String(item?.label || "").trim();
11421
+ const groupKey = getCategoryGroupKey(label);
11422
+ const categoryId = Number(item?.category_id);
11423
+ const name = normalizeFanqieText(item?.name);
11424
+ if (!groupKey || !Number.isInteger(categoryId) || !name) {
11425
+ return;
11426
+ }
11427
+ groups[groupKey].push({
11428
+ categoryId,
11429
+ label,
11430
+ name,
11431
+ coverUrl: normalizeFanqieCoverUrl(item?.cover_uri)
11432
+ });
11433
+ });
11434
+ return groups;
11435
+ };
11436
+ var getFanqieLibraryFilters = async (gender) => {
11437
+ const url3 = new URL(FANQIE_LIBRARY_CATEGORY_URL);
11438
+ url3.searchParams.set("gender", String(gender));
11439
+ const data = await fetchFanqieJson(url3.toString(), {
11440
+ headers: FANQIE_PAGE_HEADERS
11441
+ });
11442
+ return normalizeFanqieLibraryFilters(data?.data);
11443
+ };
11444
+ var resolveFanqieLibraryCategoryId = async ({
11445
+ gender,
11446
+ categoryGroup,
11447
+ categoryId
11448
+ }) => {
11449
+ if (categoryGroup === "all") {
11450
+ return -1;
11451
+ }
11452
+ const groups = await getFanqieLibraryFilters(gender);
11453
+ const currentGroup = groups[categoryGroup];
11454
+ if (currentGroup.length === 0) {
11455
+ return -1;
11456
+ }
11457
+ const matchedItem = typeof categoryId === "number" ? currentGroup.find((item) => item.categoryId === categoryId) : null;
11458
+ return matchedItem?.categoryId || currentGroup[0]?.categoryId || -1;
11459
+ };
11460
+ // src/node/crawl/fanqie/utils/book-source.ts
11461
+ var getFanqieBookSourceBaseUrls = () => {
11462
+ const raw = process.env.FANQIE_BOOK_SOURCE_BASE_URLS?.trim() || process.env.FANQIE_BOOK_SOURCE_DETAIL_BASE_URLS?.trim();
11463
+ if (!raw) {
11464
+ return [...DEFAULT_FANQIE_BOOK_SOURCE_BASE_URLS];
11465
+ }
11466
+ const list = raw.split(",").map((item) => item.trim()).filter(Boolean);
11467
+ return list.length > 0 ? list : [...DEFAULT_FANQIE_BOOK_SOURCE_BASE_URLS];
11468
+ };
11469
+ var encodeFanqieBookSourceBookId = (fanqieBookId) => {
11470
+ return Buffer.from(fanqieBookId, "utf8").toString("base64");
11471
+ };
11472
+ var decodeFanqieBookSourceBookId = (encodedBookId) => {
11473
+ const raw = encodedBookId ? String(encodedBookId).trim() : "";
11474
+ if (!raw) {
11475
+ return "";
11476
+ }
11477
+ try {
11478
+ const normalized = raw.replace(/-/g, "+").replace(/_/g, "/").padEnd(raw.length + (4 - raw.length % 4) % 4, "=");
11479
+ const decoded = Buffer.from(normalized, "base64").toString("utf8").trim();
11480
+ return /^\d+$/.test(decoded) ? decoded : raw;
11481
+ } catch {
11482
+ return raw;
11483
+ }
11484
+ };
11485
+ var createFanqieBookSourceUrl = (baseUrl, path3, searchParams) => {
11486
+ const url3 = new URL(path3, baseUrl);
11487
+ Object.entries(searchParams).forEach(([key2, value]) => {
11488
+ url3.searchParams.set(key2, value);
11489
+ });
11490
+ return url3.toString();
11491
+ };
11492
+ var requestFanqieBookSourceJson = async ({
11493
+ path: path3,
11494
+ searchParams,
11495
+ init,
11496
+ baseUrl,
11497
+ errorPrefix,
11498
+ validate
11499
+ }) => {
11500
+ const baseUrls = baseUrl ? [baseUrl] : getFanqieBookSourceBaseUrls();
11501
+ const errors2 = [];
11502
+ for (const currentBaseUrl of baseUrls) {
11503
+ try {
11504
+ const url3 = createFanqieBookSourceUrl(currentBaseUrl, path3, searchParams);
11505
+ const response = await fetchFanqieJson(url3, init);
11506
+ validate?.(response);
11507
+ return response;
11508
+ } catch (error) {
11509
+ const message = error instanceof Error ? error.message : String(error);
11510
+ errors2.push(`${currentBaseUrl}: ${message}`);
11511
+ }
11512
+ }
11513
+ throw new Error(`${errorPrefix}: ${errors2.join(" | ")}`);
11514
+ };
11515
+
11516
+ // src/node/crawl/fanqie/book/normalize.ts
11517
+ var normalizeFanqieSearchTitle = (value) => {
11518
+ const normalized = normalizeFanqieText(value);
11519
+ if (!normalized) {
11520
+ return "";
11521
+ }
11522
+ return normalized.replace(/(别名:.*?)/g, "").trim();
11523
+ };
11524
+ var normalizeFanqieBookStatus = (value) => {
11525
+ const normalized = normalizeFanqieText(value);
11526
+ if (!normalized) {
11527
+ return null;
11528
+ }
11529
+ if (normalized.includes("完结")) {
11530
+ return 1;
11531
+ }
11532
+ if (normalized.includes("连载")) {
11533
+ return 0;
11534
+ }
11535
+ return null;
11536
+ };
11537
+ var normalizeFanqieBookTags = (value) => {
11538
+ const normalized = normalizeFanqieText(value);
11539
+ if (!normalized) {
11540
+ return [];
11541
+ }
11542
+ return normalized.split(/[,,、\s]+/).map((item) => item.trim()).filter(Boolean);
11543
+ };
11544
+ var createFanqieBookItemBase = (payload) => ({
11545
+ author: normalizeFanqieText(payload.author),
11546
+ score: normalizeFanqieText(payload.score),
11547
+ coverUrl: normalizeFanqieCoverUrl(payload.thumb_url),
11548
+ description: normalizeFanqieText(payload.abstract),
11549
+ category: normalizeFanqieText(payload.category),
11550
+ tags: normalizeFanqieBookTags(payload.tags),
11551
+ wordCount: normalizeFanqieText(payload.word_number),
11552
+ readCount: normalizeFanqieText(payload.read_count),
11553
+ readCountAll: normalizeFanqieText(payload.read_count_all),
11554
+ creationStatus: normalizeFanqieBookStatus(payload.status),
11555
+ lastChapterTime: normalizeFanqieText(payload.last_chapter_update_time),
11556
+ rawTitle: payload.book_name ? String(payload.book_name) : null,
11557
+ rawAuthor: payload.author ? String(payload.author) : null,
11558
+ rawDescription: payload.abstract ? String(payload.abstract) : null,
11559
+ rawCategory: payload.category ? String(payload.category) : null,
11560
+ rawScore: payload.score ? String(payload.score) : null,
11561
+ rawWordCount: payload.word_number ? String(payload.word_number) : null,
11562
+ rawReadCount: payload.read_count ? String(payload.read_count) : null,
11563
+ rawReadCountAll: payload.read_count_all ? String(payload.read_count_all) : null
11564
+ });
11565
+ var normalizeFanqieBookSearchItem = (payload) => {
11566
+ const id = decodeFanqieBookSourceBookId(payload.book_id);
11567
+ const title = normalizeFanqieSearchTitle(payload.book_name);
11568
+ if (!id || !title) {
11569
+ return null;
11570
+ }
11571
+ return {
11572
+ id,
11573
+ title,
11574
+ ...createFanqieBookItemBase(payload)
11575
+ };
11576
+ };
11577
+ var normalizeFanqieBookDetailItem = (fanqieBookId, payload) => {
11578
+ const title = normalizeFanqieText(payload.book_name);
11579
+ if (!title) {
11580
+ throw new Error("书源详情缺少书名");
11581
+ }
11582
+ return {
11583
+ id: fanqieBookId,
11584
+ title,
11585
+ ...createFanqieBookItemBase(payload)
11586
+ };
11587
+ };
11588
+ var normalizeFanqieOfficialLibraryItem = (payload) => {
11589
+ const id = payload?.book_id ? String(payload.book_id).trim() : "";
11590
+ const title = normalizeFanqieText(payload?.book_name);
11591
+ if (!id || !title) {
11592
+ return null;
11593
+ }
11594
+ return {
11595
+ id,
11596
+ title,
11597
+ author: normalizeFanqieText(payload?.author),
11598
+ coverUrl: normalizeFanqieCoverUrl(payload?.thumb_url),
11599
+ description: normalizeFanqieText(payload?.abstract),
11600
+ category: normalizeFanqieText(payload?.category),
11601
+ tags: [],
11602
+ wordCount: normalizeFanqieText(payload?.word_count),
11603
+ readCount: normalizeFanqieText(payload?.read_count),
11604
+ readCountAll: null,
11605
+ creationStatus: payload?.creation_status === 0 || payload?.creation_status === 1 ? payload.creation_status : null,
11606
+ lastChapterTime: payload?.last_chapter_time ? String(payload.last_chapter_time) : null,
11607
+ rawTitle: payload?.book_name ? String(payload.book_name) : null,
11608
+ rawAuthor: payload?.author ? String(payload.author) : null,
11609
+ rawDescription: payload?.abstract ? String(payload.abstract) : null,
11610
+ rawCategory: payload?.category ? String(payload.category) : null,
11611
+ rawScore: null,
11612
+ rawWordCount: payload?.word_count ? String(payload.word_count) : null,
11613
+ rawReadCount: payload?.read_count ? String(payload.read_count) : null,
11614
+ rawReadCountAll: null
11615
+ };
11616
+ };
10888
11617
 
11618
+ // src/node/crawl/fanqie/book/detail.ts
11619
+ var getFanqieBookDetailById = async (fanqieBookId) => {
11620
+ const response = await requestFanqieBookSourceJson({
11621
+ path: FANQIE_BOOK_SOURCE_DETAIL_PATH,
11622
+ searchParams: {
11623
+ book_id: encodeFanqieBookSourceBookId(fanqieBookId),
11624
+ source: FANQIE_BOOK_SOURCE_DETAIL_SOURCE,
11625
+ tab: FANQIE_BOOK_SOURCE_DETAIL_TAB,
11626
+ variable: JSON.stringify(FANQIE_BOOK_SOURCE_DETAIL_VARIABLE)
11627
+ },
11628
+ init: {
11629
+ method: "POST",
11630
+ headers: FANQIE_BOOK_SOURCE_DETAIL_HEADERS,
11631
+ body: JSON.stringify({ html: "" })
11632
+ },
11633
+ errorPrefix: "番茄书籍详情获取失败",
11634
+ validate: (data) => {
11635
+ if (data?.code !== 0 || !data.data) {
11636
+ throw new Error(data?.msg || "书源详情返回为空");
11637
+ }
11638
+ }
11639
+ });
11640
+ return normalizeFanqieBookDetailItem(fanqieBookId, response.data);
11641
+ };
11642
+ // src/node/crawl/fanqie/book/library.ts
11643
+ var normalizeFanqieLibraryResults = (payload) => {
11644
+ const list = Array.isArray(payload) ? payload : [];
11645
+ return list.flatMap((item) => {
11646
+ const result = normalizeFanqieOfficialLibraryItem(item);
11647
+ return result ? [result] : [];
11648
+ });
11649
+ };
11650
+ var listFanqieLibraryBooks = async ({
11651
+ gender,
11652
+ categoryId,
11653
+ creationStatus,
11654
+ wordCount,
11655
+ sort,
11656
+ pageIndex,
11657
+ pageSize
11658
+ }) => {
11659
+ const url3 = new URL(FANQIE_LIBRARY_LIST_URL);
11660
+ url3.searchParams.set("page_count", String(pageSize));
11661
+ url3.searchParams.set("page_index", String(pageIndex));
11662
+ url3.searchParams.set("gender", String(gender));
11663
+ url3.searchParams.set("category_id", String(categoryId));
11664
+ url3.searchParams.set("creation_status", String(creationStatus));
11665
+ url3.searchParams.set("word_count", String(wordCount));
11666
+ url3.searchParams.set("book_type", "-1");
11667
+ url3.searchParams.set("sort", String(sort));
11668
+ const data = await fetchFanqieJson(url3.toString(), {
11669
+ headers: FANQIE_PAGE_HEADERS
11670
+ });
11671
+ return {
11672
+ results: normalizeFanqieLibraryResults(data?.data?.book_list),
11673
+ total: Number(data?.data?.total_count || 0)
11674
+ };
11675
+ };
11676
+ // src/node/crawl/fanqie/book/search.ts
11677
+ var createFanqieBookSourceSearchUrl = (baseUrl, query, options = {}) => {
11678
+ return createFanqieBookSourceUrl(baseUrl, FANQIE_BOOK_SOURCE_SEARCH_PATH, {
11679
+ title: query,
11680
+ tab: options.tab || FANQIE_BOOK_SOURCE_SEARCH_DEFAULT_TAB,
11681
+ source: options.source || FANQIE_BOOK_SOURCE_SEARCH_DEFAULT_SOURCE,
11682
+ page: String(options.page || 1),
11683
+ disabled_sources: options.disabledSources || FANQIE_BOOK_SOURCE_SEARCH_DEFAULT_DISABLED_SOURCES
11684
+ });
11685
+ };
11686
+ var normalizeFanqieSearchResults = (payload) => {
11687
+ const list = Array.isArray(payload) ? payload : [];
11688
+ return list.flatMap((item) => {
11689
+ const result = normalizeFanqieBookSearchItem(item);
11690
+ return result ? [result] : [];
11691
+ });
11692
+ };
11693
+ var createFanqieSearchUrl = (query, options = {}) => {
11694
+ const baseUrl = options.baseUrl || getFanqieBookSourceBaseUrls()[0];
11695
+ return createFanqieBookSourceSearchUrl(baseUrl, query, options);
11696
+ };
11697
+ var searchFanqieBooks = async (query, options = {}) => {
11698
+ const response = await requestFanqieBookSourceJson({
11699
+ path: FANQIE_BOOK_SOURCE_SEARCH_PATH,
11700
+ baseUrl: options.baseUrl,
11701
+ searchParams: {
11702
+ title: query,
11703
+ tab: options.tab || FANQIE_BOOK_SOURCE_SEARCH_DEFAULT_TAB,
11704
+ source: options.source || FANQIE_BOOK_SOURCE_SEARCH_DEFAULT_SOURCE,
11705
+ page: String(options.page || 1),
11706
+ disabled_sources: options.disabledSources || FANQIE_BOOK_SOURCE_SEARCH_DEFAULT_DISABLED_SOURCES
11707
+ },
11708
+ init: {
11709
+ method: "GET",
11710
+ headers: FANQIE_BOOK_SOURCE_SEARCH_HEADERS
11711
+ },
11712
+ errorPrefix: "番茄书籍搜索失败",
11713
+ validate: (data) => {
11714
+ if (data?.code !== 0) {
11715
+ throw new Error(data?.msg || "书源搜索失败");
11716
+ }
11717
+ }
11718
+ });
11719
+ return normalizeFanqieSearchResults(response.data);
11720
+ };
11721
+ // src/node/crawl/fanqie/chapter/index.ts
11722
+ var exports_chapter = {};
11723
+ __export(exports_chapter, {
11724
+ getFanqieChapterDirectory: () => getFanqieChapterDirectory
11725
+ });
11726
+
11727
+ // src/node/crawl/fanqie/chapter/directory.ts
11728
+ var extractVolumeName = (value) => {
11729
+ const normalized = normalizeFanqieText(value);
11730
+ if (!normalized) {
11731
+ return null;
11732
+ }
11733
+ const [volumeName] = normalized.split("|").map((item) => item.trim());
11734
+ return volumeName || null;
11735
+ };
11736
+ var normalizeFanqieBookSourceCatalog = (payload) => {
11737
+ const list = Array.isArray(payload) ? payload : [];
11738
+ return list.map((item, index) => ({
11739
+ fanqieChapterId: item?.item_id ? String(item.item_id).trim() : "",
11740
+ title: normalizeFanqieText(item?.title) || `第${index + 1}章`,
11741
+ chapterOrder: index + 1,
11742
+ volumeName: extractVolumeName(item?.first_pass_time)
11743
+ })).filter((item) => Boolean(item.fanqieChapterId));
11744
+ };
11745
+ var getFanqieChapterDirectory = async (fanqieBookId) => {
11746
+ const response = await requestFanqieBookSourceJson({
11747
+ path: FANQIE_BOOK_SOURCE_CATALOG_PATH,
11748
+ searchParams: {
11749
+ book_id: encodeFanqieBookSourceBookId(fanqieBookId),
11750
+ source: FANQIE_BOOK_SOURCE_CATALOG_SOURCE,
11751
+ tab: FANQIE_BOOK_SOURCE_CATALOG_TAB,
11752
+ variable: JSON.stringify(FANQIE_BOOK_SOURCE_CATALOG_VARIABLE)
11753
+ },
11754
+ init: {
11755
+ method: "POST",
11756
+ headers: FANQIE_BOOK_SOURCE_CATALOG_HEADERS,
11757
+ body: JSON.stringify({ html: "" })
11758
+ },
11759
+ errorPrefix: "番茄章节目录获取失败",
11760
+ validate: (data) => {
11761
+ if (data?.code !== 0) {
11762
+ throw new Error(data?.msg || "章节目录接口返回异常");
11763
+ }
11764
+ }
11765
+ });
11766
+ return normalizeFanqieBookSourceCatalog(response.data);
11767
+ };
11768
+ // src/node/crawl/fanqie/content/index.ts
11769
+ var exports_content = {};
11770
+ __export(exports_content, {
11771
+ getFanqieChapterContents: () => getFanqieChapterContents
11772
+ });
11773
+
11774
+ // src/node/crawl/fanqie/content/sidecar.ts
11775
+ import { execFile } from "node:child_process";
11776
+ import { promisify } from "node:util";
11777
+ var execFileAsync = promisify(execFile);
11778
+ var sleep = async (ms) => {
11779
+ await new Promise((resolve) => setTimeout(resolve, ms));
11780
+ };
11781
+ var assertFanqieNumericId = (value, label) => {
11782
+ if (!/^\d+$/.test(value.trim())) {
11783
+ throw new Error(`${label} 格式不正确`);
11784
+ }
11785
+ };
11786
+ var resolveSidecarOptions = (options = {}) => ({
11787
+ baseUrl: options.baseUrl || process.env.FANQIE_OFFICIAL_SIDECAR_BASE_URL || DEFAULT_FANQIE_OFFICIAL_SIDECAR_BASE_URL,
11788
+ container: options.container || process.env.FANQIE_OFFICIAL_SIDECAR_CONTAINER || DEFAULT_FANQIE_OFFICIAL_SIDECAR_CONTAINER,
11789
+ password: options.password ?? process.env.FANQIE_OFFICIAL_SIDECAR_PASSWORD ?? "",
11790
+ jobTimeoutMs: options.jobTimeoutMs || DEFAULT_FANQIE_OFFICIAL_SIDECAR_JOB_TIMEOUT_MS,
11791
+ jobPollIntervalMs: options.jobPollIntervalMs || DEFAULT_FANQIE_OFFICIAL_SIDECAR_JOB_POLL_INTERVAL_MS,
11792
+ statusTimeoutMs: options.statusTimeoutMs || DEFAULT_FANQIE_OFFICIAL_SIDECAR_STATUS_TIMEOUT_MS,
11793
+ statusPollIntervalMs: options.statusPollIntervalMs || DEFAULT_FANQIE_OFFICIAL_SIDECAR_STATUS_POLL_INTERVAL_MS
11794
+ });
11795
+ var getOfficialSidecarHeaders = (password) => {
11796
+ if (!password) {
11797
+ return {};
11798
+ }
11799
+ return {
11800
+ "x-tomato-password": password
11801
+ };
11802
+ };
11803
+ var officialSidecarFetchJson = async (path3, options, init) => {
11804
+ const response = await fetch(`${options.baseUrl}${path3}`, {
11805
+ ...init,
11806
+ headers: {
11807
+ ...getOfficialSidecarHeaders(options.password),
11808
+ ...init?.headers || {}
11809
+ },
11810
+ cache: "no-store"
11811
+ });
11812
+ if (!response.ok) {
11813
+ throw new Error(`官方正文服务请求失败: ${response.status} ${path3}`);
11814
+ }
11815
+ return await response.json();
11816
+ };
11817
+ var submitOfficialSidecarChoice = async (jobId, type, value, options) => {
11818
+ await officialSidecarFetchJson(`/api/jobs/${jobId}/${type}`, options, {
11819
+ method: "POST",
11820
+ headers: {
11821
+ "content-type": "application/json"
11822
+ },
11823
+ body: JSON.stringify({ value })
11824
+ });
11825
+ };
11826
+ var createOfficialSidecarJob = async (fanqieBookId, rangeEnd, options) => {
11827
+ return await officialSidecarFetchJson("/api/jobs", options, {
11828
+ method: "POST",
11829
+ headers: {
11830
+ "content-type": "application/json"
11831
+ },
11832
+ body: JSON.stringify({
11833
+ book_id: fanqieBookId,
11834
+ range_start: 1,
11835
+ range_end: rangeEnd
11836
+ })
11837
+ });
11838
+ };
11839
+ var pollOfficialSidecarJobUntilDone = async (jobId, options) => {
11840
+ const startedAt = Date.now();
11841
+ while (Date.now() - startedAt < options.jobTimeoutMs) {
11842
+ const response = await officialSidecarFetchJson(`/api/jobs?id=${jobId}`, options);
11843
+ const job = response.items[0];
11844
+ if (!job) {
11845
+ throw new Error("官方正文服务任务不存在");
11846
+ }
11847
+ if (job.book_name_options?.length) {
11848
+ await submitOfficialSidecarChoice(jobId, "book_name", job.book_name_options[0]?.value || "", options);
11849
+ await sleep(300);
11850
+ continue;
11851
+ }
11852
+ if (job.format_options?.length) {
11853
+ await submitOfficialSidecarChoice(jobId, "format", job.format_options[0]?.value || "", options);
11854
+ await sleep(300);
11855
+ continue;
11856
+ }
11857
+ if (job.state === "done") {
11858
+ return;
11859
+ }
11860
+ if (job.state === "failed") {
11861
+ throw new Error(job.message || "官方正文服务任务失败");
11862
+ }
11863
+ if (job.state === "canceled") {
11864
+ throw new Error("官方正文服务任务已取消");
11865
+ }
11866
+ await sleep(options.jobPollIntervalMs);
11867
+ }
11868
+ throw new Error("官方正文服务任务超时");
11869
+ };
11870
+ var deleteOfficialSidecarJob = async (jobId, options) => {
11871
+ try {
11872
+ await fetch(`${options.baseUrl}/api/jobs/${jobId}`, {
11873
+ method: "DELETE",
11874
+ headers: getOfficialSidecarHeaders(options.password),
11875
+ cache: "no-store"
11876
+ });
11877
+ } catch {}
11878
+ };
11879
+ var tryReadOfficialSidecarStatusJson = async (fanqieBookId, options) => {
11880
+ assertFanqieNumericId(fanqieBookId, "书籍 ID");
11881
+ const shell = [
11882
+ 'folder=""',
11883
+ `for candidate in /app/${fanqieBookId}_*; do [ -f "$candidate/status.json" ] && folder="$candidate" && break; done`,
11884
+ '[ -n "$folder" ] || exit 11',
11885
+ 'cat "$folder/status.json"'
11886
+ ].join("; ");
11887
+ try {
11888
+ const { stdout } = await execFileAsync("docker", ["exec", options.container, "sh", "-lc", shell], {
11889
+ maxBuffer: 200 * 1024 * 1024
11890
+ });
11891
+ return JSON.parse(stdout);
11892
+ } catch (error) {
11893
+ const execError = error;
11894
+ if (execError.code === 11) {
11895
+ return null;
11896
+ }
11897
+ throw new Error(execError.stderr?.trim() || execError.message || "读取官方正文服务状态失败");
11898
+ }
11899
+ };
11900
+ var readOfficialSidecarStatusJson = async (fanqieBookId, options) => {
11901
+ const startedAt = Date.now();
11902
+ let lastError = null;
11903
+ while (Date.now() - startedAt < options.statusTimeoutMs) {
11904
+ try {
11905
+ const status = await tryReadOfficialSidecarStatusJson(fanqieBookId, options);
11906
+ if (status) {
11907
+ return status;
11908
+ }
11909
+ } catch (error) {
11910
+ lastError = error instanceof Error ? error : new Error(error?.message || "读取官方正文服务状态失败");
11911
+ }
11912
+ await sleep(options.statusPollIntervalMs);
11913
+ }
11914
+ if (lastError) {
11915
+ throw lastError;
11916
+ }
11917
+ throw new Error("官方正文服务任务已完成,但未找到 status.json 结果文件");
11918
+ };
11919
+ var getFanqieChapterContents = async (fanqieBookId, chapterIds, sidecarOptions) => {
11920
+ assertFanqieNumericId(fanqieBookId, "书籍 ID");
11921
+ const normalizedChapterIds = chapterIds.map((item) => item.trim()).filter(Boolean);
11922
+ if (normalizedChapterIds.length === 0) {
11923
+ return new Map;
11924
+ }
11925
+ const options = resolveSidecarOptions(sidecarOptions);
11926
+ let jobId = null;
11927
+ try {
11928
+ const job = await createOfficialSidecarJob(fanqieBookId, normalizedChapterIds.length, options);
11929
+ jobId = job.id;
11930
+ await pollOfficialSidecarJobUntilDone(job.id, options);
11931
+ const status = await readOfficialSidecarStatusJson(fanqieBookId, options);
11932
+ const downloaded = status.downloaded || {};
11933
+ const result = new Map;
11934
+ normalizedChapterIds.forEach((chapterId, index) => {
11935
+ const item = downloaded[chapterId];
11936
+ if (!item?.[1]) {
11937
+ return;
11938
+ }
11939
+ result.set(chapterId, {
11940
+ title: item[0] || `第${index + 1}章`,
11941
+ content: item[1],
11942
+ chapterOrder: index + 1
11943
+ });
11944
+ });
11945
+ if (result.size === 0) {
11946
+ throw new Error("官方正文服务未返回可用章节内容");
11947
+ }
11948
+ return result;
11949
+ } finally {
11950
+ if (jobId) {
11951
+ await deleteOfficialSidecarJob(jobId, options);
11952
+ }
11953
+ }
11954
+ };
10889
11955
  // src/node.ts
10890
11956
  var _ = import_lodash.default;
10891
11957
  export {
@@ -10948,6 +12014,7 @@ export {
10948
12014
  getType,
10949
12015
  getStrLength,
10950
12016
  getSliceStr,
12017
+ getSafeCallbackPath,
10951
12018
  getRandomString,
10952
12019
  getRandomColor,
10953
12020
  getPublicUrl,
@@ -10972,6 +12039,8 @@ export {
10972
12039
  dateType as date,
10973
12040
  custom,
10974
12041
  exports_cron as cron,
12042
+ exports_crawl as crawl,
12043
+ exports_cors as cors,
10975
12044
  default4 as consola,
10976
12045
  coerce,
10977
12046
  cheerio,