@sovovs/bycli 1.8.0 → 2.1.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/clis/twitter/search.js +3 -3
- 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 +67 -7
- package/dist/src/browser/daemon-client.d.ts +2 -0
- package/dist/src/browser/daemon-client.js +1 -1
- package/dist/src/browser/extension-capabilities.d.ts +13 -0
- package/dist/src/browser/extension-capabilities.js +22 -0
- package/dist/src/browser/extension-capabilities.test.d.ts +1 -0
- package/dist/src/browser/extension-version-metadata.test.d.ts +1 -0
- 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/page.d.ts +1 -0
- package/dist/src/browser/page.js +20 -1
- 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/build-manifest.js +4 -2
- package/dist/src/capabilityRouting.d.ts +3 -2
- package/dist/src/capabilityRouting.js +10 -2
- package/dist/src/cli.js +11 -14
- package/dist/src/commanderAdapter.js +5 -5
- package/dist/src/config-paths.d.ts +19 -0
- package/dist/src/config-paths.js +31 -0
- package/dist/src/daemon.js +256 -0
- package/dist/src/discovery.d.ts +10 -3
- package/dist/src/discovery.js +20 -10
- package/dist/src/discovery.test.d.ts +1 -0
- package/dist/src/execution.d.ts +5 -0
- package/dist/src/execution.js +272 -48
- package/dist/src/help.js +8 -8
- package/dist/src/main.js +18 -2
- package/dist/src/manifest-schema.d.ts +9 -0
- package/dist/src/manifest-schema.js +162 -0
- package/dist/src/manifest-schema.test.d.ts +1 -0
- package/dist/src/manifest-types.d.ts +1 -1
- package/dist/src/observation/redaction.js +10 -4
- 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 +96 -0
- package/dist/src/recorder/runner/verify-runner-main.js +292 -0
- package/dist/src/recorder/runner/verify-runner-main.test.d.ts +1 -0
- package/dist/src/registry-api.d.ts +1 -1
- package/dist/src/registry-api.types.test.d.ts +1 -0
- package/dist/src/registry-transaction.d.ts +42 -0
- package/dist/src/registry-transaction.js +194 -0
- package/dist/src/registry-transaction.test.d.ts +1 -0
- package/dist/src/registry.d.ts +58 -16
- package/dist/src/registry.js +131 -15
- package/dist/src/serialization.d.ts +1 -1
- package/dist/src/serialization.js +3 -3
- package/dist/src/types.d.ts +2 -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/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,134 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { generateStealthJs } from './stealth.js';
|
|
3
|
-
/**
|
|
4
|
-
* Tests for the stealth anti-detection module.
|
|
5
|
-
*
|
|
6
|
-
* We test the generated JS string for expected content and structure.
|
|
7
|
-
* Evaluating in Node is fragile because stealth patches target browser
|
|
8
|
-
* globals (navigator, Performance, HTMLIFrameElement) that don't exist
|
|
9
|
-
* or behave differently in Node. Instead we verify the code string
|
|
10
|
-
* contains the right patches and is syntactically valid.
|
|
11
|
-
*/
|
|
12
|
-
describe('generateStealthJs', () => {
|
|
13
|
-
it('returns a non-empty string', () => {
|
|
14
|
-
const code = generateStealthJs();
|
|
15
|
-
expect(typeof code).toBe('string');
|
|
16
|
-
expect(code.length).toBeGreaterThan(0);
|
|
17
|
-
});
|
|
18
|
-
it('is a valid self-contained IIFE', () => {
|
|
19
|
-
const code = generateStealthJs();
|
|
20
|
-
// Should start/end as an IIFE
|
|
21
|
-
expect(code.trim()).toMatch(/^\(\(\) => \{/);
|
|
22
|
-
expect(code.trim()).toMatch(/\}\)\(\)$/);
|
|
23
|
-
});
|
|
24
|
-
it('patches navigator.webdriver', () => {
|
|
25
|
-
const code = generateStealthJs();
|
|
26
|
-
expect(code).toContain("navigator, 'webdriver'");
|
|
27
|
-
expect(code).toContain('() => false');
|
|
28
|
-
});
|
|
29
|
-
it('stubs window.chrome', () => {
|
|
30
|
-
const code = generateStealthJs();
|
|
31
|
-
expect(code).toContain('window.chrome');
|
|
32
|
-
expect(code).toContain('runtime');
|
|
33
|
-
expect(code).toContain('loadTimes');
|
|
34
|
-
expect(code).toContain('csi');
|
|
35
|
-
});
|
|
36
|
-
it('fakes navigator.plugins if empty', () => {
|
|
37
|
-
const code = generateStealthJs();
|
|
38
|
-
expect(code).toContain('navigator.plugins');
|
|
39
|
-
expect(code).toContain('PDF Viewer');
|
|
40
|
-
expect(code).toContain('Chrome PDF Viewer');
|
|
41
|
-
});
|
|
42
|
-
it('ensures navigator.languages is non-empty', () => {
|
|
43
|
-
const code = generateStealthJs();
|
|
44
|
-
expect(code).toContain('navigator.languages');
|
|
45
|
-
expect(code).toContain("'en-US'");
|
|
46
|
-
});
|
|
47
|
-
it('normalizes Permissions.query for notifications', () => {
|
|
48
|
-
const code = generateStealthJs();
|
|
49
|
-
expect(code).toContain('Permissions');
|
|
50
|
-
expect(code).toContain('notifications');
|
|
51
|
-
});
|
|
52
|
-
it('cleans automation artifacts', () => {
|
|
53
|
-
const code = generateStealthJs();
|
|
54
|
-
expect(code).toContain('__playwright');
|
|
55
|
-
expect(code).toContain('__puppeteer');
|
|
56
|
-
expect(code).toContain("'cdc_'");
|
|
57
|
-
expect(code).toContain("'__cdc_'");
|
|
58
|
-
});
|
|
59
|
-
it('filters CDP patterns from Error.stack', () => {
|
|
60
|
-
const code = generateStealthJs();
|
|
61
|
-
expect(code).toContain('puppeteer_evaluation_script');
|
|
62
|
-
expect(code).toContain("'pptr:'");
|
|
63
|
-
expect(code).toContain("'debugger://'");
|
|
64
|
-
});
|
|
65
|
-
it('neutralizes debugger statement traps', () => {
|
|
66
|
-
const code = generateStealthJs();
|
|
67
|
-
// Should patch Function constructor with new.target / Reflect.construct
|
|
68
|
-
expect(code).toContain('_OrigFunction');
|
|
69
|
-
expect(code).toContain('_PatchedFunction');
|
|
70
|
-
expect(code).toContain('new.target');
|
|
71
|
-
expect(code).toContain('Reflect.construct');
|
|
72
|
-
// Should patch eval
|
|
73
|
-
expect(code).toContain('_origEval');
|
|
74
|
-
expect(code).toContain('_patchedEval');
|
|
75
|
-
// Regex to strip debugger (lookbehind for statement boundaries)
|
|
76
|
-
expect(code).toContain('_debuggerRe');
|
|
77
|
-
});
|
|
78
|
-
it('uses shared toString disguise via WeakMap', () => {
|
|
79
|
-
const code = generateStealthJs();
|
|
80
|
-
// Shared infrastructure at the top of the IIFE
|
|
81
|
-
expect(code).toContain('_origToString');
|
|
82
|
-
expect(code).toContain('WeakMap');
|
|
83
|
-
expect(code).toContain('_disguised');
|
|
84
|
-
expect(code).toContain('_disguise');
|
|
85
|
-
// Should NOT have per-instance toString overrides on Function/eval
|
|
86
|
-
// (they go through _disguise instead)
|
|
87
|
-
});
|
|
88
|
-
it('defends console method fingerprinting', () => {
|
|
89
|
-
const code = generateStealthJs();
|
|
90
|
-
expect(code).toContain('_consoleMethods');
|
|
91
|
-
expect(code).toContain("'log'");
|
|
92
|
-
expect(code).toContain("'warn'");
|
|
93
|
-
expect(code).toContain("'error'");
|
|
94
|
-
expect(code).toContain('[native code]');
|
|
95
|
-
// Uses saved _origToString reference
|
|
96
|
-
expect(code).toContain('_origToString.call');
|
|
97
|
-
});
|
|
98
|
-
it('defends window dimension detection', () => {
|
|
99
|
-
const code = generateStealthJs();
|
|
100
|
-
expect(code).toContain('outerWidth');
|
|
101
|
-
expect(code).toContain('outerHeight');
|
|
102
|
-
expect(code).toContain('innerWidth');
|
|
103
|
-
expect(code).toContain('innerHeight');
|
|
104
|
-
});
|
|
105
|
-
it('filters Performance API entries', () => {
|
|
106
|
-
const code = generateStealthJs();
|
|
107
|
-
expect(code).toContain('getEntries');
|
|
108
|
-
expect(code).toContain('getEntriesByType');
|
|
109
|
-
expect(code).toContain('getEntriesByName');
|
|
110
|
-
expect(code).toContain('_suspiciousPatterns');
|
|
111
|
-
});
|
|
112
|
-
it('cleans document $cdc_ properties', () => {
|
|
113
|
-
const code = generateStealthJs();
|
|
114
|
-
expect(code).toContain("'$cdc_'");
|
|
115
|
-
expect(code).toContain("'$chrome_'");
|
|
116
|
-
});
|
|
117
|
-
it('patches iframe contentWindow.chrome consistency', () => {
|
|
118
|
-
const code = generateStealthJs();
|
|
119
|
-
expect(code).toContain('contentWindow');
|
|
120
|
-
expect(code).toContain('HTMLIFrameElement');
|
|
121
|
-
});
|
|
122
|
-
it('uses non-enumerable guard flag on EventTarget.prototype', () => {
|
|
123
|
-
const code = generateStealthJs();
|
|
124
|
-
expect(code).toContain('EventTarget.prototype');
|
|
125
|
-
expect(code).toContain("'__lsn'");
|
|
126
|
-
expect(code).toContain('enumerable: false');
|
|
127
|
-
});
|
|
128
|
-
it('generates syntactically valid JavaScript', () => {
|
|
129
|
-
const code = generateStealthJs();
|
|
130
|
-
// new Function() parses the code without executing it in a real
|
|
131
|
-
// browser context, catching syntax errors from template literal issues.
|
|
132
|
-
expect(() => new Function(code)).not.toThrow();
|
|
133
|
-
});
|
|
134
|
-
});
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { TargetError } from './target-errors.js';
|
|
3
|
-
describe('TargetError', () => {
|
|
4
|
-
it('creates not_found error with code and hint', () => {
|
|
5
|
-
const err = new TargetError({
|
|
6
|
-
code: 'not_found',
|
|
7
|
-
message: 'ref=99 not found in DOM',
|
|
8
|
-
hint: 'Re-run `bycli browser state` to get a fresh snapshot.',
|
|
9
|
-
});
|
|
10
|
-
expect(err).toBeInstanceOf(Error);
|
|
11
|
-
expect(err.name).toBe('TargetError');
|
|
12
|
-
expect(err.code).toBe('not_found');
|
|
13
|
-
expect(err.message).toBe('ref=99 not found in DOM');
|
|
14
|
-
expect(err.hint).toContain('fresh snapshot');
|
|
15
|
-
expect(err.candidates).toBeUndefined();
|
|
16
|
-
});
|
|
17
|
-
it('creates selector_ambiguous error with candidates + matches_n', () => {
|
|
18
|
-
const err = new TargetError({
|
|
19
|
-
code: 'selector_ambiguous',
|
|
20
|
-
message: 'CSS selector ".btn" matched 3 elements',
|
|
21
|
-
hint: 'Use a more specific selector, or pass --nth.',
|
|
22
|
-
candidates: ['<button> "Login"', '<button> "Sign Up"', '<button> "Cancel"'],
|
|
23
|
-
matches_n: 3,
|
|
24
|
-
});
|
|
25
|
-
expect(err.code).toBe('selector_ambiguous');
|
|
26
|
-
expect(err.candidates).toHaveLength(3);
|
|
27
|
-
expect(err.candidates[0]).toContain('Login');
|
|
28
|
-
expect(err.matches_n).toBe(3);
|
|
29
|
-
});
|
|
30
|
-
it('creates invalid_selector error', () => {
|
|
31
|
-
const err = new TargetError({
|
|
32
|
-
code: 'invalid_selector',
|
|
33
|
-
message: 'Invalid CSS selector: >>> (unexpected token)',
|
|
34
|
-
hint: 'Check the selector syntax.',
|
|
35
|
-
});
|
|
36
|
-
expect(err.code).toBe('invalid_selector');
|
|
37
|
-
expect(err.message).toContain('Invalid CSS selector');
|
|
38
|
-
});
|
|
39
|
-
it('creates selector_not_found error with matches_n=0', () => {
|
|
40
|
-
const err = new TargetError({
|
|
41
|
-
code: 'selector_not_found',
|
|
42
|
-
message: 'CSS selector ".missing" matched 0 elements',
|
|
43
|
-
hint: 'Check the page or use browser find.',
|
|
44
|
-
matches_n: 0,
|
|
45
|
-
});
|
|
46
|
-
expect(err.code).toBe('selector_not_found');
|
|
47
|
-
expect(err.matches_n).toBe(0);
|
|
48
|
-
});
|
|
49
|
-
it('creates selector_nth_out_of_range error', () => {
|
|
50
|
-
const err = new TargetError({
|
|
51
|
-
code: 'selector_nth_out_of_range',
|
|
52
|
-
message: 'matched 3 elements, but --nth=5 is out of range',
|
|
53
|
-
hint: 'Use --nth between 0 and 2.',
|
|
54
|
-
matches_n: 3,
|
|
55
|
-
});
|
|
56
|
-
expect(err.code).toBe('selector_nth_out_of_range');
|
|
57
|
-
expect(err.matches_n).toBe(3);
|
|
58
|
-
});
|
|
59
|
-
it('creates stale_ref error', () => {
|
|
60
|
-
const err = new TargetError({
|
|
61
|
-
code: 'stale_ref',
|
|
62
|
-
message: 'ref=12 was <button>"Login" but now points to <div>"Header"',
|
|
63
|
-
hint: 'Re-run `bycli browser state` to refresh.',
|
|
64
|
-
});
|
|
65
|
-
expect(err.code).toBe('stale_ref');
|
|
66
|
-
expect(err.message).toContain('was <button>');
|
|
67
|
-
});
|
|
68
|
-
it('serializes to JSON for structured output', () => {
|
|
69
|
-
const err = new TargetError({
|
|
70
|
-
code: 'selector_ambiguous',
|
|
71
|
-
message: 'matched 3',
|
|
72
|
-
hint: 'be specific',
|
|
73
|
-
candidates: ['a', 'b'],
|
|
74
|
-
matches_n: 3,
|
|
75
|
-
});
|
|
76
|
-
const json = err.toJSON();
|
|
77
|
-
expect(json).toEqual({
|
|
78
|
-
code: 'selector_ambiguous',
|
|
79
|
-
message: 'matched 3',
|
|
80
|
-
hint: 'be specific',
|
|
81
|
-
candidates: ['a', 'b'],
|
|
82
|
-
matches_n: 3,
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
it('omits candidates from JSON when not present', () => {
|
|
86
|
-
const err = new TargetError({
|
|
87
|
-
code: 'not_found',
|
|
88
|
-
message: 'gone',
|
|
89
|
-
hint: 'refresh',
|
|
90
|
-
});
|
|
91
|
-
const json = err.toJSON();
|
|
92
|
-
expect(json).not.toHaveProperty('candidates');
|
|
93
|
-
});
|
|
94
|
-
});
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { resolveTargetJs } from './target-resolver.js';
|
|
3
|
-
/**
|
|
4
|
-
* Tests for the target resolver JS generator.
|
|
5
|
-
*
|
|
6
|
-
* Since resolveTargetJs() produces JS strings for browser evaluate(),
|
|
7
|
-
* we test the generated JS by running it in a simulated DOM-like context
|
|
8
|
-
* and verifying the structure of the output.
|
|
9
|
-
*/
|
|
10
|
-
describe('resolveTargetJs', () => {
|
|
11
|
-
it('generates JS that returns structured resolution for numeric ref', () => {
|
|
12
|
-
const js = resolveTargetJs('12');
|
|
13
|
-
expect(js).toContain('data-bycli-ref');
|
|
14
|
-
expect(js).toContain('__bycli_ref_identity');
|
|
15
|
-
expect(js).toContain('"12"');
|
|
16
|
-
});
|
|
17
|
-
it('generates JS that handles CSS selector input', () => {
|
|
18
|
-
const js = resolveTargetJs('#submit-btn');
|
|
19
|
-
expect(js).toContain('querySelectorAll');
|
|
20
|
-
expect(js).toContain('"#submit-btn"');
|
|
21
|
-
});
|
|
22
|
-
it('generates JS with stale_ref detection for numeric refs', () => {
|
|
23
|
-
const js = resolveTargetJs('5');
|
|
24
|
-
expect(js).toContain('stale_ref');
|
|
25
|
-
expect(js).toContain('__bycli_ref_identity');
|
|
26
|
-
});
|
|
27
|
-
it('generates JS with ambiguity detection for CSS selectors', () => {
|
|
28
|
-
const js = resolveTargetJs('.btn');
|
|
29
|
-
expect(js).toContain('selector_ambiguous');
|
|
30
|
-
expect(js).toContain('candidates');
|
|
31
|
-
});
|
|
32
|
-
it('generates JS that propagates --nth option into the CSS branch', () => {
|
|
33
|
-
const js = resolveTargetJs('.btn', { nth: 2 });
|
|
34
|
-
expect(js).toContain('selector_nth_out_of_range');
|
|
35
|
-
// opt.nth=2 should be inlined so the runtime picks matches[2]
|
|
36
|
-
expect(js).toMatch(/const nth = 2;?/);
|
|
37
|
-
});
|
|
38
|
-
it('generates JS that enables firstOnMulti for read commands', () => {
|
|
39
|
-
const js = resolveTargetJs('.btn', { firstOnMulti: true });
|
|
40
|
-
expect(js).toContain('firstOnMulti = true');
|
|
41
|
-
});
|
|
42
|
-
it('generates JS with invalid_selector branch for CSS syntax errors', () => {
|
|
43
|
-
const js = resolveTargetJs('.btn');
|
|
44
|
-
expect(js).toContain('invalid_selector');
|
|
45
|
-
});
|
|
46
|
-
it('generates JS with selector_not_found branch for 0 matches', () => {
|
|
47
|
-
const js = resolveTargetJs('#does-not-exist');
|
|
48
|
-
expect(js).toContain('selector_not_found');
|
|
49
|
-
});
|
|
50
|
-
it('hands every non-numeric input to querySelectorAll (no regex shortlist)', () => {
|
|
51
|
-
// Inputs that the old isCssLike regex rejected — must all flow into the
|
|
52
|
-
// CSS branch so `find --css` and `get/click/type/select` accept the same surface.
|
|
53
|
-
for (const sel of [':root', '*', ':has(.foo)', '::shadow-root', '???']) {
|
|
54
|
-
const js = resolveTargetJs(sel);
|
|
55
|
-
expect(js).toContain('querySelectorAll');
|
|
56
|
-
// invalid selectors still route through invalid_selector at runtime,
|
|
57
|
-
// never through a frontend "Cannot parse target" rejection.
|
|
58
|
-
expect(js).not.toContain('Cannot parse target');
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
it('escapes ref value safely', () => {
|
|
62
|
-
const js = resolveTargetJs('"; alert(1); "');
|
|
63
|
-
// JSON.stringify should handle escaping
|
|
64
|
-
expect(js).not.toContain('alert(1); "');
|
|
65
|
-
expect(js).toContain('\\"');
|
|
66
|
-
});
|
|
67
|
-
it('tags every success envelope with match_level so agents can tell tiers apart', () => {
|
|
68
|
-
const numericJs = resolveTargetJs('7');
|
|
69
|
-
const cssJs = resolveTargetJs('.btn');
|
|
70
|
-
// Exact / reidentified emit the literal directly; stable flows through the
|
|
71
|
-
// classifier's `level` variable. All three strings must appear in the JS.
|
|
72
|
-
expect(numericJs).toContain("match_level: 'exact'");
|
|
73
|
-
expect(numericJs).toContain("match_level: 'reidentified'");
|
|
74
|
-
expect(numericJs).toContain("return 'stable'");
|
|
75
|
-
// Stable + exact share the same emit site (match_level: level) — make sure
|
|
76
|
-
// we didn't hardcode one of them and drop the other.
|
|
77
|
-
expect(numericJs).toContain('match_level: level');
|
|
78
|
-
// CSS path is always exact (selector ran successfully).
|
|
79
|
-
expect(cssJs).toContain("match_level: 'exact'");
|
|
80
|
-
});
|
|
81
|
-
it('cascading ref path — classifier + reidentifier are both wired in', () => {
|
|
82
|
-
const js = resolveTargetJs('3');
|
|
83
|
-
// Classifier distinguishes the three tiers
|
|
84
|
-
expect(js).toContain('function classifyMatch');
|
|
85
|
-
expect(js).toContain("return 'exact'");
|
|
86
|
-
expect(js).toContain("return 'stable'");
|
|
87
|
-
expect(js).toContain("return 'mismatch'");
|
|
88
|
-
// Strong id is the only thing that can rescue a drifted fingerprint
|
|
89
|
-
expect(js).toContain('hadStrongId');
|
|
90
|
-
// Reidentify searches live DOM with the same fingerprint shape the
|
|
91
|
-
// snapshot / find writers emit — id / testId / aria-label only.
|
|
92
|
-
expect(js).toContain('function reidentify');
|
|
93
|
-
expect(js).toContain('getElementById');
|
|
94
|
-
expect(js).toContain('[data-testid="');
|
|
95
|
-
expect(js).toContain('[aria-label="');
|
|
96
|
-
// Unique match required — never silently picks one of many candidates.
|
|
97
|
-
expect(js).toContain('candidates.length === 1');
|
|
98
|
-
// Recovered element is re-tagged + identity map refreshed so subsequent
|
|
99
|
-
// resolves land on 'exact' instead of re-walking the cascade.
|
|
100
|
-
expect(js).toContain("setAttribute('data-bycli-ref', ref)");
|
|
101
|
-
expect(js).toContain('identity[ref] = fingerprintOf(recovered)');
|
|
102
|
-
});
|
|
103
|
-
it('reidentify runs both when data-bycli-ref is missing AND when fingerprint is mismatched', () => {
|
|
104
|
-
const js = resolveTargetJs('9');
|
|
105
|
-
// Two call sites: one in the !el branch, one after classifyMatch returns mismatch.
|
|
106
|
-
const count = js.split('reidentify(fp)').length - 1;
|
|
107
|
-
expect(count).toBeGreaterThanOrEqual(2);
|
|
108
|
-
});
|
|
109
|
-
it('falls through to stale_ref only after reidentify exhausts', () => {
|
|
110
|
-
const js = resolveTargetJs('4');
|
|
111
|
-
// The stale_ref emit must sit *below* a reidentify attempt so the cascade
|
|
112
|
-
// is what produces the error — not the original strict check.
|
|
113
|
-
const reidentifyIdx = js.indexOf('const recovered = reidentify(fp);');
|
|
114
|
-
const staleIdx = js.indexOf("code: 'stale_ref'");
|
|
115
|
-
expect(reidentifyIdx).toBeGreaterThan(-1);
|
|
116
|
-
expect(staleIdx).toBeGreaterThan(reidentifyIdx);
|
|
117
|
-
});
|
|
118
|
-
});
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { buildEvaluateExpression, wrapForEval } from './utils.js';
|
|
3
|
-
describe('browser eval utils', () => {
|
|
4
|
-
it('keeps existing string eval wrapping behavior', () => {
|
|
5
|
-
expect(wrapForEval('21 + 21')).toBe('21 + 21');
|
|
6
|
-
expect(wrapForEval('() => 42')).toBe('(() => 42)()');
|
|
7
|
-
});
|
|
8
|
-
it('serializes function eval arguments as JSON', () => {
|
|
9
|
-
const code = buildEvaluateExpression((selector) => {
|
|
10
|
-
return document.querySelector(selector)?.textContent ?? null;
|
|
11
|
-
}, ['.title']);
|
|
12
|
-
expect(code).toContain('document.querySelector(selector)');
|
|
13
|
-
expect(code).toContain('(...[".title"])');
|
|
14
|
-
});
|
|
15
|
-
it('accepts compact async arrow functions', () => {
|
|
16
|
-
const fn = new Function('return async()=>42')();
|
|
17
|
-
expect(buildEvaluateExpression(fn)).toBe('(async()=>42)(...[])');
|
|
18
|
-
});
|
|
19
|
-
it('rejects string eval with stray args', () => {
|
|
20
|
-
expect(() => buildEvaluateExpression('document.title', ['ignored']))
|
|
21
|
-
.toThrow('use page.evaluate(fn, ...args)');
|
|
22
|
-
});
|
|
23
|
-
it('rejects non-JSON-serializable function args', () => {
|
|
24
|
-
const circular = {};
|
|
25
|
-
circular.self = circular;
|
|
26
|
-
expect(() => buildEvaluateExpression((value) => value, [circular]))
|
|
27
|
-
.toThrow('JSON-serializable');
|
|
28
|
-
});
|
|
29
|
-
});
|
|
@@ -1,219 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { deriveFixture, expandFixtureArgs, parseSeedArgs, validateRows, validateRowShape } from './verify-fixture.js';
|
|
3
|
-
describe('validateRows', () => {
|
|
4
|
-
it('passes when rows meet all expectations', () => {
|
|
5
|
-
const fixture = {
|
|
6
|
-
expect: {
|
|
7
|
-
rowCount: { min: 1, max: 3 },
|
|
8
|
-
columns: ['id', 'title', 'url'],
|
|
9
|
-
types: { id: 'number', title: 'string', url: 'string' },
|
|
10
|
-
patterns: { url: '^https://' },
|
|
11
|
-
notEmpty: ['title', 'url'],
|
|
12
|
-
},
|
|
13
|
-
};
|
|
14
|
-
const rows = [
|
|
15
|
-
{ id: 1, title: 'a', url: 'https://x.com/a' },
|
|
16
|
-
{ id: 2, title: 'b', url: 'https://x.com/b' },
|
|
17
|
-
];
|
|
18
|
-
expect(validateRows(rows, fixture)).toEqual([]);
|
|
19
|
-
});
|
|
20
|
-
it('reports rowCount below min', () => {
|
|
21
|
-
const failures = validateRows([], { expect: { rowCount: { min: 1 } } });
|
|
22
|
-
expect(failures).toHaveLength(1);
|
|
23
|
-
expect(failures[0]).toMatchObject({ rule: 'rowCount' });
|
|
24
|
-
expect(failures[0].detail).toContain('at least 1');
|
|
25
|
-
});
|
|
26
|
-
it('reports rowCount above max', () => {
|
|
27
|
-
const failures = validateRows([{}, {}, {}, {}], { expect: { rowCount: { max: 3 } } });
|
|
28
|
-
expect(failures).toHaveLength(1);
|
|
29
|
-
expect(failures[0].detail).toContain('at most 3');
|
|
30
|
-
});
|
|
31
|
-
it('reports missing columns per row', () => {
|
|
32
|
-
const failures = validateRows([{ a: 1 }, { a: 2, b: 3 }], { expect: { columns: ['a', 'b'] } });
|
|
33
|
-
// row 0 missing 'b', row 1 complete
|
|
34
|
-
expect(failures).toEqual([
|
|
35
|
-
{ rule: 'column', detail: 'missing column "b"', rowIndex: 0 },
|
|
36
|
-
]);
|
|
37
|
-
});
|
|
38
|
-
it('reports type mismatch including null', () => {
|
|
39
|
-
const failures = validateRows([{ a: 'abc' }, { a: null }, { a: 42 }], { expect: { types: { a: 'string' } } });
|
|
40
|
-
// row 0 string ok, row 1 null fail, row 2 number fail
|
|
41
|
-
expect(failures).toHaveLength(2);
|
|
42
|
-
expect(failures[0].rowIndex).toBe(1);
|
|
43
|
-
expect(failures[0].detail).toContain('null');
|
|
44
|
-
expect(failures[1].rowIndex).toBe(2);
|
|
45
|
-
expect(failures[1].detail).toContain('number');
|
|
46
|
-
});
|
|
47
|
-
it('accepts union types like "number|string"', () => {
|
|
48
|
-
const failures = validateRows([{ id: 1 }, { id: 'abc' }], { expect: { types: { id: 'number|string' } } });
|
|
49
|
-
expect(failures).toEqual([]);
|
|
50
|
-
});
|
|
51
|
-
it('accepts "any" as wildcard type', () => {
|
|
52
|
-
const failures = validateRows([{ v: 1 }, { v: 'x' }, { v: null }, { v: [1, 2] }], { expect: { types: { v: 'any' } } });
|
|
53
|
-
expect(failures).toEqual([]);
|
|
54
|
-
});
|
|
55
|
-
it('reports pattern mismatch with row index and truncated value', () => {
|
|
56
|
-
const failures = validateRows([{ url: 'https://ok.com' }, { url: 'not-a-url' }], { expect: { patterns: { url: '^https?://' } } });
|
|
57
|
-
expect(failures).toHaveLength(1);
|
|
58
|
-
expect(failures[0]).toMatchObject({ rule: 'pattern', rowIndex: 1 });
|
|
59
|
-
expect(failures[0].detail).toContain('not-a-url');
|
|
60
|
-
});
|
|
61
|
-
it('skips pattern check for null/undefined values', () => {
|
|
62
|
-
const failures = validateRows([{ url: null }, { url: undefined }], { expect: { patterns: { url: '^x' } } });
|
|
63
|
-
expect(failures).toEqual([]);
|
|
64
|
-
});
|
|
65
|
-
it('reports invalid regex without crashing', () => {
|
|
66
|
-
const failures = validateRows([{ a: 'x' }], { expect: { patterns: { a: '[unclosed' } } });
|
|
67
|
-
expect(failures.some((f) => f.rule === 'pattern' && f.detail.includes('invalid'))).toBe(true);
|
|
68
|
-
});
|
|
69
|
-
it('treats empty/whitespace/null as failing notEmpty', () => {
|
|
70
|
-
const failures = validateRows([{ t: '' }, { t: ' ' }, { t: null }, { t: 'ok' }], { expect: { notEmpty: ['t'] } });
|
|
71
|
-
expect(failures).toHaveLength(3);
|
|
72
|
-
expect(failures.map((f) => f.rowIndex)).toEqual([0, 1, 2]);
|
|
73
|
-
});
|
|
74
|
-
it('no failures when fixture has no expect block', () => {
|
|
75
|
-
expect(validateRows([{ anything: 1 }], {})).toEqual([]);
|
|
76
|
-
});
|
|
77
|
-
it('mustNotContain flags substring bleed in columns', () => {
|
|
78
|
-
const failures = validateRows([
|
|
79
|
-
{ description: 'Lead engineer, 5 years exp. address: Shanghai. category: IT' },
|
|
80
|
-
{ description: 'Clean text.' },
|
|
81
|
-
], {
|
|
82
|
-
expect: {
|
|
83
|
-
mustNotContain: { description: ['address:', 'category:'] },
|
|
84
|
-
},
|
|
85
|
-
});
|
|
86
|
-
expect(failures).toHaveLength(2);
|
|
87
|
-
expect(failures.every((f) => f.rule === 'mustNotContain')).toBe(true);
|
|
88
|
-
expect(failures.every((f) => f.rowIndex === 0)).toBe(true);
|
|
89
|
-
});
|
|
90
|
-
it('mustNotContain skips null/undefined values', () => {
|
|
91
|
-
const failures = validateRows([{ description: null }, { description: undefined }], { expect: { mustNotContain: { description: ['x'] } } });
|
|
92
|
-
expect(failures).toEqual([]);
|
|
93
|
-
});
|
|
94
|
-
it('mustBeTruthy catches silent 0 / false / "" fallbacks', () => {
|
|
95
|
-
const failures = validateRows([{ count: 10 }, { count: 0 }, { count: false }, { count: '' }, { count: null }], { expect: { mustBeTruthy: ['count'] } });
|
|
96
|
-
expect(failures).toHaveLength(4);
|
|
97
|
-
expect(failures.every((f) => f.rule === 'mustBeTruthy')).toBe(true);
|
|
98
|
-
expect(failures.map((f) => f.rowIndex)).toEqual([1, 2, 3, 4]);
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
describe('validateRowShape', () => {
|
|
102
|
-
it('passes flat rows with a compact key set', () => {
|
|
103
|
-
const failures = validateRowShape([
|
|
104
|
-
{ id: '1', title: 'A', author: { name: 'Ada' }, tags: ['ai', 'web'] },
|
|
105
|
-
]);
|
|
106
|
-
expect(failures).toEqual([]);
|
|
107
|
-
});
|
|
108
|
-
it('reports rows with too many top-level keys', () => {
|
|
109
|
-
const row = Object.fromEntries(Array.from({ length: 13 }, (_, i) => [`k${i}`, i]));
|
|
110
|
-
const failures = validateRowShape([row]);
|
|
111
|
-
expect(failures).toEqual([
|
|
112
|
-
{
|
|
113
|
-
rule: 'shapeKeyCount',
|
|
114
|
-
detail: 'row has 13 top-level keys, expected at most 12',
|
|
115
|
-
rowIndex: 0,
|
|
116
|
-
},
|
|
117
|
-
]);
|
|
118
|
-
});
|
|
119
|
-
it('reports nesting deeper than one level', () => {
|
|
120
|
-
const failures = validateRowShape([
|
|
121
|
-
{ title: 'A', stats: { author: { name: 'Ada' } } },
|
|
122
|
-
]);
|
|
123
|
-
expect(failures).toEqual([
|
|
124
|
-
{
|
|
125
|
-
rule: 'shapeDepth',
|
|
126
|
-
detail: '"stats" nesting depth is 2, expected at most 1',
|
|
127
|
-
rowIndex: 0,
|
|
128
|
-
},
|
|
129
|
-
]);
|
|
130
|
-
});
|
|
131
|
-
it('reports nested id-shaped fields even when one-level nesting is otherwise allowed', () => {
|
|
132
|
-
const failures = validateRowShape([
|
|
133
|
-
{ title: 'A', author: { user_id: 'u1', name: 'Ada' } },
|
|
134
|
-
]);
|
|
135
|
-
expect(failures).toEqual([
|
|
136
|
-
{
|
|
137
|
-
rule: 'shapeNestedId',
|
|
138
|
-
detail: 'id-shaped field "author.user_id" must be a top-level row key',
|
|
139
|
-
rowIndex: 0,
|
|
140
|
-
},
|
|
141
|
-
]);
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
describe('deriveFixture', () => {
|
|
145
|
-
it('returns rowCount.min=0 when rows are empty', () => {
|
|
146
|
-
expect(deriveFixture([])).toEqual({ expect: { rowCount: { min: 0 } } });
|
|
147
|
-
});
|
|
148
|
-
it('extracts columns from first row and infers types per column', () => {
|
|
149
|
-
const fixture = deriveFixture([
|
|
150
|
-
{ id: 1, title: 'a', url: 'https://x' },
|
|
151
|
-
{ id: 2, title: 'b', url: 'https://y' },
|
|
152
|
-
]);
|
|
153
|
-
expect(fixture.expect?.columns).toEqual(['id', 'title', 'url']);
|
|
154
|
-
expect(fixture.expect?.types).toEqual({
|
|
155
|
-
id: 'number',
|
|
156
|
-
title: 'string',
|
|
157
|
-
url: 'string',
|
|
158
|
-
});
|
|
159
|
-
expect(fixture.expect?.rowCount).toEqual({ min: 1 });
|
|
160
|
-
});
|
|
161
|
-
it('unions mixed types across rows as "a|b"', () => {
|
|
162
|
-
const fixture = deriveFixture([
|
|
163
|
-
{ v: 1 },
|
|
164
|
-
{ v: 'two' },
|
|
165
|
-
{ v: null },
|
|
166
|
-
]);
|
|
167
|
-
expect(fixture.expect?.types?.v).toBe('null|number|string');
|
|
168
|
-
});
|
|
169
|
-
it('embeds args when provided', () => {
|
|
170
|
-
const fixture = deriveFixture([{ x: 1 }], { limit: 5 });
|
|
171
|
-
expect(fixture.args).toEqual({ limit: 5 });
|
|
172
|
-
});
|
|
173
|
-
it('embeds positional argv array when provided', () => {
|
|
174
|
-
const fixture = deriveFixture([{ x: 1 }], ['123', '--limit', '3']);
|
|
175
|
-
expect(fixture.args).toEqual(['123', '--limit', '3']);
|
|
176
|
-
});
|
|
177
|
-
it('does not add patterns or notEmpty automatically', () => {
|
|
178
|
-
const fixture = deriveFixture([{ a: 'x' }]);
|
|
179
|
-
expect(fixture.expect?.patterns).toBeUndefined();
|
|
180
|
-
expect(fixture.expect?.notEmpty).toBeUndefined();
|
|
181
|
-
});
|
|
182
|
-
});
|
|
183
|
-
describe('expandFixtureArgs', () => {
|
|
184
|
-
it('returns [] for undefined', () => {
|
|
185
|
-
expect(expandFixtureArgs(undefined)).toEqual([]);
|
|
186
|
-
});
|
|
187
|
-
it('expands object form as --key value pairs', () => {
|
|
188
|
-
expect(expandFixtureArgs({ limit: 3, sort: 'hot' })).toEqual(['--limit', '3', '--sort', 'hot']);
|
|
189
|
-
});
|
|
190
|
-
it('passes array form verbatim, stringifying values', () => {
|
|
191
|
-
expect(expandFixtureArgs(['123456', '--limit', 3])).toEqual(['123456', '--limit', '3']);
|
|
192
|
-
});
|
|
193
|
-
it('handles empty object and empty array', () => {
|
|
194
|
-
expect(expandFixtureArgs({})).toEqual([]);
|
|
195
|
-
expect(expandFixtureArgs([])).toEqual([]);
|
|
196
|
-
});
|
|
197
|
-
it('preserves positional + flag mix (e.g. <tid> --limit 3)', () => {
|
|
198
|
-
expect(expandFixtureArgs(['https://example.com/thread-1', '--comments', '5'])).toEqual([
|
|
199
|
-
'https://example.com/thread-1',
|
|
200
|
-
'--comments',
|
|
201
|
-
'5',
|
|
202
|
-
]);
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
describe('parseSeedArgs', () => {
|
|
206
|
-
it('treats plain text as one positional arg', () => {
|
|
207
|
-
expect(parseSeedArgs('bycli-verify')).toEqual(['bycli-verify']);
|
|
208
|
-
});
|
|
209
|
-
it('accepts JSON array seed args', () => {
|
|
210
|
-
expect(parseSeedArgs('["subject", "--limit", 3]')).toEqual(['subject', '--limit', 3]);
|
|
211
|
-
});
|
|
212
|
-
it('accepts JSON object seed args', () => {
|
|
213
|
-
expect(parseSeedArgs('{"limit":3,"sort":"hot"}')).toEqual({ limit: 3, sort: 'hot' });
|
|
214
|
-
});
|
|
215
|
-
it('ignores empty input', () => {
|
|
216
|
-
expect(parseSeedArgs(undefined)).toBeUndefined();
|
|
217
|
-
expect(parseSeedArgs(' ')).toBeUndefined();
|
|
218
|
-
});
|
|
219
|
-
});
|