@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.
- package/cli-manifest.json +0 -61
- package/dist/src/adapter-shadow.js +2 -2
- package/dist/src/browser/analyze.d.ts +10 -99
- package/dist/src/browser/analyze.js +10 -226
- package/dist/src/browser/cdp.js +64 -7
- package/dist/src/browser/navigation-guard.d.ts +78 -0
- package/dist/src/browser/navigation-guard.js +142 -0
- package/dist/src/browser/navigation-guard.test.d.ts +1 -0
- package/dist/src/browser/url-policy.d.ts +89 -0
- package/dist/src/browser/url-policy.js +311 -0
- package/dist/src/browser/url-policy.test.d.ts +1 -0
- package/dist/src/browser/verify-fixture.js +2 -2
- package/dist/src/cli.js +10 -13
- package/dist/src/config-paths.d.ts +19 -0
- package/dist/src/config-paths.js +31 -0
- package/dist/src/daemon.js +240 -0
- package/dist/src/discovery.d.ts +5 -3
- package/dist/src/discovery.js +8 -6
- package/dist/src/execution.js +8 -3
- package/dist/src/main.js +18 -2
- package/dist/src/plugin.js +11 -8
- package/dist/src/recorder/highlevel/analyze.d.ts +29 -0
- package/dist/src/recorder/highlevel/analyze.js +91 -0
- package/dist/src/recorder/highlevel/init.d.ts +125 -0
- package/dist/src/recorder/highlevel/init.js +279 -0
- package/dist/src/recorder/highlevel/init.test.d.ts +1 -0
- package/dist/src/recorder/highlevel/verify.d.ts +73 -0
- package/dist/src/recorder/highlevel/verify.js +74 -0
- package/dist/src/recorder/http/analyze-runner.d.ts +28 -0
- package/dist/src/recorder/http/analyze-runner.js +78 -0
- package/dist/src/recorder/http/analyze-runner.test.d.ts +1 -0
- package/dist/src/recorder/http/handlers.d.ts +51 -0
- package/dist/src/recorder/http/handlers.js +219 -0
- package/dist/src/recorder/http/wrapper-config.d.ts +20 -0
- package/dist/src/recorder/http/wrapper-config.js +82 -0
- package/dist/src/recorder/http/wrapper-envelope.d.ts +31 -0
- package/dist/src/recorder/http/wrapper-envelope.js +51 -0
- package/dist/src/recorder/http/wrapper-gates.d.ts +16 -0
- package/dist/src/recorder/http/wrapper-gates.js +28 -0
- package/dist/src/recorder/http/wrapper-metrics.d.ts +1 -0
- package/dist/src/recorder/http/wrapper-metrics.js +5 -0
- package/dist/src/recorder/http/wrapper-registry.d.ts +50 -0
- package/dist/src/recorder/http/wrapper-registry.js +98 -0
- package/dist/src/recorder/http/wrapper-server.d.ts +59 -0
- package/dist/src/recorder/http/wrapper-server.js +240 -0
- package/dist/src/recorder/http/wrapper.test.d.ts +1 -0
- package/dist/src/recorder/observability/logger.d.ts +5 -0
- package/dist/src/recorder/observability/logger.js +13 -0
- package/dist/src/recorder/runner/config.d.ts +34 -0
- package/dist/src/recorder/runner/config.js +84 -0
- package/dist/src/recorder/runner/config.test.d.ts +1 -0
- package/dist/src/recorder/runner/reap.d.ts +79 -0
- package/dist/src/recorder/runner/reap.js +230 -0
- package/dist/src/recorder/runner/reap.test.d.ts +1 -0
- package/dist/src/recorder/runner/runner-port.d.ts +95 -0
- package/dist/src/recorder/runner/runner-port.js +518 -0
- package/dist/src/recorder/runner/runner-port.test.d.ts +1 -0
- package/dist/src/recorder/runner/session-keys.d.ts +28 -0
- package/dist/src/recorder/runner/session-keys.js +72 -0
- package/dist/src/recorder/runner/session-keys.test.d.ts +1 -0
- package/dist/src/recorder/runner/verify-runner-main.d.ts +95 -0
- package/dist/src/recorder/runner/verify-runner-main.js +275 -0
- package/dist/src/recorder/runner/verify-runner-main.test.d.ts +1 -0
- package/package.json +8 -5
- package/scripts/m1-daemon-navigate-drive.mjs +138 -0
- package/scripts/m1-extension-spike.mjs +237 -0
- package/scripts/m1-live-spike.mjs +156 -0
- package/scripts/m6b-connect-back-spike.sh +31 -0
- package/scripts/recorder.sh +186 -0
- package/clis/12306/utils.test.js +0 -331
- package/clis/1688/assets.test.js +0 -39
- package/clis/1688/download.test.js +0 -31
- package/clis/1688/item.test.js +0 -67
- package/clis/1688/search.test.js +0 -75
- package/clis/1688/shared.test.js +0 -57
- package/clis/1688/store.test.js +0 -62
- package/clis/36kr/article.test.js +0 -46
- package/clis/36kr/hot.test.js +0 -15
- package/clis/36kr/news.test.js +0 -85
- package/clis/51job/utils.test.js +0 -69
- package/clis/_atlassian/shared.test.js +0 -170
- package/clis/aibase/news.test.js +0 -59
- package/clis/amazon/bestsellers.test.js +0 -29
- package/clis/amazon/discussion.test.js +0 -124
- package/clis/amazon/offer.test.js +0 -29
- package/clis/amazon/product.test.js +0 -24
- package/clis/amazon/rankings.test.js +0 -41
- package/clis/amazon/search.test.js +0 -22
- package/clis/amazon/shared.test.js +0 -44
- package/clis/apple-podcasts/commands.test.js +0 -119
- package/clis/apple-podcasts/utils.test.js +0 -57
- package/clis/arxiv/arxiv.test.js +0 -112
- package/clis/baidu-scholar/search.test.js +0 -23
- package/clis/barchart/greeks.test.js +0 -138
- package/clis/bilibili/comment.test.js +0 -153
- package/clis/bilibili/comments.test.js +0 -142
- package/clis/bilibili/dynamic.test.js +0 -68
- package/clis/bilibili/hot.test.js +0 -17
- package/clis/bilibili/subtitle.test.js +0 -196
- package/clis/bilibili/summary.test.js +0 -210
- package/clis/bilibili/utils.test.js +0 -61
- package/clis/bilibili/video.test.js +0 -132
- package/clis/binance/commands.test.js +0 -70
- package/clis/bloomberg/utils.test.js +0 -129
- package/clis/booking/booking.test.js +0 -356
- package/clis/boss/chatlist.test.js +0 -211
- package/clis/boss/chatmsg.test.js +0 -230
- package/clis/boss/search.test.js +0 -78
- package/clis/boss/utils.test.js +0 -34
- package/clis/brave/search.test.js +0 -76
- package/clis/chaoxing/utils.test.js +0 -45
- package/clis/chatgpt/commands.test.js +0 -45
- package/clis/chatgpt/envelope.test.js +0 -108
- package/clis/chatgpt/image.test.js +0 -188
- package/clis/chatgpt/utils.test.js +0 -330
- package/clis/chatgpt-app/ax.test.js +0 -35
- package/clis/chatwise/composer.test.js +0 -186
- package/clis/chess/analyze.test.js +0 -79
- package/clis/chess/game.test.js +0 -178
- package/clis/chess/games.test.js +0 -164
- package/clis/chess/stats.test.js +0 -79
- package/clis/chess/utils.test.js +0 -230
- package/clis/claude/ask.test.js +0 -338
- package/clis/claude/commands.test.js +0 -118
- package/clis/claude/utils.test.js +0 -148
- package/clis/cnki/search.test.js +0 -18
- package/clis/codex/sidebar.test.js +0 -329
- package/clis/coingecko/coingecko.test.js +0 -109
- package/clis/confluence/commands.test.js +0 -195
- package/clis/coupang/coupang.test.js +0 -159
- package/clis/coupang/utils.test.js +0 -62
- package/clis/ctrip/ctrip.test.js +0 -719
- package/clis/dblp/dblp.test.js +0 -397
- package/clis/deepseek/ask.test.js +0 -312
- package/clis/deepseek/detail.test.js +0 -81
- package/clis/deepseek/send.test.js +0 -107
- package/clis/deepseek/utils.test.js +0 -370
- package/clis/defillama/defillama.test.js +0 -99
- package/clis/devto/devto.test.js +0 -236
- package/clis/dianping/dianping.test.js +0 -578
- package/clis/douban/download.test.js +0 -170
- package/clis/douban/movie-hot.test.js +0 -14
- package/clis/douban/search.test.js +0 -11
- package/clis/douban/subject.test.js +0 -11
- package/clis/douban/utils.test.js +0 -438
- package/clis/doubao/detail.test.js +0 -42
- package/clis/doubao/history.test.js +0 -37
- package/clis/doubao/utils.test.js +0 -344
- package/clis/douyin/_shared/browser-fetch.test.js +0 -64
- package/clis/douyin/_shared/creation-id.test.js +0 -22
- package/clis/douyin/_shared/imagex-upload.test.js +0 -87
- package/clis/douyin/_shared/sts2.test.js +0 -27
- package/clis/douyin/_shared/text-extra.test.js +0 -37
- package/clis/douyin/_shared/timing.test.js +0 -28
- package/clis/douyin/_shared/tos-upload-short-read.test.js +0 -83
- package/clis/douyin/_shared/tos-upload.test.js +0 -229
- package/clis/douyin/_shared/transcode.test.js +0 -91
- package/clis/douyin/_shared/vod-upload.test.js +0 -38
- package/clis/douyin/activities.test.js +0 -55
- package/clis/douyin/collections.test.js +0 -45
- package/clis/douyin/delete.test.js +0 -100
- package/clis/douyin/draft.test.js +0 -335
- package/clis/douyin/drafts.test.js +0 -11
- package/clis/douyin/hashtag.test.js +0 -171
- package/clis/douyin/location.test.js +0 -23
- package/clis/douyin/profile.test.js +0 -46
- package/clis/douyin/publish-upload-id.test.js +0 -170
- package/clis/douyin/publish.test.js +0 -38
- package/clis/douyin/stats.test.js +0 -22
- package/clis/douyin/update.test.js +0 -11
- package/clis/douyin/user-videos.test.js +0 -151
- package/clis/douyin/videos.test.js +0 -54
- package/clis/duckduckgo/search.test.js +0 -128
- package/clis/duckduckgo/suggest.test.js +0 -66
- package/clis/eastmoney/hot-rank.test.js +0 -59
- package/clis/endoflife/endoflife.test.js +0 -51
- package/clis/facebook/feed.test.js +0 -169
- package/clis/facebook/marketplace.test.js +0 -91
- package/clis/facebook/notifications.test.js +0 -458
- package/clis/facebook/search.test.js +0 -55
- package/clis/flathub/flathub.test.js +0 -90
- package/clis/flomo/memos.test.js +0 -144
- package/clis/gemini/ask.test.js +0 -100
- package/clis/gemini/deep-research-result.test.js +0 -155
- package/clis/gemini/deep-research.test.js +0 -181
- package/clis/gemini/reply-state.test.js +0 -671
- package/clis/gemini/utils.test.js +0 -198
- package/clis/geogebra/geogebra.test.js +0 -175
- package/clis/gitee/search.test.js +0 -65
- package/clis/gitee/user.test.js +0 -63
- package/clis/google/utils.test.js +0 -75
- package/clis/google-scholar/cite.test.js +0 -47
- package/clis/google-scholar/profile.test.js +0 -49
- package/clis/google-scholar/search.test.js +0 -71
- package/clis/goproxy/goproxy.test.js +0 -103
- package/clis/gov-law/commands.test.js +0 -39
- package/clis/gov-policy/gov-policy.test.js +0 -224
- package/clis/grok/ask.test.js +0 -29
- package/clis/grok/utils.test.js +0 -103
- package/clis/hackernews/hackernews.test.js +0 -132
- package/clis/hf/hf.test.js +0 -16
- package/clis/hupu/hot.test.js +0 -224
- package/clis/imdb/utils.test.js +0 -88
- package/clis/indeed/indeed.test.js +0 -375
- package/clis/instagram/_shared/private-publish.test.js +0 -705
- package/clis/instagram/_shared/protocol-capture.test.js +0 -114
- package/clis/instagram/download.test.js +0 -118
- package/clis/instagram/note.test.js +0 -53
- package/clis/instagram/post.test.js +0 -1647
- package/clis/instagram/reel.test.js +0 -167
- package/clis/instagram/story.test.js +0 -134
- package/clis/jd/commands.test.js +0 -41
- package/clis/jd/item.test.js +0 -354
- package/clis/jianyu/search.test.js +0 -153
- package/clis/jianyu/shared/procurement-contract.test.js +0 -72
- package/clis/jianyu/shared/procurement-detail.test.js +0 -84
- package/clis/jike/read.test.js +0 -86
- package/clis/jira/commands.test.js +0 -287
- package/clis/juejin/search.js +0 -123
- package/clis/juejin/search.test.js +0 -98
- package/clis/lesswrong/frontpage.test.js +0 -37
- package/clis/lichess/lichess.test.js +0 -85
- package/clis/linkedin/connect.test.js +0 -213
- package/clis/linkedin/inbox.test.js +0 -152
- package/clis/linkedin/job-detail.test.js +0 -38
- package/clis/linkedin/jobs-preferences.test.js +0 -43
- package/clis/linkedin/people-search.test.js +0 -216
- package/clis/linkedin/post-analytics.test.js +0 -40
- package/clis/linkedin/posts.test.js +0 -40
- package/clis/linkedin/profile-analytics.test.js +0 -67
- package/clis/linkedin/profile-experience.test.js +0 -152
- package/clis/linkedin/profile-projects.test.js +0 -111
- package/clis/linkedin/profile-read.test.js +0 -77
- package/clis/linkedin/safe-send.test.js +0 -204
- package/clis/linkedin/salesnav-inbox.test.js +0 -113
- package/clis/linkedin/salesnav-message.test.js +0 -172
- package/clis/linkedin/salesnav-search.test.js +0 -76
- package/clis/linkedin/salesnav-thread.test.js +0 -79
- package/clis/linkedin/search.test.js +0 -222
- package/clis/linkedin/sent-invitations.test.js +0 -62
- package/clis/linkedin/services-read.test.js +0 -105
- package/clis/linkedin/thread-snapshot.test.js +0 -89
- package/clis/linkedin/timeline.test.js +0 -81
- package/clis/linkedin-learning/course.test.js +0 -114
- package/clis/linkedin-learning/search.test.js +0 -144
- package/clis/linkedin-learning/trending.test.js +0 -123
- package/clis/linux-do/feed.test.js +0 -153
- package/clis/linux-do/topic-content.test.js +0 -59
- package/clis/lobsters/lobsters.test.js +0 -169
- package/clis/notebooklm/add-source.test.js +0 -97
- package/clis/notebooklm/compat.test.js +0 -16
- package/clis/notebooklm/create.test.js +0 -58
- package/clis/notebooklm/generate-audio.test.js +0 -63
- package/clis/notebooklm/generate-slides.test.js +0 -75
- package/clis/notebooklm/history.test.js +0 -58
- package/clis/notebooklm/note-list.test.js +0 -56
- package/clis/notebooklm/notes-get.test.js +0 -72
- package/clis/notebooklm/open.test.js +0 -63
- package/clis/notebooklm/rpc.test.js +0 -131
- package/clis/notebooklm/source-fulltext.test.js +0 -106
- package/clis/notebooklm/source-get.test.js +0 -84
- package/clis/notebooklm/source-guide.test.js +0 -104
- package/clis/notebooklm/summary.test.js +0 -78
- package/clis/notebooklm/utils.test.js +0 -449
- package/clis/notebooklm/write-note.test.js +0 -70
- package/clis/nuget/nuget.test.js +0 -111
- package/clis/oeis/oeis.test.js +0 -88
- package/clis/ones/task-helpers.test.js +0 -12
- package/clis/ones/worklog.test.js +0 -20
- package/clis/openfda/openfda.test.js +0 -114
- package/clis/openreview/openreview.test.js +0 -427
- package/clis/osv/osv.test.js +0 -97
- package/clis/paperreview/commands.test.js +0 -243
- package/clis/paperreview/utils.test.js +0 -49
- package/clis/pixiv/download.test.js +0 -87
- package/clis/pixiv/illusts.test.js +0 -99
- package/clis/pixiv/search.test.js +0 -83
- package/clis/powerchina/search.test.js +0 -93
- package/clis/producthunt/utils.test.js +0 -64
- package/clis/pubmed/pubmed.test.js +0 -276
- package/clis/quark/utils.test.js +0 -58
- package/clis/qwen/utils.test.js +0 -45
- package/clis/reddit/extract-media.test.js +0 -149
- package/clis/reddit/frontpage.test.js +0 -34
- package/clis/reddit/home.test.js +0 -170
- package/clis/reddit/hot.test.js +0 -32
- package/clis/reddit/popular.test.js +0 -26
- package/clis/reddit/read.test.js +0 -335
- package/clis/reddit/reply.test.js +0 -89
- package/clis/reddit/search.test.js +0 -26
- package/clis/reddit/subreddit-info.test.js +0 -163
- package/clis/reddit/subreddit.test.js +0 -31
- package/clis/reddit/subscribed.test.js +0 -168
- package/clis/reddit/whoami.test.js +0 -105
- package/clis/rednote/navigation.test.js +0 -26
- package/clis/rednote/rednote.test.js +0 -222
- package/clis/rest-countries/rest-countries.test.js +0 -83
- package/clis/reuters/reuters.test.js +0 -299
- package/clis/rfc/rfc.test.js +0 -74
- package/clis/sinafinance/stock.test.js +0 -59
- package/clis/spotify/utils.test.js +0 -67
- package/clis/stackoverflow/stackoverflow.test.js +0 -404
- package/clis/steam/steam.test.js +0 -46
- package/clis/substack/utils.test.js +0 -44
- package/clis/suno/commands.test.js +0 -188
- package/clis/suno/download.test.js +0 -151
- package/clis/suno/generate.test.js +0 -252
- package/clis/suno/utils.test.js +0 -329
- package/clis/taobao/commands.test.js +0 -41
- package/clis/tdx/hot-rank.test.js +0 -59
- package/clis/ths/hot-rank.test.js +0 -64
- package/clis/tieba/commands.test.js +0 -79
- package/clis/tieba/utils.test.js +0 -290
- package/clis/tiktok/creator-videos.test.js +0 -113
- package/clis/tiktok/refactor.test.js +0 -389
- package/clis/tiktok/write-refactor.test.js +0 -370
- package/clis/toutiao/toutiao.test.js +0 -378
- package/clis/tvmaze/tvmaze.test.js +0 -93
- package/clis/twitter/bookmark-folder.test.js +0 -395
- package/clis/twitter/bookmark-folders.test.js +0 -152
- package/clis/twitter/bookmark.test.js +0 -74
- package/clis/twitter/bookmarks.test.js +0 -205
- package/clis/twitter/delete.test.js +0 -85
- package/clis/twitter/device-follow.test.js +0 -287
- package/clis/twitter/download.test.js +0 -457
- package/clis/twitter/followers.test.js +0 -62
- package/clis/twitter/following.test.js +0 -338
- package/clis/twitter/hide-reply.test.js +0 -76
- package/clis/twitter/like.test.js +0 -73
- package/clis/twitter/likes.test.js +0 -220
- package/clis/twitter/list-add.test.js +0 -133
- package/clis/twitter/list-create.test.js +0 -169
- package/clis/twitter/list-remove.test.js +0 -110
- package/clis/twitter/list-tweets.test.js +0 -196
- package/clis/twitter/lists.test.js +0 -253
- package/clis/twitter/post.test.js +0 -258
- package/clis/twitter/profile.test.js +0 -39
- package/clis/twitter/quote.test.js +0 -194
- package/clis/twitter/reply.test.js +0 -237
- package/clis/twitter/retweet.test.js +0 -73
- package/clis/twitter/search.test.js +0 -349
- package/clis/twitter/shared.test.js +0 -794
- package/clis/twitter/thread.test.js +0 -58
- package/clis/twitter/timeline.test.js +0 -104
- package/clis/twitter/trending.test.js +0 -15
- package/clis/twitter/tweets.test.js +0 -362
- package/clis/twitter/unbookmark.test.js +0 -73
- package/clis/twitter/unlike.test.js +0 -75
- package/clis/twitter/unretweet.test.js +0 -73
- package/clis/twitter/utils.test.js +0 -169
- package/clis/uisdc/news.test.js +0 -66
- package/clis/uiverse/_shared.test.js +0 -62
- package/clis/uiverse/navigation.test.js +0 -12
- package/clis/upwork/upwork.test.js +0 -566
- package/clis/wanfang/search.test.js +0 -23
- package/clis/web/read.test.js +0 -392
- package/clis/weibo/delete.test.js +0 -94
- package/clis/weibo/envelope.test.js +0 -85
- package/clis/weibo/favorites.test.js +0 -114
- package/clis/weibo/publish.test.js +0 -192
- package/clis/weibo/user-posts.test.js +0 -92
- package/clis/weibo/utils.test.js +0 -36
- package/clis/weixin/drafts.test.js +0 -69
- package/clis/weixin/search.test.js +0 -227
- package/clis/weread/ai-outline.test.js +0 -83
- package/clis/weread/book-search.test.js +0 -242
- package/clis/weread/commands.test.js +0 -398
- package/clis/weread/private-api-regression.test.js +0 -369
- package/clis/weread/search-regression.test.js +0 -494
- package/clis/weread/utils.test.js +0 -128
- package/clis/weread-official/commands.test.js +0 -385
- package/clis/weread-official/utils.test.js +0 -242
- package/clis/wikidata/wikidata.test.js +0 -83
- package/clis/wikipedia/trending.test.js +0 -57
- package/clis/wttr/wttr.test.js +0 -84
- package/clis/xianyu/chat.test.js +0 -84
- package/clis/xianyu/im.test.js +0 -253
- package/clis/xianyu/item.test.js +0 -56
- package/clis/xianyu/publish.test.js +0 -220
- package/clis/xianyu/search.test.js +0 -17
- package/clis/xiaoe/xiaoe.test.js +0 -486
- package/clis/xiaohongshu/comments.test.js +0 -258
- package/clis/xiaohongshu/creator-note-detail.test.js +0 -451
- package/clis/xiaohongshu/creator-notes-summary.test.js +0 -94
- package/clis/xiaohongshu/creator-notes.test.js +0 -281
- package/clis/xiaohongshu/creator-stats.test.js +0 -24
- package/clis/xiaohongshu/delete-note.test.js +0 -172
- package/clis/xiaohongshu/download.test.js +0 -316
- package/clis/xiaohongshu/navigation.test.js +0 -34
- package/clis/xiaohongshu/note.test.js +0 -249
- package/clis/xiaohongshu/publish.test.js +0 -608
- package/clis/xiaohongshu/search.test.js +0 -325
- package/clis/xiaohongshu/user-helpers.test.js +0 -160
- package/clis/xiaoyuzhou/auth.test.js +0 -124
- package/clis/xiaoyuzhou/download.test.js +0 -135
- package/clis/xiaoyuzhou/podcast-episodes.test.js +0 -78
- package/clis/xiaoyuzhou/transcript.test.js +0 -195
- package/clis/xiaoyuzhou/utils.test.js +0 -39
- package/clis/xueqiu/comments.test.js +0 -696
- package/clis/xueqiu/danjuan-utils.test.js +0 -41
- package/clis/xueqiu/utils.test.js +0 -26
- package/clis/yahoo/search.test.js +0 -94
- package/clis/youdao/note.test.js +0 -99
- package/clis/youtube/channel.test.js +0 -59
- package/clis/youtube/feed.test.js +0 -131
- package/clis/youtube/transcript-group.test.js +0 -99
- package/clis/youtube/transcript.test.js +0 -296
- package/clis/youtube/utils.test.js +0 -84
- package/clis/yuanbao/ask.test.js +0 -124
- package/clis/yuanbao/new.test.js +0 -30
- package/clis/yuanbao/shared.test.js +0 -80
- package/clis/zhihu/answer-comments.test.js +0 -287
- package/clis/zhihu/answer-detail.test.js +0 -338
- package/clis/zhihu/answer.test.js +0 -54
- package/clis/zhihu/collection.test.js +0 -337
- package/clis/zhihu/collections.test.js +0 -182
- package/clis/zhihu/comment.test.js +0 -50
- package/clis/zhihu/download.test.js +0 -12
- package/clis/zhihu/favorite.test.js +0 -59
- package/clis/zhihu/follow.test.js +0 -42
- package/clis/zhihu/like.test.js +0 -41
- package/clis/zhihu/question.test.js +0 -251
- package/clis/zhihu/recommend.test.js +0 -143
- package/clis/zhihu/search.test.js +0 -198
- package/clis/zhihu/target.test.js +0 -77
- package/clis/zhihu/text.test.js +0 -24
- package/clis/zhihu/write-shared.test.js +0 -176
- package/clis/zlibrary/commands.test.js +0 -65
- package/clis/zsxq/search.test.js +0 -24
- package/clis/zsxq/topic.test.js +0 -30
- package/clis/zsxq/topics.test.js +0 -24
- package/clis/zsxq/utils.test.js +0 -31
- package/dist/src/adapter-shadow.test.js +0 -49
- package/dist/src/adapter-source.test.js +0 -29
- package/dist/src/browser/analyze.test.js +0 -166
- package/dist/src/browser/article-extract.e2e.test.js +0 -105
- package/dist/src/browser/article-extract.test.js +0 -94
- package/dist/src/browser/ax-snapshot.test.js +0 -91
- package/dist/src/browser/base-page.test.js +0 -758
- package/dist/src/browser/cdp-click-fixture.test.js +0 -87
- package/dist/src/browser/cdp.test.js +0 -79
- package/dist/src/browser/compound.test.js +0 -175
- package/dist/src/browser/daemon-client.test.js +0 -199
- package/dist/src/browser/dom-helpers.test.js +0 -106
- package/dist/src/browser/dom-snapshot.test.js +0 -313
- package/dist/src/browser/errors.test.js +0 -60
- package/dist/src/browser/extract.test.js +0 -129
- package/dist/src/browser/find.test.js +0 -180
- package/dist/src/browser/html-tree.test.js +0 -181
- package/dist/src/browser/network-cache.test.js +0 -58
- package/dist/src/browser/network-key.test.js +0 -49
- package/dist/src/browser/page.test.js +0 -378
- package/dist/src/browser/shape-filter.test.js +0 -101
- package/dist/src/browser/shape.test.js +0 -82
- package/dist/src/browser/stealth.test.js +0 -134
- package/dist/src/browser/target-errors.test.js +0 -94
- package/dist/src/browser/target-resolver.test.js +0 -118
- package/dist/src/browser/utils.test.js +0 -29
- package/dist/src/browser/verify-fixture.test.js +0 -219
- package/dist/src/browser.test.js +0 -232
- package/dist/src/build-manifest.test.js +0 -341
- package/dist/src/capabilityRouting.test.js +0 -63
- package/dist/src/cli-argv-preprocess.test.js +0 -209
- package/dist/src/cli.test.js +0 -2803
- package/dist/src/commanderAdapter.test.js +0 -352
- package/dist/src/commands/daemon.test.js +0 -219
- package/dist/src/completion.test.js +0 -24
- package/dist/src/convention-audit.test.js +0 -247
- package/dist/src/daemon.test.js +0 -60
- package/dist/src/doctor.test.js +0 -263
- package/dist/src/download/article-download.test.js +0 -235
- package/dist/src/download/index.test.js +0 -140
- package/dist/src/download/media-download.test.js +0 -112
- package/dist/src/download/progress.test.js +0 -36
- package/dist/src/electron-apps.test.js +0 -49
- package/dist/src/engine.test.js +0 -312
- package/dist/src/errors.test.js +0 -109
- package/dist/src/execution.test.js +0 -623
- package/dist/src/extension-manifest-regression.test.js +0 -12
- package/dist/src/external.test.js +0 -146
- package/dist/src/help.test.js +0 -58
- package/dist/src/hooks.test.js +0 -92
- package/dist/src/interceptor.test.js +0 -81
- package/dist/src/launcher.test.js +0 -138
- package/dist/src/node-network.test.js +0 -55
- package/dist/src/observation/artifact.test.js +0 -121
- package/dist/src/observation/manager.test.js +0 -13
- package/dist/src/observation/redaction.test.js +0 -32
- package/dist/src/observation/retention.test.js +0 -118
- package/dist/src/observation/ring-buffer.test.js +0 -22
- package/dist/src/output.test.js +0 -53
- package/dist/src/package-exports.test.js +0 -112
- package/dist/src/pipeline/executor.test.js +0 -182
- package/dist/src/pipeline/steps/download.test.js +0 -102
- package/dist/src/pipeline/steps/fetch.test.js +0 -117
- package/dist/src/pipeline/template.test.js +0 -175
- package/dist/src/pipeline/transform.test.js +0 -133
- package/dist/src/plugin-manifest.test.js +0 -179
- package/dist/src/plugin-scaffold.test.js +0 -83
- package/dist/src/plugin.test.js +0 -1324
- package/dist/src/registry.test.js +0 -209
- package/dist/src/runtime-detect.test.js +0 -39
- package/dist/src/serialization.test.js +0 -78
- package/dist/src/snapshotFormatter.test.js +0 -521
- package/dist/src/tui.test.js +0 -19
- package/dist/src/update-check.test.js +0 -116
- package/dist/src/utils.test.js +0 -155
- package/dist/src/validate.test.js +0 -90
- package/dist/src/weixin-download.test.js +0 -45
|
@@ -1,313 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for dom-snapshot.ts: DOM snapshot engine.
|
|
3
|
-
*
|
|
4
|
-
* Since the engine generates JavaScript strings for in-page evaluation,
|
|
5
|
-
* these tests validate:
|
|
6
|
-
* 1. The generated code is syntactically valid JS
|
|
7
|
-
* 2. Options are correctly embedded
|
|
8
|
-
* 3. The output structure matches expected format
|
|
9
|
-
* 4. All features are present (Shadow DOM, iframe, table, diff, etc.)
|
|
10
|
-
*/
|
|
11
|
-
import { describe, it, expect } from 'vitest';
|
|
12
|
-
import { generateSnapshotJs, scrollToRefJs, getFormStateJs } from './dom-snapshot.js';
|
|
13
|
-
describe('generateSnapshotJs', () => {
|
|
14
|
-
it('returns a non-empty string', () => {
|
|
15
|
-
const js = generateSnapshotJs();
|
|
16
|
-
expect(typeof js).toBe('string');
|
|
17
|
-
expect(js.length).toBeGreaterThan(100);
|
|
18
|
-
});
|
|
19
|
-
it('generates syntactically valid JS (can be parsed)', () => {
|
|
20
|
-
const js = generateSnapshotJs();
|
|
21
|
-
expect(() => new Function(js)).not.toThrow();
|
|
22
|
-
});
|
|
23
|
-
it('embeds default options correctly', () => {
|
|
24
|
-
const js = generateSnapshotJs();
|
|
25
|
-
expect(js).toContain('VIEWPORT_EXPAND = 800');
|
|
26
|
-
expect(js).toContain('MAX_DEPTH = 50');
|
|
27
|
-
expect(js).toContain('INTERACTIVE_ONLY = false');
|
|
28
|
-
expect(js).toContain('MAX_TEXT_LEN = 120');
|
|
29
|
-
expect(js).toContain('INCLUDE_SCROLL_INFO = true');
|
|
30
|
-
expect(js).toContain('BBOX_DEDUP = true');
|
|
31
|
-
expect(js).toContain('INCLUDE_SHADOW_DOM = true');
|
|
32
|
-
expect(js).toContain('INCLUDE_IFRAMES = true');
|
|
33
|
-
expect(js).toContain('PAINT_ORDER_CHECK = true');
|
|
34
|
-
expect(js).toContain('ANNOTATE_REFS = true');
|
|
35
|
-
expect(js).toContain('REPORT_HIDDEN = true');
|
|
36
|
-
expect(js).toContain('FILTER_ADS = true');
|
|
37
|
-
expect(js).toContain('MARKDOWN_TABLES = true');
|
|
38
|
-
expect(js).toContain('PREV_HASHES = null');
|
|
39
|
-
});
|
|
40
|
-
it('embeds custom options correctly', () => {
|
|
41
|
-
const js = generateSnapshotJs({
|
|
42
|
-
viewportExpand: 2000,
|
|
43
|
-
maxDepth: 30,
|
|
44
|
-
interactiveOnly: true,
|
|
45
|
-
maxTextLength: 200,
|
|
46
|
-
includeScrollInfo: false,
|
|
47
|
-
bboxDedup: false,
|
|
48
|
-
includeShadowDom: false,
|
|
49
|
-
includeIframes: false,
|
|
50
|
-
maxIframes: 3,
|
|
51
|
-
paintOrderCheck: false,
|
|
52
|
-
annotateRefs: false,
|
|
53
|
-
reportHidden: false,
|
|
54
|
-
filterAds: false,
|
|
55
|
-
markdownTables: false,
|
|
56
|
-
});
|
|
57
|
-
expect(js).toContain('VIEWPORT_EXPAND = 2000');
|
|
58
|
-
expect(js).toContain('MAX_DEPTH = 30');
|
|
59
|
-
expect(js).toContain('INTERACTIVE_ONLY = true');
|
|
60
|
-
expect(js).toContain('MAX_TEXT_LEN = 200');
|
|
61
|
-
expect(js).toContain('INCLUDE_SCROLL_INFO = false');
|
|
62
|
-
expect(js).toContain('BBOX_DEDUP = false');
|
|
63
|
-
expect(js).toContain('INCLUDE_SHADOW_DOM = false');
|
|
64
|
-
expect(js).toContain('INCLUDE_IFRAMES = false');
|
|
65
|
-
expect(js).toContain('MAX_IFRAMES = 3');
|
|
66
|
-
expect(js).toContain('PAINT_ORDER_CHECK = false');
|
|
67
|
-
expect(js).toContain('ANNOTATE_REFS = false');
|
|
68
|
-
expect(js).toContain('REPORT_HIDDEN = false');
|
|
69
|
-
expect(js).toContain('FILTER_ADS = false');
|
|
70
|
-
expect(js).toContain('MARKDOWN_TABLES = false');
|
|
71
|
-
});
|
|
72
|
-
it('clamps maxDepth between 1 and 200', () => {
|
|
73
|
-
expect(generateSnapshotJs({ maxDepth: -5 })).toContain('MAX_DEPTH = 1');
|
|
74
|
-
expect(generateSnapshotJs({ maxDepth: 999 })).toContain('MAX_DEPTH = 200');
|
|
75
|
-
expect(generateSnapshotJs({ maxDepth: 75 })).toContain('MAX_DEPTH = 75');
|
|
76
|
-
});
|
|
77
|
-
it('wraps output as an IIFE', () => {
|
|
78
|
-
const js = generateSnapshotJs();
|
|
79
|
-
expect(js.startsWith('(() =>')).toBe(true);
|
|
80
|
-
expect(js.trimEnd().endsWith(')()')).toBe(true);
|
|
81
|
-
});
|
|
82
|
-
it('embeds previousHashes for incremental diff', () => {
|
|
83
|
-
const hashes = JSON.stringify(['12345', '67890']);
|
|
84
|
-
const js = generateSnapshotJs({ previousHashes: hashes });
|
|
85
|
-
expect(js).toContain('new Set(["12345","67890"])');
|
|
86
|
-
});
|
|
87
|
-
it('includes all core features in generated code', () => {
|
|
88
|
-
const js = generateSnapshotJs();
|
|
89
|
-
// Tag filtering
|
|
90
|
-
expect(js).toContain('SKIP_TAGS');
|
|
91
|
-
expect(js).toContain("'script'");
|
|
92
|
-
expect(js).toContain("'style'");
|
|
93
|
-
// SVG collapsing
|
|
94
|
-
expect(js).toContain('SVG_CHILDREN');
|
|
95
|
-
// Interactive detection
|
|
96
|
-
expect(js).toContain('INTERACTIVE_TAGS');
|
|
97
|
-
expect(js).toContain('INTERACTIVE_ROLES');
|
|
98
|
-
expect(js).toContain('isInteractive');
|
|
99
|
-
// Visibility
|
|
100
|
-
expect(js).toContain('isVisibleByCSS');
|
|
101
|
-
expect(js).toContain('isInExpandedViewport');
|
|
102
|
-
// BBox dedup
|
|
103
|
-
expect(js).toContain('isContainedBy');
|
|
104
|
-
expect(js).toContain('PROPAGATING_TAGS');
|
|
105
|
-
expect(js).toContain('PROPAGATING_ROLES');
|
|
106
|
-
expect(js).toContain('isBboxPropagator');
|
|
107
|
-
expect(js).toContain('isDistinctivelyInteractive');
|
|
108
|
-
// Shadow DOM
|
|
109
|
-
expect(js).toContain('shadowRoot');
|
|
110
|
-
expect(js).toContain('|shadow|');
|
|
111
|
-
// iframe
|
|
112
|
-
expect(js).toContain('walkIframe');
|
|
113
|
-
expect(js).toContain('|iframe|');
|
|
114
|
-
// Paint order
|
|
115
|
-
expect(js).toContain('isOccludedByOverlay');
|
|
116
|
-
expect(js).toContain('elementFromPoint');
|
|
117
|
-
// Ad filtering
|
|
118
|
-
expect(js).toContain('isAdElement');
|
|
119
|
-
expect(js).toContain('AD_PATTERNS');
|
|
120
|
-
// data-ref annotation
|
|
121
|
-
expect(js).toContain('data-bycli-ref');
|
|
122
|
-
// Hidden elements report
|
|
123
|
-
expect(js).toContain('hiddenInteractives');
|
|
124
|
-
expect(js).toContain('hidden_interactive');
|
|
125
|
-
// Incremental diff
|
|
126
|
-
expect(js).toContain('hashElement');
|
|
127
|
-
expect(js).toContain('currentHashes');
|
|
128
|
-
expect(js).toContain('__bycli_prev_hashes');
|
|
129
|
-
// Table serialization
|
|
130
|
-
expect(js).toContain('serializeTable');
|
|
131
|
-
expect(js).toContain('|table|');
|
|
132
|
-
// Synthetic attributes
|
|
133
|
-
expect(js).toContain("'YYYY-MM-DD'");
|
|
134
|
-
expect(js).toContain('value=••••');
|
|
135
|
-
// Page metadata
|
|
136
|
-
expect(js).toContain('location.href');
|
|
137
|
-
expect(js).toContain('document.title');
|
|
138
|
-
});
|
|
139
|
-
it('contains proper attribute whitelist', () => {
|
|
140
|
-
const js = generateSnapshotJs();
|
|
141
|
-
const expectedAttrs = [
|
|
142
|
-
'aria-label', 'aria-expanded', 'aria-checked', 'aria-selected',
|
|
143
|
-
'placeholder', 'href', 'role', 'data-testid', 'autocomplete',
|
|
144
|
-
];
|
|
145
|
-
for (const attr of expectedAttrs) {
|
|
146
|
-
expect(js).toContain(`'${attr}'`);
|
|
147
|
-
}
|
|
148
|
-
});
|
|
149
|
-
it('includes scroll info formatting', () => {
|
|
150
|
-
const js = generateSnapshotJs();
|
|
151
|
-
expect(js).toContain('scrollHeight');
|
|
152
|
-
expect(js).toContain('scrollTop');
|
|
153
|
-
expect(js).toContain('|scroll|');
|
|
154
|
-
expect(js).toContain('page_scroll');
|
|
155
|
-
});
|
|
156
|
-
});
|
|
157
|
-
describe('BBox 99% containment filter', () => {
|
|
158
|
-
it('propagates bbox for both PROPAGATING_TAGS and PROPAGATING_ROLES', () => {
|
|
159
|
-
const js = generateSnapshotJs();
|
|
160
|
-
// Role-based propagator list covers the common wrapper-as-control patterns
|
|
161
|
-
// that show up as <div role=button><svg/><span/></div> on modern SPAs.
|
|
162
|
-
for (const role of ['button', 'link', 'menuitem', 'tab', 'option']) {
|
|
163
|
-
expect(js).toContain(`'${role}'`);
|
|
164
|
-
}
|
|
165
|
-
// propagate site uses the unified helper, not only the tag set
|
|
166
|
-
expect(js).toContain('isBboxPropagator(el, tag)');
|
|
167
|
-
});
|
|
168
|
-
it('suppresses interactive descendants at 0.99 containment when they are not distinctive', () => {
|
|
169
|
-
const js = generateSnapshotJs();
|
|
170
|
-
expect(js).toContain('isContainedBy(rect, parentPropagatingRect, 0.99)');
|
|
171
|
-
expect(js).toContain('!isDistinctivelyInteractive(el)');
|
|
172
|
-
// The suppression path flips the local interactive flag so the node is
|
|
173
|
-
// still emitted (for text / shape) but does not get its own [N] ref.
|
|
174
|
-
expect(js).toContain('interactive = false');
|
|
175
|
-
});
|
|
176
|
-
it('does not suppress inputs / href-bearing anchors even when fully contained', () => {
|
|
177
|
-
const js = generateSnapshotJs();
|
|
178
|
-
// Guards inside isDistinctivelyInteractive
|
|
179
|
-
expect(js).toContain("tag === 'input'");
|
|
180
|
-
expect(js).toContain("tag === 'select'");
|
|
181
|
-
expect(js).toContain("tag === 'textarea'");
|
|
182
|
-
expect(js).toContain("tag === 'a'");
|
|
183
|
-
expect(js).toContain("el.hasAttribute('href')");
|
|
184
|
-
// aria-label / aria-labelledby / id / test-id / name preserve distinctness
|
|
185
|
-
expect(js).toContain("el.hasAttribute('aria-label')");
|
|
186
|
-
expect(js).toContain("el.hasAttribute('aria-labelledby')");
|
|
187
|
-
expect(js).toContain("el.id");
|
|
188
|
-
expect(js).toContain("el.getAttribute('data-testid')");
|
|
189
|
-
expect(js).toContain("el.hasAttribute('name')");
|
|
190
|
-
});
|
|
191
|
-
it('keeps the existing 0.95 non-interactive dedup tier in place', () => {
|
|
192
|
-
const js = generateSnapshotJs();
|
|
193
|
-
// The original non-interactive bbox filter is still present alongside the
|
|
194
|
-
// new interactive tier — two complementary thresholds, not a replacement.
|
|
195
|
-
expect(js).toContain('isContainedBy(rect, parentPropagatingRect, 0.95)');
|
|
196
|
-
});
|
|
197
|
-
it('bbox containment branches are gated on BBOX_DEDUP flag', () => {
|
|
198
|
-
const off = generateSnapshotJs({ bboxDedup: false });
|
|
199
|
-
// When the option is off, the filter becomes inert (BBOX_DEDUP = false)
|
|
200
|
-
// but the inlined helpers still ship — we only guard at the call sites.
|
|
201
|
-
expect(off).toContain('BBOX_DEDUP = false');
|
|
202
|
-
expect(off).toContain('isBboxPropagator');
|
|
203
|
-
expect(off).toContain('isDistinctivelyInteractive');
|
|
204
|
-
});
|
|
205
|
-
});
|
|
206
|
-
describe('scrollToRefJs', () => {
|
|
207
|
-
it('generates valid JS', () => {
|
|
208
|
-
const js = scrollToRefJs('42');
|
|
209
|
-
expect(() => new Function(js)).not.toThrow();
|
|
210
|
-
});
|
|
211
|
-
it('targets data-bycli-ref', () => {
|
|
212
|
-
const js = scrollToRefJs('7');
|
|
213
|
-
expect(js).toContain('data-bycli-ref');
|
|
214
|
-
expect(js).toContain('scrollIntoView');
|
|
215
|
-
expect(js).toContain('"7"');
|
|
216
|
-
});
|
|
217
|
-
it('falls back to data-ref', () => {
|
|
218
|
-
const js = scrollToRefJs('3');
|
|
219
|
-
expect(js).toContain('data-ref');
|
|
220
|
-
});
|
|
221
|
-
it('returns scrolled info', () => {
|
|
222
|
-
const js = scrollToRefJs('1');
|
|
223
|
-
expect(js).toContain('scrolled: true');
|
|
224
|
-
expect(js).toContain('tag:');
|
|
225
|
-
});
|
|
226
|
-
});
|
|
227
|
-
describe('getFormStateJs', () => {
|
|
228
|
-
it('generates valid JS', () => {
|
|
229
|
-
const js = getFormStateJs();
|
|
230
|
-
expect(() => new Function(js)).not.toThrow();
|
|
231
|
-
});
|
|
232
|
-
it('collects form elements', () => {
|
|
233
|
-
const js = getFormStateJs();
|
|
234
|
-
expect(js).toContain('document.forms');
|
|
235
|
-
expect(js).toContain('form.elements');
|
|
236
|
-
});
|
|
237
|
-
it('collects orphan fields', () => {
|
|
238
|
-
const js = getFormStateJs();
|
|
239
|
-
expect(js).toContain('orphanFields');
|
|
240
|
-
expect(js).toContain('el.form');
|
|
241
|
-
});
|
|
242
|
-
it('handles different input types', () => {
|
|
243
|
-
const js = getFormStateJs();
|
|
244
|
-
expect(js).toContain('checkbox');
|
|
245
|
-
expect(js).toContain('radio');
|
|
246
|
-
expect(js).toContain('password');
|
|
247
|
-
expect(js).toContain('contenteditable');
|
|
248
|
-
});
|
|
249
|
-
it('extracts labels', () => {
|
|
250
|
-
const js = getFormStateJs();
|
|
251
|
-
expect(js).toContain('aria-label');
|
|
252
|
-
expect(js).toContain('label[for=');
|
|
253
|
-
expect(js).toContain('closest');
|
|
254
|
-
expect(js).toContain('placeholder');
|
|
255
|
-
});
|
|
256
|
-
it('masks passwords', () => {
|
|
257
|
-
const js = getFormStateJs();
|
|
258
|
-
expect(js).toContain('••••');
|
|
259
|
-
});
|
|
260
|
-
it('includes data-bycli-ref in output', () => {
|
|
261
|
-
const js = getFormStateJs();
|
|
262
|
-
expect(js).toContain('data-bycli-ref');
|
|
263
|
-
});
|
|
264
|
-
});
|
|
265
|
-
describe('Search Element Detection', () => {
|
|
266
|
-
it('includes SEARCH_INDICATORS set', () => {
|
|
267
|
-
const js = generateSnapshotJs();
|
|
268
|
-
expect(js).toContain('SEARCH_INDICATORS');
|
|
269
|
-
expect(js).toContain('search');
|
|
270
|
-
expect(js).toContain('magnify');
|
|
271
|
-
expect(js).toContain('glass');
|
|
272
|
-
});
|
|
273
|
-
it('includes hasFormControlDescendant function', () => {
|
|
274
|
-
const js = generateSnapshotJs();
|
|
275
|
-
expect(js).toContain('hasFormControlDescendant');
|
|
276
|
-
expect(js).toContain('input');
|
|
277
|
-
expect(js).toContain('select');
|
|
278
|
-
expect(js).toContain('textarea');
|
|
279
|
-
});
|
|
280
|
-
it('includes isSearchElement function', () => {
|
|
281
|
-
const js = generateSnapshotJs();
|
|
282
|
-
expect(js).toContain('isSearchElement');
|
|
283
|
-
expect(js).toContain('className');
|
|
284
|
-
expect(js).toContain('data-');
|
|
285
|
-
});
|
|
286
|
-
it('checks label wrapper detection in isInteractive', () => {
|
|
287
|
-
const js = generateSnapshotJs();
|
|
288
|
-
// Label elements without "for" attribute should check for form control descendants
|
|
289
|
-
expect(js).toContain('hasFormControlDescendant(el, 2)');
|
|
290
|
-
});
|
|
291
|
-
it('checks span wrapper detection in isInteractive', () => {
|
|
292
|
-
const js = generateSnapshotJs();
|
|
293
|
-
// Span elements should check for form control descendants
|
|
294
|
-
expect(js).toContain("tag === 'span'");
|
|
295
|
-
});
|
|
296
|
-
it('integrates search element detection into isInteractive', () => {
|
|
297
|
-
const js = generateSnapshotJs();
|
|
298
|
-
expect(js).toContain('isSearchElement(el)');
|
|
299
|
-
});
|
|
300
|
-
// Blocker B regression: compound contract must be emitted by `browser state`,
|
|
301
|
-
// not only by `browser find --css`. Otherwise agents inspecting the default
|
|
302
|
-
// snapshot still have to round-trip `find` on every date/select/file control.
|
|
303
|
-
it('inlines compoundInfoOf() and attaches compound info to each interactive ref', () => {
|
|
304
|
-
const js = generateSnapshotJs();
|
|
305
|
-
expect(js).toContain('function compoundInfoOf(el)');
|
|
306
|
-
// Wiring: the walk body should call compoundInfoOf on every interactive node
|
|
307
|
-
expect(js).toContain('compoundInfoOf(el)');
|
|
308
|
-
// And collect them into a per-ref map keyed by the same [N] index as the tree
|
|
309
|
-
expect(js).toContain('compoundInfos');
|
|
310
|
-
// And emit a sidecar section after the tree so agents can find the JSON
|
|
311
|
-
expect(js).toContain("'compounds ('");
|
|
312
|
-
});
|
|
313
|
-
});
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { classifyBrowserError, isTransientBrowserError } from './errors.js';
|
|
3
|
-
describe('classifyBrowserError', () => {
|
|
4
|
-
it('classifies extension transient errors with 1500ms delay', () => {
|
|
5
|
-
for (const msg of [
|
|
6
|
-
'Extension disconnected',
|
|
7
|
-
'Extension not connected',
|
|
8
|
-
'attach failed',
|
|
9
|
-
'Detached while handling command',
|
|
10
|
-
'Debugger is not attached to the tab: 123',
|
|
11
|
-
'no longer exists',
|
|
12
|
-
'No tab with id: 456',
|
|
13
|
-
'CDP connection reset',
|
|
14
|
-
'Daemon command failed',
|
|
15
|
-
'No window with id: 123',
|
|
16
|
-
]) {
|
|
17
|
-
const advice = classifyBrowserError(new Error(msg));
|
|
18
|
-
expect(advice.kind, `expected "${msg}" → extension-transient`).toBe('extension-transient');
|
|
19
|
-
expect(advice.retryable).toBe(true);
|
|
20
|
-
expect(advice.delayMs).toBe(1500);
|
|
21
|
-
}
|
|
22
|
-
});
|
|
23
|
-
it('classifies CDP target navigation errors with 200ms delay', () => {
|
|
24
|
-
const advice = classifyBrowserError(new Error('Inspected target navigated or closed'));
|
|
25
|
-
expect(advice.kind).toBe('target-navigation');
|
|
26
|
-
expect(advice.retryable).toBe(true);
|
|
27
|
-
expect(advice.delayMs).toBe(200);
|
|
28
|
-
});
|
|
29
|
-
it('classifies CDP -32000 target errors with 200ms delay', () => {
|
|
30
|
-
const advice = classifyBrowserError(new Error('{"code":-32000,"message":"Target closed"}'));
|
|
31
|
-
expect(advice.kind).toBe('target-navigation');
|
|
32
|
-
expect(advice.retryable).toBe(true);
|
|
33
|
-
expect(advice.delayMs).toBe(200);
|
|
34
|
-
});
|
|
35
|
-
it('classifies CDP -32000 execution-context errors with 200ms delay', () => {
|
|
36
|
-
const advice = classifyBrowserError(new Error('{"code":-32000,"message":"Cannot find default execution context"}'));
|
|
37
|
-
expect(advice.kind).toBe('target-navigation');
|
|
38
|
-
expect(advice.retryable).toBe(true);
|
|
39
|
-
expect(advice.delayMs).toBe(200);
|
|
40
|
-
});
|
|
41
|
-
it('returns non-retryable for unrelated errors', () => {
|
|
42
|
-
for (const msg of ['Permission denied', 'malformed exec payload', 'SyntaxError']) {
|
|
43
|
-
const advice = classifyBrowserError(new Error(msg));
|
|
44
|
-
expect(advice.kind).toBe('non-retryable');
|
|
45
|
-
expect(advice.retryable).toBe(false);
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
it('handles non-Error values', () => {
|
|
49
|
-
expect(classifyBrowserError('Extension disconnected').kind).toBe('extension-transient');
|
|
50
|
-
expect(classifyBrowserError(42).kind).toBe('non-retryable');
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
describe('isTransientBrowserError (convenience wrapper)', () => {
|
|
54
|
-
it('returns true for transient errors', () => {
|
|
55
|
-
expect(isTransientBrowserError(new Error('No window with id: 123'))).toBe(true);
|
|
56
|
-
});
|
|
57
|
-
it('returns false for non-transient errors', () => {
|
|
58
|
-
expect(isTransientBrowserError(new Error('Permission denied'))).toBe(false);
|
|
59
|
-
});
|
|
60
|
-
});
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { buildExtractHtmlJs, chunkMarkdown, runExtractFromHtml } from './extract.js';
|
|
3
|
-
describe('chunkMarkdown', () => {
|
|
4
|
-
it('returns the full content when it fits in one chunk', () => {
|
|
5
|
-
const content = 'short body';
|
|
6
|
-
const r = chunkMarkdown({ content, start: 0, chunkSize: 20000 });
|
|
7
|
-
expect(r.content).toBe(content);
|
|
8
|
-
expect(r.start).toBe(0);
|
|
9
|
-
expect(r.end).toBe(content.length);
|
|
10
|
-
expect(r.nextStartChar).toBeNull();
|
|
11
|
-
});
|
|
12
|
-
it('emits next_start_char when more content remains', () => {
|
|
13
|
-
// Build content long enough that chunkSize cuts it mid-stream.
|
|
14
|
-
const para = 'p'.repeat(400);
|
|
15
|
-
const content = [para, para, para].join('\n\n');
|
|
16
|
-
const r = chunkMarkdown({ content, start: 0, chunkSize: 500 });
|
|
17
|
-
expect(r.nextStartChar).not.toBeNull();
|
|
18
|
-
expect(r.nextStartChar).toBeGreaterThan(0);
|
|
19
|
-
expect(r.nextStartChar).toBeLessThan(content.length);
|
|
20
|
-
});
|
|
21
|
-
it('prefers to break at a paragraph boundary inside the boundary window', () => {
|
|
22
|
-
// chunkSize=500, window=15% → [425, 500). Place `\n\n` at 450 so it lands
|
|
23
|
-
// inside the window; the chunker should snap the cut back to it.
|
|
24
|
-
const a = 'a'.repeat(450);
|
|
25
|
-
const b = 'b'.repeat(400);
|
|
26
|
-
const content = `${a}\n\n${b}`;
|
|
27
|
-
const r = chunkMarkdown({ content, start: 0, chunkSize: 500 });
|
|
28
|
-
expect(r.content.endsWith('\n\n')).toBe(true);
|
|
29
|
-
expect(r.nextStartChar).toBe(r.end);
|
|
30
|
-
expect(content.slice(r.end).startsWith('b')).toBe(true);
|
|
31
|
-
});
|
|
32
|
-
it('falls back to a single newline when no paragraph boundary is in window', () => {
|
|
33
|
-
// 6 lines × 90 chars joined by `\n` → `\n` at 90, 181, 272, 363, 454.
|
|
34
|
-
// chunkSize=500 with window [425, 500) catches the `\n` at 454.
|
|
35
|
-
const line = 'l'.repeat(90);
|
|
36
|
-
const content = Array.from({ length: 6 }, () => line).join('\n');
|
|
37
|
-
const r = chunkMarkdown({ content, start: 0, chunkSize: 500 });
|
|
38
|
-
expect(r.content.endsWith('\n')).toBe(true);
|
|
39
|
-
expect(content.slice(r.end).startsWith('l')).toBe(true);
|
|
40
|
-
});
|
|
41
|
-
it('hard-cuts when no boundary is found within the window', () => {
|
|
42
|
-
const content = 'x'.repeat(5000);
|
|
43
|
-
const r = chunkMarkdown({ content, start: 0, chunkSize: 500 });
|
|
44
|
-
expect(r.end).toBe(500);
|
|
45
|
-
expect(r.content).toHaveLength(500);
|
|
46
|
-
expect(r.nextStartChar).toBe(500);
|
|
47
|
-
});
|
|
48
|
-
it('handles start >= content.length with an empty final chunk', () => {
|
|
49
|
-
const content = 'hello';
|
|
50
|
-
const r = chunkMarkdown({ content, start: 5, chunkSize: 100 });
|
|
51
|
-
expect(r.content).toBe('');
|
|
52
|
-
expect(r.nextStartChar).toBeNull();
|
|
53
|
-
});
|
|
54
|
-
it('resumes from a provided start cursor until the stream terminates', () => {
|
|
55
|
-
const content = `${'a'.repeat(100)}\n\n${'b'.repeat(100)}\n\n${'c'.repeat(100)}`;
|
|
56
|
-
const first = chunkMarkdown({ content, start: 0, chunkSize: 110 });
|
|
57
|
-
expect(first.nextStartChar).not.toBeNull();
|
|
58
|
-
const second = chunkMarkdown({ content, start: first.nextStartChar, chunkSize: 110 });
|
|
59
|
-
expect(second.start).toBe(first.nextStartChar);
|
|
60
|
-
expect(second.content.length).toBeGreaterThan(0);
|
|
61
|
-
let cursor = second.nextStartChar;
|
|
62
|
-
let safety = 20;
|
|
63
|
-
while (cursor !== null && safety-- > 0) {
|
|
64
|
-
const step = chunkMarkdown({ content, start: cursor, chunkSize: 110 });
|
|
65
|
-
cursor = step.nextStartChar;
|
|
66
|
-
}
|
|
67
|
-
expect(cursor).toBeNull();
|
|
68
|
-
});
|
|
69
|
-
it('clamps chunk size to the configured minimum', () => {
|
|
70
|
-
const content = 'a'.repeat(2000);
|
|
71
|
-
const r = chunkMarkdown({ content, start: 0, chunkSize: 1 });
|
|
72
|
-
// MIN_CHUNK_SIZE is 100 — requesting 1 should still produce >= 100 chars.
|
|
73
|
-
expect(r.end).toBeGreaterThanOrEqual(100);
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
describe('runExtractFromHtml', () => {
|
|
77
|
-
it('converts HTML to markdown and wraps it in the chunking envelope', () => {
|
|
78
|
-
const html = '<article><h1>Title</h1><p>Hello <strong>world</strong>.</p></article>';
|
|
79
|
-
const r = runExtractFromHtml({
|
|
80
|
-
html,
|
|
81
|
-
url: 'https://example.com/a',
|
|
82
|
-
title: 'Example',
|
|
83
|
-
selector: 'article',
|
|
84
|
-
start: 0,
|
|
85
|
-
chunkSize: 20000,
|
|
86
|
-
});
|
|
87
|
-
expect(r.url).toBe('https://example.com/a');
|
|
88
|
-
expect(r.title).toBe('Example');
|
|
89
|
-
expect(r.selector).toBe('article');
|
|
90
|
-
expect(r.content).toContain('# Title');
|
|
91
|
-
expect(r.content).toContain('**world**');
|
|
92
|
-
expect(r.start).toBe(0);
|
|
93
|
-
expect(r.end).toBe(r.content.length);
|
|
94
|
-
expect(r.total_chars).toBe(r.content.length);
|
|
95
|
-
expect(r.next_start_char).toBeNull();
|
|
96
|
-
});
|
|
97
|
-
it('reports total_chars and chunk_size against the final markdown', () => {
|
|
98
|
-
const body = Array.from({ length: 30 }, (_, i) => `<p>paragraph ${i} ${'x'.repeat(200)}</p>`).join('');
|
|
99
|
-
const r = runExtractFromHtml({
|
|
100
|
-
html: `<main>${body}</main>`,
|
|
101
|
-
url: 'https://example.com/b',
|
|
102
|
-
title: 't',
|
|
103
|
-
selector: 'main',
|
|
104
|
-
start: 0,
|
|
105
|
-
chunkSize: 500,
|
|
106
|
-
});
|
|
107
|
-
expect(r.total_chars).toBeGreaterThan(r.end);
|
|
108
|
-
expect(r.chunk_size).toBe(r.end - r.start);
|
|
109
|
-
expect(r.next_start_char).toBe(r.end);
|
|
110
|
-
});
|
|
111
|
-
});
|
|
112
|
-
describe('buildExtractHtmlJs', () => {
|
|
113
|
-
it('embeds the selector as a JSON literal', () => {
|
|
114
|
-
const js = buildExtractHtmlJs('main.article');
|
|
115
|
-
expect(js).toContain('"main.article"');
|
|
116
|
-
});
|
|
117
|
-
it('uses null when no selector given', () => {
|
|
118
|
-
const js = buildExtractHtmlJs(null);
|
|
119
|
-
// The expression references `sel` and compares to null.
|
|
120
|
-
expect(js).toContain('const sel = null;');
|
|
121
|
-
});
|
|
122
|
-
it('includes the denoise selector list', () => {
|
|
123
|
-
const js = buildExtractHtmlJs(null);
|
|
124
|
-
expect(js).toContain("'script'");
|
|
125
|
-
expect(js).toContain("'nav'");
|
|
126
|
-
expect(js).toContain("'iframe'");
|
|
127
|
-
expect(js).toContain("'[aria-hidden=\"true\"]'");
|
|
128
|
-
});
|
|
129
|
-
});
|