six-degrees 0.2.1 → 0.3.0-beta.1
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 +10 -0
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +39 -11
- package/.next/standalone/.next/required-server-files.json +4 -1
- package/.next/standalone/.next/routes-manifest.json +60 -0
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- 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 +10 -10
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +10 -10
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +9 -9
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/api/admin-delete/route.js +3 -2
- 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 +3 -2
- 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 +3 -2
- 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 +3 -2
- 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/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/company-scores/legacy/route/build-manifest.json +12 -0
- package/.next/standalone/.next/server/app/api/company-scores/legacy/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/company-scores/legacy/route.js +9 -0
- package/.next/standalone/.next/server/app/api/company-scores/legacy/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/company-scores/legacy/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/company-scores/legacy/route_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/api/company-scores/route.js +3 -2
- 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 +3 -2
- package/.next/standalone/.next/server/app/api/connections/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/export/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/data/export/route/build-manifest.json +12 -0
- package/.next/standalone/.next/server/app/api/data/export/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/data/export/route.js +7 -0
- package/.next/standalone/.next/server/app/api/data/export/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/data/export/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/data/export/route_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/api/data/import/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/data/import/route/build-manifest.json +12 -0
- package/.next/standalone/.next/server/app/api/data/import/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/data/import/route.js +8 -0
- package/.next/standalone/.next/server/app/api/data/import/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/data/import/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/data/import/route_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/api/data/restart/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/data/restart/route/build-manifest.json +12 -0
- package/.next/standalone/.next/server/app/api/data/restart/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/data/restart/route.js +8 -0
- package/.next/standalone/.next/server/app/api/data/restart/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/data/restart/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/data/restart/route_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/api/data/reveal/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/data/reveal/route/build-manifest.json +12 -0
- package/.next/standalone/.next/server/app/api/data/reveal/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/data/reveal/route.js +7 -0
- package/.next/standalone/.next/server/app/api/data/reveal/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/data/reveal/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/data/reveal/route_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/api/data/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/data/route/build-manifest.json +12 -0
- package/.next/standalone/.next/server/app/api/data/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/data/route.js +7 -0
- package/.next/standalone/.next/server/app/api/data/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/data/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/data/route_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/api/delete-cluster/route.js +3 -2
- 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 +2 -2
- package/.next/standalone/.next/server/app/api/ingest/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/route.js +3 -2
- package/.next/standalone/.next/server/app/api/network/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/notifications/route.js +3 -2
- 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 -2
- package/.next/standalone/.next/server/app/api/outreach/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/queue/route.js +3 -2
- package/.next/standalone/.next/server/app/api/queue/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/scraper/route.js +3 -2
- package/.next/standalone/.next/server/app/api/scraper/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/settings/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/settings/route/build-manifest.json +12 -0
- package/.next/standalone/.next/server/app/api/settings/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/settings/route.js +9 -0
- package/.next/standalone/.next/server/app/api/settings/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/settings/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/settings/route_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/api/settings/sector-preview/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/settings/sector-preview/route/build-manifest.json +12 -0
- package/.next/standalone/.next/server/app/api/settings/sector-preview/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/settings/sector-preview/route.js +8 -0
- package/.next/standalone/.next/server/app/api/settings/sector-preview/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/settings/sector-preview/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/settings/sector-preview/route_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route/build-manifest.json +12 -0
- package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route.js +8 -0
- package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/api/setup-profile/route.js +3 -2
- package/.next/standalone/.next/server/app/api/setup-profile/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/unlock/route.js +3 -2
- package/.next/standalone/.next/server/app/api/unlock/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/update/route.js +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 +3 -2
- 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 +3 -2
- package/.next/standalone/.next/server/app/api/users/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/avatars/[file]/route.js +2 -1
- package/.next/standalone/.next/server/app/avatars/[file]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/favicon.ico/route.js +2 -1
- package/.next/standalone/.next/server/app/favicon.ico/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 +12 -12
- package/.next/standalone/.next/server/app/import.segments/_full.segment.rsc +12 -12
- package/.next/standalone/.next/server/app/import.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/import.segments/import/__PAGE__.segment.rsc +11 -11
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +12 -12
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +11 -11
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +12 -12
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- 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 +12 -12
- package/.next/standalone/.next/server/app/launch.segments/_full.segment.rsc +12 -12
- package/.next/standalone/.next/server/app/launch.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/launch.segments/launch/__PAGE__.segment.rsc +11 -11
- 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 +12 -12
- package/.next/standalone/.next/server/app/paths.segments/_full.segment.rsc +12 -12
- package/.next/standalone/.next/server/app/paths.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/paths.segments/paths/__PAGE__.segment.rsc +11 -11
- 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 +12 -12
- package/.next/standalone/.next/server/app/profile.segments/_full.segment.rsc +12 -12
- package/.next/standalone/.next/server/app/profile.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/profile.segments/profile/__PAGE__.segment.rsc +11 -11
- 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 +12 -12
- package/.next/standalone/.next/server/app/queue.segments/_full.segment.rsc +12 -12
- package/.next/standalone/.next/server/app/queue.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/queue.segments/queue/__PAGE__.segment.rsc +11 -11
- package/.next/standalone/.next/server/app/settings/page/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/settings/page/build-manifest.json +20 -0
- package/.next/standalone/.next/server/app/settings/page/next-font-manifest.json +11 -0
- package/.next/standalone/.next/server/app/settings/page/react-loadable-manifest.json +1 -0
- package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/settings/page.js +13 -0
- package/.next/standalone/.next/server/app/settings/page.js.map +5 -0
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/settings.html +1 -0
- package/.next/standalone/.next/server/app/settings.meta +26 -0
- package/.next/standalone/.next/server/app/settings.rsc +24 -0
- package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +24 -0
- package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +4 -0
- package/.next/standalone/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +28 -0
- 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 +12 -12
- package/.next/standalone/.next/server/app/setup.segments/_full.segment.rsc +12 -12
- package/.next/standalone/.next/server/app/setup.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/setup.segments/setup/__PAGE__.segment.rsc +11 -11
- package/.next/standalone/.next/server/app-paths-manifest.json +10 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-rnla2._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__00la_y0._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__07t47cm._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0cj5jqc._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0e0fei3._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0i5hdu4._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ljcioj._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ljnyho._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o5-tcg._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0qrnous._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0t3v1a1._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0uag-k5._.js +8 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0um1uey._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0wmexuc._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0xu34sz._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__133knx1._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__14ou7j5._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__15-17hl._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__16iu0hv._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__19b7mig._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1bbcwub._.js +8 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1eris29._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1gjo_cq._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1kbl21f._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1qcm08_._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1rphsdk._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1t3rugi._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1w25q0m._.js +3 -0
- package/.next/standalone/.next/server/chunks/_072jl0y._.js +3 -0
- package/.next/standalone/.next/server/chunks/_0lw8_i0._.js +3 -0
- package/.next/standalone/.next/server/chunks/_1_fynia._.js +21 -3
- package/.next/standalone/.next/server/chunks/_1fn30_l._.js +3 -0
- package/.next/standalone/.next/server/chunks/_1itd1o_._.js +2 -1
- package/.next/standalone/.next/server/chunks/_1iwkh8t._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1mxn_lb._.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_company-scores_legacy_route_actions_1ncspu_.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_data_export_route_actions_1lkimty.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_data_import_route_actions_1eji0kd.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_data_restart_route_actions_00_-u6o.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_data_reveal_route_actions_0-moqtn.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_data_route_actions_0j0w5ep.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_settings_route_actions_1bpm9ac.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_settings_sector-preview_route_actions_0w79454.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_settings_sector-suggestions_route_actions_1eoqugp.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_db-client_15le7cw.js +155 -0
- package/.next/standalone/.next/server/chunks/{lib_rpc_0mc73lk.js → lib_rpc_00tx2sm.js} +1 -1
- package/.next/standalone/.next/server/chunks/lib_rpc_02-qa4d.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_02ndsg2.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_08q5vp6.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0_8i3ft.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0c9d9ti.js +5 -2
- package/.next/standalone/.next/server/chunks/lib_rpc_0huvw-3.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0ip2vdh.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0jzisgh.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0yi9xvd.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1jyhd0o.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1kn59-i.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1q2jkza.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1vpm1sk.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1x6p_31.js +3 -0
- package/.next/standalone/.next/server/chunks/node_modules_next_1zc5q0a._.js +158 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0mocm6d._.js → [root-of-the-server]__0an6blh._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1m0255u._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0bipr5s._.js → [root-of-the-server]__1z1j3ek._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_07u8byv._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_08r_mlf._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{_0ggabjc._.js → _0942v8n._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_1djpk8t._.js → _0hl_t7r._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_0tb83dw._.js +132 -0
- package/.next/standalone/.next/server/chunks/ssr/_1-87bs1._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_17gui6a._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_settings_page_actions_11x4dq5.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_launch_page_19t0dyd.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_page_1fazqbf.js +4 -4
- package/.next/standalone/.next/server/chunks/ssr/app_profile_page_0u3-l5y.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_queue_page_0c6znd6.js +5 -5
- package/.next/standalone/.next/server/chunks/ssr/app_settings_page_1f8mjj_.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_setup_page_1-282zx.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/lib_0i8v-s3._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules_1f8t_rp._.js +23 -0
- package/.next/standalone/.next/server/chunks/ssr/node_modules_1nkgb45._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{node_modules_01xbj_9._.js → node_modules_1wax83z._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{node_modules_next_dist_00nap8h._.js → node_modules_next_dist_1bunoni._.js} +2 -2
- package/.next/standalone/.next/server/edge/chunks/[root-of-the-server]__0pco7a6._.js +2 -2
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/middleware-manifest.json +15 -7
- 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/06x3jiiz96m5t.js +1 -0
- package/.next/standalone/.next/static/chunks/{34at5d2ifl62i.js → 0ngx9s6-zgshe.js} +5 -5
- package/.next/standalone/.next/static/chunks/0q-_khndfxubv.js +2 -0
- package/.next/standalone/.next/static/chunks/1c79veqedvubt.js +1 -0
- package/.next/standalone/.next/static/chunks/1chp3nx45meg5.js +44 -0
- package/.next/standalone/.next/static/chunks/1irqj0lf-fu3d.js +1 -0
- package/.next/standalone/.next/static/chunks/1uadqylrod5_v.js +1 -0
- package/.next/standalone/.next/static/chunks/23u0zclgwpc4d.js +1 -0
- package/.next/standalone/.next/static/chunks/289ktj97di6zg.js +1 -0
- package/.next/standalone/.next/static/chunks/2b3lmf8e6uj8m.js +16 -0
- package/.next/standalone/.next/static/chunks/2jz311xl1pp_8.js +1 -0
- package/.next/standalone/.next/static/chunks/3npq5yd7c4dmn.js +2 -0
- package/.next/standalone/.next/static/frfHFnRYTA0Wjrw8oZJtY/_clientMiddlewareManifest.js +14 -0
- package/.next/standalone/CHANGELOG.md +284 -0
- package/.next/standalone/CONTRIBUTING.md +5 -2
- package/.next/standalone/README.md +138 -30
- package/.next/standalone/SECURITY.md +117 -10
- package/.next/standalone/app/api/company-scores/legacy/route.js +32 -0
- package/.next/standalone/app/api/company-scores/route.js +20 -11
- package/.next/standalone/app/api/data/export/route.js +49 -0
- package/.next/standalone/app/api/data/import/route.js +83 -0
- package/.next/standalone/app/api/data/restart/route.js +37 -0
- package/.next/standalone/app/api/data/reveal/route.js +14 -0
- package/.next/standalone/app/api/data/route.js +39 -0
- package/.next/standalone/app/api/ingest/route.js +15 -47
- package/.next/standalone/app/api/scraper/route.js +264 -125
- package/.next/standalone/app/api/settings/route.js +58 -0
- package/.next/standalone/app/api/settings/sector-preview/route.js +38 -0
- package/.next/standalone/app/api/settings/sector-suggestions/route.js +20 -0
- package/.next/standalone/app/api/update/route.js +188 -23
- package/.next/standalone/app/avatars/[file]/route.js +4 -2
- package/.next/standalone/app/components/CompanyScores.js +28 -7
- package/.next/standalone/app/components/EmptyState.js +8 -0
- package/.next/standalone/app/components/ForceGraph.js +39 -35
- package/.next/standalone/app/components/LegacyScoresCard.js +129 -0
- package/.next/standalone/app/components/Sidebar.js +11 -10
- package/.next/standalone/app/components/UpdatePanel.js +306 -119
- package/.next/standalone/app/components/UserProvider.js +4 -3
- package/.next/standalone/app/components/settings/DataSection.js +437 -0
- package/.next/standalone/app/components/settings/SectorSection.js +445 -0
- package/.next/standalone/app/components/ui.js +62 -0
- package/.next/standalone/app/launch/page.js +30 -20
- package/.next/standalone/app/page.js +13 -0
- package/.next/standalone/app/paths/page.js +12 -4
- package/.next/standalone/app/profile/page.js +41 -8
- package/.next/standalone/app/queue/page.js +8 -2
- package/.next/standalone/app/settings/page.js +96 -0
- package/.next/standalone/app/setup/page.js +32 -16
- package/.next/standalone/bin/args.mjs +44 -0
- package/.next/standalone/bin/six-degrees.mjs +9 -15
- package/.next/standalone/lib/companies.js +44 -119
- package/.next/standalone/lib/csv.js +8 -0
- package/.next/standalone/lib/data-export.js +170 -0
- package/.next/standalone/lib/data-folder.js +238 -0
- package/.next/standalone/lib/data-import.js +878 -0
- package/.next/standalone/lib/db-client.js +48 -3
- package/.next/standalone/lib/durable.js +73 -0
- package/.next/standalone/lib/gate.js +90 -1
- package/.next/standalone/lib/ingest.js +40 -0
- package/.next/standalone/lib/legacy-offer.js +185 -0
- package/.next/standalone/lib/legacy-scores.js +77 -0
- package/.next/standalone/lib/linkedin-limits.js +124 -1
- package/.next/standalone/lib/rpc.js +121 -13
- package/.next/standalone/lib/sample-stats.js +16 -0
- package/.next/standalone/lib/scan-progress.js +13 -0
- package/.next/standalone/lib/scan-state.js +53 -0
- package/.next/standalone/lib/scanner-python.js +685 -0
- package/.next/standalone/lib/scanner-setup.js +104 -0
- package/.next/standalone/lib/scoring.js +747 -138
- package/.next/standalone/lib/scraper-client.js +9 -5
- package/.next/standalone/lib/sector-directory.js +1532 -0
- package/.next/standalone/lib/sector-focus.js +163 -0
- package/.next/standalone/lib/sector-labels.js +81 -0
- package/.next/standalone/lib/settings-effects.js +59 -0
- package/.next/standalone/lib/settings.js +83 -0
- package/.next/standalone/lib/updater-job.js +838 -0
- package/.next/standalone/lib/updater.js +494 -0
- package/.next/standalone/middleware.js +25 -28
- package/.next/standalone/next.config.mjs +19 -3
- package/.next/standalone/package-lock.json +2 -2
- package/.next/standalone/package.json +1 -2
- package/.next/standalone/scripts/apply-update.sh +482 -0
- package/.next/standalone/scripts/build-app.mjs +251 -9
- package/.next/standalone/scripts/requirements.txt +106 -4
- package/.next/standalone/scripts/test-release-server.mjs +143 -0
- package/.next/standalone/server.js +1 -1
- package/.next/static/chunks/06x3jiiz96m5t.js +1 -0
- package/.next/static/chunks/{34at5d2ifl62i.js → 0ngx9s6-zgshe.js} +5 -5
- package/.next/static/chunks/0q-_khndfxubv.js +2 -0
- package/.next/static/chunks/1c79veqedvubt.js +1 -0
- package/.next/static/chunks/1chp3nx45meg5.js +44 -0
- package/.next/static/chunks/1irqj0lf-fu3d.js +1 -0
- package/.next/static/chunks/1uadqylrod5_v.js +1 -0
- package/.next/static/chunks/23u0zclgwpc4d.js +1 -0
- package/.next/static/chunks/289ktj97di6zg.js +1 -0
- package/.next/static/chunks/2b3lmf8e6uj8m.js +16 -0
- package/.next/static/chunks/2jz311xl1pp_8.js +1 -0
- package/.next/static/chunks/3npq5yd7c4dmn.js +2 -0
- package/.next/static/frfHFnRYTA0Wjrw8oZJtY/_clientMiddlewareManifest.js +14 -0
- package/CHANGELOG.md +284 -0
- package/README.md +138 -30
- package/SECURITY.md +117 -10
- package/bin/args.mjs +44 -0
- package/bin/six-degrees.mjs +9 -15
- package/lib/companies.js +44 -119
- package/lib/csv.js +8 -0
- package/lib/data-export.js +170 -0
- package/lib/data-folder.js +238 -0
- package/lib/data-import.js +878 -0
- package/lib/db-client.js +48 -3
- package/lib/durable.js +73 -0
- package/lib/gate.js +90 -1
- package/lib/ingest.js +40 -0
- package/lib/legacy-offer.js +185 -0
- package/lib/legacy-scores.js +77 -0
- package/lib/linkedin-limits.js +124 -1
- package/lib/rpc.js +121 -13
- package/lib/sample-stats.js +16 -0
- package/lib/scan-progress.js +13 -0
- package/lib/scan-state.js +53 -0
- package/lib/scanner-python.js +685 -0
- package/lib/scanner-setup.js +104 -0
- package/lib/scoring.js +747 -138
- package/lib/scraper-client.js +9 -5
- package/lib/sector-directory.js +1532 -0
- package/lib/sector-focus.js +163 -0
- package/lib/sector-labels.js +81 -0
- package/lib/settings-effects.js +59 -0
- package/lib/settings.js +83 -0
- package/lib/updater-job.js +838 -0
- package/lib/updater.js +494 -0
- package/package.json +1 -2
- package/scripts/requirements.txt +106 -4
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0jrmmj-._.js +0 -150
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0qx548g._.js +0 -151
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1an3leo._.js +0 -150
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1m8zedk._.js +0 -155
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1zor0ys._.js +0 -3
- package/.next/standalone/.next/server/chunks/_0_rp-01._.js +0 -3
- package/.next/standalone/.next/server/chunks/_0hzdk79._.js +0 -3
- package/.next/standalone/.next/server/chunks/_0ow7u5r._.js +0 -3
- package/.next/standalone/.next/server/chunks/_0t0zc2d._.js +0 -3
- package/.next/standalone/.next/server/chunks/_0wa631d._.js +0 -3
- package/.next/standalone/.next/server/chunks/_13ccnao._.js +0 -3
- package/.next/standalone/.next/server/chunks/_13es063._.js +0 -3
- package/.next/standalone/.next/server/chunks/_142872j._.js +0 -3
- package/.next/standalone/.next/server/chunks/_18wsb_a._.js +0 -8
- package/.next/standalone/.next/server/chunks/_1e-4h5h._.js +0 -3
- package/.next/standalone/.next/server/chunks/_1gvvd7n._.js +0 -158
- package/.next/standalone/.next/server/chunks/_1moda66._.js +0 -3
- package/.next/standalone/.next/server/chunks/_1p6ghzv._.js +0 -3
- package/.next/standalone/.next/server/chunks/_1xmmi_v._.js +0 -3
- package/.next/standalone/.next/server/chunks/_1z-yrlf._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0bf3x4q._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/app_paths_page_1ol90m7.js +0 -4
- package/.next/standalone/.next/static/ZGTiDrZ5NHGETgGdlhssu/_clientMiddlewareManifest.js +0 -6
- package/.next/standalone/.next/static/chunks/0jk64vo17avsb.js +0 -1
- package/.next/standalone/.next/static/chunks/0s65dsrthylo6.js +0 -2
- package/.next/standalone/.next/static/chunks/0uyfdhi8dek85.js +0 -16
- package/.next/standalone/.next/static/chunks/220-ne82aus0c.js +0 -44
- package/.next/standalone/.next/static/chunks/229haorf0f19b.js +0 -2
- package/.next/standalone/.next/static/chunks/2g54uafnwuakc.js +0 -1
- package/.next/standalone/.next/static/chunks/2spwqs6b6dx13.js +0 -1
- package/.next/standalone/.next/static/chunks/38dlepuubiox0.js +0 -1
- package/.next/standalone/scripts/score-connections.sql +0 -80
- package/.next/standalone/scripts/score_new_connections.sql +0 -77
- package/.next/standalone/site/app.js +0 -84
- package/.next/standalone/site/index.html +0 -221
- package/.next/standalone/site/style.css +0 -155
- package/.next/standalone/site/video/install-mac-poster.jpg +0 -0
- package/.next/standalone/site/video/install-mac.mp4 +0 -0
- package/.next/standalone/site/video/linux-poster.jpg +0 -0
- package/.next/standalone/site/video/linux.mp4 +0 -0
- package/.next/standalone/site/video/scan-poster.jpg +0 -0
- package/.next/standalone/site/video/scan.mp4 +0 -0
- package/.next/static/ZGTiDrZ5NHGETgGdlhssu/_clientMiddlewareManifest.js +0 -6
- package/.next/static/chunks/0jk64vo17avsb.js +0 -1
- package/.next/static/chunks/0s65dsrthylo6.js +0 -2
- package/.next/static/chunks/0uyfdhi8dek85.js +0 -16
- package/.next/static/chunks/220-ne82aus0c.js +0 -44
- package/.next/static/chunks/229haorf0f19b.js +0 -2
- package/.next/static/chunks/2g54uafnwuakc.js +0 -1
- package/.next/static/chunks/2spwqs6b6dx13.js +0 -1
- package/.next/static/chunks/38dlepuubiox0.js +0 -1
- package/scripts/score_new_connections.sql +0 -77
- /package/.next/standalone/.next/static/{ZGTiDrZ5NHGETgGdlhssu → frfHFnRYTA0Wjrw8oZJtY}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{ZGTiDrZ5NHGETgGdlhssu → frfHFnRYTA0Wjrw8oZJtY}/_ssgManifest.js +0 -0
- /package/.next/static/{ZGTiDrZ5NHGETgGdlhssu → frfHFnRYTA0Wjrw8oZJtY}/_buildManifest.js +0 -0
- /package/.next/static/{ZGTiDrZ5NHGETgGdlhssu → frfHFnRYTA0Wjrw8oZJtY}/_ssgManifest.js +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { existsSync, readFileSync, mkdirSync, mkdtempSync, rmSync } from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { projectRoot, dataDir } from '../../../lib/paths';
|
|
5
5
|
import { resolveProfile, networkCounts } from '../../../lib/profile';
|
|
@@ -7,6 +7,12 @@ import { scanProgress } from '../../../lib/scan-progress';
|
|
|
7
7
|
import { linkedinState, writeLimits, liftCooldown } from '../../../lib/linkedin-limits';
|
|
8
8
|
import { pausedList, readProgress, readUnclear } from '../../../lib/paused';
|
|
9
9
|
import { getDb } from '../../../lib/db-client';
|
|
10
|
+
import { registerScanState } from '../../../lib/scan-state';
|
|
11
|
+
import { pendingImport } from '../../../lib/data-import';
|
|
12
|
+
import {
|
|
13
|
+
pythonLooker, thisHostKey, scannerCommand, installSteps, downloadedPython, downloadVerified,
|
|
14
|
+
placeDownloadedPython, sweepSetupLeftovers, megabytes, SETUP_WORK_PREFIX, ScannerSetupError,
|
|
15
|
+
} from '../../../lib/scanner-python';
|
|
10
16
|
|
|
11
17
|
// The app runs the scraper itself.
|
|
12
18
|
//
|
|
@@ -22,17 +28,19 @@ import { getDb } from '../../../lib/db-client';
|
|
|
22
28
|
|
|
23
29
|
const MAX_LOG = 500;
|
|
24
30
|
|
|
25
|
-
|
|
31
|
+
// Registered in lib/scan-state.js so other routes can see whether a job runs.
|
|
32
|
+
const state = registerScanState({
|
|
26
33
|
running: false,
|
|
27
34
|
action: null,
|
|
28
35
|
log: [],
|
|
29
36
|
startedAt: null,
|
|
30
37
|
exitCode: null,
|
|
31
38
|
child: null,
|
|
39
|
+
abort: null, // a step that runs inside this server (the Python download): its AbortController
|
|
32
40
|
stopping: false,
|
|
33
41
|
stderrTail: [],
|
|
34
42
|
failure: null, // the last lines of stderr from a run that failed — its reason
|
|
35
|
-
};
|
|
43
|
+
});
|
|
36
44
|
|
|
37
45
|
/** Stop the running job without orphaning the browser it opened.
|
|
38
46
|
*
|
|
@@ -40,9 +48,16 @@ const state = {
|
|
|
40
48
|
* signals Chrome too — killing only the Python process would leave a browser
|
|
41
49
|
* window open with a live LinkedIn session in it. SIGTERM first, because the
|
|
42
50
|
* scraper catches it and closes the browser itself; SIGKILL only if that is
|
|
43
|
-
* ignored.
|
|
51
|
+
* ignored. A step running inside this server (the Python download) stops at
|
|
52
|
+
* once, and nothing it fetched is kept.
|
|
44
53
|
*/
|
|
45
54
|
function stopChild() {
|
|
55
|
+
if (state.abort) {
|
|
56
|
+
state.stopping = true;
|
|
57
|
+
push('Stopping…');
|
|
58
|
+
state.abort.abort(new ScannerSetupError('Stopped.'));
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
46
61
|
const child = state.child;
|
|
47
62
|
if (!child) return;
|
|
48
63
|
state.stopping = true;
|
|
@@ -69,68 +84,25 @@ function push(line) {
|
|
|
69
84
|
if (state.log.length > MAX_LOG) state.log.splice(0, state.log.length - MAX_LOG);
|
|
70
85
|
}
|
|
71
86
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
//
|
|
89
|
-
//
|
|
90
|
-
//
|
|
91
|
-
// which is how a Rosetta-launched app showed up (TRAPS §30).
|
|
92
|
-
const IMPORTS = 'import requests; from PIL import Image; from playwright.sync_api import sync_playwright';
|
|
93
|
-
|
|
94
|
-
/** The interpreter we install into and run from: our own, inside the data dir.
|
|
95
|
-
*
|
|
96
|
-
* Two traps make "just use python3" wrong, and between them they are why a
|
|
97
|
-
* hand-run install could appear to succeed and still leave nothing working:
|
|
98
|
-
*
|
|
99
|
-
* 1. `python3` on PATH is often Homebrew's, while the interpreter that already
|
|
100
|
-
* has Playwright is /usr/bin/python3. Installing into one and running the
|
|
101
|
-
* other looks identical to a broken install.
|
|
102
|
-
* 2. Homebrew and system Pythons are "externally managed" (PEP 668) and refuse
|
|
103
|
-
* `pip install` outright, with an error most people read as a dead end.
|
|
104
|
-
*
|
|
105
|
-
* A virtualenv in the data directory has neither problem, is thrown away with
|
|
106
|
-
* the rest of the folder, and never touches the machine's Python.
|
|
107
|
-
*/
|
|
108
|
-
function venvPython() {
|
|
109
|
-
const base = path.join(dataDir(), 'venv');
|
|
110
|
-
return process.platform === 'win32'
|
|
111
|
-
? path.join(base, 'Scripts', 'python.exe')
|
|
112
|
-
: path.join(base, 'bin', 'python');
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/** A Python that can actually run the scraper right now, or null. */
|
|
116
|
-
async function findUsablePython() {
|
|
117
|
-
const venv = venvPython();
|
|
118
|
-
if (existsSync(venv) && await probe(venv, ['-c', IMPORTS], 8000)) return venv;
|
|
119
|
-
// Respect an existing working install rather than forcing a venv on someone
|
|
120
|
-
// who already did this by hand.
|
|
121
|
-
for (const c of ['/usr/bin/python3', 'python3', 'python']) {
|
|
122
|
-
if (await probe(c, ['-c', IMPORTS], 8000)) return c;
|
|
123
|
-
}
|
|
124
|
-
return null;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/** Any Python at all — used to build the venv. */
|
|
128
|
-
async function findAnyPython() {
|
|
129
|
-
for (const c of ['python3', '/usr/bin/python3', 'python']) {
|
|
130
|
-
if (await probe(c, ['--version'], 4000)) return c;
|
|
131
|
-
}
|
|
132
|
-
return null;
|
|
133
|
-
}
|
|
87
|
+
// Which Python runs the scanner is decided in lib/scanner-python.js
|
|
88
|
+
// (choosePython): the Mac app's own first (SIX_DEGREES_PYTHON, from the app),
|
|
89
|
+
// then the scanner's environment in the data folder, then a Python on this
|
|
90
|
+
// computer that already has the packages.
|
|
91
|
+
//
|
|
92
|
+
// The environment is what Install builds, never the machine's own Python, for
|
|
93
|
+
// two reasons that between them are why a hand-run install could appear to
|
|
94
|
+
// succeed and still leave nothing working (TRAPS §14):
|
|
95
|
+
//
|
|
96
|
+
// 1. `python3` on PATH is often Homebrew's, while the interpreter that already
|
|
97
|
+
// has Playwright is /usr/bin/python3. Installing into one and running the
|
|
98
|
+
// other looks identical to a broken install.
|
|
99
|
+
// 2. Homebrew and system Pythons are "externally managed" (PEP 668) and refuse
|
|
100
|
+
// `pip install` outright, with an error most people read as a dead end.
|
|
101
|
+
//
|
|
102
|
+
// A virtualenv in the data directory has neither problem, is thrown away with
|
|
103
|
+
// the rest of the folder, and never touches the machine's Python. When the
|
|
104
|
+
// computer has no Python to build it from, "Set up the scanner" first downloads
|
|
105
|
+
// a standalone one into the data folder (a click, never by itself).
|
|
134
106
|
|
|
135
107
|
/** Who the scraper found to be private, so the app can report honestly.
|
|
136
108
|
*
|
|
@@ -155,17 +127,47 @@ function bridgeSkips() {
|
|
|
155
127
|
}
|
|
156
128
|
|
|
157
129
|
// The machine checks (Python, Chrome, signed in) spawn processes, so they are
|
|
158
|
-
// cached for a few seconds
|
|
159
|
-
// it
|
|
160
|
-
//
|
|
130
|
+
// cached for a few seconds, and callers that arrive while a look is under way
|
|
131
|
+
// share it rather than each starting their own Pythons. Everything about the
|
|
132
|
+
// running job — its log, how far it has got — and the counts are read fresh on
|
|
133
|
+
// every call: the page polls every 1.5 s, and a progress bar that moves every
|
|
134
|
+
// 4 s looks stuck.
|
|
161
135
|
let cached = { at: 0, value: null };
|
|
136
|
+
let looking = null;
|
|
137
|
+
let lookGeneration = 0;
|
|
162
138
|
|
|
163
|
-
|
|
164
|
-
|
|
139
|
+
/** Forget the last look, after something that changes its answer (a job starting or ending). */
|
|
140
|
+
function forgetChecks() {
|
|
141
|
+
lookGeneration++;
|
|
142
|
+
cached = { at: 0, value: null };
|
|
143
|
+
looking = null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// The Python the app ships can't change while this server runs, so once it has
|
|
147
|
+
// been seen to work it isn't started again to ask: each look costs a Python
|
|
148
|
+
// start, and the page asks every 1.5 seconds. Once it has failed it isn't
|
|
149
|
+
// started again at all until the server restarts (it used to be asked again
|
|
150
|
+
// every minute), so a Python macOS refuses to run can't keep bringing macOS's
|
|
151
|
+
// alert back; the page says what happened instead. (lib/scanner-python.js
|
|
152
|
+
// pythonLooker.) Kept once per server process, like the scan state
|
|
153
|
+
// (lib/scan-state.js): a second copy of this module (the bundler's, or a reload
|
|
154
|
+
// while developing) must not start a failed Python afresh.
|
|
155
|
+
const LOOKER = Symbol.for('six-degrees.python-looker');
|
|
156
|
+
const lookForPython = (globalThis[LOOKER] ??= pythonLooker());
|
|
157
|
+
|
|
158
|
+
let host;
|
|
159
|
+
function hostOnce() {
|
|
160
|
+
if (host === undefined) host = thisHostKey();
|
|
161
|
+
return host;
|
|
162
|
+
}
|
|
165
163
|
|
|
164
|
+
async function look() {
|
|
166
165
|
const root = projectRoot();
|
|
167
|
-
const
|
|
168
|
-
|
|
166
|
+
const python = await lookForPython(process.env.SIX_DEGREES_PYTHON || '', {
|
|
167
|
+
app: process.env.SIX_DEGREES_APP || '',
|
|
168
|
+
dataDir: dataDir(),
|
|
169
|
+
host: hostOnce(),
|
|
170
|
+
});
|
|
169
171
|
|
|
170
172
|
// Where Playwright's "chrome" channel looks for Google Chrome (Chromium doesn't count).
|
|
171
173
|
const chrome =
|
|
@@ -175,9 +177,22 @@ async function machineChecks() {
|
|
|
175
177
|
|
|
176
178
|
const signedIn = existsSync(path.join(dataDir(), 'chrome-profile', 'Default', 'Cookies'));
|
|
177
179
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
180
|
+
return { root, python, chrome, signedIn };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function machineChecks() {
|
|
184
|
+
if (Date.now() - cached.at < 4000 && cached.value) return Promise.resolve(cached.value);
|
|
185
|
+
if (looking) return looking;
|
|
186
|
+
const generation = lookGeneration;
|
|
187
|
+
const current = look().then((value) => {
|
|
188
|
+
// A job that started or ended meanwhile makes this look stale: don't keep it.
|
|
189
|
+
if (generation === lookGeneration) cached = { at: Date.now(), value };
|
|
190
|
+
return value;
|
|
191
|
+
}).finally(() => {
|
|
192
|
+
if (looking === current) looking = null;
|
|
193
|
+
});
|
|
194
|
+
looking = current;
|
|
195
|
+
return current;
|
|
181
196
|
}
|
|
182
197
|
|
|
183
198
|
/** People whose list was only partly read, for the Scan page's Paused list. */
|
|
@@ -211,13 +226,27 @@ async function status() {
|
|
|
211
226
|
if (me) network = networkCounts(me.id);
|
|
212
227
|
} catch {}
|
|
213
228
|
|
|
229
|
+
const py = m.python;
|
|
214
230
|
return {
|
|
215
|
-
ready: Boolean(m.root &&
|
|
231
|
+
ready: Boolean(m.root && py.run && m.chrome),
|
|
216
232
|
checks: {
|
|
217
233
|
scriptsFound: Boolean(m.root),
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
234
|
+
// A Python that runs the scanner, or one Install can build its environment from.
|
|
235
|
+
python: Boolean(py.run || py.base),
|
|
236
|
+
pythonPath: py.run?.path || py.base?.path || null,
|
|
237
|
+
pythonVersion: py.run?.version || py.base?.version || null,
|
|
238
|
+
// Where the one that runs it comes from: 'bundled' (the Mac app's own),
|
|
239
|
+
// 'custom' (named in SIX_DEGREES_PYTHON), 'venv' or 'system'.
|
|
240
|
+
pythonSource: py.run?.source || null,
|
|
241
|
+
dependencies: Boolean(py.run),
|
|
242
|
+
// The named Python, when it didn't work: { source, problem }.
|
|
243
|
+
ownPython: py.own,
|
|
244
|
+
// What Install would build the scanner's environment from: { source: 'system'|'downloaded', version }.
|
|
245
|
+
installFrom: py.base ? { source: py.base.source, version: py.base.version } : null,
|
|
246
|
+
// A Python this computer has that won't do, when there is no other: { version, venv }.
|
|
247
|
+
systemPython: py.systemFound,
|
|
248
|
+
// What "Set up the scanner" would download, when there is nothing to install from.
|
|
249
|
+
download: py.download ? { version: py.download.version, size: py.download.size, from: 'github.com' } : null,
|
|
221
250
|
chrome: m.chrome,
|
|
222
251
|
signedIn: m.signedIn,
|
|
223
252
|
},
|
|
@@ -243,7 +272,10 @@ export async function GET() {
|
|
|
243
272
|
// so a name that begins with "-" can never be read as a flag of its own — and
|
|
244
273
|
// spawn takes an array, so there is no shell for it to escape into either.
|
|
245
274
|
const ACTIONS = {
|
|
246
|
-
install: { label: 'Installing the
|
|
275
|
+
install: { label: 'Installing the scanner’s Python packages' },
|
|
276
|
+
// Only when this computer has no Python to install into: downloads the one
|
|
277
|
+
// pinned for it (lib/scanner-python.js), then installs as above.
|
|
278
|
+
setup: { label: 'Setting up the scanner' },
|
|
247
279
|
login: { flag: '--login', label: 'Opening LinkedIn so you can sign in' },
|
|
248
280
|
full: { flag: '--full', label: 'Scanning your whole network' },
|
|
249
281
|
refresh: { flag: '--refresh', label: 'Checking for new connections' },
|
|
@@ -278,6 +310,86 @@ function cleanName(raw) {
|
|
|
278
310
|
return name;
|
|
279
311
|
}
|
|
280
312
|
|
|
313
|
+
/**
|
|
314
|
+
* Install, and "Set up the scanner", as steps: { plan, cleanup }, or why not:
|
|
315
|
+
* { error, status }. `found` is choosePython()'s answer. Every step is a fixed
|
|
316
|
+
* command, or a `run` done inside this server; nothing comes from the request.
|
|
317
|
+
*
|
|
318
|
+
* install build the scanner's environment from `found.base` (this
|
|
319
|
+
* computer's Python, or the one a setup downloaded), install the
|
|
320
|
+
* pinned packages into it, and check that they load there, so a pip
|
|
321
|
+
* that put them elsewhere fails with its reason, not "Finished"
|
|
322
|
+
* setup when there is nothing to build from: download the standalone
|
|
323
|
+
* Python pinned for this computer, check its SHA-256, unpack it into
|
|
324
|
+
* the data folder, then install as above. The download is checked
|
|
325
|
+
* before anything is unpacked: downloadVerified only names the file
|
|
326
|
+
* once it matches, and deletes it otherwise.
|
|
327
|
+
*/
|
|
328
|
+
function setupPlan(action, found, { root, data, say }) {
|
|
329
|
+
if (found.run) {
|
|
330
|
+
return {
|
|
331
|
+
status: 409,
|
|
332
|
+
error: found.run.source === 'bundled'
|
|
333
|
+
? 'The scanner is already set up: its Python comes with the app.'
|
|
334
|
+
: 'The scanner is already set up.',
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
const base = found.base;
|
|
338
|
+
if (action === 'install' && !base) {
|
|
339
|
+
return found.download
|
|
340
|
+
? { status: 409, error: 'This computer has no Python the scanner can use. Use Set up the scanner instead.' }
|
|
341
|
+
: { status: 500, error: 'No Python 3.10 to 3.14 is installed, or none is on this app’s PATH.' };
|
|
342
|
+
}
|
|
343
|
+
if (action === 'setup' && base?.source === 'system') {
|
|
344
|
+
return { status: 409, error: `This computer already has Python ${base.version}, so nothing needs downloading. Use Install instead.` };
|
|
345
|
+
}
|
|
346
|
+
if (action === 'setup' && !base && !found.download) {
|
|
347
|
+
return { status: 409, error: 'There is no Python download for this computer. Install Python 3.10 to 3.14, then reload.' };
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// The environment, the pinned packages without the pip settings that would
|
|
351
|
+
// put them elsewhere, and a check that they load there
|
|
352
|
+
// (lib/scanner-python.js installSteps).
|
|
353
|
+
const requirements = path.join(root, 'scripts', 'requirements.txt');
|
|
354
|
+
const install = (from) => installSteps({ base: from, dataDir: data, requirements });
|
|
355
|
+
// A setup that already got as far as the download (it was stopped, or the
|
|
356
|
+
// packages failed) carries on from its Python rather than fetching it again.
|
|
357
|
+
if (base) return { plan: install(base), cleanup: null };
|
|
358
|
+
|
|
359
|
+
const dl = found.download;
|
|
360
|
+
mkdirSync(data, { recursive: true });
|
|
361
|
+
sweepSetupLeftovers(data);
|
|
362
|
+
const work = mkdtempSync(path.join(data, SETUP_WORK_PREFIX));
|
|
363
|
+
const archive = path.join(work, dl.file);
|
|
364
|
+
let tenths = -1;
|
|
365
|
+
return {
|
|
366
|
+
plan: [
|
|
367
|
+
{
|
|
368
|
+
note: `Downloading Python ${dl.version} from GitHub (${megabytes(dl.size)} MB)`,
|
|
369
|
+
run: async ({ signal }) => {
|
|
370
|
+
await downloadVerified(dl.url, archive, {
|
|
371
|
+
sha256: dl.sha256,
|
|
372
|
+
size: dl.size,
|
|
373
|
+
signal,
|
|
374
|
+
onProgress: (got, total) => {
|
|
375
|
+
const t = Math.floor((got / total) * 10);
|
|
376
|
+
if (t > tenths) {
|
|
377
|
+
tenths = t;
|
|
378
|
+
say(`Downloading Python ${dl.version}: ${megabytes(got)} of ${megabytes(total)} MB`);
|
|
379
|
+
}
|
|
380
|
+
},
|
|
381
|
+
});
|
|
382
|
+
say('Its checksum matches the one Six Degrees has for it.');
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
{ cmd: 'tar', args: ['-xzf', archive, '-C', work], note: 'Unpacking it' },
|
|
386
|
+
{ run: () => placeDownloadedPython(work, data, work) },
|
|
387
|
+
...install({ path: downloadedPython(data), source: 'downloaded' }),
|
|
388
|
+
],
|
|
389
|
+
cleanup: () => rmSync(work, { recursive: true, force: true }),
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
|
|
281
393
|
export async function POST(request) {
|
|
282
394
|
let body = {};
|
|
283
395
|
try { body = await request.json(); } catch {}
|
|
@@ -342,6 +454,12 @@ export async function POST(request) {
|
|
|
342
454
|
// The 1st-degree scans aren't searches, but they open LinkedIn with automation too.
|
|
343
455
|
const searches = spec.searches || action.startsWith('auto-bridge')
|
|
344
456
|
|| ['bridge', 'rescrape', 'company', 'full', 'refresh'].includes(action);
|
|
457
|
+
// A staged import replaces the network at the next start, so anything scanned
|
|
458
|
+
// now would land in the copy that is about to be set aside. Setting the
|
|
459
|
+
// scanner up touches no network data.
|
|
460
|
+
if (!['install', 'setup', 'login'].includes(action) && pendingImport(dataDir())) {
|
|
461
|
+
return Response.json({ error: 'An import is waiting to finish. Restart Six Degrees first (Settings → Your data), then scan.' }, { status: 409 });
|
|
462
|
+
}
|
|
345
463
|
const cooldown = linkedinState(dataDir()).cooldown;
|
|
346
464
|
if (searches && cooldown) {
|
|
347
465
|
return Response.json({ error: `Scanning is paused until ${new Date(cooldown.until).toLocaleString()} — ${cooldown.reason}.`, cooldown }, { status: 409 });
|
|
@@ -354,53 +472,47 @@ export async function POST(request) {
|
|
|
354
472
|
if (!root) {
|
|
355
473
|
return Response.json({ error: 'Could not find scripts/scrape.py next to the app.' }, { status: 500 });
|
|
356
474
|
}
|
|
357
|
-
let python;
|
|
358
|
-
if (action === 'install') {
|
|
359
|
-
python = await findAnyPython();
|
|
360
|
-
if (!python) {
|
|
361
|
-
return Response.json({ error: 'Python 3 is not installed, or not on this app’s PATH.' }, { status: 500 });
|
|
362
|
-
}
|
|
363
|
-
} else {
|
|
364
|
-
python = await findUsablePython();
|
|
365
|
-
if (!python) {
|
|
366
|
-
return Response.json({ error: 'The scraper’s packages are not installed yet. Do step 1 first.' }, { status: 409 });
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
475
|
|
|
370
|
-
|
|
371
|
-
|
|
476
|
+
// Which Python, looked at afresh: what the page saw may be seconds old.
|
|
477
|
+
forgetChecks();
|
|
478
|
+
const found = (await machineChecks()).python;
|
|
479
|
+
if (state.running) {
|
|
480
|
+
return Response.json({ error: 'Something is already running.', action: state.action }, { status: 409 });
|
|
481
|
+
}
|
|
372
482
|
|
|
373
483
|
// Installing is several commands, so it runs as a small sequential plan
|
|
374
484
|
// rather than a shell string — nothing here is ever concatenated from input.
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
]
|
|
485
|
+
let plan;
|
|
486
|
+
let cleanup = null;
|
|
487
|
+
if (action === 'install' || action === 'setup') {
|
|
488
|
+
const made = setupPlan(action, found, { root, data: dataDir(), say: push });
|
|
489
|
+
if (made.error) return Response.json({ error: made.error }, { status: made.status });
|
|
490
|
+
({ plan, cleanup } = made);
|
|
491
|
+
} else {
|
|
492
|
+
if (!found.run) {
|
|
493
|
+
return Response.json({ error: 'The scanner’s packages are not installed yet. Do step 1 first.' }, { status: 409 });
|
|
494
|
+
}
|
|
495
|
+
// The app's own Python runs isolated from this user's Python settings; any
|
|
496
|
+
// Python runs it without writing bytecode beside scrape.py, which in the Mac
|
|
497
|
+
// app is inside the signed app (lib/scanner-python.js scannerCommand).
|
|
498
|
+
plan = [scannerCommand(found.run, path.join(root, 'scripts', 'scrape.py'), [
|
|
499
|
+
// `--flag=value` is one token on purpose: a name beginning with
|
|
500
|
+
// "-" can then never be read as a flag of its own.
|
|
501
|
+
...(name ? [`${spec.flag}=${name}`] : profileUrl ? [`${spec.flag}=${profileUrl}`] : spec.flag.split(' ')),
|
|
502
|
+
...(maxBridges && (action.startsWith('auto-bridge') || action === 'resume-all') ? [`--max-bridges=${maxBridges}`] : []),
|
|
503
|
+
...(tiers.length && action.startsWith('auto-bridge') ? [`--tiers=${tiers.join(',')}`] : []),
|
|
504
|
+
...(action.startsWith('auto-bridge') ? [`--order=${order}`] : []),
|
|
505
|
+
// Resuming always reads to the end: a remembered "10 pages" would
|
|
506
|
+
// otherwise leave everyone paused at page 11 and do nothing.
|
|
507
|
+
...(readsCircles ? [`--max-pages=${action.startsWith('resume') ? 100 : maxPages}`] : []),
|
|
508
|
+
...(deeper ? ['--deeper'] : []),
|
|
509
|
+
])];
|
|
510
|
+
}
|
|
399
511
|
|
|
400
512
|
// The scraper writes back through this very app, so point it at the port we
|
|
401
513
|
// are actually being served on rather than guessing 3000.
|
|
402
|
-
const
|
|
403
|
-
const port =
|
|
514
|
+
const hostHeader = request.headers.get('host') || '127.0.0.1:3000';
|
|
515
|
+
const port = hostHeader.includes(':') ? hostHeader.split(':').pop() : '80';
|
|
404
516
|
|
|
405
517
|
state.running = true;
|
|
406
518
|
state.stopping = false;
|
|
@@ -410,7 +522,7 @@ export async function POST(request) {
|
|
|
410
522
|
state.log = [spec.label + (name ? ` ${name}…` : '…')];
|
|
411
523
|
state.stderrTail = [];
|
|
412
524
|
state.failure = null;
|
|
413
|
-
|
|
525
|
+
forgetChecks();
|
|
414
526
|
|
|
415
527
|
// Name the profile outright. The scraper would otherwise ask the app which one
|
|
416
528
|
// to use; passing it means the page and the scrape cannot disagree.
|
|
@@ -446,8 +558,13 @@ export async function POST(request) {
|
|
|
446
558
|
state.stopping = false;
|
|
447
559
|
state.running = false;
|
|
448
560
|
state.child = null;
|
|
561
|
+
state.abort = null;
|
|
449
562
|
state.exitCode = code;
|
|
450
|
-
|
|
563
|
+
// A setup's private folder (the download and what was unpacked from it).
|
|
564
|
+
if (cleanup) {
|
|
565
|
+
try { cleanup(); } catch { /* swept at the next setup (sweepSetupLeftovers) */ }
|
|
566
|
+
}
|
|
567
|
+
forgetChecks();
|
|
451
568
|
}
|
|
452
569
|
|
|
453
570
|
function runStep(i) {
|
|
@@ -455,10 +572,32 @@ export async function POST(request) {
|
|
|
455
572
|
const step = plan[i];
|
|
456
573
|
if (step.note) push(step.note + '…');
|
|
457
574
|
|
|
575
|
+
// A step done inside this server: the download, and moving what it unpacked
|
|
576
|
+
// into place. Stop aborts it (stopChild).
|
|
577
|
+
if (step.run) {
|
|
578
|
+
const ctrl = new AbortController();
|
|
579
|
+
state.abort = ctrl;
|
|
580
|
+
Promise.resolve()
|
|
581
|
+
.then(() => step.run({ signal: ctrl.signal }))
|
|
582
|
+
.then(() => {
|
|
583
|
+
state.abort = null;
|
|
584
|
+
if (state.stopping) return finish(0);
|
|
585
|
+
runStep(i + 1);
|
|
586
|
+
}, (err) => {
|
|
587
|
+
state.abort = null;
|
|
588
|
+
if (state.stopping) return finish(0);
|
|
589
|
+
// Kept where finish() looks for a failure's reason, for the page's red box.
|
|
590
|
+
state.stderrTail.push(err instanceof ScannerSetupError ? err.message : `Could not finish: ${err.message}`);
|
|
591
|
+
finish(1);
|
|
592
|
+
});
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
|
|
458
596
|
let child;
|
|
459
597
|
try {
|
|
460
|
-
// Its own process group, so cancelling reaches the browser as well.
|
|
461
|
-
|
|
598
|
+
// Its own process group, so cancelling reaches the browser as well. A
|
|
599
|
+
// step's env() makes its own environment from this one.
|
|
600
|
+
child = spawn(step.cmd, step.args, { cwd: root, env: step.env ? step.env(childEnv) : childEnv, detached: true });
|
|
462
601
|
} catch (err) {
|
|
463
602
|
push(`Could not start: ${err.message}`);
|
|
464
603
|
return finish(-1);
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { getDb } from '../../../lib/db-client';
|
|
4
|
+
import { readSettings, writeSettings, SettingsError } from '../../../lib/settings';
|
|
5
|
+
import { afterSettingsChange } from '../../../lib/settings-effects';
|
|
6
|
+
import { dataDir, projectRoot, isGitCheckout } from '../../../lib/paths';
|
|
7
|
+
import { installKind } from '../../../lib/release';
|
|
8
|
+
import { APP_VERSION } from '../../../lib/app-version';
|
|
9
|
+
|
|
10
|
+
// The Settings page's data: what the user has chosen, plus plain facts about
|
|
11
|
+
// this copy (version, how it was installed, where its data lives). Reading is
|
|
12
|
+
// local only; saving is a POST, so the cross-site guard in middleware.js
|
|
13
|
+
// applies to it like every other write.
|
|
14
|
+
|
|
15
|
+
function about() {
|
|
16
|
+
const dir = path.resolve(dataDir());
|
|
17
|
+
return {
|
|
18
|
+
version: APP_VERSION,
|
|
19
|
+
// A git checkout is its own kind here: it updates with git, not a release.
|
|
20
|
+
kind: isGitCheckout() ? 'git' : installKind(process.env, projectRoot()),
|
|
21
|
+
dataDir: dir,
|
|
22
|
+
customDataDir: dir !== path.join(homedir(), '.six-degrees'),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function GET() {
|
|
27
|
+
try {
|
|
28
|
+
return Response.json({ settings: readSettings(getDb()), about: about() });
|
|
29
|
+
} catch (err) {
|
|
30
|
+
return Response.json({ error: err.message }, { status: 500 });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function POST(request) {
|
|
35
|
+
let body;
|
|
36
|
+
try {
|
|
37
|
+
body = await request.json();
|
|
38
|
+
} catch {
|
|
39
|
+
return Response.json({ error: 'Send the settings as JSON.' }, { status: 400 });
|
|
40
|
+
}
|
|
41
|
+
let before, settings;
|
|
42
|
+
try {
|
|
43
|
+
before = readSettings(getDb());
|
|
44
|
+
settings = writeSettings(getDb(), body?.settings);
|
|
45
|
+
} catch (err) {
|
|
46
|
+
return Response.json({ error: err.message }, { status: err instanceof SettingsError ? 400 : 500 });
|
|
47
|
+
}
|
|
48
|
+
// What the change sets in motion (lib/settings-effects.js): a new sector
|
|
49
|
+
// focus rescores everyone. The save has landed either way, so a failure
|
|
50
|
+
// here is reported with the saved settings (and whatever work did finish)
|
|
51
|
+
// rather than hidden.
|
|
52
|
+
try {
|
|
53
|
+
const effects = afterSettingsChange(getDb(), before, settings);
|
|
54
|
+
return Response.json(effects ? { settings, effects } : { settings });
|
|
55
|
+
} catch (err) {
|
|
56
|
+
return Response.json({ settings, ...(err.effects ? { effects: err.effects } : {}), error: err.message }, { status: 500 });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { getDb } from '../../../../lib/db-client';
|
|
2
|
+
import { companyOverrides, readForScoring, scoringRows, sectorFocusOf } from '../../../../lib/rpc';
|
|
3
|
+
import { parseSectorFocus, previewSectorFocus } from '../../../../lib/sector-focus';
|
|
4
|
+
|
|
5
|
+
// Settings → Your sector, before you save: what a sector focus would change,
|
|
6
|
+
// against the one saved now. It reads the network once and scores it twice in
|
|
7
|
+
// memory, with the inputs rescoreAll() uses (readForScoring: each company's
|
|
8
|
+
// industry and its sectors from the directory), and writes nothing. A save
|
|
9
|
+
// counts its changes with the same function (rescoreAll's compareWith), so the
|
|
10
|
+
// two agree. A POST because it takes a body; the cross-site guard in
|
|
11
|
+
// middleware.js covers it like every write.
|
|
12
|
+
|
|
13
|
+
export async function POST(request) {
|
|
14
|
+
let body;
|
|
15
|
+
try {
|
|
16
|
+
body = await request.json();
|
|
17
|
+
} catch {
|
|
18
|
+
return Response.json({ error: 'Send the sector focus as JSON.' }, { status: 400 });
|
|
19
|
+
}
|
|
20
|
+
let to;
|
|
21
|
+
try {
|
|
22
|
+
to = parseSectorFocus(body?.sectorFocus);
|
|
23
|
+
} catch (err) {
|
|
24
|
+
return Response.json({ error: err.message }, { status: 400 });
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
const db = getDb();
|
|
28
|
+
const rows = scoringRows(db, { withPeople: true });
|
|
29
|
+
return Response.json(previewSectorFocus(rows, {
|
|
30
|
+
overrides: companyOverrides(db),
|
|
31
|
+
read: readForScoring(rows),
|
|
32
|
+
from: sectorFocusOf(db),
|
|
33
|
+
to,
|
|
34
|
+
}));
|
|
35
|
+
} catch (err) {
|
|
36
|
+
return Response.json({ error: err.message }, { status: 500 });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { getDb } from '../../../../lib/db-client';
|
|
2
|
+
import { readForScoring, scoringRows } from '../../../../lib/rpc';
|
|
3
|
+
import { suggestSectors } from '../../../../lib/sector-directory';
|
|
4
|
+
|
|
5
|
+
// Settings → Your sector: "Suggested from your network". The directory's
|
|
6
|
+
// sectors most of your scanned people work in, counted here from the rows
|
|
7
|
+
// with the same read rescoring uses, so a suggestion's companies are the ones
|
|
8
|
+
// picking it would lean. Its own read-only GET, not part of GET
|
|
9
|
+
// /api/settings: that one is also what the profile page loads, and this reads
|
|
10
|
+
// every row. A CSV import or the sample network isn't in the database, so it
|
|
11
|
+
// isn't counted (the page says so).
|
|
12
|
+
|
|
13
|
+
export async function GET() {
|
|
14
|
+
try {
|
|
15
|
+
const rows = scoringRows(getDb());
|
|
16
|
+
return Response.json({ scored: rows.length, suggestions: suggestSectors(rows, readForScoring(rows)) });
|
|
17
|
+
} catch (err) {
|
|
18
|
+
return Response.json({ error: err.message }, { status: 500 });
|
|
19
|
+
}
|
|
20
|
+
}
|