six-degrees 0.7.0 → 0.8.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/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/app-path-routes-manifest.json +1 -0
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +32 -4
- package/.next/standalone/.next/routes-manifest.json +6 -0
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +16 -16
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +16 -16
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/api/admin-delete/route.js +1 -1
- package/.next/standalone/.next/server/app/api/admin-delete/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/admin-update/route.js +1 -1
- package/.next/standalone/.next/server/app/api/admin-update/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/bridges/route.js +1 -1
- package/.next/standalone/.next/server/app/api/bridges/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/bulk-import/route.js +1 -1
- package/.next/standalone/.next/server/app/api/bulk-import/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/company-scores/legacy/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/company-scores/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/connections/route.js +1 -1
- package/.next/standalone/.next/server/app/api/connections/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/backup/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/csv/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/export/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/import/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/restart/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/restore/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/reveal/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/delete-cluster/route.js +1 -1
- package/.next/standalone/.next/server/app/api/delete-cluster/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/ingest/route.js +1 -1
- package/.next/standalone/.next/server/app/api/ingest/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/notifications/route.js +1 -1
- package/.next/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/outreach/route.js +3 -3
- package/.next/standalone/.next/server/app/api/outreach/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/queue/route.js +1 -1
- package/.next/standalone/.next/server/app/api/queue/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/scraper/route.js +4 -4
- package/.next/standalone/.next/server/app/api/scraper/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/settings/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/settings/sector-preview/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/setup-profile/route.js +1 -1
- package/.next/standalone/.next/server/app/api/setup-profile/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/social/crm/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/social/messages/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/social/route.js +1 -1
- package/.next/standalone/.next/server/app/api/social/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/unlock/route.js +1 -1
- package/.next/standalone/.next/server/app/api/unlock/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/update/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/update-images/route.js +1 -1
- package/.next/standalone/.next/server/app/api/update-images/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/users/route.js +1 -1
- package/.next/standalone/.next/server/app/api/users/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/avatars/[file]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/import/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/import/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/import.html +1 -1
- package/.next/standalone/.next/server/app/import.rsc +18 -18
- package/.next/standalone/.next/server/app/import.segments/_full.segment.rsc +18 -18
- package/.next/standalone/.next/server/app/import.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/import.segments/import/__PAGE__.segment.rsc +16 -16
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +19 -19
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +16 -16
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +19 -19
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/insights/page/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/insights/page/build-manifest.json +20 -0
- package/.next/standalone/.next/server/app/insights/page/next-font-manifest.json +11 -0
- package/.next/standalone/.next/server/app/insights/page/react-loadable-manifest.json +1 -0
- package/.next/standalone/.next/server/app/insights/page/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/insights/page.js +13 -0
- package/.next/standalone/.next/server/app/insights/page.js.map +5 -0
- package/.next/standalone/.next/server/app/insights/page.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/insights/page_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/insights.html +1 -0
- package/.next/standalone/.next/server/app/insights.meta +26 -0
- package/.next/standalone/.next/server/app/insights.rsc +35 -0
- package/.next/standalone/.next/server/app/insights.segments/_full.segment.rsc +35 -0
- package/.next/standalone/.next/server/app/insights.segments/_tree.segment.rsc +4 -0
- package/.next/standalone/.next/server/app/insights.segments/insights/__PAGE__.segment.rsc +33 -0
- package/.next/standalone/.next/server/app/launch/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/launch/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/launch.html +2 -2
- package/.next/standalone/.next/server/app/launch.rsc +18 -18
- package/.next/standalone/.next/server/app/launch.segments/_full.segment.rsc +18 -18
- package/.next/standalone/.next/server/app/launch.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/launch.segments/launch/__PAGE__.segment.rsc +16 -16
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/paths/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/paths/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/paths.html +1 -1
- package/.next/standalone/.next/server/app/paths.rsc +19 -19
- package/.next/standalone/.next/server/app/paths.segments/_full.segment.rsc +19 -19
- package/.next/standalone/.next/server/app/paths.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/paths.segments/paths/__PAGE__.segment.rsc +16 -16
- package/.next/standalone/.next/server/app/profile/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/profile/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/profile.html +1 -1
- package/.next/standalone/.next/server/app/profile.rsc +19 -19
- package/.next/standalone/.next/server/app/profile.segments/_full.segment.rsc +19 -19
- package/.next/standalone/.next/server/app/profile.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/profile.segments/profile/__PAGE__.segment.rsc +16 -16
- package/.next/standalone/.next/server/app/queue/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/queue/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/queue.html +1 -1
- package/.next/standalone/.next/server/app/queue.rsc +19 -19
- package/.next/standalone/.next/server/app/queue.segments/_full.segment.rsc +19 -19
- package/.next/standalone/.next/server/app/queue.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/queue.segments/queue/__PAGE__.segment.rsc +21 -20
- package/.next/standalone/.next/server/app/scores/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/scores/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/scores.html +1 -1
- package/.next/standalone/.next/server/app/scores.rsc +18 -18
- package/.next/standalone/.next/server/app/scores.segments/_full.segment.rsc +18 -18
- package/.next/standalone/.next/server/app/scores.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/scores.segments/scores/__PAGE__.segment.rsc +16 -16
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/settings.html +1 -1
- package/.next/standalone/.next/server/app/settings.rsc +19 -19
- package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +19 -19
- package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +16 -16
- package/.next/standalone/.next/server/app/setup/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/setup.html +1 -1
- package/.next/standalone/.next/server/app/setup.rsc +19 -19
- package/.next/standalone/.next/server/app/setup.segments/_full.segment.rsc +19 -19
- package/.next/standalone/.next/server/app/setup.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/setup.segments/setup/__PAGE__.segment.rsc +16 -16
- package/.next/standalone/.next/server/app/social/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/social/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/social.html +1 -1
- package/.next/standalone/.next/server/app/social.rsc +18 -18
- package/.next/standalone/.next/server/app/social.segments/_full.segment.rsc +18 -18
- package/.next/standalone/.next/server/app/social.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/social.segments/social/__PAGE__.segment.rsc +16 -16
- package/.next/standalone/.next/server/app-paths-manifest.json +1 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__063s0x7._.js → [root-of-the-server]__0r-0t-9._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ra6xyg._.js +15 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__20rwfdw._.js → [root-of-the-server]__0rcpjad._.js} +5 -2
- package/.next/standalone/.next/server/chunks/_1_fynia._.js +7 -7
- package/.next/standalone/.next/server/chunks/_1itd1o_._.js +1 -1
- package/.next/standalone/.next/server/chunks/lib_02iz_9q._.js +2 -2
- package/.next/standalone/.next/server/chunks/lib_05agvk3._.js +2 -2
- package/.next/standalone/.next/server/chunks/lib_0v84u66._.js +2 -2
- package/.next/standalone/.next/server/chunks/lib_db-client_0_dch35.js +9 -9
- package/.next/standalone/.next/server/chunks/{lib_rpc_0jnfp2e.js → lib_rpc_00nndai.js} +1 -1
- package/.next/standalone/.next/server/chunks/{lib_rpc_0b17r6v.js → lib_rpc_03ibb-x.js} +1 -1
- package/.next/standalone/.next/server/chunks/{lib_rpc_0doq0t-.js → lib_rpc_03ri3ji.js} +1 -1
- package/.next/standalone/.next/server/chunks/{lib_rpc_0fd2rlw.js → lib_rpc_078ce48.js} +1 -1
- package/.next/standalone/.next/server/chunks/lib_rpc_0a69mgr.js +3 -0
- package/.next/standalone/.next/server/chunks/{lib_rpc_005abye.js → lib_rpc_0cej3t8.js} +1 -1
- package/.next/standalone/.next/server/chunks/lib_rpc_0cmh4g6.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0nx2_pc.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0r79-w1.js +3 -0
- package/.next/standalone/.next/server/chunks/{lib_rpc_0is4qoc.js → lib_rpc_0t552z6.js} +1 -1
- package/.next/standalone/.next/server/chunks/lib_rpc_0yx8ynd.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_12p-o9-.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1466dnb.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1sryxv6.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1trn-x5.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1ygu_mv.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0egamyq._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_04am_h8._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_07zb7lu._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0_79idq._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0gds903._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0klqt9z._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{_1k9qfsk._.js → _14jak5s._.js} +6 -2
- package/.next/standalone/.next/server/chunks/ssr/_14nxd3q._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1cj2yog._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1fm1ih_._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1hdqw0-._.js +7 -0
- package/.next/standalone/.next/server/chunks/ssr/_1k2nmbw._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_0gy11ar._.js → _1r62six._.js} +6 -2
- package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_insights_page_actions_0-0tf75.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_components_0uuff77._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_components_1j3q-h5._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{_01o3i5y._.js → app_components_settings_SectorPicker_1c-29qw.js} +2 -6
- package/.next/standalone/.next/server/chunks/ssr/app_components_ui_0pdmz2z.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_insights_page_1d_chja.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_page_1fazqbf.js +14 -14
- package/.next/standalone/.next/server/chunks/ssr/app_queue_page_0c6znd6.js +3 -3
- package/.next/standalone/.next/server/chunks/ssr/app_settings_page_1f8mjj_.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_setup_page_1-282zx.js +3 -3
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/middleware-manifest.json +5 -5
- package/.next/standalone/.next/server/next-font-manifest.js +1 -1
- package/.next/standalone/.next/server/next-font-manifest.json +4 -0
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/prefetch-hints.json +14 -0
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/static/chunks/02wnf8dkxegxg.js +34 -0
- package/.next/standalone/.next/static/chunks/0838_s6w-i_ik.js +1 -0
- package/.next/{static/chunks/2eo9pqq9gay3r.js → standalone/.next/static/chunks/0ka4prpk3w9wn.js} +4 -4
- package/.next/standalone/.next/static/chunks/0s_xxwp7z88vv.js +1 -0
- package/.next/standalone/.next/static/chunks/1-nnl8amfm4yh.js +57 -0
- package/.next/standalone/.next/static/chunks/112mv-com-mqa.js +21 -0
- package/.next/{static/chunks/44yn0tcd9dfk3.js → standalone/.next/static/chunks/1bkt_hhjzvbh8.js} +1 -1
- package/.next/standalone/.next/static/chunks/1cmphb3kh2ers.css +3 -0
- package/.next/{static/chunks/1qbjermbf68xj.js → standalone/.next/static/chunks/1d8yv3k_atka7.js} +1 -1
- package/.next/standalone/.next/static/chunks/23hc51oa4vkhc.js +5 -0
- package/.next/standalone/.next/static/chunks/27n8k9wxlz5-7.js +1 -0
- package/.next/standalone/.next/static/chunks/{0p3g5tfostsep.js → 2q9i7pcxcnph1.js} +2 -2
- package/.next/{static/chunks/2zf4jmz9l3li2.js → standalone/.next/static/chunks/2w-y83mjlpnse.js} +1 -1
- package/.next/standalone/.next/static/chunks/2ws1xf293j-tm.js +5 -0
- package/.next/standalone/.next/static/chunks/3cqn-r-y5rrw1.js +5 -0
- package/.next/standalone/.next/static/chunks/3opgg1efrx8q6.js +1 -0
- package/.next/standalone/.next/static/chunks/3p51fpxxi4d5y.js +34 -0
- package/.next/{static/chunks/0an_b6zi1r_qq.js → standalone/.next/static/chunks/3y40z6of3r9py.js} +1 -5
- package/.next/standalone/.next/static/chunks/403dpflln8ah5.js +1 -0
- package/.next/standalone/CHANGELOG.md +60 -0
- package/.next/standalone/README.md +25 -1
- package/.next/standalone/SECURITY.md +8 -0
- package/.next/standalone/app/api/outreach/route.js +13 -73
- package/.next/standalone/app/api/scraper/route.js +97 -4
- package/.next/standalone/app/components/AppHeader.js +22 -6
- package/.next/standalone/app/components/AppTabs.js +4 -0
- package/.next/standalone/app/components/AutoConnect.js +286 -0
- package/.next/standalone/app/components/ChainView.js +75 -2
- package/.next/standalone/app/components/FilterPanel.js +8 -1
- package/.next/standalone/app/components/LinkedInLimits.js +38 -13
- package/.next/standalone/app/components/OutlinkQuest.js +32 -11
- package/.next/standalone/app/components/ScanStatusBar.js +18 -3
- package/.next/standalone/app/components/ScanTrail.js +13 -3
- package/.next/standalone/app/components/SeparationView.js +6 -22
- package/.next/standalone/app/components/Sidebar.js +147 -38
- package/.next/standalone/app/components/insights/Boards.js +275 -0
- package/.next/standalone/app/components/insights/PeopleView.js +266 -0
- package/.next/standalone/app/components/insights/Rail.js +156 -0
- package/.next/standalone/app/components/insights/Report.js +382 -0
- package/.next/standalone/app/components/insights/parts.js +239 -0
- package/.next/standalone/app/components/settings/UsageSection.js +50 -3
- package/.next/standalone/app/globals.css +2 -3
- package/.next/standalone/app/insights/page.js +220 -0
- package/.next/standalone/app/page.js +1 -17
- package/.next/standalone/app/queue/page.js +30 -18
- package/.next/standalone/app/setup/page.js +16 -9
- package/.next/standalone/lib/auto-connect.js +145 -0
- package/.next/standalone/lib/companies.js +1 -1
- package/.next/standalone/lib/in-progress.js +46 -0
- package/.next/standalone/lib/insights-board.js +613 -0
- package/.next/standalone/lib/linkedin-limits.js +36 -11
- package/.next/standalone/lib/requests-client.js +10 -0
- package/.next/standalone/lib/requests.js +124 -0
- package/.next/standalone/lib/scan-state.js +1 -0
- package/.next/standalone/lib/scraper-client.js +18 -5
- package/.next/standalone/lib/settings.js +3 -0
- package/.next/standalone/lib/usage.js +5 -0
- package/.next/standalone/package-lock.json +2 -2
- package/.next/standalone/package.json +1 -1
- package/.next/standalone/scripts/scrape.py +569 -9
- package/.next/static/chunks/02wnf8dkxegxg.js +34 -0
- package/.next/static/chunks/0838_s6w-i_ik.js +1 -0
- package/.next/{standalone/.next/static/chunks/2eo9pqq9gay3r.js → static/chunks/0ka4prpk3w9wn.js} +4 -4
- package/.next/static/chunks/0s_xxwp7z88vv.js +1 -0
- package/.next/static/chunks/1-nnl8amfm4yh.js +57 -0
- package/.next/static/chunks/112mv-com-mqa.js +21 -0
- package/.next/{standalone/.next/static/chunks/44yn0tcd9dfk3.js → static/chunks/1bkt_hhjzvbh8.js} +1 -1
- package/.next/static/chunks/1cmphb3kh2ers.css +3 -0
- package/.next/{standalone/.next/static/chunks/1qbjermbf68xj.js → static/chunks/1d8yv3k_atka7.js} +1 -1
- package/.next/static/chunks/23hc51oa4vkhc.js +5 -0
- package/.next/static/chunks/27n8k9wxlz5-7.js +1 -0
- package/.next/static/chunks/{0p3g5tfostsep.js → 2q9i7pcxcnph1.js} +2 -2
- package/.next/{standalone/.next/static/chunks/2zf4jmz9l3li2.js → static/chunks/2w-y83mjlpnse.js} +1 -1
- package/.next/static/chunks/2ws1xf293j-tm.js +5 -0
- package/.next/static/chunks/3cqn-r-y5rrw1.js +5 -0
- package/.next/static/chunks/3opgg1efrx8q6.js +1 -0
- package/.next/static/chunks/3p51fpxxi4d5y.js +34 -0
- package/.next/{standalone/.next/static/chunks/0an_b6zi1r_qq.js → static/chunks/3y40z6of3r9py.js} +1 -5
- package/.next/static/chunks/403dpflln8ah5.js +1 -0
- package/CHANGELOG.md +60 -0
- package/README.md +25 -1
- package/SECURITY.md +8 -0
- package/lib/auto-connect.js +145 -0
- package/lib/companies.js +1 -1
- package/lib/in-progress.js +46 -0
- package/lib/insights-board.js +613 -0
- package/lib/linkedin-limits.js +36 -11
- package/lib/requests-client.js +10 -0
- package/lib/requests.js +124 -0
- package/lib/scan-state.js +1 -0
- package/lib/scraper-client.js +18 -5
- package/lib/settings.js +3 -0
- package/lib/usage.js +5 -0
- package/package.json +1 -1
- package/scripts/scrape.py +569 -9
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_4y8z7._.js +0 -8
- package/.next/standalone/.next/server/chunks/_02twlca._.js +0 -10
- package/.next/standalone/.next/server/chunks/lib_rpc_0nef_n7.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_0plrzra.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_13a4m0c.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1if17c-.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1j-zw7v.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1sngt-q.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1v2sgme.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1vh-tgx.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1zaubz7.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_02sband._.js +0 -7
- package/.next/standalone/.next/server/chunks/ssr/_189g8lg._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_1f_n00p._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/app_components_0t908t-._.js +0 -3
- package/.next/standalone/.next/static/chunks/0e3g0sil79cus.js +0 -21
- package/.next/standalone/.next/static/chunks/0zd0t7e60bn4z.js +0 -1
- package/.next/standalone/.next/static/chunks/163b-20vwa06i.js +0 -34
- package/.next/standalone/.next/static/chunks/1pa-2v1ajvrko.js +0 -1
- package/.next/standalone/.next/static/chunks/23_1z47al92nz.js +0 -57
- package/.next/standalone/.next/static/chunks/260bomtr214m8.js +0 -34
- package/.next/standalone/.next/static/chunks/2xk2ra4piiuod.js +0 -34
- package/.next/standalone/.next/static/chunks/3b0-14cxqsprt.js +0 -1
- package/.next/standalone/.next/static/chunks/3jarus1_38s2z.js +0 -1
- package/.next/standalone/.next/static/chunks/3ssu341hav_sq.css +0 -3
- package/.next/standalone/.next/static/chunks/3v1qd3sqhqn0d.js +0 -1
- package/.next/static/chunks/0e3g0sil79cus.js +0 -21
- package/.next/static/chunks/0zd0t7e60bn4z.js +0 -1
- package/.next/static/chunks/163b-20vwa06i.js +0 -34
- package/.next/static/chunks/1pa-2v1ajvrko.js +0 -1
- package/.next/static/chunks/23_1z47al92nz.js +0 -57
- package/.next/static/chunks/260bomtr214m8.js +0 -34
- package/.next/static/chunks/2xk2ra4piiuod.js +0 -34
- package/.next/static/chunks/3b0-14cxqsprt.js +0 -1
- package/.next/static/chunks/3jarus1_38s2z.js +0 -1
- package/.next/static/chunks/3ssu341hav_sq.css +0 -3
- package/.next/static/chunks/3v1qd3sqhqn0d.js +0 -1
- /package/.next/standalone/.next/static/{ZVpLBK-2vpllv2HxOFs2d → 3NCyJyAPDCEYsYTwF1Ieu}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{ZVpLBK-2vpllv2HxOFs2d → 3NCyJyAPDCEYsYTwF1Ieu}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{ZVpLBK-2vpllv2HxOFs2d → 3NCyJyAPDCEYsYTwF1Ieu}/_ssgManifest.js +0 -0
- /package/.next/static/{ZVpLBK-2vpllv2HxOFs2d → 3NCyJyAPDCEYsYTwF1Ieu}/_buildManifest.js +0 -0
- /package/.next/static/{ZVpLBK-2vpllv2HxOFs2d → 3NCyJyAPDCEYsYTwF1Ieu}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{ZVpLBK-2vpllv2HxOFs2d → 3NCyJyAPDCEYsYTwF1Ieu}/_ssgManifest.js +0 -0
package/lib/linkedin-limits.js
CHANGED
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
// and the scanner also keeps any two a minute apart. Searches did too, at 373 in
|
|
14
14
|
// 24 hours: the page asks before a risky search budget (lib/search-risk.js).
|
|
15
15
|
//
|
|
16
|
+
// Auto's connection requests (lib/auto-connect.js) are a third list in the same
|
|
17
|
+
// record, "invites", with caps of their own: 15 in any 24 hours, 80 in any 7 days.
|
|
18
|
+
//
|
|
16
19
|
// They belong to the LinkedIn account, not to a profile in the app. TRAPS §35.
|
|
17
20
|
|
|
18
21
|
import {
|
|
@@ -22,12 +25,14 @@ import path from 'node:path';
|
|
|
22
25
|
import { durable } from './durable.js';
|
|
23
26
|
import { PACES, DEFAULT_PACE } from './scan-pace.js';
|
|
24
27
|
import { countAfter } from './usage.js';
|
|
28
|
+
import { INVITE_CAPS } from './auto-connect.js';
|
|
25
29
|
|
|
26
30
|
export const DEFAULT_LIMITS = { daily: 50, monthly: 250, profiles: 50, pace: DEFAULT_PACE };
|
|
27
31
|
export const DAILY_CHOICES = [25, 50, 100, 200, 500];
|
|
28
32
|
export const MONTHLY_CHOICES = [100, 250, 500, 1000, 0]; // 0 = no monthly cap (Premium)
|
|
29
33
|
export const PROFILE_CHOICES = [10, 25, 50, 100]; // profile views a day; never unlimited
|
|
30
34
|
const DAY = 24 * 3600;
|
|
35
|
+
const WEEK = 7 * DAY;
|
|
31
36
|
const PACIFIC = 'America/Los_Angeles';
|
|
32
37
|
|
|
33
38
|
function readJson(file) {
|
|
@@ -102,17 +107,16 @@ export function writeLimits(dir, { daily, monthly, profiles, pace } = {}) {
|
|
|
102
107
|
}
|
|
103
108
|
|
|
104
109
|
/**
|
|
105
|
-
* The scanner's record as
|
|
106
|
-
*
|
|
110
|
+
* The scanner's record as three lists of times (epoch seconds): searches,
|
|
111
|
+
* profile views and Auto's connection requests. null when it can't be read. No
|
|
112
|
+
* record at all is nothing done yet.
|
|
107
113
|
*/
|
|
108
114
|
function readActivity(dir) {
|
|
109
115
|
const file = path.join(dir, 'linkedin-activity.json');
|
|
110
116
|
const data = existsSync(file) ? readJson(file) : { searches: [], profiles: [] };
|
|
111
117
|
if (!data || typeof data !== 'object') return null;
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
profiles: (Array.isArray(data.profiles) ? data.profiles : []).map(Number),
|
|
115
|
-
};
|
|
118
|
+
const times = (v) => (Array.isArray(v) ? v : []).map(Number);
|
|
119
|
+
return { searches: times(data.searches), profiles: times(data.profiles), invites: times(data.invites) };
|
|
116
120
|
}
|
|
117
121
|
|
|
118
122
|
/**
|
|
@@ -123,14 +127,19 @@ function readActivity(dir) {
|
|
|
123
127
|
*/
|
|
124
128
|
export function usage(dir, now = Date.now()) {
|
|
125
129
|
const rec = readActivity(dir);
|
|
126
|
-
if (!rec)
|
|
127
|
-
|
|
130
|
+
if (!rec) {
|
|
131
|
+
return { searchesToday: Infinity, searchesMonth: 0, profilesToday: Infinity, invitesToday: Infinity, invitesWeek: Infinity, unreadable: true };
|
|
132
|
+
}
|
|
133
|
+
const { searches: s, profiles: p, invites: i } = rec;
|
|
128
134
|
const dayAgo = now / 1000 - DAY;
|
|
129
135
|
const month0 = monthStartPacific(now) / 1000;
|
|
130
136
|
return {
|
|
131
137
|
searchesToday: s.filter((t) => t > dayAgo).length,
|
|
132
138
|
searchesMonth: s.filter((t) => t >= month0).length,
|
|
133
139
|
profilesToday: p.filter((t) => t > dayAgo).length,
|
|
140
|
+
// Auto's connection requests (lib/auto-connect.js), against their own caps.
|
|
141
|
+
invitesToday: i.filter((t) => t > dayAgo).length,
|
|
142
|
+
invitesWeek: i.filter((t) => t > now / 1000 - WEEK).length,
|
|
134
143
|
};
|
|
135
144
|
}
|
|
136
145
|
|
|
@@ -177,12 +186,18 @@ export function linkedinState(dir, now = Date.now()) {
|
|
|
177
186
|
const limits = readLimits(dir);
|
|
178
187
|
const use = usage(dir, now);
|
|
179
188
|
const left = (limit, used) => (limit ? Math.max(0, limit - used) : null);
|
|
189
|
+
// When Auto may send again once a cap is reached: the request that stops
|
|
190
|
+
// counting soonest frees the next (freesAt below).
|
|
191
|
+
const invites = use.unreadable ? null : readActivity(dir)?.invites || [];
|
|
180
192
|
return {
|
|
181
193
|
...use,
|
|
182
194
|
limits,
|
|
183
195
|
leftToday: left(limits.daily, use.searchesToday),
|
|
184
196
|
leftMonth: left(limits.monthly, use.searchesMonth),
|
|
185
197
|
profilesLeftToday: left(limits.profiles, use.profilesToday),
|
|
198
|
+
inviteCaps: INVITE_CAPS,
|
|
199
|
+
invitesFreeAt: invites ? freesAt(invites, now, INVITE_CAPS.day) : null,
|
|
200
|
+
invitesWeekFreeAt: invites ? freesAt(invites, now, INVITE_CAPS.week, WEEK) : null,
|
|
186
201
|
monthResets: nextMonthStartPacific(now),
|
|
187
202
|
cooldown: readCooldown(dir, now),
|
|
188
203
|
};
|
|
@@ -325,6 +340,9 @@ export function linkedinUsage(dir, now = Date.now()) {
|
|
|
325
340
|
now,
|
|
326
341
|
searchesToday: rec ? state.searchesToday : null,
|
|
327
342
|
profilesToday: rec ? state.profilesToday : null,
|
|
343
|
+
// Auto's connection requests: the last 24 hours and the last 7 days, against INVITE_CAPS.
|
|
344
|
+
invitesToday: rec ? state.invitesToday : null,
|
|
345
|
+
invitesWeek: rec ? state.invitesWeek : null,
|
|
328
346
|
searchesMonth: rec ? state.searchesMonth : null,
|
|
329
347
|
leftMonth: rec ? state.leftMonth : null,
|
|
330
348
|
searchesLastHour: known((r) => countWithin(r.searches, now, 3600)),
|
|
@@ -363,7 +381,7 @@ const isTime = (v) => typeof v === 'number' && Number.isFinite(v) && v >= 0;
|
|
|
363
381
|
export function budgetFileProblem(name, data) {
|
|
364
382
|
if (!isObject(data)) return 'it is not a JSON object';
|
|
365
383
|
if (name === ACTIVITY) {
|
|
366
|
-
if (!isTimeList(data.searches) || !isTimeList(data.profiles)) return 'its record of searches is not a list of times';
|
|
384
|
+
if (!isTimeList(data.searches) || !isTimeList(data.profiles) || !isTimeList(data.invites)) return 'its record of searches is not a list of times';
|
|
367
385
|
} else if (name === COOLDOWN) {
|
|
368
386
|
if (!isTime(data.until)) return 'its pause has no end time';
|
|
369
387
|
if (data.reason !== undefined && typeof data.reason !== 'string') return 'its pause has a reason that is not text';
|
|
@@ -415,7 +433,8 @@ const hasFile = (p) => {
|
|
|
415
433
|
/**
|
|
416
434
|
* Merge the budget files in `from` (an import's, already checked with
|
|
417
435
|
* budgetFileProblem) into the data folder `dir`:
|
|
418
|
-
* linkedin-activity.json both computers' searches
|
|
436
|
+
* linkedin-activity.json both computers' searches, profile views and Auto's
|
|
437
|
+
* connection requests (mergeTimes)
|
|
419
438
|
* linkedin-cooldown.json whichever pause ends later
|
|
420
439
|
* scan-limits.json this computer's, if it has one; the copy's otherwise.
|
|
421
440
|
* The limits are a choice someone made here, and an
|
|
@@ -437,10 +456,16 @@ export function mergeBudgetFiles({ dir, from }) {
|
|
|
437
456
|
const mine = hasFile(here(ACTIVITY)) ? readJson(here(ACTIVITY)) : {};
|
|
438
457
|
const searches = isObject(mine) ? readableTimes(mine.searches) : null;
|
|
439
458
|
const profiles = isObject(mine) ? readableTimes(mine.profiles) : null;
|
|
440
|
-
|
|
459
|
+
const invites = isObject(mine) ? readableTimes(mine.invites) : null;
|
|
460
|
+
if (searches && profiles && invites) {
|
|
461
|
+
// Auto's requests too, or a copy brought in would forget the ones sent here
|
|
462
|
+
// (and Auto's caps would start again from nothing). Written only when
|
|
463
|
+
// either side has any, so a record from before Auto keeps its old layout.
|
|
464
|
+
const sent = mergeTimes(invites, activity.invites);
|
|
441
465
|
durable.writeJsonDurably(here(ACTIVITY), {
|
|
442
466
|
searches: mergeTimes(searches, activity.searches),
|
|
443
467
|
profiles: mergeTimes(profiles, activity.profiles),
|
|
468
|
+
...(sent.length ? { invites: sent } : {}),
|
|
444
469
|
});
|
|
445
470
|
}
|
|
446
471
|
}
|
package/lib/requests-client.js
CHANGED
|
@@ -96,6 +96,16 @@ export async function markRequested(row, { bridgeId } = {}) {
|
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
/**
|
|
100
|
+
* A request the app has already saved, shown everywhere at once with nothing
|
|
101
|
+
* posted: Auto's, which the server marks itself once LinkedIn shows it pending
|
|
102
|
+
* (app/api/scraper/route.js, lib/requests.js).
|
|
103
|
+
*/
|
|
104
|
+
export function sawRequested(row) {
|
|
105
|
+
mine.set(keyFor(row), true);
|
|
106
|
+
publish();
|
|
107
|
+
}
|
|
108
|
+
|
|
99
109
|
/** Take a request back, everywhere. */
|
|
100
110
|
export async function undoRequest(row) {
|
|
101
111
|
const key = keyFor(row);
|
package/lib/requests.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// Requests you've sent, kept per PERSON: the server side of
|
|
2
|
+
// lib/requests-client.js.
|
|
3
|
+
//
|
|
4
|
+
// A person has one row per bridge whose circle holds them (the table's key is
|
|
5
|
+
// profile + bridge + user), and a request used to mark only the copy that was
|
|
6
|
+
// clicked. The other copies still offered "Add", the Pending count counted
|
|
7
|
+
// copies, and every click awarded XP again. Now a request marks every copy of
|
|
8
|
+
// the person, in both columns, the bridge you asked through is kept (so once
|
|
9
|
+
// they accept, their card and circle say who introduced you: lib/promote.js
|
|
10
|
+
// keeps it), XP comes once per person, and Undo takes back all of it.
|
|
11
|
+
//
|
|
12
|
+
// Two callers: POST /api/outreach (a Connect pressed on a page), and the
|
|
13
|
+
// scanner's route once Auto has sent a request and LinkedIn showed it pending
|
|
14
|
+
// (app/api/scraper/route.js; Blake, 2026-10-03). Both go through markSent here,
|
|
15
|
+
// so a request sent by Auto is the same request everywhere as one sent by hand.
|
|
16
|
+
|
|
17
|
+
import { db as supabase } from './db.js';
|
|
18
|
+
import { resolveProfile } from './profile.js';
|
|
19
|
+
|
|
20
|
+
// XP rewards by tier
|
|
21
|
+
export const XP_SEND = { S: 25, A: 15, B: 10, C: 5, D: 2 };
|
|
22
|
+
export const XP_ACCEPT = { S: 100, A: 60, B: 35, C: 15, D: 5 };
|
|
23
|
+
const TIER_ORDER = ['S', 'A', 'B', 'C', 'D'];
|
|
24
|
+
|
|
25
|
+
export async function awardXP(userId, amount) {
|
|
26
|
+
if (!userId || !amount) return;
|
|
27
|
+
// Upsert: create if not exists, change if exists. Never below zero: an Undo
|
|
28
|
+
// takes back what a send gave, including sends from before sends gave any.
|
|
29
|
+
const { data: existing } = await supabase.from('user_stats').select('xp').eq('id', userId).single();
|
|
30
|
+
if (existing) {
|
|
31
|
+
await supabase.from('user_stats').update({ xp: Math.max(0, (existing.xp || 0) + amount) }).eq('id', userId);
|
|
32
|
+
} else if (amount > 0) {
|
|
33
|
+
await supabase.from('user_stats').insert([{ id: userId, xp: amount, level: 1 }]).catch(() => {});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** The profile a request belongs to: the one sent, else this machine's. */
|
|
38
|
+
export function whose(userId) {
|
|
39
|
+
if (userId) return String(userId);
|
|
40
|
+
try { return resolveProfile({ create: false })?.id || null; } catch { return null; }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Every copy of one person: by the clicked row's profile URL, or the URL given. */
|
|
44
|
+
export function copiesOf(db, { connectionId, profileUrl, userId }) {
|
|
45
|
+
let url = profileUrl ? String(profileUrl) : null;
|
|
46
|
+
let user = userId ? String(userId) : null;
|
|
47
|
+
let clicked = null;
|
|
48
|
+
if (connectionId != null) {
|
|
49
|
+
clicked = db.prepare('SELECT id, profile_url, user_id FROM linkedin_connections WHERE id = ?').get(String(connectionId)) || null;
|
|
50
|
+
if (clicked) {
|
|
51
|
+
url = clicked.profile_url || url;
|
|
52
|
+
user = user || clicked.user_id || null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const cols = 'id, degree, tier, outreach_status, unlock_status, unlocked_from_bridge_id';
|
|
56
|
+
let rows = [];
|
|
57
|
+
if (url) {
|
|
58
|
+
rows = user
|
|
59
|
+
? db.prepare(`SELECT ${cols} FROM linkedin_connections WHERE profile_url = ? AND user_id = ?`).all(url, user)
|
|
60
|
+
: db.prepare(`SELECT ${cols} FROM linkedin_connections WHERE profile_url = ?`).all(url);
|
|
61
|
+
} else if (clicked) {
|
|
62
|
+
rows = db.prepare(`SELECT ${cols} FROM linkedin_connections WHERE id = ?`).all(clicked.id);
|
|
63
|
+
}
|
|
64
|
+
return { url, user, rows };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const inList = (ids) => ids.map(() => '?').join(', ');
|
|
68
|
+
export const bestTier = (rows) => TIER_ORDER.find((t) => rows.some((r) => r.tier === t)) || 'D';
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Mark a request to this person as sent, on every copy of them, through
|
|
72
|
+
* `bridgeId` (one of your own connections) when given: the rows only, at once.
|
|
73
|
+
* Returns { saved, xp, already, user }: `saved` is how many rows (0 when nobody
|
|
74
|
+
* is on file, as for the sample network, which lives in the browser), `xp` what
|
|
75
|
+
* the send is worth (once per person), for markSent or the caller to award.
|
|
76
|
+
*/
|
|
77
|
+
export function markSentRows(db, { connectionId, profileUrl, userId, bridgeId } = {}) {
|
|
78
|
+
const { user, rows } = copiesOf(db, { connectionId, profileUrl, userId });
|
|
79
|
+
if (!rows.length) return { saved: 0, xp: 0, already: false, user };
|
|
80
|
+
const ids = rows.map((r) => r.id);
|
|
81
|
+
const now = new Date().toISOString();
|
|
82
|
+
const already = rows.some((r) => r.outreach_status === 'sent' || r.outreach_status === 'accepted');
|
|
83
|
+
db.prepare(`UPDATE linkedin_connections SET outreach_status = 'sent', updated_at = ?
|
|
84
|
+
WHERE id IN (${inList(ids)}) AND (outreach_status IS NULL OR outreach_status NOT IN ('sent', 'accepted'))`).run(now, ...ids);
|
|
85
|
+
db.prepare(`UPDATE linkedin_connections SET unlock_status = 'pending', updated_at = ?
|
|
86
|
+
WHERE id IN (${inList(ids)}) AND (unlock_status IS NULL OR unlock_status = 'locked')`).run(now, ...ids);
|
|
87
|
+
// Who you asked through: one of your own connections, or nothing.
|
|
88
|
+
if (bridgeId != null) {
|
|
89
|
+
const bridge = db.prepare(
|
|
90
|
+
`SELECT id, name FROM linkedin_connections WHERE id = ? AND degree = 1${user ? ' AND user_id = ?' : ''}`,
|
|
91
|
+
).get(...[String(bridgeId), ...(user ? [user] : [])]);
|
|
92
|
+
if (bridge) {
|
|
93
|
+
db.prepare(`UPDATE linkedin_connections SET unlocked_from_bridge_id = ?, unlocked_from_name = ?, updated_at = ?
|
|
94
|
+
WHERE id IN (${inList(ids)}) AND degree = 2 AND unlocked_from_bridge_id IS NULL`).run(bridge.id, bridge.name, now, ...ids);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const xp = already ? 0 : XP_SEND[bestTier(rows)] || 2;
|
|
98
|
+
return { saved: ids.length, xp, already, user };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** markSentRows, and its XP awarded: { saved, xp, already }. */
|
|
102
|
+
export async function markSent(db, request = {}) {
|
|
103
|
+
const { saved, xp, already, user } = markSentRows(db, request);
|
|
104
|
+
if (saved) await awardXP(whose(user), xp);
|
|
105
|
+
return { saved, xp, already };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Where this person stands for a new request, over every copy of them:
|
|
110
|
+
* { connected, requested, accepted }. `connected`: a copy of them is one of
|
|
111
|
+
* your own connections; `requested`: a request is out (sent or pending);
|
|
112
|
+
* `accepted`: one was accepted (or their path is unlocked).
|
|
113
|
+
*/
|
|
114
|
+
export function requestStanding(db, { profileUrl, userId }) {
|
|
115
|
+
if (!profileUrl) return { connected: false, requested: false, accepted: false };
|
|
116
|
+
const rows = userId
|
|
117
|
+
? db.prepare('SELECT degree, outreach_status, unlock_status FROM linkedin_connections WHERE profile_url = ? AND user_id = ?').all(String(profileUrl), String(userId))
|
|
118
|
+
: db.prepare('SELECT degree, outreach_status, unlock_status FROM linkedin_connections WHERE profile_url = ?').all(String(profileUrl));
|
|
119
|
+
return {
|
|
120
|
+
connected: rows.some((r) => r.degree === 1),
|
|
121
|
+
requested: rows.some((r) => r.outreach_status === 'sent' || r.unlock_status === 'pending'),
|
|
122
|
+
accepted: rows.some((r) => r.outreach_status === 'accepted' || r.unlock_status === 'unlocked'),
|
|
123
|
+
};
|
|
124
|
+
}
|
package/lib/scan-state.js
CHANGED
|
@@ -41,6 +41,7 @@ export function runningNow(job) {
|
|
|
41
41
|
case 'setup': return 'The scanner is being set up';
|
|
42
42
|
case 'login': return 'The LinkedIn sign-in window is open';
|
|
43
43
|
case 'photos': return 'Profile photos are being saved';
|
|
44
|
+
case 'connect': return 'A connection request is being sent';
|
|
44
45
|
default: return 'A scan is running';
|
|
45
46
|
}
|
|
46
47
|
}
|
package/lib/scraper-client.js
CHANGED
|
@@ -39,7 +39,13 @@ export async function startScrape(action, name, extra = {}) {
|
|
|
39
39
|
body: JSON.stringify(scanRequest(action, { ...(name ? { name } : {}), ...extra })),
|
|
40
40
|
});
|
|
41
41
|
const d = await r.json().catch(() => ({}));
|
|
42
|
-
if (!r.ok)
|
|
42
|
+
if (!r.ok) {
|
|
43
|
+
// The answer's flags travel with the error (needsAutoAcceptance, say), so a
|
|
44
|
+
// button can tell a question to ask from a reason to show.
|
|
45
|
+
const err = new Error(d.error || 'Could not start the scanner.');
|
|
46
|
+
err.details = d;
|
|
47
|
+
throw err;
|
|
48
|
+
}
|
|
43
49
|
return d;
|
|
44
50
|
}
|
|
45
51
|
|
|
@@ -55,12 +61,13 @@ export async function resumePoint(id) {
|
|
|
55
61
|
}
|
|
56
62
|
|
|
57
63
|
/** Nothing noted: before the first answer, and for the sample or a CSV. */
|
|
58
|
-
export const NO_SCAN_NOTES = Object.freeze({ skips: [], read: [] });
|
|
64
|
+
export const NO_SCAN_NOTES = Object.freeze({ skips: [], read: [], lists: {} });
|
|
59
65
|
|
|
60
66
|
/**
|
|
61
67
|
* What the scanner noted, for lib/reach.js: `skips`, the hidden lists
|
|
62
|
-
* ([{ profileUrl, … }]),
|
|
63
|
-
* read
|
|
68
|
+
* ([{ profileUrl, … }]), `read`, the profile URLs of every list it has
|
|
69
|
+
* read, and `lists`, how far each was read ({ url: { pages, more, total } }).
|
|
70
|
+
* Never throws: with no answer (the app restarting, say) nothing is
|
|
64
71
|
* noted, so a hidden list shows as ready rather than anyone going missing.
|
|
65
72
|
*/
|
|
66
73
|
export async function loadScanNotes() {
|
|
@@ -68,7 +75,11 @@ export async function loadScanNotes() {
|
|
|
68
75
|
const r = await fetch('/api/scraper?reach=1');
|
|
69
76
|
if (!r.ok) return NO_SCAN_NOTES;
|
|
70
77
|
const d = await r.json();
|
|
71
|
-
|
|
78
|
+
// `lists` is how far each list was read, for the rings round a dot
|
|
79
|
+
// (lib/reach.js scanBars). Dropped here until 10/3, every scanned
|
|
80
|
+
// connection drew 2 of 5 bars, "partly read", even with the whole list in.
|
|
81
|
+
const lists = d.lists && typeof d.lists === 'object' && !Array.isArray(d.lists) ? d.lists : {};
|
|
82
|
+
return { skips: Array.isArray(d.skips) ? d.skips : [], read: Array.isArray(d.read) ? d.read : [], lists };
|
|
72
83
|
} catch {
|
|
73
84
|
return NO_SCAN_NOTES;
|
|
74
85
|
}
|
|
@@ -314,6 +325,8 @@ export function busyReason(job = now) {
|
|
|
314
325
|
case 'full':
|
|
315
326
|
case 'refresh':
|
|
316
327
|
return 'Your own connections are being scanned';
|
|
328
|
+
case 'connect':
|
|
329
|
+
return who ? `A request to ${who} is being sent` : 'A connection request is being sent';
|
|
317
330
|
default:
|
|
318
331
|
return runningNow(job.action);
|
|
319
332
|
}
|
package/lib/settings.js
CHANGED
|
@@ -14,6 +14,7 @@ import { SECTOR_FOCUS_SETTING, FIELD_ASKED_SETTING } from './sector-focus.js';
|
|
|
14
14
|
import { TIER_SCALE_SETTING } from './scoring.js';
|
|
15
15
|
import { TITLE_RANKING_SETTING } from './title-ranking.js';
|
|
16
16
|
import { SCAN_RISK_SETTING } from './scan-risk.js';
|
|
17
|
+
import { AUTO_ACCEPTED_SETTING } from './auto-connect.js';
|
|
17
18
|
import { THEME_SETTING } from './themes.js';
|
|
18
19
|
import { GALAXY_LAYOUTS_SETTING } from './galaxy-layouts.js';
|
|
19
20
|
|
|
@@ -28,6 +29,8 @@ export const SETTINGS = {
|
|
|
28
29
|
titleRanking: TITLE_RANKING_SETTING,
|
|
29
30
|
// The Scan page's "I understand" before the first scan: when it was given (lib/scan-risk.js).
|
|
30
31
|
scanRiskAccepted: SCAN_RISK_SETTING,
|
|
32
|
+
// Auto's one-time question before its first connection request: when it was answered yes (lib/auto-connect.js).
|
|
33
|
+
autoConnectAccepted: AUTO_ACCEPTED_SETTING,
|
|
31
34
|
// Settings → Appearance: the look (lib/themes.js). Here so it travels with a
|
|
32
35
|
// backup or a restore; the browser keeps a copy to paint with first (lib/theme-store.js).
|
|
33
36
|
theme: THEME_SETTING,
|
package/lib/usage.js
CHANGED
|
@@ -193,3 +193,8 @@ export function hoursText([from, to] = AUTO.hours) {
|
|
|
193
193
|
const h = (n) => `${n % 12 || 12} ${n < 12 ? 'AM' : 'PM'}`;
|
|
194
194
|
return `${h(from)} to ${h(to)}`;
|
|
195
195
|
}
|
|
196
|
+
|
|
197
|
+
// Auto's connection requests have caps of their own, in the same record
|
|
198
|
+
// (lib/auto-connect.js, scripts/scrape.py INVITE_DAY_CAP and INVITE_WEEK_CAP):
|
|
199
|
+
// the usage page shows them beside the searches and profile views.
|
|
200
|
+
export { INVITE_CAPS, REPORTED_WEEKLY_INVITES } from './auto-connect.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "six-degrees",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Map your professional network as a galaxy — leverage tiers, bridges into new circles, and the shortest path to anyone. Runs locally on your own data.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Blake Burford",
|