@sovovs/bycli 1.8.0 → 2.0.0

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 (509) hide show
  1. package/cli-manifest.json +0 -61
  2. package/dist/src/adapter-shadow.js +2 -2
  3. package/dist/src/browser/analyze.d.ts +10 -99
  4. package/dist/src/browser/analyze.js +10 -226
  5. package/dist/src/browser/cdp.js +64 -7
  6. package/dist/src/browser/navigation-guard.d.ts +78 -0
  7. package/dist/src/browser/navigation-guard.js +142 -0
  8. package/dist/src/browser/navigation-guard.test.d.ts +1 -0
  9. package/dist/src/browser/url-policy.d.ts +89 -0
  10. package/dist/src/browser/url-policy.js +311 -0
  11. package/dist/src/browser/url-policy.test.d.ts +1 -0
  12. package/dist/src/browser/verify-fixture.js +2 -2
  13. package/dist/src/cli.js +10 -13
  14. package/dist/src/config-paths.d.ts +19 -0
  15. package/dist/src/config-paths.js +31 -0
  16. package/dist/src/daemon.js +240 -0
  17. package/dist/src/discovery.d.ts +5 -3
  18. package/dist/src/discovery.js +8 -6
  19. package/dist/src/execution.js +8 -3
  20. package/dist/src/main.js +18 -2
  21. package/dist/src/plugin.js +11 -8
  22. package/dist/src/recorder/highlevel/analyze.d.ts +29 -0
  23. package/dist/src/recorder/highlevel/analyze.js +91 -0
  24. package/dist/src/recorder/highlevel/init.d.ts +125 -0
  25. package/dist/src/recorder/highlevel/init.js +279 -0
  26. package/dist/src/recorder/highlevel/init.test.d.ts +1 -0
  27. package/dist/src/recorder/highlevel/verify.d.ts +73 -0
  28. package/dist/src/recorder/highlevel/verify.js +74 -0
  29. package/dist/src/recorder/http/analyze-runner.d.ts +28 -0
  30. package/dist/src/recorder/http/analyze-runner.js +78 -0
  31. package/dist/src/recorder/http/analyze-runner.test.d.ts +1 -0
  32. package/dist/src/recorder/http/handlers.d.ts +51 -0
  33. package/dist/src/recorder/http/handlers.js +219 -0
  34. package/dist/src/recorder/http/wrapper-config.d.ts +20 -0
  35. package/dist/src/recorder/http/wrapper-config.js +82 -0
  36. package/dist/src/recorder/http/wrapper-envelope.d.ts +31 -0
  37. package/dist/src/recorder/http/wrapper-envelope.js +51 -0
  38. package/dist/src/recorder/http/wrapper-gates.d.ts +16 -0
  39. package/dist/src/recorder/http/wrapper-gates.js +28 -0
  40. package/dist/src/recorder/http/wrapper-metrics.d.ts +1 -0
  41. package/dist/src/recorder/http/wrapper-metrics.js +5 -0
  42. package/dist/src/recorder/http/wrapper-registry.d.ts +50 -0
  43. package/dist/src/recorder/http/wrapper-registry.js +98 -0
  44. package/dist/src/recorder/http/wrapper-server.d.ts +59 -0
  45. package/dist/src/recorder/http/wrapper-server.js +240 -0
  46. package/dist/src/recorder/http/wrapper.test.d.ts +1 -0
  47. package/dist/src/recorder/observability/logger.d.ts +5 -0
  48. package/dist/src/recorder/observability/logger.js +13 -0
  49. package/dist/src/recorder/runner/config.d.ts +34 -0
  50. package/dist/src/recorder/runner/config.js +84 -0
  51. package/dist/src/recorder/runner/config.test.d.ts +1 -0
  52. package/dist/src/recorder/runner/reap.d.ts +79 -0
  53. package/dist/src/recorder/runner/reap.js +230 -0
  54. package/dist/src/recorder/runner/reap.test.d.ts +1 -0
  55. package/dist/src/recorder/runner/runner-port.d.ts +95 -0
  56. package/dist/src/recorder/runner/runner-port.js +518 -0
  57. package/dist/src/recorder/runner/runner-port.test.d.ts +1 -0
  58. package/dist/src/recorder/runner/session-keys.d.ts +28 -0
  59. package/dist/src/recorder/runner/session-keys.js +72 -0
  60. package/dist/src/recorder/runner/session-keys.test.d.ts +1 -0
  61. package/dist/src/recorder/runner/verify-runner-main.d.ts +95 -0
  62. package/dist/src/recorder/runner/verify-runner-main.js +275 -0
  63. package/dist/src/recorder/runner/verify-runner-main.test.d.ts +1 -0
  64. package/package.json +8 -5
  65. package/scripts/m1-daemon-navigate-drive.mjs +138 -0
  66. package/scripts/m1-extension-spike.mjs +237 -0
  67. package/scripts/m1-live-spike.mjs +156 -0
  68. package/scripts/m6b-connect-back-spike.sh +31 -0
  69. package/scripts/recorder.sh +186 -0
  70. package/clis/12306/utils.test.js +0 -331
  71. package/clis/1688/assets.test.js +0 -39
  72. package/clis/1688/download.test.js +0 -31
  73. package/clis/1688/item.test.js +0 -67
  74. package/clis/1688/search.test.js +0 -75
  75. package/clis/1688/shared.test.js +0 -57
  76. package/clis/1688/store.test.js +0 -62
  77. package/clis/36kr/article.test.js +0 -46
  78. package/clis/36kr/hot.test.js +0 -15
  79. package/clis/36kr/news.test.js +0 -85
  80. package/clis/51job/utils.test.js +0 -69
  81. package/clis/_atlassian/shared.test.js +0 -170
  82. package/clis/aibase/news.test.js +0 -59
  83. package/clis/amazon/bestsellers.test.js +0 -29
  84. package/clis/amazon/discussion.test.js +0 -124
  85. package/clis/amazon/offer.test.js +0 -29
  86. package/clis/amazon/product.test.js +0 -24
  87. package/clis/amazon/rankings.test.js +0 -41
  88. package/clis/amazon/search.test.js +0 -22
  89. package/clis/amazon/shared.test.js +0 -44
  90. package/clis/apple-podcasts/commands.test.js +0 -119
  91. package/clis/apple-podcasts/utils.test.js +0 -57
  92. package/clis/arxiv/arxiv.test.js +0 -112
  93. package/clis/baidu-scholar/search.test.js +0 -23
  94. package/clis/barchart/greeks.test.js +0 -138
  95. package/clis/bilibili/comment.test.js +0 -153
  96. package/clis/bilibili/comments.test.js +0 -142
  97. package/clis/bilibili/dynamic.test.js +0 -68
  98. package/clis/bilibili/hot.test.js +0 -17
  99. package/clis/bilibili/subtitle.test.js +0 -196
  100. package/clis/bilibili/summary.test.js +0 -210
  101. package/clis/bilibili/utils.test.js +0 -61
  102. package/clis/bilibili/video.test.js +0 -132
  103. package/clis/binance/commands.test.js +0 -70
  104. package/clis/bloomberg/utils.test.js +0 -129
  105. package/clis/booking/booking.test.js +0 -356
  106. package/clis/boss/chatlist.test.js +0 -211
  107. package/clis/boss/chatmsg.test.js +0 -230
  108. package/clis/boss/search.test.js +0 -78
  109. package/clis/boss/utils.test.js +0 -34
  110. package/clis/brave/search.test.js +0 -76
  111. package/clis/chaoxing/utils.test.js +0 -45
  112. package/clis/chatgpt/commands.test.js +0 -45
  113. package/clis/chatgpt/envelope.test.js +0 -108
  114. package/clis/chatgpt/image.test.js +0 -188
  115. package/clis/chatgpt/utils.test.js +0 -330
  116. package/clis/chatgpt-app/ax.test.js +0 -35
  117. package/clis/chatwise/composer.test.js +0 -186
  118. package/clis/chess/analyze.test.js +0 -79
  119. package/clis/chess/game.test.js +0 -178
  120. package/clis/chess/games.test.js +0 -164
  121. package/clis/chess/stats.test.js +0 -79
  122. package/clis/chess/utils.test.js +0 -230
  123. package/clis/claude/ask.test.js +0 -338
  124. package/clis/claude/commands.test.js +0 -118
  125. package/clis/claude/utils.test.js +0 -148
  126. package/clis/cnki/search.test.js +0 -18
  127. package/clis/codex/sidebar.test.js +0 -329
  128. package/clis/coingecko/coingecko.test.js +0 -109
  129. package/clis/confluence/commands.test.js +0 -195
  130. package/clis/coupang/coupang.test.js +0 -159
  131. package/clis/coupang/utils.test.js +0 -62
  132. package/clis/ctrip/ctrip.test.js +0 -719
  133. package/clis/dblp/dblp.test.js +0 -397
  134. package/clis/deepseek/ask.test.js +0 -312
  135. package/clis/deepseek/detail.test.js +0 -81
  136. package/clis/deepseek/send.test.js +0 -107
  137. package/clis/deepseek/utils.test.js +0 -370
  138. package/clis/defillama/defillama.test.js +0 -99
  139. package/clis/devto/devto.test.js +0 -236
  140. package/clis/dianping/dianping.test.js +0 -578
  141. package/clis/douban/download.test.js +0 -170
  142. package/clis/douban/movie-hot.test.js +0 -14
  143. package/clis/douban/search.test.js +0 -11
  144. package/clis/douban/subject.test.js +0 -11
  145. package/clis/douban/utils.test.js +0 -438
  146. package/clis/doubao/detail.test.js +0 -42
  147. package/clis/doubao/history.test.js +0 -37
  148. package/clis/doubao/utils.test.js +0 -344
  149. package/clis/douyin/_shared/browser-fetch.test.js +0 -64
  150. package/clis/douyin/_shared/creation-id.test.js +0 -22
  151. package/clis/douyin/_shared/imagex-upload.test.js +0 -87
  152. package/clis/douyin/_shared/sts2.test.js +0 -27
  153. package/clis/douyin/_shared/text-extra.test.js +0 -37
  154. package/clis/douyin/_shared/timing.test.js +0 -28
  155. package/clis/douyin/_shared/tos-upload-short-read.test.js +0 -83
  156. package/clis/douyin/_shared/tos-upload.test.js +0 -229
  157. package/clis/douyin/_shared/transcode.test.js +0 -91
  158. package/clis/douyin/_shared/vod-upload.test.js +0 -38
  159. package/clis/douyin/activities.test.js +0 -55
  160. package/clis/douyin/collections.test.js +0 -45
  161. package/clis/douyin/delete.test.js +0 -100
  162. package/clis/douyin/draft.test.js +0 -335
  163. package/clis/douyin/drafts.test.js +0 -11
  164. package/clis/douyin/hashtag.test.js +0 -171
  165. package/clis/douyin/location.test.js +0 -23
  166. package/clis/douyin/profile.test.js +0 -46
  167. package/clis/douyin/publish-upload-id.test.js +0 -170
  168. package/clis/douyin/publish.test.js +0 -38
  169. package/clis/douyin/stats.test.js +0 -22
  170. package/clis/douyin/update.test.js +0 -11
  171. package/clis/douyin/user-videos.test.js +0 -151
  172. package/clis/douyin/videos.test.js +0 -54
  173. package/clis/duckduckgo/search.test.js +0 -128
  174. package/clis/duckduckgo/suggest.test.js +0 -66
  175. package/clis/eastmoney/hot-rank.test.js +0 -59
  176. package/clis/endoflife/endoflife.test.js +0 -51
  177. package/clis/facebook/feed.test.js +0 -169
  178. package/clis/facebook/marketplace.test.js +0 -91
  179. package/clis/facebook/notifications.test.js +0 -458
  180. package/clis/facebook/search.test.js +0 -55
  181. package/clis/flathub/flathub.test.js +0 -90
  182. package/clis/flomo/memos.test.js +0 -144
  183. package/clis/gemini/ask.test.js +0 -100
  184. package/clis/gemini/deep-research-result.test.js +0 -155
  185. package/clis/gemini/deep-research.test.js +0 -181
  186. package/clis/gemini/reply-state.test.js +0 -671
  187. package/clis/gemini/utils.test.js +0 -198
  188. package/clis/geogebra/geogebra.test.js +0 -175
  189. package/clis/gitee/search.test.js +0 -65
  190. package/clis/gitee/user.test.js +0 -63
  191. package/clis/google/utils.test.js +0 -75
  192. package/clis/google-scholar/cite.test.js +0 -47
  193. package/clis/google-scholar/profile.test.js +0 -49
  194. package/clis/google-scholar/search.test.js +0 -71
  195. package/clis/goproxy/goproxy.test.js +0 -103
  196. package/clis/gov-law/commands.test.js +0 -39
  197. package/clis/gov-policy/gov-policy.test.js +0 -224
  198. package/clis/grok/ask.test.js +0 -29
  199. package/clis/grok/utils.test.js +0 -103
  200. package/clis/hackernews/hackernews.test.js +0 -132
  201. package/clis/hf/hf.test.js +0 -16
  202. package/clis/hupu/hot.test.js +0 -224
  203. package/clis/imdb/utils.test.js +0 -88
  204. package/clis/indeed/indeed.test.js +0 -375
  205. package/clis/instagram/_shared/private-publish.test.js +0 -705
  206. package/clis/instagram/_shared/protocol-capture.test.js +0 -114
  207. package/clis/instagram/download.test.js +0 -118
  208. package/clis/instagram/note.test.js +0 -53
  209. package/clis/instagram/post.test.js +0 -1647
  210. package/clis/instagram/reel.test.js +0 -167
  211. package/clis/instagram/story.test.js +0 -134
  212. package/clis/jd/commands.test.js +0 -41
  213. package/clis/jd/item.test.js +0 -354
  214. package/clis/jianyu/search.test.js +0 -153
  215. package/clis/jianyu/shared/procurement-contract.test.js +0 -72
  216. package/clis/jianyu/shared/procurement-detail.test.js +0 -84
  217. package/clis/jike/read.test.js +0 -86
  218. package/clis/jira/commands.test.js +0 -287
  219. package/clis/juejin/search.js +0 -123
  220. package/clis/juejin/search.test.js +0 -98
  221. package/clis/lesswrong/frontpage.test.js +0 -37
  222. package/clis/lichess/lichess.test.js +0 -85
  223. package/clis/linkedin/connect.test.js +0 -213
  224. package/clis/linkedin/inbox.test.js +0 -152
  225. package/clis/linkedin/job-detail.test.js +0 -38
  226. package/clis/linkedin/jobs-preferences.test.js +0 -43
  227. package/clis/linkedin/people-search.test.js +0 -216
  228. package/clis/linkedin/post-analytics.test.js +0 -40
  229. package/clis/linkedin/posts.test.js +0 -40
  230. package/clis/linkedin/profile-analytics.test.js +0 -67
  231. package/clis/linkedin/profile-experience.test.js +0 -152
  232. package/clis/linkedin/profile-projects.test.js +0 -111
  233. package/clis/linkedin/profile-read.test.js +0 -77
  234. package/clis/linkedin/safe-send.test.js +0 -204
  235. package/clis/linkedin/salesnav-inbox.test.js +0 -113
  236. package/clis/linkedin/salesnav-message.test.js +0 -172
  237. package/clis/linkedin/salesnav-search.test.js +0 -76
  238. package/clis/linkedin/salesnav-thread.test.js +0 -79
  239. package/clis/linkedin/search.test.js +0 -222
  240. package/clis/linkedin/sent-invitations.test.js +0 -62
  241. package/clis/linkedin/services-read.test.js +0 -105
  242. package/clis/linkedin/thread-snapshot.test.js +0 -89
  243. package/clis/linkedin/timeline.test.js +0 -81
  244. package/clis/linkedin-learning/course.test.js +0 -114
  245. package/clis/linkedin-learning/search.test.js +0 -144
  246. package/clis/linkedin-learning/trending.test.js +0 -123
  247. package/clis/linux-do/feed.test.js +0 -153
  248. package/clis/linux-do/topic-content.test.js +0 -59
  249. package/clis/lobsters/lobsters.test.js +0 -169
  250. package/clis/notebooklm/add-source.test.js +0 -97
  251. package/clis/notebooklm/compat.test.js +0 -16
  252. package/clis/notebooklm/create.test.js +0 -58
  253. package/clis/notebooklm/generate-audio.test.js +0 -63
  254. package/clis/notebooklm/generate-slides.test.js +0 -75
  255. package/clis/notebooklm/history.test.js +0 -58
  256. package/clis/notebooklm/note-list.test.js +0 -56
  257. package/clis/notebooklm/notes-get.test.js +0 -72
  258. package/clis/notebooklm/open.test.js +0 -63
  259. package/clis/notebooklm/rpc.test.js +0 -131
  260. package/clis/notebooklm/source-fulltext.test.js +0 -106
  261. package/clis/notebooklm/source-get.test.js +0 -84
  262. package/clis/notebooklm/source-guide.test.js +0 -104
  263. package/clis/notebooklm/summary.test.js +0 -78
  264. package/clis/notebooklm/utils.test.js +0 -449
  265. package/clis/notebooklm/write-note.test.js +0 -70
  266. package/clis/nuget/nuget.test.js +0 -111
  267. package/clis/oeis/oeis.test.js +0 -88
  268. package/clis/ones/task-helpers.test.js +0 -12
  269. package/clis/ones/worklog.test.js +0 -20
  270. package/clis/openfda/openfda.test.js +0 -114
  271. package/clis/openreview/openreview.test.js +0 -427
  272. package/clis/osv/osv.test.js +0 -97
  273. package/clis/paperreview/commands.test.js +0 -243
  274. package/clis/paperreview/utils.test.js +0 -49
  275. package/clis/pixiv/download.test.js +0 -87
  276. package/clis/pixiv/illusts.test.js +0 -99
  277. package/clis/pixiv/search.test.js +0 -83
  278. package/clis/powerchina/search.test.js +0 -93
  279. package/clis/producthunt/utils.test.js +0 -64
  280. package/clis/pubmed/pubmed.test.js +0 -276
  281. package/clis/quark/utils.test.js +0 -58
  282. package/clis/qwen/utils.test.js +0 -45
  283. package/clis/reddit/extract-media.test.js +0 -149
  284. package/clis/reddit/frontpage.test.js +0 -34
  285. package/clis/reddit/home.test.js +0 -170
  286. package/clis/reddit/hot.test.js +0 -32
  287. package/clis/reddit/popular.test.js +0 -26
  288. package/clis/reddit/read.test.js +0 -335
  289. package/clis/reddit/reply.test.js +0 -89
  290. package/clis/reddit/search.test.js +0 -26
  291. package/clis/reddit/subreddit-info.test.js +0 -163
  292. package/clis/reddit/subreddit.test.js +0 -31
  293. package/clis/reddit/subscribed.test.js +0 -168
  294. package/clis/reddit/whoami.test.js +0 -105
  295. package/clis/rednote/navigation.test.js +0 -26
  296. package/clis/rednote/rednote.test.js +0 -222
  297. package/clis/rest-countries/rest-countries.test.js +0 -83
  298. package/clis/reuters/reuters.test.js +0 -299
  299. package/clis/rfc/rfc.test.js +0 -74
  300. package/clis/sinafinance/stock.test.js +0 -59
  301. package/clis/spotify/utils.test.js +0 -67
  302. package/clis/stackoverflow/stackoverflow.test.js +0 -404
  303. package/clis/steam/steam.test.js +0 -46
  304. package/clis/substack/utils.test.js +0 -44
  305. package/clis/suno/commands.test.js +0 -188
  306. package/clis/suno/download.test.js +0 -151
  307. package/clis/suno/generate.test.js +0 -252
  308. package/clis/suno/utils.test.js +0 -329
  309. package/clis/taobao/commands.test.js +0 -41
  310. package/clis/tdx/hot-rank.test.js +0 -59
  311. package/clis/ths/hot-rank.test.js +0 -64
  312. package/clis/tieba/commands.test.js +0 -79
  313. package/clis/tieba/utils.test.js +0 -290
  314. package/clis/tiktok/creator-videos.test.js +0 -113
  315. package/clis/tiktok/refactor.test.js +0 -389
  316. package/clis/tiktok/write-refactor.test.js +0 -370
  317. package/clis/toutiao/toutiao.test.js +0 -378
  318. package/clis/tvmaze/tvmaze.test.js +0 -93
  319. package/clis/twitter/bookmark-folder.test.js +0 -395
  320. package/clis/twitter/bookmark-folders.test.js +0 -152
  321. package/clis/twitter/bookmark.test.js +0 -74
  322. package/clis/twitter/bookmarks.test.js +0 -205
  323. package/clis/twitter/delete.test.js +0 -85
  324. package/clis/twitter/device-follow.test.js +0 -287
  325. package/clis/twitter/download.test.js +0 -457
  326. package/clis/twitter/followers.test.js +0 -62
  327. package/clis/twitter/following.test.js +0 -338
  328. package/clis/twitter/hide-reply.test.js +0 -76
  329. package/clis/twitter/like.test.js +0 -73
  330. package/clis/twitter/likes.test.js +0 -220
  331. package/clis/twitter/list-add.test.js +0 -133
  332. package/clis/twitter/list-create.test.js +0 -169
  333. package/clis/twitter/list-remove.test.js +0 -110
  334. package/clis/twitter/list-tweets.test.js +0 -196
  335. package/clis/twitter/lists.test.js +0 -253
  336. package/clis/twitter/post.test.js +0 -258
  337. package/clis/twitter/profile.test.js +0 -39
  338. package/clis/twitter/quote.test.js +0 -194
  339. package/clis/twitter/reply.test.js +0 -237
  340. package/clis/twitter/retweet.test.js +0 -73
  341. package/clis/twitter/search.test.js +0 -349
  342. package/clis/twitter/shared.test.js +0 -794
  343. package/clis/twitter/thread.test.js +0 -58
  344. package/clis/twitter/timeline.test.js +0 -104
  345. package/clis/twitter/trending.test.js +0 -15
  346. package/clis/twitter/tweets.test.js +0 -362
  347. package/clis/twitter/unbookmark.test.js +0 -73
  348. package/clis/twitter/unlike.test.js +0 -75
  349. package/clis/twitter/unretweet.test.js +0 -73
  350. package/clis/twitter/utils.test.js +0 -169
  351. package/clis/uisdc/news.test.js +0 -66
  352. package/clis/uiverse/_shared.test.js +0 -62
  353. package/clis/uiverse/navigation.test.js +0 -12
  354. package/clis/upwork/upwork.test.js +0 -566
  355. package/clis/wanfang/search.test.js +0 -23
  356. package/clis/web/read.test.js +0 -392
  357. package/clis/weibo/delete.test.js +0 -94
  358. package/clis/weibo/envelope.test.js +0 -85
  359. package/clis/weibo/favorites.test.js +0 -114
  360. package/clis/weibo/publish.test.js +0 -192
  361. package/clis/weibo/user-posts.test.js +0 -92
  362. package/clis/weibo/utils.test.js +0 -36
  363. package/clis/weixin/drafts.test.js +0 -69
  364. package/clis/weixin/search.test.js +0 -227
  365. package/clis/weread/ai-outline.test.js +0 -83
  366. package/clis/weread/book-search.test.js +0 -242
  367. package/clis/weread/commands.test.js +0 -398
  368. package/clis/weread/private-api-regression.test.js +0 -369
  369. package/clis/weread/search-regression.test.js +0 -494
  370. package/clis/weread/utils.test.js +0 -128
  371. package/clis/weread-official/commands.test.js +0 -385
  372. package/clis/weread-official/utils.test.js +0 -242
  373. package/clis/wikidata/wikidata.test.js +0 -83
  374. package/clis/wikipedia/trending.test.js +0 -57
  375. package/clis/wttr/wttr.test.js +0 -84
  376. package/clis/xianyu/chat.test.js +0 -84
  377. package/clis/xianyu/im.test.js +0 -253
  378. package/clis/xianyu/item.test.js +0 -56
  379. package/clis/xianyu/publish.test.js +0 -220
  380. package/clis/xianyu/search.test.js +0 -17
  381. package/clis/xiaoe/xiaoe.test.js +0 -486
  382. package/clis/xiaohongshu/comments.test.js +0 -258
  383. package/clis/xiaohongshu/creator-note-detail.test.js +0 -451
  384. package/clis/xiaohongshu/creator-notes-summary.test.js +0 -94
  385. package/clis/xiaohongshu/creator-notes.test.js +0 -281
  386. package/clis/xiaohongshu/creator-stats.test.js +0 -24
  387. package/clis/xiaohongshu/delete-note.test.js +0 -172
  388. package/clis/xiaohongshu/download.test.js +0 -316
  389. package/clis/xiaohongshu/navigation.test.js +0 -34
  390. package/clis/xiaohongshu/note.test.js +0 -249
  391. package/clis/xiaohongshu/publish.test.js +0 -608
  392. package/clis/xiaohongshu/search.test.js +0 -325
  393. package/clis/xiaohongshu/user-helpers.test.js +0 -160
  394. package/clis/xiaoyuzhou/auth.test.js +0 -124
  395. package/clis/xiaoyuzhou/download.test.js +0 -135
  396. package/clis/xiaoyuzhou/podcast-episodes.test.js +0 -78
  397. package/clis/xiaoyuzhou/transcript.test.js +0 -195
  398. package/clis/xiaoyuzhou/utils.test.js +0 -39
  399. package/clis/xueqiu/comments.test.js +0 -696
  400. package/clis/xueqiu/danjuan-utils.test.js +0 -41
  401. package/clis/xueqiu/utils.test.js +0 -26
  402. package/clis/yahoo/search.test.js +0 -94
  403. package/clis/youdao/note.test.js +0 -99
  404. package/clis/youtube/channel.test.js +0 -59
  405. package/clis/youtube/feed.test.js +0 -131
  406. package/clis/youtube/transcript-group.test.js +0 -99
  407. package/clis/youtube/transcript.test.js +0 -296
  408. package/clis/youtube/utils.test.js +0 -84
  409. package/clis/yuanbao/ask.test.js +0 -124
  410. package/clis/yuanbao/new.test.js +0 -30
  411. package/clis/yuanbao/shared.test.js +0 -80
  412. package/clis/zhihu/answer-comments.test.js +0 -287
  413. package/clis/zhihu/answer-detail.test.js +0 -338
  414. package/clis/zhihu/answer.test.js +0 -54
  415. package/clis/zhihu/collection.test.js +0 -337
  416. package/clis/zhihu/collections.test.js +0 -182
  417. package/clis/zhihu/comment.test.js +0 -50
  418. package/clis/zhihu/download.test.js +0 -12
  419. package/clis/zhihu/favorite.test.js +0 -59
  420. package/clis/zhihu/follow.test.js +0 -42
  421. package/clis/zhihu/like.test.js +0 -41
  422. package/clis/zhihu/question.test.js +0 -251
  423. package/clis/zhihu/recommend.test.js +0 -143
  424. package/clis/zhihu/search.test.js +0 -198
  425. package/clis/zhihu/target.test.js +0 -77
  426. package/clis/zhihu/text.test.js +0 -24
  427. package/clis/zhihu/write-shared.test.js +0 -176
  428. package/clis/zlibrary/commands.test.js +0 -65
  429. package/clis/zsxq/search.test.js +0 -24
  430. package/clis/zsxq/topic.test.js +0 -30
  431. package/clis/zsxq/topics.test.js +0 -24
  432. package/clis/zsxq/utils.test.js +0 -31
  433. package/dist/src/adapter-shadow.test.js +0 -49
  434. package/dist/src/adapter-source.test.js +0 -29
  435. package/dist/src/browser/analyze.test.js +0 -166
  436. package/dist/src/browser/article-extract.e2e.test.js +0 -105
  437. package/dist/src/browser/article-extract.test.js +0 -94
  438. package/dist/src/browser/ax-snapshot.test.js +0 -91
  439. package/dist/src/browser/base-page.test.js +0 -758
  440. package/dist/src/browser/cdp-click-fixture.test.js +0 -87
  441. package/dist/src/browser/cdp.test.js +0 -79
  442. package/dist/src/browser/compound.test.js +0 -175
  443. package/dist/src/browser/daemon-client.test.js +0 -199
  444. package/dist/src/browser/dom-helpers.test.js +0 -106
  445. package/dist/src/browser/dom-snapshot.test.js +0 -313
  446. package/dist/src/browser/errors.test.js +0 -60
  447. package/dist/src/browser/extract.test.js +0 -129
  448. package/dist/src/browser/find.test.js +0 -180
  449. package/dist/src/browser/html-tree.test.js +0 -181
  450. package/dist/src/browser/network-cache.test.js +0 -58
  451. package/dist/src/browser/network-key.test.js +0 -49
  452. package/dist/src/browser/page.test.js +0 -378
  453. package/dist/src/browser/shape-filter.test.js +0 -101
  454. package/dist/src/browser/shape.test.js +0 -82
  455. package/dist/src/browser/stealth.test.js +0 -134
  456. package/dist/src/browser/target-errors.test.js +0 -94
  457. package/dist/src/browser/target-resolver.test.js +0 -118
  458. package/dist/src/browser/utils.test.js +0 -29
  459. package/dist/src/browser/verify-fixture.test.js +0 -219
  460. package/dist/src/browser.test.js +0 -232
  461. package/dist/src/build-manifest.test.js +0 -341
  462. package/dist/src/capabilityRouting.test.js +0 -63
  463. package/dist/src/cli-argv-preprocess.test.js +0 -209
  464. package/dist/src/cli.test.js +0 -2803
  465. package/dist/src/commanderAdapter.test.js +0 -352
  466. package/dist/src/commands/daemon.test.js +0 -219
  467. package/dist/src/completion.test.js +0 -24
  468. package/dist/src/convention-audit.test.js +0 -247
  469. package/dist/src/daemon.test.js +0 -60
  470. package/dist/src/doctor.test.js +0 -263
  471. package/dist/src/download/article-download.test.js +0 -235
  472. package/dist/src/download/index.test.js +0 -140
  473. package/dist/src/download/media-download.test.js +0 -112
  474. package/dist/src/download/progress.test.js +0 -36
  475. package/dist/src/electron-apps.test.js +0 -49
  476. package/dist/src/engine.test.js +0 -312
  477. package/dist/src/errors.test.js +0 -109
  478. package/dist/src/execution.test.js +0 -623
  479. package/dist/src/extension-manifest-regression.test.js +0 -12
  480. package/dist/src/external.test.js +0 -146
  481. package/dist/src/help.test.js +0 -58
  482. package/dist/src/hooks.test.js +0 -92
  483. package/dist/src/interceptor.test.js +0 -81
  484. package/dist/src/launcher.test.js +0 -138
  485. package/dist/src/node-network.test.js +0 -55
  486. package/dist/src/observation/artifact.test.js +0 -121
  487. package/dist/src/observation/manager.test.js +0 -13
  488. package/dist/src/observation/redaction.test.js +0 -32
  489. package/dist/src/observation/retention.test.js +0 -118
  490. package/dist/src/observation/ring-buffer.test.js +0 -22
  491. package/dist/src/output.test.js +0 -53
  492. package/dist/src/package-exports.test.js +0 -112
  493. package/dist/src/pipeline/executor.test.js +0 -182
  494. package/dist/src/pipeline/steps/download.test.js +0 -102
  495. package/dist/src/pipeline/steps/fetch.test.js +0 -117
  496. package/dist/src/pipeline/template.test.js +0 -175
  497. package/dist/src/pipeline/transform.test.js +0 -133
  498. package/dist/src/plugin-manifest.test.js +0 -179
  499. package/dist/src/plugin-scaffold.test.js +0 -83
  500. package/dist/src/plugin.test.js +0 -1324
  501. package/dist/src/registry.test.js +0 -209
  502. package/dist/src/runtime-detect.test.js +0 -39
  503. package/dist/src/serialization.test.js +0 -78
  504. package/dist/src/snapshotFormatter.test.js +0 -521
  505. package/dist/src/tui.test.js +0 -19
  506. package/dist/src/update-check.test.js +0 -116
  507. package/dist/src/utils.test.js +0 -155
  508. package/dist/src/validate.test.js +0 -90
  509. package/dist/src/weixin-download.test.js +0 -45
@@ -1,134 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { generateStealthJs } from './stealth.js';
3
- /**
4
- * Tests for the stealth anti-detection module.
5
- *
6
- * We test the generated JS string for expected content and structure.
7
- * Evaluating in Node is fragile because stealth patches target browser
8
- * globals (navigator, Performance, HTMLIFrameElement) that don't exist
9
- * or behave differently in Node. Instead we verify the code string
10
- * contains the right patches and is syntactically valid.
11
- */
12
- describe('generateStealthJs', () => {
13
- it('returns a non-empty string', () => {
14
- const code = generateStealthJs();
15
- expect(typeof code).toBe('string');
16
- expect(code.length).toBeGreaterThan(0);
17
- });
18
- it('is a valid self-contained IIFE', () => {
19
- const code = generateStealthJs();
20
- // Should start/end as an IIFE
21
- expect(code.trim()).toMatch(/^\(\(\) => \{/);
22
- expect(code.trim()).toMatch(/\}\)\(\)$/);
23
- });
24
- it('patches navigator.webdriver', () => {
25
- const code = generateStealthJs();
26
- expect(code).toContain("navigator, 'webdriver'");
27
- expect(code).toContain('() => false');
28
- });
29
- it('stubs window.chrome', () => {
30
- const code = generateStealthJs();
31
- expect(code).toContain('window.chrome');
32
- expect(code).toContain('runtime');
33
- expect(code).toContain('loadTimes');
34
- expect(code).toContain('csi');
35
- });
36
- it('fakes navigator.plugins if empty', () => {
37
- const code = generateStealthJs();
38
- expect(code).toContain('navigator.plugins');
39
- expect(code).toContain('PDF Viewer');
40
- expect(code).toContain('Chrome PDF Viewer');
41
- });
42
- it('ensures navigator.languages is non-empty', () => {
43
- const code = generateStealthJs();
44
- expect(code).toContain('navigator.languages');
45
- expect(code).toContain("'en-US'");
46
- });
47
- it('normalizes Permissions.query for notifications', () => {
48
- const code = generateStealthJs();
49
- expect(code).toContain('Permissions');
50
- expect(code).toContain('notifications');
51
- });
52
- it('cleans automation artifacts', () => {
53
- const code = generateStealthJs();
54
- expect(code).toContain('__playwright');
55
- expect(code).toContain('__puppeteer');
56
- expect(code).toContain("'cdc_'");
57
- expect(code).toContain("'__cdc_'");
58
- });
59
- it('filters CDP patterns from Error.stack', () => {
60
- const code = generateStealthJs();
61
- expect(code).toContain('puppeteer_evaluation_script');
62
- expect(code).toContain("'pptr:'");
63
- expect(code).toContain("'debugger://'");
64
- });
65
- it('neutralizes debugger statement traps', () => {
66
- const code = generateStealthJs();
67
- // Should patch Function constructor with new.target / Reflect.construct
68
- expect(code).toContain('_OrigFunction');
69
- expect(code).toContain('_PatchedFunction');
70
- expect(code).toContain('new.target');
71
- expect(code).toContain('Reflect.construct');
72
- // Should patch eval
73
- expect(code).toContain('_origEval');
74
- expect(code).toContain('_patchedEval');
75
- // Regex to strip debugger (lookbehind for statement boundaries)
76
- expect(code).toContain('_debuggerRe');
77
- });
78
- it('uses shared toString disguise via WeakMap', () => {
79
- const code = generateStealthJs();
80
- // Shared infrastructure at the top of the IIFE
81
- expect(code).toContain('_origToString');
82
- expect(code).toContain('WeakMap');
83
- expect(code).toContain('_disguised');
84
- expect(code).toContain('_disguise');
85
- // Should NOT have per-instance toString overrides on Function/eval
86
- // (they go through _disguise instead)
87
- });
88
- it('defends console method fingerprinting', () => {
89
- const code = generateStealthJs();
90
- expect(code).toContain('_consoleMethods');
91
- expect(code).toContain("'log'");
92
- expect(code).toContain("'warn'");
93
- expect(code).toContain("'error'");
94
- expect(code).toContain('[native code]');
95
- // Uses saved _origToString reference
96
- expect(code).toContain('_origToString.call');
97
- });
98
- it('defends window dimension detection', () => {
99
- const code = generateStealthJs();
100
- expect(code).toContain('outerWidth');
101
- expect(code).toContain('outerHeight');
102
- expect(code).toContain('innerWidth');
103
- expect(code).toContain('innerHeight');
104
- });
105
- it('filters Performance API entries', () => {
106
- const code = generateStealthJs();
107
- expect(code).toContain('getEntries');
108
- expect(code).toContain('getEntriesByType');
109
- expect(code).toContain('getEntriesByName');
110
- expect(code).toContain('_suspiciousPatterns');
111
- });
112
- it('cleans document $cdc_ properties', () => {
113
- const code = generateStealthJs();
114
- expect(code).toContain("'$cdc_'");
115
- expect(code).toContain("'$chrome_'");
116
- });
117
- it('patches iframe contentWindow.chrome consistency', () => {
118
- const code = generateStealthJs();
119
- expect(code).toContain('contentWindow');
120
- expect(code).toContain('HTMLIFrameElement');
121
- });
122
- it('uses non-enumerable guard flag on EventTarget.prototype', () => {
123
- const code = generateStealthJs();
124
- expect(code).toContain('EventTarget.prototype');
125
- expect(code).toContain("'__lsn'");
126
- expect(code).toContain('enumerable: false');
127
- });
128
- it('generates syntactically valid JavaScript', () => {
129
- const code = generateStealthJs();
130
- // new Function() parses the code without executing it in a real
131
- // browser context, catching syntax errors from template literal issues.
132
- expect(() => new Function(code)).not.toThrow();
133
- });
134
- });
@@ -1,94 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { TargetError } from './target-errors.js';
3
- describe('TargetError', () => {
4
- it('creates not_found error with code and hint', () => {
5
- const err = new TargetError({
6
- code: 'not_found',
7
- message: 'ref=99 not found in DOM',
8
- hint: 'Re-run `bycli browser state` to get a fresh snapshot.',
9
- });
10
- expect(err).toBeInstanceOf(Error);
11
- expect(err.name).toBe('TargetError');
12
- expect(err.code).toBe('not_found');
13
- expect(err.message).toBe('ref=99 not found in DOM');
14
- expect(err.hint).toContain('fresh snapshot');
15
- expect(err.candidates).toBeUndefined();
16
- });
17
- it('creates selector_ambiguous error with candidates + matches_n', () => {
18
- const err = new TargetError({
19
- code: 'selector_ambiguous',
20
- message: 'CSS selector ".btn" matched 3 elements',
21
- hint: 'Use a more specific selector, or pass --nth.',
22
- candidates: ['<button> "Login"', '<button> "Sign Up"', '<button> "Cancel"'],
23
- matches_n: 3,
24
- });
25
- expect(err.code).toBe('selector_ambiguous');
26
- expect(err.candidates).toHaveLength(3);
27
- expect(err.candidates[0]).toContain('Login');
28
- expect(err.matches_n).toBe(3);
29
- });
30
- it('creates invalid_selector error', () => {
31
- const err = new TargetError({
32
- code: 'invalid_selector',
33
- message: 'Invalid CSS selector: >>> (unexpected token)',
34
- hint: 'Check the selector syntax.',
35
- });
36
- expect(err.code).toBe('invalid_selector');
37
- expect(err.message).toContain('Invalid CSS selector');
38
- });
39
- it('creates selector_not_found error with matches_n=0', () => {
40
- const err = new TargetError({
41
- code: 'selector_not_found',
42
- message: 'CSS selector ".missing" matched 0 elements',
43
- hint: 'Check the page or use browser find.',
44
- matches_n: 0,
45
- });
46
- expect(err.code).toBe('selector_not_found');
47
- expect(err.matches_n).toBe(0);
48
- });
49
- it('creates selector_nth_out_of_range error', () => {
50
- const err = new TargetError({
51
- code: 'selector_nth_out_of_range',
52
- message: 'matched 3 elements, but --nth=5 is out of range',
53
- hint: 'Use --nth between 0 and 2.',
54
- matches_n: 3,
55
- });
56
- expect(err.code).toBe('selector_nth_out_of_range');
57
- expect(err.matches_n).toBe(3);
58
- });
59
- it('creates stale_ref error', () => {
60
- const err = new TargetError({
61
- code: 'stale_ref',
62
- message: 'ref=12 was <button>"Login" but now points to <div>"Header"',
63
- hint: 'Re-run `bycli browser state` to refresh.',
64
- });
65
- expect(err.code).toBe('stale_ref');
66
- expect(err.message).toContain('was <button>');
67
- });
68
- it('serializes to JSON for structured output', () => {
69
- const err = new TargetError({
70
- code: 'selector_ambiguous',
71
- message: 'matched 3',
72
- hint: 'be specific',
73
- candidates: ['a', 'b'],
74
- matches_n: 3,
75
- });
76
- const json = err.toJSON();
77
- expect(json).toEqual({
78
- code: 'selector_ambiguous',
79
- message: 'matched 3',
80
- hint: 'be specific',
81
- candidates: ['a', 'b'],
82
- matches_n: 3,
83
- });
84
- });
85
- it('omits candidates from JSON when not present', () => {
86
- const err = new TargetError({
87
- code: 'not_found',
88
- message: 'gone',
89
- hint: 'refresh',
90
- });
91
- const json = err.toJSON();
92
- expect(json).not.toHaveProperty('candidates');
93
- });
94
- });
@@ -1,118 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { resolveTargetJs } from './target-resolver.js';
3
- /**
4
- * Tests for the target resolver JS generator.
5
- *
6
- * Since resolveTargetJs() produces JS strings for browser evaluate(),
7
- * we test the generated JS by running it in a simulated DOM-like context
8
- * and verifying the structure of the output.
9
- */
10
- describe('resolveTargetJs', () => {
11
- it('generates JS that returns structured resolution for numeric ref', () => {
12
- const js = resolveTargetJs('12');
13
- expect(js).toContain('data-bycli-ref');
14
- expect(js).toContain('__bycli_ref_identity');
15
- expect(js).toContain('"12"');
16
- });
17
- it('generates JS that handles CSS selector input', () => {
18
- const js = resolveTargetJs('#submit-btn');
19
- expect(js).toContain('querySelectorAll');
20
- expect(js).toContain('"#submit-btn"');
21
- });
22
- it('generates JS with stale_ref detection for numeric refs', () => {
23
- const js = resolveTargetJs('5');
24
- expect(js).toContain('stale_ref');
25
- expect(js).toContain('__bycli_ref_identity');
26
- });
27
- it('generates JS with ambiguity detection for CSS selectors', () => {
28
- const js = resolveTargetJs('.btn');
29
- expect(js).toContain('selector_ambiguous');
30
- expect(js).toContain('candidates');
31
- });
32
- it('generates JS that propagates --nth option into the CSS branch', () => {
33
- const js = resolveTargetJs('.btn', { nth: 2 });
34
- expect(js).toContain('selector_nth_out_of_range');
35
- // opt.nth=2 should be inlined so the runtime picks matches[2]
36
- expect(js).toMatch(/const nth = 2;?/);
37
- });
38
- it('generates JS that enables firstOnMulti for read commands', () => {
39
- const js = resolveTargetJs('.btn', { firstOnMulti: true });
40
- expect(js).toContain('firstOnMulti = true');
41
- });
42
- it('generates JS with invalid_selector branch for CSS syntax errors', () => {
43
- const js = resolveTargetJs('.btn');
44
- expect(js).toContain('invalid_selector');
45
- });
46
- it('generates JS with selector_not_found branch for 0 matches', () => {
47
- const js = resolveTargetJs('#does-not-exist');
48
- expect(js).toContain('selector_not_found');
49
- });
50
- it('hands every non-numeric input to querySelectorAll (no regex shortlist)', () => {
51
- // Inputs that the old isCssLike regex rejected — must all flow into the
52
- // CSS branch so `find --css` and `get/click/type/select` accept the same surface.
53
- for (const sel of [':root', '*', ':has(.foo)', '::shadow-root', '???']) {
54
- const js = resolveTargetJs(sel);
55
- expect(js).toContain('querySelectorAll');
56
- // invalid selectors still route through invalid_selector at runtime,
57
- // never through a frontend "Cannot parse target" rejection.
58
- expect(js).not.toContain('Cannot parse target');
59
- }
60
- });
61
- it('escapes ref value safely', () => {
62
- const js = resolveTargetJs('"; alert(1); "');
63
- // JSON.stringify should handle escaping
64
- expect(js).not.toContain('alert(1); "');
65
- expect(js).toContain('\\"');
66
- });
67
- it('tags every success envelope with match_level so agents can tell tiers apart', () => {
68
- const numericJs = resolveTargetJs('7');
69
- const cssJs = resolveTargetJs('.btn');
70
- // Exact / reidentified emit the literal directly; stable flows through the
71
- // classifier's `level` variable. All three strings must appear in the JS.
72
- expect(numericJs).toContain("match_level: 'exact'");
73
- expect(numericJs).toContain("match_level: 'reidentified'");
74
- expect(numericJs).toContain("return 'stable'");
75
- // Stable + exact share the same emit site (match_level: level) — make sure
76
- // we didn't hardcode one of them and drop the other.
77
- expect(numericJs).toContain('match_level: level');
78
- // CSS path is always exact (selector ran successfully).
79
- expect(cssJs).toContain("match_level: 'exact'");
80
- });
81
- it('cascading ref path — classifier + reidentifier are both wired in', () => {
82
- const js = resolveTargetJs('3');
83
- // Classifier distinguishes the three tiers
84
- expect(js).toContain('function classifyMatch');
85
- expect(js).toContain("return 'exact'");
86
- expect(js).toContain("return 'stable'");
87
- expect(js).toContain("return 'mismatch'");
88
- // Strong id is the only thing that can rescue a drifted fingerprint
89
- expect(js).toContain('hadStrongId');
90
- // Reidentify searches live DOM with the same fingerprint shape the
91
- // snapshot / find writers emit — id / testId / aria-label only.
92
- expect(js).toContain('function reidentify');
93
- expect(js).toContain('getElementById');
94
- expect(js).toContain('[data-testid="');
95
- expect(js).toContain('[aria-label="');
96
- // Unique match required — never silently picks one of many candidates.
97
- expect(js).toContain('candidates.length === 1');
98
- // Recovered element is re-tagged + identity map refreshed so subsequent
99
- // resolves land on 'exact' instead of re-walking the cascade.
100
- expect(js).toContain("setAttribute('data-bycli-ref', ref)");
101
- expect(js).toContain('identity[ref] = fingerprintOf(recovered)');
102
- });
103
- it('reidentify runs both when data-bycli-ref is missing AND when fingerprint is mismatched', () => {
104
- const js = resolveTargetJs('9');
105
- // Two call sites: one in the !el branch, one after classifyMatch returns mismatch.
106
- const count = js.split('reidentify(fp)').length - 1;
107
- expect(count).toBeGreaterThanOrEqual(2);
108
- });
109
- it('falls through to stale_ref only after reidentify exhausts', () => {
110
- const js = resolveTargetJs('4');
111
- // The stale_ref emit must sit *below* a reidentify attempt so the cascade
112
- // is what produces the error — not the original strict check.
113
- const reidentifyIdx = js.indexOf('const recovered = reidentify(fp);');
114
- const staleIdx = js.indexOf("code: 'stale_ref'");
115
- expect(reidentifyIdx).toBeGreaterThan(-1);
116
- expect(staleIdx).toBeGreaterThan(reidentifyIdx);
117
- });
118
- });
@@ -1,29 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { buildEvaluateExpression, wrapForEval } from './utils.js';
3
- describe('browser eval utils', () => {
4
- it('keeps existing string eval wrapping behavior', () => {
5
- expect(wrapForEval('21 + 21')).toBe('21 + 21');
6
- expect(wrapForEval('() => 42')).toBe('(() => 42)()');
7
- });
8
- it('serializes function eval arguments as JSON', () => {
9
- const code = buildEvaluateExpression((selector) => {
10
- return document.querySelector(selector)?.textContent ?? null;
11
- }, ['.title']);
12
- expect(code).toContain('document.querySelector(selector)');
13
- expect(code).toContain('(...[".title"])');
14
- });
15
- it('accepts compact async arrow functions', () => {
16
- const fn = new Function('return async()=>42')();
17
- expect(buildEvaluateExpression(fn)).toBe('(async()=>42)(...[])');
18
- });
19
- it('rejects string eval with stray args', () => {
20
- expect(() => buildEvaluateExpression('document.title', ['ignored']))
21
- .toThrow('use page.evaluate(fn, ...args)');
22
- });
23
- it('rejects non-JSON-serializable function args', () => {
24
- const circular = {};
25
- circular.self = circular;
26
- expect(() => buildEvaluateExpression((value) => value, [circular]))
27
- .toThrow('JSON-serializable');
28
- });
29
- });
@@ -1,219 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { deriveFixture, expandFixtureArgs, parseSeedArgs, validateRows, validateRowShape } from './verify-fixture.js';
3
- describe('validateRows', () => {
4
- it('passes when rows meet all expectations', () => {
5
- const fixture = {
6
- expect: {
7
- rowCount: { min: 1, max: 3 },
8
- columns: ['id', 'title', 'url'],
9
- types: { id: 'number', title: 'string', url: 'string' },
10
- patterns: { url: '^https://' },
11
- notEmpty: ['title', 'url'],
12
- },
13
- };
14
- const rows = [
15
- { id: 1, title: 'a', url: 'https://x.com/a' },
16
- { id: 2, title: 'b', url: 'https://x.com/b' },
17
- ];
18
- expect(validateRows(rows, fixture)).toEqual([]);
19
- });
20
- it('reports rowCount below min', () => {
21
- const failures = validateRows([], { expect: { rowCount: { min: 1 } } });
22
- expect(failures).toHaveLength(1);
23
- expect(failures[0]).toMatchObject({ rule: 'rowCount' });
24
- expect(failures[0].detail).toContain('at least 1');
25
- });
26
- it('reports rowCount above max', () => {
27
- const failures = validateRows([{}, {}, {}, {}], { expect: { rowCount: { max: 3 } } });
28
- expect(failures).toHaveLength(1);
29
- expect(failures[0].detail).toContain('at most 3');
30
- });
31
- it('reports missing columns per row', () => {
32
- const failures = validateRows([{ a: 1 }, { a: 2, b: 3 }], { expect: { columns: ['a', 'b'] } });
33
- // row 0 missing 'b', row 1 complete
34
- expect(failures).toEqual([
35
- { rule: 'column', detail: 'missing column "b"', rowIndex: 0 },
36
- ]);
37
- });
38
- it('reports type mismatch including null', () => {
39
- const failures = validateRows([{ a: 'abc' }, { a: null }, { a: 42 }], { expect: { types: { a: 'string' } } });
40
- // row 0 string ok, row 1 null fail, row 2 number fail
41
- expect(failures).toHaveLength(2);
42
- expect(failures[0].rowIndex).toBe(1);
43
- expect(failures[0].detail).toContain('null');
44
- expect(failures[1].rowIndex).toBe(2);
45
- expect(failures[1].detail).toContain('number');
46
- });
47
- it('accepts union types like "number|string"', () => {
48
- const failures = validateRows([{ id: 1 }, { id: 'abc' }], { expect: { types: { id: 'number|string' } } });
49
- expect(failures).toEqual([]);
50
- });
51
- it('accepts "any" as wildcard type', () => {
52
- const failures = validateRows([{ v: 1 }, { v: 'x' }, { v: null }, { v: [1, 2] }], { expect: { types: { v: 'any' } } });
53
- expect(failures).toEqual([]);
54
- });
55
- it('reports pattern mismatch with row index and truncated value', () => {
56
- const failures = validateRows([{ url: 'https://ok.com' }, { url: 'not-a-url' }], { expect: { patterns: { url: '^https?://' } } });
57
- expect(failures).toHaveLength(1);
58
- expect(failures[0]).toMatchObject({ rule: 'pattern', rowIndex: 1 });
59
- expect(failures[0].detail).toContain('not-a-url');
60
- });
61
- it('skips pattern check for null/undefined values', () => {
62
- const failures = validateRows([{ url: null }, { url: undefined }], { expect: { patterns: { url: '^x' } } });
63
- expect(failures).toEqual([]);
64
- });
65
- it('reports invalid regex without crashing', () => {
66
- const failures = validateRows([{ a: 'x' }], { expect: { patterns: { a: '[unclosed' } } });
67
- expect(failures.some((f) => f.rule === 'pattern' && f.detail.includes('invalid'))).toBe(true);
68
- });
69
- it('treats empty/whitespace/null as failing notEmpty', () => {
70
- const failures = validateRows([{ t: '' }, { t: ' ' }, { t: null }, { t: 'ok' }], { expect: { notEmpty: ['t'] } });
71
- expect(failures).toHaveLength(3);
72
- expect(failures.map((f) => f.rowIndex)).toEqual([0, 1, 2]);
73
- });
74
- it('no failures when fixture has no expect block', () => {
75
- expect(validateRows([{ anything: 1 }], {})).toEqual([]);
76
- });
77
- it('mustNotContain flags substring bleed in columns', () => {
78
- const failures = validateRows([
79
- { description: 'Lead engineer, 5 years exp. address: Shanghai. category: IT' },
80
- { description: 'Clean text.' },
81
- ], {
82
- expect: {
83
- mustNotContain: { description: ['address:', 'category:'] },
84
- },
85
- });
86
- expect(failures).toHaveLength(2);
87
- expect(failures.every((f) => f.rule === 'mustNotContain')).toBe(true);
88
- expect(failures.every((f) => f.rowIndex === 0)).toBe(true);
89
- });
90
- it('mustNotContain skips null/undefined values', () => {
91
- const failures = validateRows([{ description: null }, { description: undefined }], { expect: { mustNotContain: { description: ['x'] } } });
92
- expect(failures).toEqual([]);
93
- });
94
- it('mustBeTruthy catches silent 0 / false / "" fallbacks', () => {
95
- const failures = validateRows([{ count: 10 }, { count: 0 }, { count: false }, { count: '' }, { count: null }], { expect: { mustBeTruthy: ['count'] } });
96
- expect(failures).toHaveLength(4);
97
- expect(failures.every((f) => f.rule === 'mustBeTruthy')).toBe(true);
98
- expect(failures.map((f) => f.rowIndex)).toEqual([1, 2, 3, 4]);
99
- });
100
- });
101
- describe('validateRowShape', () => {
102
- it('passes flat rows with a compact key set', () => {
103
- const failures = validateRowShape([
104
- { id: '1', title: 'A', author: { name: 'Ada' }, tags: ['ai', 'web'] },
105
- ]);
106
- expect(failures).toEqual([]);
107
- });
108
- it('reports rows with too many top-level keys', () => {
109
- const row = Object.fromEntries(Array.from({ length: 13 }, (_, i) => [`k${i}`, i]));
110
- const failures = validateRowShape([row]);
111
- expect(failures).toEqual([
112
- {
113
- rule: 'shapeKeyCount',
114
- detail: 'row has 13 top-level keys, expected at most 12',
115
- rowIndex: 0,
116
- },
117
- ]);
118
- });
119
- it('reports nesting deeper than one level', () => {
120
- const failures = validateRowShape([
121
- { title: 'A', stats: { author: { name: 'Ada' } } },
122
- ]);
123
- expect(failures).toEqual([
124
- {
125
- rule: 'shapeDepth',
126
- detail: '"stats" nesting depth is 2, expected at most 1',
127
- rowIndex: 0,
128
- },
129
- ]);
130
- });
131
- it('reports nested id-shaped fields even when one-level nesting is otherwise allowed', () => {
132
- const failures = validateRowShape([
133
- { title: 'A', author: { user_id: 'u1', name: 'Ada' } },
134
- ]);
135
- expect(failures).toEqual([
136
- {
137
- rule: 'shapeNestedId',
138
- detail: 'id-shaped field "author.user_id" must be a top-level row key',
139
- rowIndex: 0,
140
- },
141
- ]);
142
- });
143
- });
144
- describe('deriveFixture', () => {
145
- it('returns rowCount.min=0 when rows are empty', () => {
146
- expect(deriveFixture([])).toEqual({ expect: { rowCount: { min: 0 } } });
147
- });
148
- it('extracts columns from first row and infers types per column', () => {
149
- const fixture = deriveFixture([
150
- { id: 1, title: 'a', url: 'https://x' },
151
- { id: 2, title: 'b', url: 'https://y' },
152
- ]);
153
- expect(fixture.expect?.columns).toEqual(['id', 'title', 'url']);
154
- expect(fixture.expect?.types).toEqual({
155
- id: 'number',
156
- title: 'string',
157
- url: 'string',
158
- });
159
- expect(fixture.expect?.rowCount).toEqual({ min: 1 });
160
- });
161
- it('unions mixed types across rows as "a|b"', () => {
162
- const fixture = deriveFixture([
163
- { v: 1 },
164
- { v: 'two' },
165
- { v: null },
166
- ]);
167
- expect(fixture.expect?.types?.v).toBe('null|number|string');
168
- });
169
- it('embeds args when provided', () => {
170
- const fixture = deriveFixture([{ x: 1 }], { limit: 5 });
171
- expect(fixture.args).toEqual({ limit: 5 });
172
- });
173
- it('embeds positional argv array when provided', () => {
174
- const fixture = deriveFixture([{ x: 1 }], ['123', '--limit', '3']);
175
- expect(fixture.args).toEqual(['123', '--limit', '3']);
176
- });
177
- it('does not add patterns or notEmpty automatically', () => {
178
- const fixture = deriveFixture([{ a: 'x' }]);
179
- expect(fixture.expect?.patterns).toBeUndefined();
180
- expect(fixture.expect?.notEmpty).toBeUndefined();
181
- });
182
- });
183
- describe('expandFixtureArgs', () => {
184
- it('returns [] for undefined', () => {
185
- expect(expandFixtureArgs(undefined)).toEqual([]);
186
- });
187
- it('expands object form as --key value pairs', () => {
188
- expect(expandFixtureArgs({ limit: 3, sort: 'hot' })).toEqual(['--limit', '3', '--sort', 'hot']);
189
- });
190
- it('passes array form verbatim, stringifying values', () => {
191
- expect(expandFixtureArgs(['123456', '--limit', 3])).toEqual(['123456', '--limit', '3']);
192
- });
193
- it('handles empty object and empty array', () => {
194
- expect(expandFixtureArgs({})).toEqual([]);
195
- expect(expandFixtureArgs([])).toEqual([]);
196
- });
197
- it('preserves positional + flag mix (e.g. <tid> --limit 3)', () => {
198
- expect(expandFixtureArgs(['https://example.com/thread-1', '--comments', '5'])).toEqual([
199
- 'https://example.com/thread-1',
200
- '--comments',
201
- '5',
202
- ]);
203
- });
204
- });
205
- describe('parseSeedArgs', () => {
206
- it('treats plain text as one positional arg', () => {
207
- expect(parseSeedArgs('bycli-verify')).toEqual(['bycli-verify']);
208
- });
209
- it('accepts JSON array seed args', () => {
210
- expect(parseSeedArgs('["subject", "--limit", 3]')).toEqual(['subject', '--limit', 3]);
211
- });
212
- it('accepts JSON object seed args', () => {
213
- expect(parseSeedArgs('{"limit":3,"sort":"hot"}')).toEqual({ limit: 3, sort: 'hot' });
214
- });
215
- it('ignores empty input', () => {
216
- expect(parseSeedArgs(undefined)).toBeUndefined();
217
- expect(parseSeedArgs(' ')).toBeUndefined();
218
- });
219
- });