@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,180 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { JSDOM } from 'jsdom';
|
|
3
|
-
import { buildFindJs, buildSemanticFindJs, FIND_ATTR_WHITELIST, isFindError } from './find.js';
|
|
4
|
-
/**
|
|
5
|
-
* These tests validate the shape and options of the generated JS string
|
|
6
|
-
* (no DOM available in the default vitest unit env). Runtime behavior of
|
|
7
|
-
* the generated JS against a real DOM is covered by the browser e2e suite.
|
|
8
|
-
*/
|
|
9
|
-
describe('buildFindJs', () => {
|
|
10
|
-
it('produces syntactically valid JS that can be parsed', () => {
|
|
11
|
-
expect(() => new Function(`return (${buildFindJs('.btn')});`)).not.toThrow();
|
|
12
|
-
});
|
|
13
|
-
it('embeds the selector via JSON.stringify (injection-safe)', () => {
|
|
14
|
-
const js = buildFindJs('[data-x="a\"b"]');
|
|
15
|
-
// Unescaped literal break-out must not appear
|
|
16
|
-
expect(js).not.toContain('[data-x="a"b"]');
|
|
17
|
-
// The JSON-encoded form (with escaped quotes) should
|
|
18
|
-
expect(js).toContain(JSON.stringify('[data-x="a\"b"]'));
|
|
19
|
-
});
|
|
20
|
-
it('emits invalid_selector + selector_not_found branches', () => {
|
|
21
|
-
const js = buildFindJs('.btn');
|
|
22
|
-
expect(js).toContain("code: 'invalid_selector'");
|
|
23
|
-
expect(js).toContain("code: 'selector_not_found'");
|
|
24
|
-
});
|
|
25
|
-
it('emits matches_n + entries + per-entry shape', () => {
|
|
26
|
-
const js = buildFindJs('.btn');
|
|
27
|
-
expect(js).toContain('matches_n: matches.length');
|
|
28
|
-
expect(js).toContain('entries.push(');
|
|
29
|
-
// Per-entry keys reviewers signed off on: nth, ref, tag, role, text, attrs, visible
|
|
30
|
-
expect(js).toContain('nth: i');
|
|
31
|
-
expect(js).toContain('ref: refNum');
|
|
32
|
-
expect(js).toContain('tag: el.tagName.toLowerCase()');
|
|
33
|
-
expect(js).toContain("el.getAttribute('role')");
|
|
34
|
-
expect(js).toContain('visible: isVisible(el)');
|
|
35
|
-
});
|
|
36
|
-
it('allocates fresh refs for untagged matches (write attribute + identity map)', () => {
|
|
37
|
-
const js = buildFindJs('.btn');
|
|
38
|
-
// On the just-annotated branch we must flip the attribute on the element
|
|
39
|
-
// so downstream `browser click <ref>` works off the find output.
|
|
40
|
-
expect(js).toContain("el.setAttribute('data-bycli-ref'");
|
|
41
|
-
// The fingerprint must also land in the shared identity map so the
|
|
42
|
-
// target resolver's stale-ref check has data to verify against.
|
|
43
|
-
expect(js).toContain('__bycli_ref_identity');
|
|
44
|
-
expect(js).toContain("identity['' + refNum] = fingerprintOf(el)");
|
|
45
|
-
// Allocation walks both the identity map and any existing data-bycli-ref
|
|
46
|
-
// annotations — guards against collisions after a soft nav.
|
|
47
|
-
expect(js).toContain("document.querySelectorAll('[data-bycli-ref]')");
|
|
48
|
-
});
|
|
49
|
-
it('fingerprint shape matches the snapshot / resolver contract', () => {
|
|
50
|
-
const js = buildFindJs('.btn');
|
|
51
|
-
// The six fields resolveTargetJs verifies in its stale_ref check.
|
|
52
|
-
for (const field of ['tag:', 'role:', 'text:', 'ariaLabel:', 'id:', 'testId:']) {
|
|
53
|
-
expect(js).toContain(field);
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
it('embeds defaults for limit and textMax', () => {
|
|
57
|
-
const js = buildFindJs('.btn');
|
|
58
|
-
expect(js).toContain('LIMIT = 50');
|
|
59
|
-
expect(js).toContain('TEXT_MAX = 120');
|
|
60
|
-
});
|
|
61
|
-
it('overrides limit and textMax when requested', () => {
|
|
62
|
-
const js = buildFindJs('.btn', { limit: 3, textMax: 20 });
|
|
63
|
-
expect(js).toContain('LIMIT = 3');
|
|
64
|
-
expect(js).toContain('TEXT_MAX = 20');
|
|
65
|
-
});
|
|
66
|
-
it('embeds the attribute whitelist verbatim (no style/onclick leaking)', () => {
|
|
67
|
-
const js = buildFindJs('.btn');
|
|
68
|
-
// Whitelist fields appear inside the generated JS
|
|
69
|
-
for (const key of FIND_ATTR_WHITELIST) {
|
|
70
|
-
expect(js).toContain(`"${key}"`);
|
|
71
|
-
}
|
|
72
|
-
// Sensitive / high-noise attrs must stay out of the whitelist
|
|
73
|
-
expect(FIND_ATTR_WHITELIST).not.toContain('style');
|
|
74
|
-
expect(FIND_ATTR_WHITELIST).not.toContain('onclick');
|
|
75
|
-
expect(FIND_ATTR_WHITELIST).not.toContain('onload');
|
|
76
|
-
});
|
|
77
|
-
it('inlines compoundInfoOf and attaches compound field per entry', () => {
|
|
78
|
-
const js = buildFindJs('input, select');
|
|
79
|
-
// Helper definition is inlined so each matched element can be classified.
|
|
80
|
-
expect(js).toContain('function compoundInfoOf(el)');
|
|
81
|
-
// The emitted entry opts in only when compound data is present — no noisy
|
|
82
|
-
// compound: null on every non-form element.
|
|
83
|
-
expect(js).toContain('const compound = compoundInfoOf(el);');
|
|
84
|
-
expect(js).toContain('if (compound) entry.compound = compound;');
|
|
85
|
-
// Spot-check all three compound families are covered in the inlined helper.
|
|
86
|
-
expect(js).toContain("'YYYY-MM-DD'");
|
|
87
|
-
expect(js).toContain("control: 'file'");
|
|
88
|
-
expect(js).toContain("control: 'select'");
|
|
89
|
-
});
|
|
90
|
-
it('keeps the whitelist small and explicit (guardrail against silent expansion)', () => {
|
|
91
|
-
expect(FIND_ATTR_WHITELIST).toEqual([
|
|
92
|
-
'id',
|
|
93
|
-
'class',
|
|
94
|
-
'name',
|
|
95
|
-
'type',
|
|
96
|
-
'placeholder',
|
|
97
|
-
'aria-label',
|
|
98
|
-
'title',
|
|
99
|
-
'href',
|
|
100
|
-
'value',
|
|
101
|
-
'role',
|
|
102
|
-
'data-testid',
|
|
103
|
-
]);
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
describe('isFindError', () => {
|
|
107
|
-
it('narrows { error: ... } as FindError', () => {
|
|
108
|
-
const payload = { error: { code: 'invalid_selector', message: 'x' } };
|
|
109
|
-
expect(isFindError(payload)).toBe(true);
|
|
110
|
-
if (isFindError(payload)) {
|
|
111
|
-
const err = payload;
|
|
112
|
-
expect(err.error.code).toBe('invalid_selector');
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
it('rejects successful envelopes', () => {
|
|
116
|
-
expect(isFindError({ matches_n: 0, entries: [] })).toBe(false);
|
|
117
|
-
expect(isFindError(null)).toBe(false);
|
|
118
|
-
expect(isFindError(undefined)).toBe(false);
|
|
119
|
-
expect(isFindError('string')).toBe(false);
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
describe('buildSemanticFindJs', () => {
|
|
123
|
-
function runSemanticFind(html, opts) {
|
|
124
|
-
const dom = new JSDOM(html, { runScripts: 'outside-only' });
|
|
125
|
-
return {
|
|
126
|
-
dom,
|
|
127
|
-
result: dom.window.eval(buildSemanticFindJs(opts)),
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
it('produces syntactically valid JS and embeds semantic criteria safely', () => {
|
|
131
|
-
const js = buildSemanticFindJs({ role: 'button', name: 'Save "now"', testid: 'submit' });
|
|
132
|
-
expect(() => new Function(`return (${js});`)).not.toThrow();
|
|
133
|
-
expect(js).toContain(JSON.stringify({
|
|
134
|
-
role: 'button',
|
|
135
|
-
name: 'Save "now"',
|
|
136
|
-
label: '',
|
|
137
|
-
text: '',
|
|
138
|
-
testid: 'submit',
|
|
139
|
-
}));
|
|
140
|
-
});
|
|
141
|
-
it('matches native roles, accessible name, labels, text, and test ids', () => {
|
|
142
|
-
const js = buildSemanticFindJs({ role: 'button', name: 'Save', label: 'Category', text: 'Travel', testid: 'category' });
|
|
143
|
-
expect(js).toContain('function nativeRole(el)');
|
|
144
|
-
expect(js).toContain('function accessibleName(el)');
|
|
145
|
-
expect(js).toContain('function labelText(el)');
|
|
146
|
-
expect(js).toContain('CRITERIA.role');
|
|
147
|
-
expect(js).toContain('CRITERIA.name');
|
|
148
|
-
expect(js).toContain('CRITERIA.label');
|
|
149
|
-
expect(js).toContain('CRITERIA.text');
|
|
150
|
-
expect(js).toContain('CRITERIA.testid');
|
|
151
|
-
});
|
|
152
|
-
it('allocates refs exactly like CSS find so downstream actions can click them', () => {
|
|
153
|
-
const js = buildSemanticFindJs({ role: 'button', name: 'Save' });
|
|
154
|
-
expect(js).toContain("el.setAttribute('data-bycli-ref'");
|
|
155
|
-
expect(js).toContain('__bycli_ref_identity');
|
|
156
|
-
expect(js).toContain("identity['' + refNum] = fingerprintOf(el)");
|
|
157
|
-
expect(js).toContain("document.querySelectorAll('[data-bycli-ref]')");
|
|
158
|
-
});
|
|
159
|
-
it('executes semantic role/name/testid matching and allocates a clickable ref', () => {
|
|
160
|
-
const { dom, result } = runSemanticFind('<button aria-label="Save expense" data-testid="save-button">Ignored copy</button>', { role: 'button', name: 'Save', testid: 'save' });
|
|
161
|
-
expect(result).toMatchObject({
|
|
162
|
-
matches_n: 1,
|
|
163
|
-
entries: [
|
|
164
|
-
{ nth: 0, ref: 1, tag: 'button', role: 'button', attrs: { 'aria-label': 'Save expense', 'data-testid': 'save-button' } },
|
|
165
|
-
],
|
|
166
|
-
});
|
|
167
|
-
const button = dom.window.document.querySelector('button');
|
|
168
|
-
expect(button.getAttribute('data-bycli-ref')).toBe('1');
|
|
169
|
-
expect(dom.window.__bycli_ref_identity['1']).toMatchObject({ tag: 'button', ariaLabel: 'Save expense' });
|
|
170
|
-
});
|
|
171
|
-
it('matches associated labels and placeholders for form controls', () => {
|
|
172
|
-
const { result } = runSemanticFind('<label for="category">Category</label><input id="category" placeholder="Expense category" value="Travel" />', { role: 'textbox', label: 'Category', name: 'Expense category' });
|
|
173
|
-
expect(result).toMatchObject({
|
|
174
|
-
matches_n: 1,
|
|
175
|
-
entries: [
|
|
176
|
-
{ nth: 0, ref: 1, tag: 'input', role: 'textbox' },
|
|
177
|
-
],
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
});
|
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { buildHtmlTreeJs } from './html-tree.js';
|
|
3
|
-
/**
|
|
4
|
-
* The serializer runs in a page context via `page.evaluate`. In unit tests we
|
|
5
|
-
* substitute `document` with a minimal stub that mirrors the DOM surface used
|
|
6
|
-
* by the expression, then Function-eval the returned JS.
|
|
7
|
-
*/
|
|
8
|
-
function runTreeJs(root, selectorMatches, selector, budgets = {}) {
|
|
9
|
-
const js = buildHtmlTreeJs({ selector, ...budgets });
|
|
10
|
-
const fakeDocument = {
|
|
11
|
-
querySelectorAll: () => selectorMatches,
|
|
12
|
-
documentElement: root,
|
|
13
|
-
};
|
|
14
|
-
const fn = new Function('document', `return ${js};`);
|
|
15
|
-
return fn(fakeDocument);
|
|
16
|
-
}
|
|
17
|
-
function runTreeJsInvalid(selector, errorMessage) {
|
|
18
|
-
const js = buildHtmlTreeJs({ selector });
|
|
19
|
-
const fakeDocument = {
|
|
20
|
-
querySelectorAll: () => { const e = new Error(errorMessage); e.name = 'SyntaxError'; throw e; },
|
|
21
|
-
documentElement: null,
|
|
22
|
-
};
|
|
23
|
-
const fn = new Function('document', `return ${js};`);
|
|
24
|
-
return fn(fakeDocument);
|
|
25
|
-
}
|
|
26
|
-
function el(tag, attrs, children, extras = {}) {
|
|
27
|
-
return {
|
|
28
|
-
nodeType: 1,
|
|
29
|
-
tagName: tag.toUpperCase(),
|
|
30
|
-
attributes: Object.entries(attrs).map(([name, value]) => ({ name, value })),
|
|
31
|
-
childNodes: children,
|
|
32
|
-
getAttribute: (name) => (name in attrs ? attrs[name] : null),
|
|
33
|
-
value: extras.value,
|
|
34
|
-
multiple: extras.multiple,
|
|
35
|
-
files: extras.files,
|
|
36
|
-
options: extras.options,
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
function txt(value) { return { nodeType: 3, nodeValue: value }; }
|
|
40
|
-
describe('buildHtmlTreeJs', () => {
|
|
41
|
-
it('serializes a simple element into {tag, attrs, text, children}', () => {
|
|
42
|
-
const root = el('div', { class: 'hero', id: 'x' }, [txt('Hello')]);
|
|
43
|
-
const result = runTreeJs(root, [root], null);
|
|
44
|
-
expect(result.selector).toBeNull();
|
|
45
|
-
expect(result.matched).toBe(1);
|
|
46
|
-
expect(result.tree).toEqual({
|
|
47
|
-
tag: 'div',
|
|
48
|
-
attrs: { class: 'hero', id: 'x' },
|
|
49
|
-
text: 'Hello',
|
|
50
|
-
children: [],
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
it('collapses whitespace in direct text content only', () => {
|
|
54
|
-
const root = el('p', {}, [
|
|
55
|
-
txt(' line \n one '),
|
|
56
|
-
el('span', {}, [txt('inner text')]),
|
|
57
|
-
txt('\tline two\t'),
|
|
58
|
-
]);
|
|
59
|
-
const result = runTreeJs(root, [root], null);
|
|
60
|
-
expect(result.tree?.text).toBe('line one line two');
|
|
61
|
-
expect(result.tree?.children[0].text).toBe('inner text');
|
|
62
|
-
});
|
|
63
|
-
it('recurses into element children and preserves their attrs', () => {
|
|
64
|
-
const root = el('ul', { role: 'list' }, [
|
|
65
|
-
el('li', { 'data-id': '1' }, [txt('first')]),
|
|
66
|
-
el('li', { 'data-id': '2' }, [txt('second')]),
|
|
67
|
-
]);
|
|
68
|
-
const result = runTreeJs(root, [root], null);
|
|
69
|
-
expect(result.tree?.children).toHaveLength(2);
|
|
70
|
-
expect(result.tree?.children[0]).toEqual({
|
|
71
|
-
tag: 'li',
|
|
72
|
-
attrs: { 'data-id': '1' },
|
|
73
|
-
text: 'first',
|
|
74
|
-
children: [],
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
it('returns matched=N and serializes only the first match', () => {
|
|
78
|
-
const first = el('article', { id: 'a' }, [txt('first')]);
|
|
79
|
-
const second = el('article', { id: 'b' }, [txt('second')]);
|
|
80
|
-
const result = runTreeJs(null, [first, second], 'article');
|
|
81
|
-
expect(result.matched).toBe(2);
|
|
82
|
-
expect(result.tree?.attrs.id).toBe('a');
|
|
83
|
-
});
|
|
84
|
-
it('returns tree=null and matched=0 when selector matches nothing', () => {
|
|
85
|
-
const result = runTreeJs(null, [], '.nothing');
|
|
86
|
-
expect(result.matched).toBe(0);
|
|
87
|
-
expect(result.tree).toBeNull();
|
|
88
|
-
});
|
|
89
|
-
it('catches SyntaxError from querySelectorAll and returns {invalidSelector:true, reason}', () => {
|
|
90
|
-
const result = runTreeJsInvalid('##$@@', "'##$@@' is not a valid selector");
|
|
91
|
-
expect(result.invalidSelector).toBe(true);
|
|
92
|
-
expect(result.selector).toBe('##$@@');
|
|
93
|
-
expect(result.reason).toContain('not a valid selector');
|
|
94
|
-
});
|
|
95
|
-
it('omits `truncated` when no budget is hit', () => {
|
|
96
|
-
const root = el('div', {}, [el('span', {}, [txt('ok')])]);
|
|
97
|
-
const result = runTreeJs(root, [root], null, { depth: 5, childrenMax: 10, textMax: 100 });
|
|
98
|
-
expect(result.truncated).toBeUndefined();
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
describe('buildHtmlTreeJs budget knobs', () => {
|
|
102
|
-
it('caps tree at `depth` and reports truncated.depth', () => {
|
|
103
|
-
const deep = el('a', {}, [
|
|
104
|
-
el('b', {}, [
|
|
105
|
-
el('c', {}, [el('d', {}, [txt('deep')])]),
|
|
106
|
-
]),
|
|
107
|
-
]);
|
|
108
|
-
// depth=1 → root + one level of children; grandchildren should be dropped.
|
|
109
|
-
const result = runTreeJs(deep, [deep], null, { depth: 1 });
|
|
110
|
-
expect(result.tree?.tag).toBe('a');
|
|
111
|
-
expect(result.tree?.children).toHaveLength(1);
|
|
112
|
-
expect(result.tree?.children[0].tag).toBe('b');
|
|
113
|
-
// The "b" node had element children but we hit the depth budget before
|
|
114
|
-
// recursing into them — children array is empty, truncated.depth is true.
|
|
115
|
-
expect(result.tree?.children[0].children).toEqual([]);
|
|
116
|
-
expect(result.truncated?.depth).toBe(true);
|
|
117
|
-
});
|
|
118
|
-
it('depth=0 keeps only the root', () => {
|
|
119
|
-
const root = el('ul', {}, [
|
|
120
|
-
el('li', {}, [txt('a')]),
|
|
121
|
-
el('li', {}, [txt('b')]),
|
|
122
|
-
]);
|
|
123
|
-
const result = runTreeJs(root, [root], null, { depth: 0 });
|
|
124
|
-
expect(result.tree?.children).toEqual([]);
|
|
125
|
-
expect(result.truncated?.depth).toBe(true);
|
|
126
|
-
});
|
|
127
|
-
it('caps children per node at `childrenMax` and reports children_dropped count', () => {
|
|
128
|
-
const root = el('ul', {}, [
|
|
129
|
-
el('li', {}, [txt('1')]),
|
|
130
|
-
el('li', {}, [txt('2')]),
|
|
131
|
-
el('li', {}, [txt('3')]),
|
|
132
|
-
el('li', {}, [txt('4')]),
|
|
133
|
-
el('li', {}, [txt('5')]),
|
|
134
|
-
]);
|
|
135
|
-
const result = runTreeJs(root, [root], null, { childrenMax: 2 });
|
|
136
|
-
expect(result.tree?.children).toHaveLength(2);
|
|
137
|
-
expect(result.truncated?.children_dropped).toBe(3);
|
|
138
|
-
});
|
|
139
|
-
it('caps direct text per node at `textMax` and reports text_truncated count', () => {
|
|
140
|
-
const root = el('p', {}, [
|
|
141
|
-
txt('a'.repeat(50)),
|
|
142
|
-
el('span', {}, [txt('b'.repeat(50))]),
|
|
143
|
-
]);
|
|
144
|
-
const result = runTreeJs(root, [root], null, { textMax: 10 });
|
|
145
|
-
expect(result.tree?.text).toHaveLength(10);
|
|
146
|
-
expect(result.tree?.children[0].text).toHaveLength(10);
|
|
147
|
-
expect(result.truncated?.text_truncated).toBe(2);
|
|
148
|
-
});
|
|
149
|
-
// Blocker B regression: compound contract must ride along with the
|
|
150
|
-
// json tree so `browser get html --as json` surfaces the full contract
|
|
151
|
-
// to agents without an extra round-trip.
|
|
152
|
-
it('attaches compound info to date/file/select nodes and omits it elsewhere', () => {
|
|
153
|
-
const date = el('input', { type: 'date', min: '2026-01-01' }, [], { value: '2026-04-21' });
|
|
154
|
-
const file = el('input', { type: 'file', accept: 'image/*' }, [], { multiple: true, files: [{ name: 'a.png' }] });
|
|
155
|
-
const sel = el('select', { name: 'country' }, [], {
|
|
156
|
-
options: [
|
|
157
|
-
{ value: 'us', label: 'United States', selected: true },
|
|
158
|
-
{ value: 'ca', label: 'Canada' },
|
|
159
|
-
],
|
|
160
|
-
});
|
|
161
|
-
const plain = el('input', { type: 'text' }, [], { value: 'hi' });
|
|
162
|
-
const root = el('form', {}, [date, file, sel, plain]);
|
|
163
|
-
const result = runTreeJs(root, [root], null);
|
|
164
|
-
expect(result.tree?.children[0].compound).toMatchObject({ control: 'date', format: 'YYYY-MM-DD', current: '2026-04-21', min: '2026-01-01' });
|
|
165
|
-
expect(result.tree?.children[1].compound).toMatchObject({ control: 'file', multiple: true, current: ['a.png'], accept: 'image/*' });
|
|
166
|
-
expect(result.tree?.children[2].compound).toMatchObject({ control: 'select', multiple: false, current: 'United States' });
|
|
167
|
-
expect(result.tree?.children[3].compound).toBeUndefined();
|
|
168
|
-
});
|
|
169
|
-
it('combines budgets and reports every hit', () => {
|
|
170
|
-
const root = el('ul', {}, [
|
|
171
|
-
el('li', {}, [txt('x'.repeat(20)), el('em', {}, [txt('y')])]),
|
|
172
|
-
el('li', {}, []),
|
|
173
|
-
el('li', {}, []),
|
|
174
|
-
]);
|
|
175
|
-
const result = runTreeJs(root, [root], null, { depth: 1, childrenMax: 2, textMax: 5 });
|
|
176
|
-
expect(result.tree?.children).toHaveLength(2);
|
|
177
|
-
expect(result.truncated?.children_dropped).toBe(1);
|
|
178
|
-
expect(result.truncated?.text_truncated).toBe(1);
|
|
179
|
-
expect(result.truncated?.depth).toBe(true);
|
|
180
|
-
});
|
|
181
|
-
});
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
2
|
-
import * as fs from 'node:fs';
|
|
3
|
-
import * as os from 'node:os';
|
|
4
|
-
import * as path from 'node:path';
|
|
5
|
-
import { DEFAULT_TTL_MS, findEntry, getCachePath, loadNetworkCache, saveNetworkCache, } from './network-cache.js';
|
|
6
|
-
function makeEntry(key, body = { ok: true }) {
|
|
7
|
-
return { key, url: `https://x.com/${key}`, method: 'GET', status: 200, size: 2, ct: 'application/json', body };
|
|
8
|
-
}
|
|
9
|
-
describe('network-cache', () => {
|
|
10
|
-
let baseDir;
|
|
11
|
-
beforeEach(() => {
|
|
12
|
-
baseDir = fs.mkdtempSync(path.join(os.tmpdir(), 'bycli-netcache-'));
|
|
13
|
-
});
|
|
14
|
-
afterEach(() => {
|
|
15
|
-
fs.rmSync(baseDir, { recursive: true, force: true });
|
|
16
|
-
});
|
|
17
|
-
it('sanitizes session names into safe filenames', () => {
|
|
18
|
-
const p = getCachePath('twitter/agent 1', baseDir);
|
|
19
|
-
expect(path.basename(p)).toBe('twitter_agent_1.json');
|
|
20
|
-
});
|
|
21
|
-
it('round-trips entries through save + load', () => {
|
|
22
|
-
saveNetworkCache('ws', [makeEntry('UserTweets'), makeEntry('UserByScreenName')], baseDir);
|
|
23
|
-
const res = loadNetworkCache('ws', { baseDir });
|
|
24
|
-
expect(res.status).toBe('ok');
|
|
25
|
-
expect(res.file?.entries).toHaveLength(2);
|
|
26
|
-
expect(res.file?.entries[0].key).toBe('UserTweets');
|
|
27
|
-
});
|
|
28
|
-
it('reports missing when cache file does not exist', () => {
|
|
29
|
-
expect(loadNetworkCache('nope', { baseDir }).status).toBe('missing');
|
|
30
|
-
});
|
|
31
|
-
it('reports expired when the cache is older than ttl', () => {
|
|
32
|
-
saveNetworkCache('ws', [makeEntry('A')], baseDir);
|
|
33
|
-
const future = Date.now() + DEFAULT_TTL_MS + 60_000;
|
|
34
|
-
const res = loadNetworkCache('ws', { baseDir, now: future });
|
|
35
|
-
expect(res.status).toBe('expired');
|
|
36
|
-
expect(res.file?.entries).toHaveLength(1);
|
|
37
|
-
});
|
|
38
|
-
it('reports corrupt for malformed json', () => {
|
|
39
|
-
const file = getCachePath('ws', baseDir);
|
|
40
|
-
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
41
|
-
fs.writeFileSync(file, '{not json');
|
|
42
|
-
expect(loadNetworkCache('ws', { baseDir }).status).toBe('corrupt');
|
|
43
|
-
});
|
|
44
|
-
it('reports corrupt for wrong schema version', () => {
|
|
45
|
-
const file = getCachePath('ws', baseDir);
|
|
46
|
-
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
47
|
-
fs.writeFileSync(file, JSON.stringify({ version: 0, entries: [] }));
|
|
48
|
-
expect(loadNetworkCache('ws', { baseDir }).status).toBe('corrupt');
|
|
49
|
-
});
|
|
50
|
-
it('findEntry returns matching entry or null', () => {
|
|
51
|
-
const file = {
|
|
52
|
-
version: 1, session: 'ws', savedAt: new Date().toISOString(),
|
|
53
|
-
entries: [makeEntry('A'), makeEntry('B')],
|
|
54
|
-
};
|
|
55
|
-
expect(findEntry(file, 'B')?.key).toBe('B');
|
|
56
|
-
expect(findEntry(file, 'missing')).toBeNull();
|
|
57
|
-
});
|
|
58
|
-
});
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { assignKeys, deriveKey } from './network-key.js';
|
|
3
|
-
describe('deriveKey', () => {
|
|
4
|
-
it('extracts operationName from Twitter-style graphql URLs', () => {
|
|
5
|
-
expect(deriveKey({
|
|
6
|
-
method: 'GET',
|
|
7
|
-
url: 'https://x.com/i/api/graphql/6fWQaBPK51aGyC_VC7t9GQ/UserTweets?variables=...',
|
|
8
|
-
})).toBe('UserTweets');
|
|
9
|
-
});
|
|
10
|
-
it('handles graphql URLs without a query id', () => {
|
|
11
|
-
expect(deriveKey({
|
|
12
|
-
method: 'POST',
|
|
13
|
-
url: 'https://example.com/graphql/MyOp?vars=1',
|
|
14
|
-
})).toBe('MyOp');
|
|
15
|
-
});
|
|
16
|
-
it('uses METHOD host+pathname for REST calls', () => {
|
|
17
|
-
expect(deriveKey({
|
|
18
|
-
method: 'get',
|
|
19
|
-
url: 'https://api.example.com/v1/users?page=1',
|
|
20
|
-
})).toBe('GET api.example.com/v1/users');
|
|
21
|
-
});
|
|
22
|
-
it('falls back to truncated raw url when URL parsing fails', () => {
|
|
23
|
-
const key = deriveKey({ method: 'GET', url: 'not-a-valid-url' });
|
|
24
|
-
expect(key.startsWith('GET ')).toBe(true);
|
|
25
|
-
expect(key).toContain('not-a-valid-url');
|
|
26
|
-
});
|
|
27
|
-
});
|
|
28
|
-
describe('assignKeys', () => {
|
|
29
|
-
it('disambiguates collisions with #N suffixes', () => {
|
|
30
|
-
const out = assignKeys([
|
|
31
|
-
{ url: 'https://x.com/i/api/graphql/a/UserTweets', method: 'GET' },
|
|
32
|
-
{ url: 'https://x.com/i/api/graphql/b/UserTweets', method: 'GET' },
|
|
33
|
-
{ url: 'https://api.example.com/v1/u', method: 'GET' },
|
|
34
|
-
{ url: 'https://api.example.com/v1/u', method: 'GET' },
|
|
35
|
-
{ url: 'https://api.example.com/v1/u', method: 'GET' },
|
|
36
|
-
]);
|
|
37
|
-
expect(out.map(o => o.key)).toEqual([
|
|
38
|
-
'UserTweets',
|
|
39
|
-
'UserTweets#2',
|
|
40
|
-
'GET api.example.com/v1/u',
|
|
41
|
-
'GET api.example.com/v1/u#2',
|
|
42
|
-
'GET api.example.com/v1/u#3',
|
|
43
|
-
]);
|
|
44
|
-
});
|
|
45
|
-
it('preserves extra fields on each request', () => {
|
|
46
|
-
const out = assignKeys([{ url: 'https://a.com/x', method: 'GET', status: 200 }]);
|
|
47
|
-
expect(out[0]).toMatchObject({ status: 200, key: 'GET a.com/x' });
|
|
48
|
-
});
|
|
49
|
-
});
|