six-degrees 0.2.1 → 0.3.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 +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/qqqcxvzBUizkECrT4swyK/_clientMiddlewareManifest.js +14 -0
- package/.next/standalone/CHANGELOG.md +298 -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/qqqcxvzBUizkECrT4swyK/_clientMiddlewareManifest.js +14 -0
- package/CHANGELOG.md +298 -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 → qqqcxvzBUizkECrT4swyK}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{ZGTiDrZ5NHGETgGdlhssu → qqqcxvzBUizkECrT4swyK}/_ssgManifest.js +0 -0
- /package/.next/static/{ZGTiDrZ5NHGETgGdlhssu → qqqcxvzBUizkECrT4swyK}/_buildManifest.js +0 -0
- /package/.next/static/{ZGTiDrZ5NHGETgGdlhssu → qqqcxvzBUizkECrT4swyK}/_ssgManifest.js +0 -0
package/lib/db-client.js
CHANGED
|
@@ -4,6 +4,7 @@ import { homedir } from 'node:os';
|
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { SCHEMA_SQL } from '../db/schema.js';
|
|
6
6
|
import { APP_VERSION } from './app-version.js';
|
|
7
|
+
import { applyPendingImport, recordImport } from './data-import.js';
|
|
7
8
|
|
|
8
9
|
// The database lives OUTSIDE the app directory so an installed copy never
|
|
9
10
|
// writes into its own package. Override with SIX_DEGREES_HOME.
|
|
@@ -36,10 +37,42 @@ export const BOOLEAN_COLUMNS = {
|
|
|
36
37
|
|
|
37
38
|
let _db = null;
|
|
38
39
|
|
|
40
|
+
// Set once this server process has opened the database, by any copy of this
|
|
41
|
+
// module. The bundler can give a route its own copy, with its own _db, and a
|
|
42
|
+
// copy that opens late must not finish an import staged while another copy's
|
|
43
|
+
// handle was already open: the swap would happen under that handle. There is
|
|
44
|
+
// one globalThis per process, like lib/scan-state.js.
|
|
45
|
+
const OPENED = Symbol.for('six-degrees.db-opened');
|
|
46
|
+
|
|
47
|
+
/** The database file: SIX_DEGREES_DB, or six-degrees.sqlite in the data folder. */
|
|
48
|
+
export function dbFile() {
|
|
49
|
+
return process.env.SIX_DEGREES_DB || path.join(dataDir(), 'six-degrees.sqlite');
|
|
50
|
+
}
|
|
51
|
+
|
|
39
52
|
export function getDb() {
|
|
40
53
|
if (_db) return _db;
|
|
41
54
|
|
|
42
|
-
const file =
|
|
55
|
+
const file = dbFile();
|
|
56
|
+
|
|
57
|
+
// A network imported from another computer (Settings → Your data) is swapped
|
|
58
|
+
// in here, at the start, before anything opens the database: it can't be
|
|
59
|
+
// swapped under a handle that is already open, this process's or another's
|
|
60
|
+
// (it waits while another copy of Six Degrees has the database open). What
|
|
61
|
+
// was here is kept in backups/ first. If a step fails, what is in place opens
|
|
62
|
+
// as usual and the next start carries on (lib/data-import.js).
|
|
63
|
+
let imported = null;
|
|
64
|
+
if (!globalThis[OPENED]) {
|
|
65
|
+
try {
|
|
66
|
+
imported = applyPendingImport({ dir: dataDir(), dbFile: file });
|
|
67
|
+
if (imported) {
|
|
68
|
+
console.log(`Six Degrees: finished the import (${imported.people} people). What was here before: ${imported.keptDatabase || 'nothing to keep'}.`);
|
|
69
|
+
}
|
|
70
|
+
} catch (err) {
|
|
71
|
+
console.error(`Six Degrees: could not finish the import (will try again next start): ${err.message}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
globalThis[OPENED] = true;
|
|
75
|
+
|
|
43
76
|
_db = new DatabaseSync(file);
|
|
44
77
|
|
|
45
78
|
// Before this version changes anything: if a different version opened this
|
|
@@ -53,12 +86,24 @@ export function getDb() {
|
|
|
53
86
|
|
|
54
87
|
// Idempotent: every statement is CREATE ... IF NOT EXISTS, so this doubles
|
|
55
88
|
// as first-run setup and as a no-op on every later boot.
|
|
56
|
-
_db
|
|
57
|
-
|
|
89
|
+
applySchema(_db);
|
|
90
|
+
|
|
91
|
+
if (imported) {
|
|
92
|
+
try { recordImport(_db, imported); } catch { /* only the Settings page's note is lost */ }
|
|
93
|
+
}
|
|
58
94
|
|
|
59
95
|
return _db;
|
|
60
96
|
}
|
|
61
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Every table, index and column this version uses, made if missing. An import
|
|
100
|
+
* is rebuilt into exactly this (lib/data-import.js), so only rows travel.
|
|
101
|
+
*/
|
|
102
|
+
export function applySchema(db) {
|
|
103
|
+
db.exec(SCHEMA_SQL);
|
|
104
|
+
addMissingColumns(db);
|
|
105
|
+
}
|
|
106
|
+
|
|
62
107
|
// CREATE TABLE IF NOT EXISTS never adds a column to a table that already
|
|
63
108
|
// exists, so columns added after first release are added here, once.
|
|
64
109
|
const ADDED_COLUMNS = [
|
package/lib/durable.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Writes that have to be on the disk before the next step may count on them.
|
|
2
|
+
//
|
|
3
|
+
// A rename is atomic, but it isn't durable. After a power cut the new name can
|
|
4
|
+
// be on the disk while the bytes it names aren't, so a copy that "exists" can
|
|
5
|
+
// be empty. SQLite doesn't sync what VACUUM INTO writes either (its docs say
|
|
6
|
+
// so). So a file is synced before it is renamed into place, and its folder
|
|
7
|
+
// after, so the rename itself is on the disk before anything relies on it.
|
|
8
|
+
//
|
|
9
|
+
// On a Mac, Node's fsync is F_FULLFSYNC (libuv), which also empties the drive's
|
|
10
|
+
// own write cache; a plain fsync there doesn't.
|
|
11
|
+
|
|
12
|
+
import fs from 'node:fs';
|
|
13
|
+
import path from 'node:path';
|
|
14
|
+
|
|
15
|
+
// A folder that isn't there has no rename to make durable, and some file
|
|
16
|
+
// systems can't open or sync one: nothing more can be done then.
|
|
17
|
+
const FOLDER_CANT_SYNC = new Set(['ENOENT', 'EINVAL', 'ENOTSUP', 'ENOSYS', 'EISDIR', 'EPERM', 'EBADF']);
|
|
18
|
+
|
|
19
|
+
function syncFile(file) {
|
|
20
|
+
const fd = fs.openSync(file, 'r');
|
|
21
|
+
try {
|
|
22
|
+
fs.fsyncSync(fd);
|
|
23
|
+
} finally {
|
|
24
|
+
fs.closeSync(fd);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function syncFolder(folder) {
|
|
29
|
+
let fd;
|
|
30
|
+
try {
|
|
31
|
+
fd = fs.openSync(folder, 'r');
|
|
32
|
+
} catch (err) {
|
|
33
|
+
if (FOLDER_CANT_SYNC.has(err.code)) return;
|
|
34
|
+
throw err;
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
fs.fsyncSync(fd);
|
|
38
|
+
} catch (err) {
|
|
39
|
+
if (!FOLDER_CANT_SYNC.has(err.code)) throw err;
|
|
40
|
+
} finally {
|
|
41
|
+
fs.closeSync(fd);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Replace `file` with `data` so that after a power cut it holds either the old contents or the new, never part of either. */
|
|
46
|
+
function writeFileDurably(file, data) {
|
|
47
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
48
|
+
try {
|
|
49
|
+
const fd = fs.openSync(tmp, 'w');
|
|
50
|
+
try {
|
|
51
|
+
fs.writeFileSync(fd, data);
|
|
52
|
+
fs.fsyncSync(fd);
|
|
53
|
+
} finally {
|
|
54
|
+
fs.closeSync(fd);
|
|
55
|
+
}
|
|
56
|
+
fs.renameSync(tmp, file);
|
|
57
|
+
} catch (err) {
|
|
58
|
+
fs.rmSync(tmp, { force: true });
|
|
59
|
+
throw err;
|
|
60
|
+
}
|
|
61
|
+
syncFolder(path.dirname(file));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** The same, for a JSON document, laid out the way the scanner writes its own files. */
|
|
65
|
+
function writeJsonDurably(file, value) {
|
|
66
|
+
writeFileDurably(file, `${JSON.stringify(value, null, 2)}\n`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Called through this object rather than directly, so a test can see what was
|
|
71
|
+
* synced and in which order (tests/data-transfer.test.mjs).
|
|
72
|
+
*/
|
|
73
|
+
export const durable = { syncFile, syncFolder, writeFileDurably, writeJsonDurably };
|
package/lib/gate.js
CHANGED
|
@@ -7,6 +7,11 @@
|
|
|
7
7
|
// irreversibly destroy or rewrite data; /api/scraper spawns processes and
|
|
8
8
|
// /api/update changes the code that will run next, both of which are more power
|
|
9
9
|
// than any of the others, so they sit behind the same gate.
|
|
10
|
+
//
|
|
11
|
+
// The four under /api/data are Settings → Your data: export hands over the
|
|
12
|
+
// whole network in one file, import replaces it, restart stops the server, and
|
|
13
|
+
// reveal starts a process. Reading /api/data itself (sizes, the folder's path)
|
|
14
|
+
// changes nothing and stays open, like every other read.
|
|
10
15
|
export const DESTRUCTIVE_ROUTES = [
|
|
11
16
|
'/api/admin-delete',
|
|
12
17
|
'/api/admin-update',
|
|
@@ -14,6 +19,10 @@ export const DESTRUCTIVE_ROUTES = [
|
|
|
14
19
|
'/api/setup-profile',
|
|
15
20
|
'/api/scraper',
|
|
16
21
|
'/api/update',
|
|
22
|
+
'/api/data/export',
|
|
23
|
+
'/api/data/import',
|
|
24
|
+
'/api/data/restart',
|
|
25
|
+
'/api/data/reveal',
|
|
17
26
|
];
|
|
18
27
|
|
|
19
28
|
const LOOPBACK = new Set(['127.0.0.1', '::1', 'localhost']);
|
|
@@ -29,11 +38,21 @@ const LOOPBACK = new Set(['127.0.0.1', '::1', 'localhost']);
|
|
|
29
38
|
* Sec-Fetch-Site is set by the browser and cannot be forged by script, so it is
|
|
30
39
|
* the reliable signal. Origin is the fallback for browsers that omit it.
|
|
31
40
|
* Non-browser callers (curl, scripts/scrape.py) send neither and are unaffected.
|
|
41
|
+
*
|
|
42
|
+
* 'same-site' is not enough on its own: a site ignores the port, so a page
|
|
43
|
+
* served from any other port on 127.0.0.1 (another local app, a dev server, a
|
|
44
|
+
* tool's web UI) is "same-site" with this one. Such a write has to show an
|
|
45
|
+
* Origin that is exactly this host and port. The app's own pages are always
|
|
46
|
+
* 'same-origin', so nothing legitimate is refused.
|
|
32
47
|
*/
|
|
33
48
|
export function isCrossSiteWrite({ method, secFetchSite, origin, host }) {
|
|
34
49
|
if (!method || !['POST', 'PUT', 'PATCH', 'DELETE'].includes(method.toUpperCase())) return false;
|
|
35
50
|
|
|
36
|
-
if (secFetchSite)
|
|
51
|
+
if (secFetchSite) {
|
|
52
|
+
if (secFetchSite === 'same-origin' || secFetchSite === 'none') return false;
|
|
53
|
+
if (secFetchSite !== 'same-site') return true;
|
|
54
|
+
if (!origin) return true; // same-site but no Origin to pin the port: refuse
|
|
55
|
+
}
|
|
37
56
|
|
|
38
57
|
if (origin) {
|
|
39
58
|
try {
|
|
@@ -46,6 +65,38 @@ export function isCrossSiteWrite({ method, secFetchSite, origin, host }) {
|
|
|
46
65
|
return false; // no browser headers at all: not a browser
|
|
47
66
|
}
|
|
48
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Was this request sent to this app under someone else's name?
|
|
70
|
+
*
|
|
71
|
+
* DNS rebinding: a page on any website can make its own domain resolve to
|
|
72
|
+
* 127.0.0.1. The browser then treats the app as that page's own origin, so
|
|
73
|
+
* Sec-Fetch-Site says 'same-origin', the write check above lets it through,
|
|
74
|
+
* and CORS lets the page read every answer, the whole network included. What
|
|
75
|
+
* gives it away is the Host header: it carries the attacker's name, because
|
|
76
|
+
* that is the address the page used.
|
|
77
|
+
*
|
|
78
|
+
* When the server is bound to loopback, every real caller (this app's pages,
|
|
79
|
+
* the Mac app's window, the scanner, curl) addresses it as 127.0.0.1,
|
|
80
|
+
* localhost or [::1], so any other Host is refused. Bound anywhere else, the
|
|
81
|
+
* operator chose that on purpose and ADMIN_TOKEN guards what matters.
|
|
82
|
+
*/
|
|
83
|
+
export function isRebound({ bind, host }) {
|
|
84
|
+
if (!boundToLoopback(bind)) return false;
|
|
85
|
+
if (!host) return false; // a client that sends no Host names nothing to judge
|
|
86
|
+
let hostname;
|
|
87
|
+
try {
|
|
88
|
+
hostname = new URL(`http://${host}`).hostname.toLowerCase();
|
|
89
|
+
} catch {
|
|
90
|
+
return true; // an unparseable Host is not a real caller
|
|
91
|
+
}
|
|
92
|
+
return !LOOPBACK_HOSTNAMES.has(hostname);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// How URL() writes the loopback names (it keeps IPv6 in brackets). 0.0.0.0 is
|
|
96
|
+
// here because the scanner's own allow-list accepts it for APP_URL; a rebinding
|
|
97
|
+
// page can only ever send its own domain, never this.
|
|
98
|
+
const LOOPBACK_HOSTNAMES = new Set(['127.0.0.1', 'localhost', '[::1]', '0.0.0.0']);
|
|
99
|
+
|
|
49
100
|
export function isDestructive(pathname) {
|
|
50
101
|
return DESTRUCTIVE_ROUTES.some((p) => pathname === p || pathname.startsWith(`${p}/`));
|
|
51
102
|
}
|
|
@@ -61,6 +112,44 @@ function sameSecret(a, b) {
|
|
|
61
112
|
return diff === 0;
|
|
62
113
|
}
|
|
63
114
|
|
|
115
|
+
/**
|
|
116
|
+
* Everything middleware.js decides about a request, as one answer: null to let
|
|
117
|
+
* it through, or { status, error } to refuse it. In order: a request addressed
|
|
118
|
+
* to another name (421), a write from another site (403), then, for the routes
|
|
119
|
+
* in DESTRUCTIVE_ROUTES, the gate (gateDecision).
|
|
120
|
+
*
|
|
121
|
+
* One function, so the one route middleware.js leaves alone (the import, whose
|
|
122
|
+
* body must not be buffered first: see middleware.js) makes exactly the same
|
|
123
|
+
* checks itself, before it reads anything.
|
|
124
|
+
*
|
|
125
|
+
* @param {{ method: string, pathname: string, headers: { get(name: string): string | null },
|
|
126
|
+
* env: { SIX_DEGREES_BIND?: string, HOSTNAME?: string, ADMIN_TOKEN?: string } }} request
|
|
127
|
+
*/
|
|
128
|
+
export function requestRefusal({ method, pathname, headers, env = {} }) {
|
|
129
|
+
const header = (name) => headers?.get?.(name) ?? null;
|
|
130
|
+
const bind = env.SIX_DEGREES_BIND || env.HOSTNAME;
|
|
131
|
+
|
|
132
|
+
if (isRebound({ bind, host: header('host') })) {
|
|
133
|
+
return { status: 421, error: 'This app only answers to 127.0.0.1 and localhost.' };
|
|
134
|
+
}
|
|
135
|
+
if (isCrossSiteWrite({
|
|
136
|
+
method,
|
|
137
|
+
secFetchSite: header('sec-fetch-site'),
|
|
138
|
+
origin: header('origin'),
|
|
139
|
+
host: header('host'),
|
|
140
|
+
})) {
|
|
141
|
+
return { status: 403, error: 'Cross-site requests are not accepted. This app only answers to pages it serves.' };
|
|
142
|
+
}
|
|
143
|
+
if (!isDestructive(pathname)) return null;
|
|
144
|
+
|
|
145
|
+
const decision = gateDecision({
|
|
146
|
+
bind,
|
|
147
|
+
token: env.ADMIN_TOKEN,
|
|
148
|
+
bearer: (header('authorization') || '').replace(/^Bearer\s+/i, ''),
|
|
149
|
+
});
|
|
150
|
+
return decision.allow ? null : { status: decision.status, error: decision.reason };
|
|
151
|
+
}
|
|
152
|
+
|
|
64
153
|
/**
|
|
65
154
|
* @param {{ bind?: string, token?: string, bearer?: string }} ctx
|
|
66
155
|
* @returns {{ allow: boolean, status?: number, reason: string }}
|
package/lib/ingest.js
CHANGED
|
@@ -60,3 +60,43 @@ export function toIsoDate(text) {
|
|
|
60
60
|
if (month < 0 || day < 1 || day > 31) return null;
|
|
61
61
|
return `${m[3]}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
|
|
62
62
|
}
|
|
63
|
+
|
|
64
|
+
// Tiers that make a new connection worth a notification of its own.
|
|
65
|
+
const HIGH_VALUE = new Set(['S', 'A']);
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* The notifications a refresh of your own connections (1st degree) leaves:
|
|
69
|
+
* how many are new, and one for each new person the model scored S or A.
|
|
70
|
+
*
|
|
71
|
+
* `added` are the records this request inserted: new means new to the
|
|
72
|
+
* database, not missing from a lookup (a re-scan of people already saved
|
|
73
|
+
* used to count everyone past the first hundred as new). `tierOf(url)` is
|
|
74
|
+
* the tier each was stored with by the rescore that follows the insert, so
|
|
75
|
+
* "high-value" is the model's judgement (lib/scoring.js), not a list of
|
|
76
|
+
* words or famous names matched anywhere in the headline, which read
|
|
77
|
+
* "Metadata Engineer" as Meta and "Engineer at Applewood Bakery" as Apple.
|
|
78
|
+
* `checked` is how many records the refresh sent.
|
|
79
|
+
*/
|
|
80
|
+
export function refreshNotifications({ added = [], checked = 0, tierOf = () => null, userId = null } = {}) {
|
|
81
|
+
const user_id = userId || null;
|
|
82
|
+
if (!added.length) {
|
|
83
|
+
return [{
|
|
84
|
+
user_id, type: 'refresh_summary', title: 'Network up to date',
|
|
85
|
+
message: `Checked ${checked} connections — no new additions`, icon: '✓',
|
|
86
|
+
}];
|
|
87
|
+
}
|
|
88
|
+
const out = [{
|
|
89
|
+
user_id,
|
|
90
|
+
type: 'refresh_summary',
|
|
91
|
+
title: `${added.length} new connection${added.length > 1 ? 's' : ''} found!`,
|
|
92
|
+
message: added.slice(0, 3).map((r) => r.name).join(', ') + (added.length > 3 ? ` +${added.length - 3} more` : ''),
|
|
93
|
+
icon: '🔄',
|
|
94
|
+
}];
|
|
95
|
+
for (const r of added.filter((a) => HIGH_VALUE.has(tierOf(a.profile_url))).slice(0, 5)) {
|
|
96
|
+
out.push({
|
|
97
|
+
user_id, type: 'new_elite_connection', title: `High-value connection: ${r.name}`,
|
|
98
|
+
message: r.headline?.substring(0, 60), icon: '👑',
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// Paths → Scores: the one-time offer to keep the curated list's old scores as
|
|
2
|
+
// your own, after the list was made neutral (lib/legacy-scores.js has the old
|
|
3
|
+
// scores and why each changed).
|
|
4
|
+
//
|
|
5
|
+
// Who is offered what. The offer opens when rescoreAll() first replaces scores
|
|
6
|
+
// that were computed with the old list (lib/rpc.js), so a database that never
|
|
7
|
+
// had them, a fresh one, never sees it. It then lists each old entry that
|
|
8
|
+
// someone in this network works at now, whose built-in score is different now,
|
|
9
|
+
// and that you haven't scored yourself. A CSV import and the sample aren't
|
|
10
|
+
// scored on the server, so the page doesn't ask while one is open.
|
|
11
|
+
//
|
|
12
|
+
// Keeping writes the old score under every name scoring uses for the entry's
|
|
13
|
+
// companies, as Paths → Scores sets a score (setCompanyScores), then rescores
|
|
14
|
+
// everyone once for the whole batch. Keep or No thanks, it is never offered
|
|
15
|
+
// again, and an offer whose first look finds nothing to offer closes itself.
|
|
16
|
+
|
|
17
|
+
import { getDb } from './db-client.js';
|
|
18
|
+
import { readNetwork, companyScore, KNOWN_COMPANIES } from './scoring.js';
|
|
19
|
+
import { industryKeyOf } from './companies.js';
|
|
20
|
+
import { scoringRows, companyOverrides, setCompanyScores, rescoreAll } from './rpc.js';
|
|
21
|
+
import { LEGACY_SCORES, LEGACY_OFFER_KEY } from './legacy-scores.js';
|
|
22
|
+
|
|
23
|
+
const LISTED = new Set(KNOWN_COMPANIES.map(([name]) => name));
|
|
24
|
+
const OLD = LEGACY_SCORES.map(([name, score, names, industry]) => ({ name, score, names, industry }));
|
|
25
|
+
const OLD_BY_NAME = new Map(OLD.map((e) => [e.name, e]));
|
|
26
|
+
// As lib/scoring.js cleanCompany reads it: a name that says it is a school
|
|
27
|
+
// only matched a school on the list. Copied rather than imported, because this
|
|
28
|
+
// is how the old list matched and must stay so whatever the live rule becomes.
|
|
29
|
+
const SCHOOL_NAME = /\b(school|college|university)\b/;
|
|
30
|
+
|
|
31
|
+
// Most names an answer may carry: the old list had 34 changed entries.
|
|
32
|
+
const MAX_ANSWER = 100;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The old list's entry that scored a company, found by the name scoring gives
|
|
36
|
+
* the company now (cleanCompany's), or null. A company still on the list keeps
|
|
37
|
+
* its name, so its old entry counts only if it was rescored. A removed entry's
|
|
38
|
+
* names are found by its own names (lib/legacy-scores.js), the way
|
|
39
|
+
* cleanCompany found them: "University of Central Florida" is its own name
|
|
40
|
+
* now, and was UCF's. Another company the old alias also caught ("Hard Rock
|
|
41
|
+
* Hotel" for Hard Rock Digital) isn't that entry's.
|
|
42
|
+
*/
|
|
43
|
+
export function legacyEntryFor(name) {
|
|
44
|
+
if (!name) return null;
|
|
45
|
+
if (LISTED.has(name)) return OLD_BY_NAME.get(name) || null;
|
|
46
|
+
const lower = name.toLowerCase();
|
|
47
|
+
const bare = lower.replace(/^the /, '');
|
|
48
|
+
const school = SCHOOL_NAME.test(lower);
|
|
49
|
+
for (const entry of OLD) {
|
|
50
|
+
if (LISTED.has(entry.name) || (school && entry.industry !== 'education')) continue;
|
|
51
|
+
if (entry.names.test(lower) || entry.names.test(bare) || lower === entry.name.toLowerCase()) return entry;
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 'open', 'kept', 'declined', 'none' (it opened, and there was nothing in
|
|
58
|
+
* this network to offer), or null when this database was never offered.
|
|
59
|
+
*/
|
|
60
|
+
export function legacyOfferState(db = getDb()) {
|
|
61
|
+
return db.prepare('SELECT value FROM app_meta WHERE key = ?').get(LEGACY_OFFER_KEY)?.value ?? null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* What Paths → Scores offers, or null: nothing when the offer isn't open
|
|
66
|
+
* (never was, or was answered) or no company in the network lost an old score
|
|
67
|
+
* that you haven't replaced with one of your own. That last closes it for
|
|
68
|
+
* good ('none'): the network the old list scored is the one read here, so
|
|
69
|
+
* nothing could turn up later, and every visit to Paths → Scores would
|
|
70
|
+
* otherwise read the whole network again to find nothing.
|
|
71
|
+
*
|
|
72
|
+
* The companies are the ones Paths → Scores lists, those someone works at now,
|
|
73
|
+
* so a kept score shows there as yours and Auto can hand it back. (A name only
|
|
74
|
+
* in former roles would be a score nothing lists; its people's former role
|
|
75
|
+
* counts at 70% and moves them little.) Each is compared by its built-in score
|
|
76
|
+
* now (the list or the estimate, before any sector lean) with the old entry's.
|
|
77
|
+
*
|
|
78
|
+
* @returns null | { companies: [{ name, was, now, estimated, people, names }] },
|
|
79
|
+
* one per old entry, most people first: the score it had (`was`), the
|
|
80
|
+
* built-in score its main name has now (`now`, `estimated` when that comes
|
|
81
|
+
* from the network rather than the list), how many people work there now,
|
|
82
|
+
* and every name it covers, as scoring writes them.
|
|
83
|
+
*/
|
|
84
|
+
export function legacyOffer(db = getDb()) {
|
|
85
|
+
if (legacyOfferState(db) !== 'open') return null;
|
|
86
|
+
const rows = scoringRows(db);
|
|
87
|
+
if (!rows.length) return closeEmpty(db);
|
|
88
|
+
const overrides = companyOverrides(db);
|
|
89
|
+
const read = readNetwork(rows, { industryOf: industryKeyOf });
|
|
90
|
+
|
|
91
|
+
const offered = new Map(); // old entry name → what it offers
|
|
92
|
+
const entryOf = new Map(); // company name → that offer
|
|
93
|
+
for (const [name, { headcount, industry }] of read.companies) {
|
|
94
|
+
if (!headcount || overrides.has(name)) continue; // nobody there now, or yours already
|
|
95
|
+
const entry = legacyEntryFor(name);
|
|
96
|
+
if (!entry) continue;
|
|
97
|
+
const now = companyScore(name, { headcount, industry });
|
|
98
|
+
if (now.score === entry.score) continue; // the estimate landed where the list was
|
|
99
|
+
const o = offered.get(entry.name) || { name: entry.name, was: entry.score, names: [], people: new Set() };
|
|
100
|
+
o.names.push({ name, now, headcount });
|
|
101
|
+
offered.set(entry.name, o);
|
|
102
|
+
entryOf.set(name, o);
|
|
103
|
+
}
|
|
104
|
+
if (!offered.size) return closeEmpty(db);
|
|
105
|
+
|
|
106
|
+
// People, each once, across all of an entry's names.
|
|
107
|
+
for (const r of rows) {
|
|
108
|
+
for (const role of read.people.get(r).roles) {
|
|
109
|
+
if (!role.former && entryOf.has(role.company)) entryOf.get(role.company).people.add(r.profile_url || r.id);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const companies = [...offered.values()].map((o) => {
|
|
113
|
+
// Its main name: where most of its people are (then the first by name).
|
|
114
|
+
const main = o.names.reduce((a, b) => (b.headcount > a.headcount || (b.headcount === a.headcount && b.name < a.name) ? b : a));
|
|
115
|
+
return {
|
|
116
|
+
name: o.name,
|
|
117
|
+
was: o.was,
|
|
118
|
+
now: main.now.score,
|
|
119
|
+
estimated: main.now.source !== 'known',
|
|
120
|
+
people: o.people.size,
|
|
121
|
+
names: o.names.map((n) => n.name).sort(),
|
|
122
|
+
};
|
|
123
|
+
});
|
|
124
|
+
companies.sort((a, b) => b.people - a.people || a.name.localeCompare(b.name));
|
|
125
|
+
return { companies };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Close an open offer that has nothing to offer; null, as legacyOffer() says it. */
|
|
129
|
+
function closeEmpty(db) {
|
|
130
|
+
db.prepare("UPDATE app_meta SET value = 'none' WHERE key = ? AND value = 'open'").run(LEGACY_OFFER_KEY);
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* The names an answer keeps, from the body the page sends: {keep: [names]},
|
|
136
|
+
* where an empty list is No thanks. Throws an Error fit to show on anything
|
|
137
|
+
* else.
|
|
138
|
+
*/
|
|
139
|
+
export function parseLegacyAnswer(body) {
|
|
140
|
+
const keep = body?.keep;
|
|
141
|
+
if (!Array.isArray(keep) || keep.length > MAX_ANSWER || !keep.every((n) => typeof n === 'string' && n.length <= 200)) {
|
|
142
|
+
throw new Error('Send the companies to keep as a list of names (an empty list for No thanks).');
|
|
143
|
+
}
|
|
144
|
+
return keep;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Answer the offer: keep, as your own, the old scores of the entries named in
|
|
149
|
+
* `keep` (names from the offer; any other name is ignored), or keep none (No
|
|
150
|
+
* thanks). Either way it is never offered again. The scores and the answer
|
|
151
|
+
* are written together, all or nothing; then everyone is rescored once for
|
|
152
|
+
* the whole batch, and only if something was kept. Answering an offer that
|
|
153
|
+
* isn't open (another window already did) changes nothing.
|
|
154
|
+
*
|
|
155
|
+
* `rescore` is lib/rpc.js rescoreAll; tests pass a counting stand-in.
|
|
156
|
+
* @returns { kept: [{ name, score, names }], scored }
|
|
157
|
+
* @throws when the rescore fails, after the answer is saved: the message says
|
|
158
|
+
* so, `.kept` says what was kept, and the next load of the map rescores.
|
|
159
|
+
*/
|
|
160
|
+
export function answerLegacyOffer(keep = [], { db = getDb(), rescore = rescoreAll } = {}) {
|
|
161
|
+
if (legacyOfferState(db) !== 'open') return { kept: [], scored: 0 };
|
|
162
|
+
const wanted = new Set(keep);
|
|
163
|
+
const kept = (legacyOffer(db)?.companies || [])
|
|
164
|
+
.filter((c) => wanted.has(c.name))
|
|
165
|
+
.map((c) => ({ name: c.name, score: c.was, names: c.names }));
|
|
166
|
+
db.exec('BEGIN');
|
|
167
|
+
try {
|
|
168
|
+
setCompanyScores(db, kept.flatMap((c) => c.names.map((n) => [n, c.score])));
|
|
169
|
+
db.prepare('UPDATE app_meta SET value = ? WHERE key = ?').run(kept.length ? 'kept' : 'declined', LEGACY_OFFER_KEY);
|
|
170
|
+
db.exec('COMMIT');
|
|
171
|
+
} catch (err) {
|
|
172
|
+
db.exec('ROLLBACK');
|
|
173
|
+
throw err;
|
|
174
|
+
}
|
|
175
|
+
if (!kept.length) return { kept, scored: 0 };
|
|
176
|
+
try {
|
|
177
|
+
return { kept, scored: rescore().scored };
|
|
178
|
+
} catch (err) {
|
|
179
|
+
// The kept scores are saved and the stored ones are not yet computed with
|
|
180
|
+
// them. Without a model stamp they count as stale, so the next load of the
|
|
181
|
+
// map redoes it (rpc.js rescoreIfStale), as a Settings save's retry does.
|
|
182
|
+
try { db.prepare("DELETE FROM app_meta WHERE key = 'scoring_version'").run(); } catch { /* then the next change to any score rescores */ }
|
|
183
|
+
throw Object.assign(new Error(`Kept, but rescoring your network failed (${err.message}). It will try again the next time the map loads.`), { kept });
|
|
184
|
+
}
|
|
185
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// The curated company list's scores from before it was made neutral, kept for
|
|
2
|
+
// one thing only: the one-time offer on Paths → Scores to keep them as your
|
|
3
|
+
// own (lib/legacy-offer.js). Nothing scores with these. lib/scoring.js never
|
|
4
|
+
// imports this file, and the model must never read it.
|
|
5
|
+
//
|
|
6
|
+
// The list used to mix well-known companies with picks from one person's
|
|
7
|
+
// career and region (Snap at 9, UCF at 5, a group of 6s from one network),
|
|
8
|
+
// and those picks were everyone's defaults. It now follows a written rule
|
|
9
|
+
// (lib/scoring.js, docs/brain/SCORING.md). Each row here is an entry that the
|
|
10
|
+
// rule removed or rescored, with the score exactly as it was:
|
|
11
|
+
//
|
|
12
|
+
// [name, the score it had, the names it is offered for, its industry]
|
|
13
|
+
//
|
|
14
|
+
// The names and industry are kept because a removed entry no longer
|
|
15
|
+
// canonicalises: "University of Central Florida" used to be read as "UCF" and
|
|
16
|
+
// is now a name of its own. The names are the entry's own company's, as they
|
|
17
|
+
// read once cleaned, and end there ($) unless every company starting so is
|
|
18
|
+
// that company's (UCF's colleges, AdventHealth's hospitals, Havas's
|
|
19
|
+
// agencies). The old aliases were wider and caught other companies too
|
|
20
|
+
// ("Snap-on" and "Specs Optical" read as Snap, "Hard Rock Hotel" as Hard Rock
|
|
21
|
+
// Digital): the old list scored those by mistake, and keeping must not give
|
|
22
|
+
// them its score. Rows are in the old list's order, since the first to match
|
|
23
|
+
// won.
|
|
24
|
+
|
|
25
|
+
export const LEGACY_SCORES = [
|
|
26
|
+
// Rescored: scored like its peers Pinterest, Reddit and X.
|
|
27
|
+
['Snap', 9, /^(snap|snapchat)( inc)?$/, 'media'],
|
|
28
|
+
// Removed: private companies that are neither household names nor Fortune
|
|
29
|
+
// 500-sized, and small startups.
|
|
30
|
+
['Anduril', 9, /^anduril( industries)?$/, 'defense'],
|
|
31
|
+
['Polymarket', 9, /^polymarket$/, 'finance'],
|
|
32
|
+
['Kalshi', 9, /^kalshi$/, 'finance'],
|
|
33
|
+
['Databricks', 8, /^databricks$/, 'tech'],
|
|
34
|
+
['Figma', 8, /^figma$/, 'tech'],
|
|
35
|
+
['Scale AI', 8, /^(scale ai|scale\.ai|scale\.com)$/, 'tech'],
|
|
36
|
+
['Whatnot', 8, /^whatnot$/, 'consumer'],
|
|
37
|
+
// Removed: executive search firms, known to executives rather than to most
|
|
38
|
+
// professionals, and two of the field's five big firms (not its largest).
|
|
39
|
+
['Egon Zehnder', 8, /^egon zehnder( international)?$/, 'consulting'],
|
|
40
|
+
['Heidrick & Struggles', 8, /^heidrick( (&|and) struggles)?$/, 'consulting'],
|
|
41
|
+
// Rescored: a household name, but a company of a few hundred people, so the
|
|
42
|
+
// list's floor (7) rather than "major" (8).
|
|
43
|
+
['MrBeast', 8, /^(mrbeast|beast industries)$/, 'media'],
|
|
44
|
+
// Removed: startups and mid-size companies known inside one field.
|
|
45
|
+
['Whop', 7, /^whop$/, 'tech'],
|
|
46
|
+
['Hims & Hers', 7, /^hims( (&|and) hers( health)?)?$/, 'health'],
|
|
47
|
+
['Riot Games', 7, /^riot games$/, 'entertainment'],
|
|
48
|
+
['Notion', 7, /^notion( labs)?$/, 'tech'],
|
|
49
|
+
['Vercel', 7, /^vercel$/, 'tech'],
|
|
50
|
+
['Plaid', 7, /^plaid$/, 'finance'],
|
|
51
|
+
['Brex', 7, /^brex$/, 'finance'],
|
|
52
|
+
['Ramp', 7, /^ramp$/, 'finance'],
|
|
53
|
+
['Mercury', 7, /^mercury$/, 'finance'],
|
|
54
|
+
['Chime', 7, /^chime$/, 'finance'],
|
|
55
|
+
// Removed: the only military branch on the list (no Army, Navy, Air Force or
|
|
56
|
+
// Marines), so a regional pick rather than a rule.
|
|
57
|
+
['U.S. Space Force', 7, /^((u\.?s\.?|united states) )?space force$/, 'defense'],
|
|
58
|
+
// Removed: the "notable" 6s, drawn from one network and one region, and the
|
|
59
|
+
// one "local institution".
|
|
60
|
+
['Grindr', 6, /^grindr$/, 'tech'],
|
|
61
|
+
['Genius Sports', 6, /^genius sports( group)?$/, 'entertainment'],
|
|
62
|
+
['Later', 6, /^later$/, 'media'],
|
|
63
|
+
['Hard Rock Digital', 6, /^hard rock digital$/, 'entertainment'],
|
|
64
|
+
['Vanta', 6, /^vanta$/, 'tech'],
|
|
65
|
+
['Kaseya', 6, /^kaseya$/, 'tech'],
|
|
66
|
+
['Havas', 6, /^havas\b/, 'media'],
|
|
67
|
+
['VaynerMedia', 6, /^vayner ?media$/, 'media'],
|
|
68
|
+
['AdventHealth', 6, /^adventhealth\b/, 'health'],
|
|
69
|
+
['University of Florida', 6, /^(uf|university of florida)$/, 'education'],
|
|
70
|
+
['The Athletic', 6, /^the athletic( media( company)?)?$/, 'media'],
|
|
71
|
+
['UCF', 5, /^(ucf|university of central florida)\b/, 'education'],
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
// Where the offer's state lives in app_meta. lib/rpc.js rescoreAll() sets it
|
|
75
|
+
// to 'open' the first time it replaces scores that came from the old list;
|
|
76
|
+
// answering sets it to 'kept' or 'declined', and nothing sets it back.
|
|
77
|
+
export const LEGACY_OFFER_KEY = 'legacy_scores_offer';
|