specific-ai-cli 0.0.0-stage → 0.1.1

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.
Files changed (277) hide show
  1. package/LICENSE +21 -0
  2. package/README.en.md +177 -0
  3. package/README.ja.md +177 -0
  4. package/README.ko.md +177 -0
  5. package/README.md +174 -2
  6. package/README.zh-TW.md +177 -0
  7. package/assets/banner.txt +3 -0
  8. package/dist/cli/argv.js +98 -0
  9. package/dist/cli/commands/auth.js +11 -0
  10. package/dist/cli/commands/config.js +23 -0
  11. package/dist/cli/commands/doctor.js +134 -0
  12. package/dist/cli/commands/platforms.js +24 -0
  13. package/dist/cli/commands/version.js +4 -0
  14. package/dist/cli/confirm.js +22 -0
  15. package/dist/cli/dispatch.js +440 -0
  16. package/dist/cli/help.js +223 -0
  17. package/dist/cli/hints.js +60 -0
  18. package/dist/cli/main.js +18 -0
  19. package/dist/cli/output.js +66 -0
  20. package/dist/core/auth-handlers.js +41 -0
  21. package/dist/core/auth-store.js +119 -0
  22. package/dist/core/capabilities.js +163 -0
  23. package/dist/core/config.js +121 -0
  24. package/dist/core/cookies.js +164 -0
  25. package/dist/core/errors.js +37 -0
  26. package/dist/core/files.js +89 -0
  27. package/dist/core/fsutil.js +52 -0
  28. package/dist/core/hash.js +4 -0
  29. package/dist/core/http.js +331 -0
  30. package/dist/core/image.js +280 -0
  31. package/dist/core/log.js +40 -0
  32. package/dist/core/login.js +191 -0
  33. package/dist/core/mp4.js +130 -0
  34. package/dist/core/normalize.js +207 -0
  35. package/dist/core/options.js +110 -0
  36. package/dist/core/paths.js +38 -0
  37. package/dist/core/pb.js +59 -0
  38. package/dist/core/py.js +181 -0
  39. package/dist/core/rand.js +131 -0
  40. package/dist/core/registry.js +129 -0
  41. package/dist/core/schemas.js +41 -0
  42. package/dist/core/stream.js +104 -0
  43. package/dist/core/toolkit.js +35 -0
  44. package/dist/core/vm.js +73 -0
  45. package/dist/core/vocab.js +157 -0
  46. package/dist/platforms/_shared/impaas.js +410 -0
  47. package/dist/platforms/bilibili/index.js +216 -0
  48. package/dist/platforms/bilibili/web/api.js +806 -0
  49. package/dist/platforms/bilibili/web/client.js +311 -0
  50. package/dist/platforms/bilibili/web/commands.js +783 -0
  51. package/dist/platforms/bilibili/web/ddddocr.js +342 -0
  52. package/dist/platforms/bilibili/web/gaia.js +130 -0
  53. package/dist/platforms/bilibili/web/geetest-manual.js +154 -0
  54. package/dist/platforms/bilibili/web/geetest-vision.js +232 -0
  55. package/dist/platforms/bilibili/web/geetest.js +450 -0
  56. package/dist/platforms/bilibili/web/normalize.js +234 -0
  57. package/dist/platforms/bilibili/web/profile.js +135 -0
  58. package/dist/platforms/bilibili/web/resolve.js +112 -0
  59. package/dist/platforms/bilibili/web/sign.js +223 -0
  60. package/dist/platforms/bilibili/web/upos.js +102 -0
  61. package/dist/platforms/douyin/index.js +142 -0
  62. package/dist/platforms/douyin/web/api.js +863 -0
  63. package/dist/platforms/douyin/web/client.js +413 -0
  64. package/dist/platforms/douyin/web/commands.js +712 -0
  65. package/dist/platforms/douyin/web/creator.js +601 -0
  66. package/dist/platforms/douyin/web/crypto.js +247 -0
  67. package/dist/platforms/douyin/web/dtrait.js +190 -0
  68. package/dist/platforms/douyin/web/im.js +502 -0
  69. package/dist/platforms/douyin/web/image.js +5 -0
  70. package/dist/platforms/douyin/web/live.js +225 -0
  71. package/dist/platforms/douyin/web/mssdk.js +148 -0
  72. package/dist/platforms/douyin/web/normalize.js +326 -0
  73. package/dist/platforms/douyin/web/passport.js +1247 -0
  74. package/dist/platforms/douyin/web/profile.js +221 -0
  75. package/dist/platforms/douyin/web/proto.js +26 -0
  76. package/dist/platforms/douyin/web/resolve.js +88 -0
  77. package/dist/platforms/douyin/web/sign.js +436 -0
  78. package/dist/platforms/douyin/web/tos.js +78 -0
  79. package/dist/platforms/index.js +17 -0
  80. package/dist/platforms/jd/index.js +67 -0
  81. package/dist/platforms/jd/web/aks.js +115 -0
  82. package/dist/platforms/jd/web/api.js +549 -0
  83. package/dist/platforms/jd/web/chat.js +132 -0
  84. package/dist/platforms/jd/web/client.js +470 -0
  85. package/dist/platforms/jd/web/commands.js +557 -0
  86. package/dist/platforms/jd/web/jcap/helper.js +90 -0
  87. package/dist/platforms/jd/web/jcap/host.js +107 -0
  88. package/dist/platforms/jd/web/jcap/lsd.js +300 -0
  89. package/dist/platforms/jd/web/jcap/solver.js +995 -0
  90. package/dist/platforms/jd/web/jcap.js +150 -0
  91. package/dist/platforms/jd/web/js.js +420 -0
  92. package/dist/platforms/jd/web/login.js +775 -0
  93. package/dist/platforms/jd/web/normalize.js +297 -0
  94. package/dist/platforms/jd/web/profile.js +256 -0
  95. package/dist/platforms/jd/web/util.js +175 -0
  96. package/dist/platforms/jd/web/webm.js +133 -0
  97. package/dist/platforms/kuaishou/index.js +98 -0
  98. package/dist/platforms/kuaishou/web/api.js +694 -0
  99. package/dist/platforms/kuaishou/web/captcha-crypto.js +134 -0
  100. package/dist/platforms/kuaishou/web/captcha-fp.js +180 -0
  101. package/dist/platforms/kuaishou/web/captcha.js +273 -0
  102. package/dist/platforms/kuaishou/web/client.js +487 -0
  103. package/dist/platforms/kuaishou/web/commands.js +863 -0
  104. package/dist/platforms/kuaishou/web/gap.js +144 -0
  105. package/dist/platforms/kuaishou/web/gdfp.js +376 -0
  106. package/dist/platforms/kuaishou/web/gql.js +11 -0
  107. package/dist/platforms/kuaishou/web/live.js +125 -0
  108. package/dist/platforms/kuaishou/web/normalize.js +241 -0
  109. package/dist/platforms/kuaishou/web/oracle.js +329 -0
  110. package/dist/platforms/kuaishou/web/profile.js +259 -0
  111. package/dist/platforms/kuaishou/web/resolve.js +84 -0
  112. package/dist/platforms/kuaishou/web/session.js +592 -0
  113. package/dist/platforms/kuaishou/web/sign.js +568 -0
  114. package/dist/platforms/taobao/index.js +56 -0
  115. package/dist/platforms/taobao/web/api.js +96 -0
  116. package/dist/platforms/taobao/web/client.js +102 -0
  117. package/dist/platforms/taobao/web/commands.js +172 -0
  118. package/dist/platforms/taobao/web/im.js +68 -0
  119. package/dist/platforms/taobao/web/normalize.js +103 -0
  120. package/dist/platforms/taobao/web/profile.js +85 -0
  121. package/dist/platforms/taobao/web/resolve.js +51 -0
  122. package/dist/platforms/taobao/web/sign.js +35 -0
  123. package/dist/platforms/tiktok/index.js +120 -0
  124. package/dist/platforms/tiktok/web/api.js +1234 -0
  125. package/dist/platforms/tiktok/web/client.js +362 -0
  126. package/dist/platforms/tiktok/web/commands.js +821 -0
  127. package/dist/platforms/tiktok/web/im.js +133 -0
  128. package/dist/platforms/tiktok/web/jsrun.js +126 -0
  129. package/dist/platforms/tiktok/web/normalize.js +286 -0
  130. package/dist/platforms/tiktok/web/profile.js +169 -0
  131. package/dist/platforms/tiktok/web/resolve.js +156 -0
  132. package/dist/platforms/tiktok/web/sign.js +585 -0
  133. package/dist/platforms/tiktok/web/upload.js +805 -0
  134. package/dist/platforms/tiktok/web/wire.js +490 -0
  135. package/dist/platforms/weibo/index.js +91 -0
  136. package/dist/platforms/weibo/web/api.js +206 -0
  137. package/dist/platforms/weibo/web/client.js +155 -0
  138. package/dist/platforms/weibo/web/commands.js +202 -0
  139. package/dist/platforms/weibo/web/normalize.js +137 -0
  140. package/dist/platforms/weibo/web/profile.js +246 -0
  141. package/dist/platforms/weibo/web/resolve.js +60 -0
  142. package/dist/platforms/weibo/web/sign.js +85 -0
  143. package/dist/platforms/x/index.js +107 -0
  144. package/dist/platforms/x/web/api.js +443 -0
  145. package/dist/platforms/x/web/article.js +212 -0
  146. package/dist/platforms/x/web/client.js +202 -0
  147. package/dist/platforms/x/web/commands.js +363 -0
  148. package/dist/platforms/x/web/graphql.js +108 -0
  149. package/dist/platforms/x/web/normalize.js +235 -0
  150. package/dist/platforms/x/web/profile.js +99 -0
  151. package/dist/platforms/x/web/resolve.js +44 -0
  152. package/dist/platforms/x/web/transaction.js +171 -0
  153. package/dist/platforms/xhs/index.js +101 -0
  154. package/dist/platforms/xhs/web/api.js +278 -0
  155. package/dist/platforms/xhs/web/client.js +540 -0
  156. package/dist/platforms/xhs/web/commands.js +1046 -0
  157. package/dist/platforms/xhs/web/creator-api.js +609 -0
  158. package/dist/platforms/xhs/web/creator.js +203 -0
  159. package/dist/platforms/xhs/web/js.js +194 -0
  160. package/dist/platforms/xhs/web/login.js +230 -0
  161. package/dist/platforms/xhs/web/normalize.js +166 -0
  162. package/dist/platforms/xhs/web/profile.js +127 -0
  163. package/dist/platforms/xhs/web/push.js +361 -0
  164. package/dist/platforms/xhs/web/resolve.js +69 -0
  165. package/dist/platforms/xhs/web/state.js +368 -0
  166. package/dist/platforms/xianyu/index.js +59 -0
  167. package/dist/platforms/xianyu/web/api.js +312 -0
  168. package/dist/platforms/xianyu/web/client.js +134 -0
  169. package/dist/platforms/xianyu/web/commands.js +305 -0
  170. package/dist/platforms/xianyu/web/im.js +73 -0
  171. package/dist/platforms/xianyu/web/normalize.js +123 -0
  172. package/dist/platforms/xianyu/web/profile.js +146 -0
  173. package/dist/platforms/xianyu/web/resolve.js +48 -0
  174. package/dist/platforms/xianyu/web/sign.js +79 -0
  175. package/package.json +77 -4
  176. package/static/douyin/Live.proto +194 -0
  177. package/static/douyin/Request.proto +89 -0
  178. package/static/douyin/Response.proto +46 -0
  179. package/static/douyin/acrawler_runtime/ac_vm.js +2 -0
  180. package/static/douyin/acrawler_runtime/browser_window_shape.json +1 -0
  181. package/static/douyin/acrawler_runtime/canvas_actual_exact.json +1 -0
  182. package/static/douyin/acrawler_runtime/run_ac_node.js +1005 -0
  183. package/static/douyin/acrawler_runtime/runner_window_keys_baseline.txt +240 -0
  184. package/static/douyin/challenge_profile.json +801 -0
  185. package/static/douyin/challenge_template_runner.js +217 -0
  186. package/static/douyin/dtrait_profile.json +66 -0
  187. package/static/douyin/mstoken_common_profile.json +1 -0
  188. package/static/douyin/mstoken_profile.json +214 -0
  189. package/static/jd/h5st5_env.js +750 -0
  190. package/static/jd/h5st5_lib.js +5 -0
  191. package/static/jd/h5st5_server.js +49 -0
  192. package/static/jd/jcap/env/env_core.js +512 -0
  193. package/static/jd/jcap/env/run.js +1307 -0
  194. package/static/jd/jcap/run/jcap_ujb96b.js +39 -0
  195. package/static/jd/package.json +4 -0
  196. package/static/jd/pc_tk_lib.js +1 -0
  197. package/static/jd/pc_tk_server.js +63 -0
  198. package/static/jd/summer_cryptico_runner.js +50 -0
  199. package/static/jd/webm/env/run.js +453 -0
  200. package/static/jd/webm/run/jdwebm-riskhandle.js +49 -0
  201. package/static/kuaishou/bundles/weapon/kwf-0.0.2.2cee19b4b7dec496.js +1 -0
  202. package/static/kuaishou/bundles/weapon/kws-0-0.0.1-obfuscated.4b7f3de1ab0ddcc2.js +1 -0
  203. package/static/kuaishou/bundles/weapon/kws-1-0.0.1-obfuscated.346ebdb1c78df316.js +1 -0
  204. package/static/kuaishou/bundles/weapon/kws-10-0.0.1-obfuscated.a33db41897b983af.js +1 -0
  205. package/static/kuaishou/bundles/weapon/kws-11-0.0.1-obfuscated.5e0a90af726d8a7e.js +1 -0
  206. package/static/kuaishou/bundles/weapon/kws-12-0.0.1-obfuscated.20ea29345706ec8c.js +1 -0
  207. package/static/kuaishou/bundles/weapon/kws-13-0.0.1-obfuscated.6b74e9640ff18648.js +1 -0
  208. package/static/kuaishou/bundles/weapon/kws-14-0.0.1-obfuscated.551e4429d156bf66.js +1 -0
  209. package/static/kuaishou/bundles/weapon/kws-15-0.0.1-obfuscated.8625b087aaba4a60.js +1 -0
  210. package/static/kuaishou/bundles/weapon/kws-16-0.0.1-obfuscated.d5ffa4020f3be99a.js +1 -0
  211. package/static/kuaishou/bundles/weapon/kws-17-0.0.1-obfuscated.6d13e19b22f50a80.js +1 -0
  212. package/static/kuaishou/bundles/weapon/kws-18-0.0.1-obfuscated.6d31405044166289.js +1 -0
  213. package/static/kuaishou/bundles/weapon/kws-19-0.0.1-obfuscated.a9a8d925942f19e4.js +1 -0
  214. package/static/kuaishou/bundles/weapon/kws-2-0.0.1-obfuscated.b849a136ae4a6b3e.js +1 -0
  215. package/static/kuaishou/bundles/weapon/kws-3-0.0.1-obfuscated.f9e77ebf6186e2ac.js +1 -0
  216. package/static/kuaishou/bundles/weapon/kws-4-0.0.1-obfuscated.2c3c155aa52ceb0e.js +1 -0
  217. package/static/kuaishou/bundles/weapon/kws-5-0.0.1-obfuscated.4e553d17691b3c5a.js +1 -0
  218. package/static/kuaishou/bundles/weapon/kws-6-0.0.1-obfuscated.f73ddd3ff13e1217.js +1 -0
  219. package/static/kuaishou/bundles/weapon/kws-7-0.0.1-obfuscated.1628b8729beaab7e.js +1 -0
  220. package/static/kuaishou/bundles/weapon/kws-8-0.0.1-obfuscated.6da6baef7aa81fff.js +1 -0
  221. package/static/kuaishou/bundles/weapon/kws-9-0.0.1-obfuscated.8645d55e5323b743.js +1 -0
  222. package/static/kuaishou/fixtures/current_follow_script_813.network-response +2 -0
  223. package/static/kuaishou/fixtures/live_ws_maps.json +61 -0
  224. package/static/kuaishou/fixtures/live_ws_proto.json +1342 -0
  225. package/static/kuaishou/js/cp-kwf.js +1 -0
  226. package/static/kuaishou/tools/kwf_oracle.js +799 -0
  227. package/static/kuaishou/tools/kws_oracle.js +885 -0
  228. package/static/kuaishou/tools/like_token_oracle.js +70 -0
  229. package/static/taobao/taobao_js_20260407.js +570 -0
  230. package/static/tiktok/Tiktok_Request.json +597 -0
  231. package/static/tiktok/package.json +3 -0
  232. package/static/tiktok/shop_bsid/core.js +1 -0
  233. package/static/tiktok/shop_bsid/env_core.js +512 -0
  234. package/static/tiktok/shop_bsid/loader.js +1 -0
  235. package/static/tiktok/shop_bsid/run.js +753 -0
  236. package/static/tiktok/shop_bsid/sign.js +100 -0
  237. package/static/tiktok/shop_bsid/webmssdk.js +1 -0
  238. package/static/tiktok/shop_bsid/webmssdk_ex.js +1 -0
  239. package/static/tiktok/signing/env/env_core.js +512 -0
  240. package/static/tiktok/signing/env/sign.js +455 -0
  241. package/static/tiktok/signing/sdk_config.json +230 -0
  242. package/static/tiktok/signing/source/webmssdk.js +1 -0
  243. package/static/tiktok/signing/source/webmssdk_ex.js +1 -0
  244. package/static/tiktok/signing/source/webmssdk_legacy.js +1 -0
  245. package/static/x/graphql.json +7777 -0
  246. package/static/x/transaction_l1.json +12 -0
  247. package/static/xhs/package.json +3 -0
  248. package/static/xhs/xhs_core/js/b1.js +210 -0
  249. package/static/xhs/xhs_core/js/mns.js +340 -0
  250. package/static/xhs/xhs_core/js/mns_0101_keystream.json +5 -0
  251. package/static/xhs/xhs_core/js/mns_keystreams.json +1 -0
  252. package/static/xhs/xhs_core/js/sign.js +225 -0
  253. package/static/xhs/xhs_core/js/websectiga_cli.js +220 -0
  254. package/static/xhs/xhs_core/js/websectiga_env.js +512 -0
  255. package/static/xhs/xhs_core/js/xs_common.js +79 -0
  256. package/static/xhs/xhs_creator/js/profile.js +115 -0
  257. package/static/xhs/xhs_creator/js/rap_fingerprint_creator.json +6 -0
  258. package/static/xhs/xhs_creator/js/reference_profile.json +282 -0
  259. package/static/xhs/xhs_creator/js/xhs_creator_sign.js +29 -0
  260. package/static/xhs/xhs_creator/js/xhs_creator_signature.js +23 -0
  261. package/static/xhs/xhs_pc/js/aes_decrypt.js +22 -0
  262. package/static/xhs/xhs_pc/js/aes_encrypt.js +30 -0
  263. package/static/xhs/xhs_pc/js/b1.js +9 -0
  264. package/static/xhs/xhs_pc/js/deflate.js +320 -0
  265. package/static/xhs/xhs_pc/js/mns.js +4 -0
  266. package/static/xhs/xhs_pc/js/profile.js +141 -0
  267. package/static/xhs/xhs_pc/js/rap.js +173 -0
  268. package/static/xhs/xhs_pc/js/rap_cli.js +30 -0
  269. package/static/xhs/xhs_pc/js/rap_crypto.js +73 -0
  270. package/static/xhs/xhs_pc/js/rap_fingerprint_template.json +8 -0
  271. package/static/xhs/xhs_pc/js/reference_profile.json +262 -0
  272. package/static/xhs/xhs_pc/js/sign.js +6 -0
  273. package/static/xhs/xhs_pc/js/web_ssk.js +82 -0
  274. package/static/xhs/xhs_pc/js/xs_common.js +4 -0
  275. package/static/xianyu/et_f.js +2 -0
  276. package/static/xianyu/gen_tfstk.js +410 -0
  277. package/static/xianyu/goofish_js_version_2.js +568 -0
@@ -0,0 +1,297 @@
1
+ import * as n from '../../../core/normalize.js';
2
+ import { withRaw } from '../../../core/schemas.js';
3
+ import { stripTags } from './util.js';
4
+ /**
5
+ * 京东原始对象 → 归一化类型(AGENTS 6.2)。
6
+ * 上游对商品详情、评价、优惠券、咚咚等接口只返回原始 JSON,这里按字段名在对象里查找,缺失时为 null;原始对象用 --raw 取。
7
+ */
8
+ export const itemUrl = (sku) => `https://item.jd.com/${sku}.html`;
9
+ export const shopUrl = (shopId) => `https://mall.jd.com/index-${shopId}.html`;
10
+ /** 京东图片:`jfs/t1/...` 这类相对路径补成 CDN 地址。 */
11
+ export function image(path) {
12
+ const s = n.str(path);
13
+ if (!s)
14
+ return null;
15
+ if (/^(https?:)?\/\//.test(s))
16
+ return n.url(s);
17
+ return `https://img14.360buyimg.com/n1/${s.replace(/^\/+/, '')}`;
18
+ }
19
+ /** 在对象里按键名找第一个非空值(广度优先,限深度)。 */
20
+ export function find(obj, keys, depth = 5) {
21
+ let level = [obj];
22
+ for (let d = 0; d <= depth && level.length; d++) {
23
+ const next = [];
24
+ for (const o of level) {
25
+ if (!o || typeof o !== 'object')
26
+ continue;
27
+ if (!Array.isArray(o)) {
28
+ for (const k of keys) {
29
+ const v = o[k];
30
+ if (v != null && v !== '' && !(Array.isArray(v) && !v.length))
31
+ return v;
32
+ }
33
+ }
34
+ for (const v of Object.values(o))
35
+ if (v && typeof v === 'object')
36
+ next.push(v);
37
+ }
38
+ level = next;
39
+ }
40
+ return undefined;
41
+ }
42
+ /** 按键名找数组(广度优先);找不到返回 null。 */
43
+ export function findArrayByKey(obj, key, depth = 6) {
44
+ let level = [obj];
45
+ for (let d = 0; d <= depth && level.length; d++) {
46
+ const next = [];
47
+ for (const o of level) {
48
+ if (!o || typeof o !== 'object')
49
+ continue;
50
+ const v = o[key];
51
+ if (!Array.isArray(o) && Array.isArray(v))
52
+ return v;
53
+ for (const x of Object.values(o))
54
+ if (x && typeof x === 'object')
55
+ next.push(x);
56
+ }
57
+ level = next;
58
+ }
59
+ return null;
60
+ }
61
+ /** 找第一个"元素带某些键"的对象数组(广度优先);找不到返回空数组。 */
62
+ export function findList(obj, keys, depth = 6) {
63
+ let level = [obj];
64
+ for (let d = 0; d <= depth && level.length; d++) {
65
+ const next = [];
66
+ for (const o of level) {
67
+ if (!o || typeof o !== 'object')
68
+ continue;
69
+ if (Array.isArray(o) && o.some((e) => e && typeof e === 'object' && keys.some((k) => k in e)))
70
+ return o;
71
+ for (const v of Object.values(o))
72
+ if (v && typeof v === 'object')
73
+ next.push(v);
74
+ }
75
+ level = next;
76
+ }
77
+ return [];
78
+ }
79
+ export function shopRef(shopId, name) {
80
+ const id = n.str(shopId);
81
+ return id ? { id, name: n.str(name), url: shopUrl(id) } : name ? { id: String(name), name: String(name), url: null } : null;
82
+ }
83
+ /** 搜索结果里的一件商品(wareList 的元素,字段同上游 search_wares)。 */
84
+ export function ware(w) {
85
+ const id = n.id(w.wareId ?? w.skuId ?? w.sku);
86
+ return n.item({
87
+ id,
88
+ kind: 'goods',
89
+ url: n.url(w.productUrl) ?? itemUrl(id),
90
+ title: stripTags(w.wareName ?? w.shortName ?? w.skuName ?? w.name) || null,
91
+ author: shopRef(w.shopId ?? w.venderId, w.shopName),
92
+ cover: image(w.imageurl ?? w.imageUrl ?? w.imgUrl ?? w.image),
93
+ stats: { comments: n.count(w.comment ?? w.commentCount) },
94
+ price: n.price(w.jdPrice ?? w.finalPrice?.price ?? w.price),
95
+ }, w);
96
+ }
97
+ /** 商品详情里的店铺:shopInfo.shop,没有时再按键名 shop 找。 */
98
+ function detailShop(d) {
99
+ const shop = d?.shopInfo?.shop ?? find(d, ['shop']);
100
+ return shop && typeof shop === 'object' ? shop : {};
101
+ }
102
+ /** 商品详情里的商家 venderId(咚咚咨询的对象):店铺上的,其次 wareInfo 的;0 表示没有。 */
103
+ export function detailVenderId(d) {
104
+ const v = n.str(detailShop(d).venderId ?? d?.wareInfo?.venderId ?? find(d, ['venderId']));
105
+ return v && v !== '0' ? v : null;
106
+ }
107
+ /**
108
+ * 商品详情(pc_detailpage_wareBusiness)。上游只返回原始 JSON、没有解析代码,真机上这个接口一直被 403,没有核对过真实响应;
109
+ * 先取确定的路径(wareInfo.wname、price.p、shopInfo.shop、wareInfo / imageInfo 下的主图列表),没有时再按专有键名广搜兜底。
110
+ * 兜底不用 url / p / name 这类通用键名:浅层的 name、url 会压过深层真正的 wname、big。
111
+ */
112
+ export function detail(sku, d) {
113
+ const ware = d?.wareInfo && typeof d.wareInfo === 'object' ? d.wareInfo : {};
114
+ const shop = detailShop(d);
115
+ const list = [ware.imageList, d?.imageInfo?.imageList, d?.imageInfo].find(Array.isArray) ?? findList(d, ['big', 'imgUrl', 'imageUrl']);
116
+ const images = list.map((x) => image(typeof x === 'string' ? x : (x?.big ?? x?.imgUrl ?? x?.imageUrl))).filter(Boolean);
117
+ return n.item({
118
+ id: sku,
119
+ kind: 'goods',
120
+ url: itemUrl(sku),
121
+ title: n.str(ware.wname ?? find(d, ['wname', 'skuName', 'wareName'])),
122
+ author: shopRef(shop.shopId ?? find(d, ['shopId']), shop.name ?? find(d, ['shopName'])),
123
+ cover: images[0] ?? image(ware.imageUrl ?? find(d, ['imageUrl', 'imgUrl'])),
124
+ media: images.map((url, i) => n.media({ id: String(i + 1), type: 'image', url })),
125
+ stats: { comments: n.count(find(d, ['commentCount', 'allCnt', 'commentNum'])) },
126
+ price: n.price(d?.price?.p ?? d?.price?.finalPrice?.price ?? find(d, ['jdPrice'])),
127
+ }, d);
128
+ }
129
+ /**
130
+ * 商品评价(getLegoWareDetailComment)。响应里同时有评价 `commentInfoList` 和问答 `questionList`,
131
+ * 问答条目也有 `content`,按字段名去猜会拿成问答,所以先按键名取评价列表。
132
+ */
133
+ export function comments(sku, d) {
134
+ const list = findArrayByKey(d, 'commentInfoList') ?? findList(d, ['commentData', 'commentId']);
135
+ return list.map((c) => n.comment({
136
+ id: n.id(c.commentId ?? c.id ?? c.guid),
137
+ item_id: sku,
138
+ author: n.userRef({ id: c.userNickName ?? c.nickName ?? c.nickname ?? c.userId, name: c.userNickName ?? c.nickName ?? c.nickname }),
139
+ text: String(c.commentData ?? c.content ?? ''),
140
+ created_at: n.time(c.commentDate ?? c.creationTime ?? c.date),
141
+ stats: { likes: n.count(c.praiseCnt ?? c.usefulVoteCount ?? c.likeCount), replies: n.count(c.replyCnt ?? c.replyCount) },
142
+ }, c));
143
+ }
144
+ /** 推荐优惠券(getRecommendCoupon)。 */
145
+ export function coupons(d) {
146
+ return findList(d, ['discount', 'quota', 'couponId', 'batchId']).map((c) => {
147
+ const quota = n.price(c.quota ?? c.threshold);
148
+ return withRaw({
149
+ id: n.id(c.couponId ?? c.batchId ?? c.roleId ?? c.key ?? c.id),
150
+ title: n.str(c.name ?? c.couponTitle ?? c.title ?? c.desc ?? c.limitStr),
151
+ discount: n.price(c.discount ?? c.discountAmount ?? c.parValue),
152
+ threshold: quota && quota.amount > 0 ? quota : null,
153
+ start_at: n.time(c.beginTime ?? c.startTime),
154
+ end_at: n.time(c.endTime),
155
+ }, c);
156
+ });
157
+ }
158
+ /**
159
+ * 浏览历史 / 关注商品里的一件商品(pc_myjd_getBrowseHistory、pc_follow_product_new)。
160
+ * 这两个接口的真实响应还没核对过字段(2026-09-28 真机 user collects 的 cover、author 都取不到),
161
+ * 这里在元素上按商品接口常见的键名取,图片多认几种写法(searchWare 的 imageurl 等)。
162
+ */
163
+ export function listed(w) {
164
+ const id = n.id(w.skuId ?? w.wareId ?? w.sku ?? w.productId);
165
+ return n.item({
166
+ id,
167
+ kind: 'goods',
168
+ url: itemUrl(id),
169
+ title: n.str(stripTags(w.wname ?? w.skuName ?? w.wareName ?? w.name ?? w.title)),
170
+ author: shopRef(w.shopId ?? w.venderId, w.shopName ?? w.venderName),
171
+ cover: image(w.imgUrl ?? w.imageUrl ?? w.imageurl ?? w.imagePath ?? w.skuImg ?? w.image ?? w.img),
172
+ price: n.price(w.jdPrice ?? w.price ?? w.p),
173
+ }, w);
174
+ }
175
+ /** 购物车数量(pcCart_jc_getCartNum 的 cartNum,上游 get_cart_num 注释里实抓的字段)。 */
176
+ export function cartCount(d) {
177
+ return n.count(d?.cartNum ?? find(d, ['cartNum']));
178
+ }
179
+ export function listedItems(d) {
180
+ return findList(d, ['skuId', 'wareId', 'productId']).map(listed);
181
+ }
182
+ /** 订单(订单中心 HTML)。商品的 SKU 与商品名取自同一个链接(parseOrders 的 skus,与 products 一一对应)。 */
183
+ export function order(o) {
184
+ const skus = o.skus ?? [];
185
+ return withRaw({
186
+ id: o.orderId,
187
+ status: o.status || null,
188
+ total: n.price(o.amount),
189
+ items: o.products.map((title, i) => {
190
+ const sku = skus[i] ?? null;
191
+ return n.item({ id: sku ?? '', kind: 'goods', url: sku ? itemUrl(sku) : null, title });
192
+ }),
193
+ created_at: n.time(o.time),
194
+ }, o);
195
+ }
196
+ /** 搜索热词(pc_search_hotwords 的 data):先滤掉没有词的条目,再逐条归一化,原始对象与输出一一对应。 */
197
+ export function keywordsFromHot(d) {
198
+ const text = (x) => n.str(x?.n ?? x?.ext_columns?.text ?? x?.keyword);
199
+ return (Array.isArray(d?.data) ? d.data : []).filter((x) => text(x)).map((x) => n.keyword({ text: text(x) }, x));
200
+ }
201
+ export function keywordsFromRel(d) {
202
+ const list = Array.isArray(d?.data) ? d.data : Array.isArray(d?.resultKeywords) ? d.resultKeywords : [];
203
+ return list.filter((x) => x?.keyword).map((x) => n.keyword({ text: String(x.keyword) }, x));
204
+ }
205
+ /** 当前账号(passport loginservice)。 */
206
+ export function me(pin, data) {
207
+ const id = data?.Identity?.Name || pin;
208
+ return n.user({ id: String(pin || id), name: n.str(data?.Identity?.Unick || data?.Identity?.Name), handle: pin || null, url: 'https://home.jd.com/' }, data);
209
+ }
210
+ export const meRef = (pin, name) => ({ id: pin, name: name || pin, url: 'https://home.jd.com/' });
211
+ // ---------------------------------------------------------------- 咚咚
212
+ /** 历史会话(getChatSessionLog):列表在 chatSessions(2026-09-29 真机),没有时再找带 venderId 的数组。 */
213
+ export function conversations(d) {
214
+ return (Array.isArray(d?.chatSessions) ? d.chatSessions : findList(d, ['venderId'])).map((s) => {
215
+ const last = s.lastMsg ?? s.lastMessage ?? s.msg ?? {};
216
+ return n.conversation({
217
+ id: n.id(s.venderId),
218
+ peer: { id: n.id(s.venderId), name: n.str(s.venderName ?? s.shopName ?? s.name), url: s.shopId ? shopUrl(s.shopId) : null },
219
+ unread: n.count(s.unreadCount ?? s.unread ?? s.unReadNum),
220
+ last_message: n.str(typeof last === 'string' ? last : (last.content ?? last.body?.content)),
221
+ updated_at: n.time(s.timestamp ?? s.lastTime ?? s.time ?? last.timestamp ?? last.datetime),
222
+ }, s);
223
+ });
224
+ }
225
+ /** 咚咚消息正文里尽量抽出可读文本(JdChatWS.extract_text)。 */
226
+ export function extractText(body) {
227
+ if (!body || typeof body !== 'object')
228
+ return null;
229
+ if (typeof body.content === 'string' && body.content.trim())
230
+ return body.content;
231
+ const data = body.data && typeof body.data === 'object' ? body.data : {};
232
+ const tpl = data.tplData;
233
+ for (const holder of [tpl, data, body]) {
234
+ if (!holder || typeof holder !== 'object')
235
+ continue;
236
+ for (const key of ['message', 'title', 'text', 'content', 'answer', 'desc']) {
237
+ const v = holder[key];
238
+ if (typeof v === 'string' && v.trim())
239
+ return v;
240
+ }
241
+ }
242
+ return null;
243
+ }
244
+ const MSG_TYPES = { text: 'text', image: 'image', img: 'image', video: 'video', template2: 'card', card: 'card' };
245
+ const userOf = (pin) => (pin ? { id: String(pin), name: String(pin), url: null } : null);
246
+ /**
247
+ * WS 下行帧 → msg listen 输出的消息(JdChatWS._handle 的分支):
248
+ * - chat_message / event_message:有可读文本或图片时输出;
249
+ * - sys_msg(系统消息)、revoke_message(撤回)、chat_session_open / chat_session_close(会话建立 / 结束):type 为 other,
250
+ * text 是上游日志里的那段说明,撤回时带上被撤回的消息 id;
251
+ * - 心跳、回执、失败等协议帧:不输出(返回 null)。
252
+ */
253
+ export function chatEvent(p) {
254
+ if (!p || typeof p !== 'object')
255
+ return null;
256
+ const body = p.body && typeof p.body === 'object' ? p.body : {};
257
+ const vender = String(body.chatinfo?.venderId ?? body.venderId ?? '');
258
+ const other = (text, from = null) => n.message({ id: n.id(p.id), conversation_id: vender, from, type: 'other', text, created_at: n.time(p.timestamp ?? p.datetime) }, p);
259
+ switch (p.type) {
260
+ case 'chat_message':
261
+ case 'event_message': {
262
+ const m = message(p, vender);
263
+ return m.text || m.media.length ? m : null;
264
+ }
265
+ case 'sys_msg':
266
+ return other(extractText(body));
267
+ case 'revoke_message': {
268
+ const text = n.str(body.revokeContentToC) ?? '对方撤回了一条消息';
269
+ const revoked = n.str(body.revokeMsgId ?? body.msgId ?? body.mid ?? body.id);
270
+ return other(revoked ? `${text}(被撤回的消息:${revoked})` : text, userOf(p.from?.pin));
271
+ }
272
+ case 'chat_session_open': {
273
+ const waiter = n.str(body.waiter?.pin);
274
+ return other(`会话建立:商家 ${vender || '-'},客服 ${waiter ?? '-'}`, userOf(waiter));
275
+ }
276
+ case 'chat_session_close':
277
+ return other('会话结束');
278
+ default:
279
+ return null;
280
+ }
281
+ }
282
+ /** 一条咚咚消息(WS 下行帧或 queryLastLogs 的元素)。 */
283
+ export function message(p, venderId) {
284
+ const body = p.body ?? {};
285
+ const from = p.from ?? {};
286
+ const kind = String(body.type ?? '');
287
+ const url = kind === 'image' ? n.url(body.url ?? body.content) : null;
288
+ return n.message({
289
+ id: n.id(p.id ?? p.mid ?? p.msgId),
290
+ conversation_id: n.id(body.chatinfo?.venderId ?? p.venderId ?? venderId),
291
+ from: from.pin ? { id: String(from.pin), name: n.str(from.nickname ?? from.pin), url: null } : null,
292
+ type: MSG_TYPES[kind] ?? (extractText(body) ? 'text' : 'other'),
293
+ text: extractText(body),
294
+ media: url ? [n.media({ type: 'image', url })] : [],
295
+ created_at: n.time(p.timestamp ?? p.datetime ?? p.time),
296
+ }, p);
297
+ }
@@ -0,0 +1,256 @@
1
+ /**
2
+ * 设备画像、请求头与业务常量(上游 utils/fingerprint.py、builder/header.py、utils/jd_util.py)。
3
+ * UA 报 Chrome 152(与上游一致);TLS 指纹用 wreq-js 最高的 chrome_149(上游 curl_cffi 的 chrome 同样对不齐 UA)。
4
+ */
5
+ export const PROFILE = {
6
+ profileId: 'chrome152-win32-rtx5060ti-hc32-v2',
7
+ ua: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/152.0.0.0 Safari/537.36',
8
+ secChUa: '"Chromium";v="152", "Not?A_Brand";v="24", "Google Chrome";v="152"',
9
+ secChUaMobile: '?0',
10
+ secChUaPlatform: '"Windows"',
11
+ browserLanguage: 'zh-CN',
12
+ browserPlatform: 'Win32',
13
+ screenWidth: 2560,
14
+ screenHeight: 1440,
15
+ };
16
+ export const BROWSER = 'chrome_149';
17
+ export const ACCEPT_ENCODING = 'gzip, deflate, br, zstd';
18
+ export const ACCEPT_LANGUAGE = 'zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7,ja;q=0.6';
19
+ // ---------------------------------------------------------------- 业务常量(utils/jd_util.py)
20
+ export const APPID_PASSPORT = '73806';
21
+ export const APPID_DONGDONG = '2b51e';
22
+ export const APPID_PC_ITEM = 'fb5df';
23
+ export const APPID_PC_SEARCH = 'f06cc';
24
+ export const CLIENT_WH5 = { appid: 'wh5', client: 'wh5', clientVersion: '1.0.0', loginType: '3' };
25
+ export const CLIENT_IMH5 = { client: 'imh5', appid: 'imh5', clientVersion: '1.0.0', loginType: '3' };
26
+ export const CLIENT_PC_ITEM = { appid: 'pc-item-soa', client: 'pc', clientVersion: '1.0.0', loginType: '3' };
27
+ export const CLIENT_PC_ITEM_V3 = { appid: 'item-v3', client: 'pc', clientVersion: '1.0.0', loginType: '3' };
28
+ export const CLIENT_PC_SEARCH = { appid: 'search-pc-java', client: 'pc', clientVersion: '1.0.0', loginType: '3' };
29
+ export const DEFAULT_AREA = '1_2800_55812_0';
30
+ export const ORDER_PC_SEARCH = ['appid', 't', 'client', 'clientVersion', 'cthr', 'uuid', 'loginType', 'keyword', 'functionId', 'body', 'x-api-eid-token', 'h5st'];
31
+ export const ORDER_PC_ITEM = ['functionId', 'body', 'h5st', 'uuid', 'loginType', 'appid', 'clientVersion', 'client', 't', 'x-api-eid-token', 'scval'];
32
+ export const ORDER_PC_ITEM_RELWORDS = ['appid', 'functionId', 'client', 'clientVersion', 'uuid', 'skuid', 'num', 'rettype', 'type_name', 'body'];
33
+ export const ORDER_PC_API = ['functionId', 'appid', 'loginType', 'x-api-eid-token', 'h5st', 't', 'client', 'clientVersion', 'body'];
34
+ export const ORDER_PC_SEARCH_PLAIN = ['appid', 'functionId', 'client', 'clientVersion', 'uuid', 'body', 't'];
35
+ export const ORDER_PC_SEARCH_RELWORDS = ['appid', 'functionId', 'client', 'clientVersion', 'uuid', 'keyword', 'num', 'rettype', 'type_name', 'body', 't'];
36
+ export const ORDER_DD_WITH_TIME = ['functionId', 'client', 'appid', 't', 'clientVersion', 'loginType', 'h5st', 'x-api-eid-token'];
37
+ export const ORDER_DD_NO_TIME = ['functionId', 'appid', 'client', 'clientVersion', 'loginType', 'h5st', 'x-api-eid-token'];
38
+ export const RP_CLIENT_CHAT = 'h5_1.0.0';
39
+ export const RP_CLIENT_SEARCH = 'h5_2.1.0';
40
+ export const RP_CLIENT_ITEM = 'h5_2.2.0';
41
+ /** 参与 h5st 签名的 key(builder/params.py H5ST_SIGN_KEYS)。 */
42
+ export const H5ST_SIGN_KEYS = ['appid', 'functionId', 'body', 'client', 'clientVersion', 'jsonp', 't'];
43
+ // ---------------------------------------------------------------- 站点
44
+ export const API_URL = 'https://api.m.jd.com';
45
+ export const ITEM_ORIGIN = 'https://item.jd.com';
46
+ export const ITEM_REFERER = 'https://item.jd.com/';
47
+ export const SEARCH_ORIGIN = 'https://search.jd.com';
48
+ export const SEARCH_REFERER = 'https://search.jd.com/Search?keyword=%E6%89%8B%E6%9C%BA&enc=utf-8';
49
+ export const CHAT_ORIGIN = 'https://jdcs.jd.com';
50
+ export const CHAT_REFERER = 'https://jdcs.jd.com/';
51
+ export const ORDER_URL = 'https://order.jd.com/center/list.action';
52
+ export const QR_URL = 'https://qr.m.jd.com';
53
+ export const PASSPORT_URL = 'https://passport.jd.com';
54
+ /** Chrome 地址栏与 Network 都保留 ReturnUrl 的未预编码形式。 */
55
+ export const LOGIN_PAGE = 'https://passport.jd.com/new/login.aspx?ReturnUrl=https://home.jd.com/index.html';
56
+ /** 凭证里 cookie 统一存的域:上游的 cookie 是一张不分域的表,发往所有京东站点。 */
57
+ export const COOKIE_DOMAIN = '.jd.com';
58
+ // ---------------------------------------------------------------- 请求头(builder/header.py)
59
+ /** 有序请求头,对应上游的 Header:改值不影响位置,新键追加到末尾;值为 null 表示显式去掉该头。 */
60
+ export class Header {
61
+ pairs = [];
62
+ set(key, value) {
63
+ const i = this.pairs.findIndex(([k]) => k === key);
64
+ if (i >= 0)
65
+ this.pairs[i] = [key, value];
66
+ else
67
+ this.pairs.push([key, value]);
68
+ return this;
69
+ }
70
+ update(values) {
71
+ for (const [k, v] of Object.entries(values))
72
+ this.set(k, v);
73
+ return this;
74
+ }
75
+ referer(url) {
76
+ return this.set('referer', url);
77
+ }
78
+ origin(url) {
79
+ return this.set('origin', url);
80
+ }
81
+ remove(key) {
82
+ this.pairs = this.pairs.filter(([k]) => k !== key);
83
+ return this;
84
+ }
85
+ has(key) {
86
+ return this.pairs.some(([k]) => k === key);
87
+ }
88
+ /** 按给定顺序重排;不在 order 里的键按原顺序垫在后面。 */
89
+ reorder(order) {
90
+ const map = new Map(this.pairs);
91
+ const out = [];
92
+ for (const k of order)
93
+ if (map.has(k))
94
+ out.push([k, map.get(k)]);
95
+ for (const [k, v] of this.pairs)
96
+ if (!order.includes(k))
97
+ out.push([k, v]);
98
+ this.pairs = out;
99
+ return this;
100
+ }
101
+ get() {
102
+ return this.pairs;
103
+ }
104
+ }
105
+ export const FETCH_ORDER = [
106
+ 'sec-ch-ua-platform', 'referer', 'sec-ch-ua', 'sec-ch-ua-mobile', 'user-agent', 'accept', 'accept-encoding',
107
+ 'accept-language', 'content-type', 'origin', 'priority', 'sec-fetch-dest', 'sec-fetch-mode', 'sec-fetch-site',
108
+ ];
109
+ export const AXIOS_ORDER = [
110
+ 'sec-ch-ua-platform', 'referer', 'sec-ch-ua', 'sec-ch-ua-mobile', 'user-agent', 'accept', 'x-referer-page',
111
+ 'content-type', 'x-rp-client', 'accept-encoding', 'accept-language', 'origin', 'priority',
112
+ 'sec-fetch-dest', 'sec-fetch-mode', 'sec-fetch-site',
113
+ ];
114
+ /** PC / 咚咚接口的 XHR 头(HeaderBuilder.build_xhr)。 */
115
+ export function xhr(options = {}) {
116
+ const h = new Header().update({
117
+ 'sec-ch-ua-platform': PROFILE.secChUaPlatform,
118
+ 'sec-ch-ua': PROFILE.secChUa,
119
+ 'sec-ch-ua-mobile': PROFILE.secChUaMobile,
120
+ 'user-agent': PROFILE.ua,
121
+ accept: options.accept || (options.axios ? 'application/json, text/plain, */*' : '*/*'),
122
+ 'accept-encoding': ACCEPT_ENCODING,
123
+ 'accept-language': ACCEPT_LANGUAGE,
124
+ priority: 'u=1, i',
125
+ 'sec-fetch-dest': 'empty',
126
+ 'sec-fetch-mode': 'cors',
127
+ 'sec-fetch-site': 'same-site',
128
+ });
129
+ if (options.form)
130
+ h.set('content-type', options.contentType || 'application/x-www-form-urlencoded;charset=UTF-8');
131
+ else if (options.contentType != null)
132
+ h.set('content-type', options.contentType);
133
+ return h;
134
+ }
135
+ /** HeaderBuilder.build 的通用档(GET / POST / FORM / JSONP / DOC)。 */
136
+ export function basic(type) {
137
+ const h = new Header().update({
138
+ 'user-agent': PROFILE.ua,
139
+ 'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8',
140
+ 'cache-control': 'no-cache',
141
+ pragma: 'no-cache',
142
+ 'sec-ch-ua': PROFILE.secChUa,
143
+ 'sec-ch-ua-mobile': '?0',
144
+ 'sec-ch-ua-platform': PROFILE.secChUaPlatform,
145
+ });
146
+ const xhrPart = { 'sec-fetch-dest': 'empty', 'sec-fetch-mode': 'cors', 'sec-fetch-site': 'same-site' };
147
+ if (type === 'GET')
148
+ h.update({ accept: 'application/json, text/plain, */*', ...xhrPart });
149
+ else if (type === 'POST')
150
+ h.update({ accept: 'application/json, text/plain, */*', 'content-type': 'application/json; charset=UTF-8', ...xhrPart });
151
+ else if (type === 'FORM')
152
+ h.update({ accept: 'application/json, text/plain, */*', 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8', ...xhrPart });
153
+ else if (type === 'JSONP')
154
+ h.update({ accept: '*/*', 'sec-fetch-dest': 'script', 'sec-fetch-mode': 'no-cors', 'sec-fetch-site': 'same-site' });
155
+ else {
156
+ h.update({
157
+ accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
158
+ 'sec-fetch-dest': 'document',
159
+ 'sec-fetch-mode': 'navigate',
160
+ 'sec-fetch-site': 'none',
161
+ 'sec-fetch-user': '?1',
162
+ 'upgrade-insecure-requests': '1',
163
+ });
164
+ }
165
+ return h;
166
+ }
167
+ /** `qr.m.jd.com/show` 的图片请求头。 */
168
+ export function qrImage() {
169
+ return new Header()
170
+ .update({
171
+ 'sec-ch-ua-platform': PROFILE.secChUaPlatform,
172
+ 'user-agent': PROFILE.ua,
173
+ referer: 'https://passport.jd.com/',
174
+ 'sec-ch-ua': PROFILE.secChUa,
175
+ 'sec-ch-ua-mobile': PROFILE.secChUaMobile,
176
+ accept: 'image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8',
177
+ 'accept-encoding': ACCEPT_ENCODING,
178
+ 'accept-language': ACCEPT_LANGUAGE,
179
+ priority: 'u=1, i',
180
+ 'sec-fetch-dest': 'image',
181
+ 'sec-fetch-mode': 'no-cors',
182
+ 'sec-fetch-site': 'same-site',
183
+ })
184
+ .reorder([
185
+ 'sec-ch-ua-platform', 'referer', 'user-agent', 'sec-ch-ua', 'sec-ch-ua-mobile', 'accept', 'accept-encoding',
186
+ 'accept-language', 'priority', 'sec-fetch-dest', 'sec-fetch-mode', 'sec-fetch-site',
187
+ ]);
188
+ }
189
+ /** 扫码轮询的 `<script>` 请求头。 */
190
+ export function qrJsonp() {
191
+ return new Header()
192
+ .update({
193
+ 'sec-ch-ua-platform': PROFILE.secChUaPlatform,
194
+ 'user-agent': PROFILE.ua,
195
+ referer: 'https://passport.jd.com/',
196
+ 'sec-ch-ua': PROFILE.secChUa,
197
+ 'sec-ch-ua-mobile': PROFILE.secChUaMobile,
198
+ accept: '*/*',
199
+ 'accept-encoding': ACCEPT_ENCODING,
200
+ 'accept-language': ACCEPT_LANGUAGE,
201
+ 'sec-fetch-dest': 'script',
202
+ 'sec-fetch-mode': 'no-cors',
203
+ 'sec-fetch-site': 'same-site',
204
+ })
205
+ .reorder([
206
+ 'sec-ch-ua-platform', 'referer', 'user-agent', 'sec-ch-ua', 'sec-ch-ua-mobile', 'accept', 'accept-encoding',
207
+ 'accept-language', 'sec-fetch-dest', 'sec-fetch-mode', 'sec-fetch-site',
208
+ ]);
209
+ }
210
+ /** passport 的 jQuery AJAX 头(build_qr_validation),带页面级的 SGM / JDAS 链路头。 */
211
+ export function qrValidation(trace = {}) {
212
+ const h = new Header();
213
+ for (const key of ['sgm-context', 'jdas-trace-id'])
214
+ if (trace[key])
215
+ h.set(key, trace[key]);
216
+ h.set('sec-ch-ua-platform', PROFILE.secChUaPlatform);
217
+ h.set('referer', 'https://passport.jd.com/new/login.aspx?ReturnUrl=https://home.jd.com/index.html');
218
+ h.set('sec-ch-ua', PROFILE.secChUa);
219
+ h.set('sec-ch-ua-mobile', PROFILE.secChUaMobile);
220
+ h.set('x-requested-with', 'XMLHttpRequest');
221
+ h.set('user-agent', PROFILE.ua);
222
+ h.set('accept', 'application/json, text/javascript, */*; q=0.01');
223
+ for (const key of ['jdas-page-id', 'jdas-session-id'])
224
+ if (trace[key])
225
+ h.set(key, trace[key]);
226
+ return h.update({
227
+ 'accept-encoding': ACCEPT_ENCODING,
228
+ 'accept-language': ACCEPT_LANGUAGE,
229
+ priority: 'u=1, i',
230
+ 'sec-fetch-dest': 'empty',
231
+ 'sec-fetch-mode': 'cors',
232
+ 'sec-fetch-site': 'same-origin',
233
+ });
234
+ }
235
+ /** 订单中心 HTML 导航请求头。 */
236
+ export function orderDoc() {
237
+ return new Header().update({
238
+ 'upgrade-insecure-requests': '1',
239
+ 'user-agent': PROFILE.ua,
240
+ accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
241
+ 'accept-encoding': ACCEPT_ENCODING,
242
+ 'accept-language': ACCEPT_LANGUAGE,
243
+ priority: 'u=0, i',
244
+ 'sec-fetch-dest': 'document',
245
+ 'sec-fetch-mode': 'navigate',
246
+ 'sec-fetch-site': 'none',
247
+ 'sec-fetch-user': '?1',
248
+ });
249
+ }
250
+ /** 咚咚 WebSocket 握手头。 */
251
+ export const WS_HEADERS = {
252
+ 'User-Agent': PROFILE.ua,
253
+ 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
254
+ 'Cache-Control': 'no-cache',
255
+ Pragma: 'no-cache',
256
+ };