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/scoring.js
CHANGED
|
@@ -12,21 +12,29 @@
|
|
|
12
12
|
// the first part that names a job, skipping "Ex-" and
|
|
13
13
|
// "Former" parts; student clubs and fraternities don't
|
|
14
14
|
// count as company titles.
|
|
15
|
-
// company weight 0.45 + 0.055 × company score, so 0.
|
|
16
|
-
// up to 1.0 (a company scored 10)
|
|
15
|
+
// company weight 0.45 + 0.055 × company score, so 0.615 when no company
|
|
16
|
+
// is found (score 3) up to 1.0 (a company scored 10);
|
|
17
|
+
// 0.505 is the floor, for a company you score 1.
|
|
17
18
|
// reach bonus (≤1.5) real signals only, matched as whole words: investor,
|
|
18
|
-
// YC,
|
|
19
|
-
// TEDx/keynote/patents
|
|
19
|
+
// YC, 30 under 30, audience or revenue in the millions,
|
|
20
|
+
// TEDx/keynote/patents, and the top honours of every
|
|
21
|
+
// field (a Nobel, a Pulitzer, an Oscar, an Olympic
|
|
22
|
+
// medal…). Halved at a company scored 4 or less,
|
|
23
|
+
// judged before any sector lean.
|
|
20
24
|
// bridge boost (≤1) for a 1st-degree person whose circle is mapped, when
|
|
21
25
|
// their circle is unusually strong. Recomputed each time,
|
|
22
26
|
// never ratcheted upward.
|
|
23
27
|
//
|
|
24
28
|
// Company scores come from, in order: the score you set, a curated list of
|
|
25
29
|
// well-known companies, and otherwise how many of your people work there.
|
|
30
|
+
// If you picked sectors in Settings, a company in one of them gets +1 or +2
|
|
31
|
+
// on top (never above 10, never on a score you set). That is worked out fresh
|
|
32
|
+
// on every rescore and never written into your company scores.
|
|
26
33
|
// Everything here is a plain function; tests/scoring.test.mjs pins it down.
|
|
27
34
|
|
|
28
35
|
// Bump when the model changes: the app rescores stored rows once on next load.
|
|
29
|
-
|
|
36
|
+
// 3: one industry per company (not per person), and the sector lean.
|
|
37
|
+
export const SCORING_VERSION = 3;
|
|
30
38
|
|
|
31
39
|
// ── title ────────────────────────────────────────────────────────────────────
|
|
32
40
|
|
|
@@ -65,12 +73,72 @@ const TITLE_RULES = [
|
|
|
65
73
|
];
|
|
66
74
|
|
|
67
75
|
const INTERN = /\b(intern|internship|trainee|apprentice)\b/;
|
|
68
|
-
|
|
76
|
+
// Matched against lowercase text. "CS @ University of Utah" is a student; a
|
|
77
|
+
// school's short name ("CS @ NYU") is SHORT_NAME's job, below.
|
|
78
|
+
const STUDENT = /\b(student|undergrad(uate)?|aspiring|phd candidate|recent grad(uate)?|class of 20\d\d|incoming (analyst|intern|associate|swe|engineer)|gpa|freshman|sophomore|b\.?s\.? candidate)\b|^\s*(honors )?(cs|computer science|ms ?cs|mscs|bscs|engineering|finance|marketing|business|economics|accounting|biology|psychology|information technology)\b.*(\s(@|at)\s*)(university|college)\b/;
|
|
79
|
+
const MAJOR = /^\s*(honors )?(cs|computer science|ms ?cs|mscs|bscs|engineering|finance|marketing|business|economics|accounting|biology|psychology|information technology)\b/i;
|
|
80
|
+
// A school's short name as people write it, in capitals: two to four letters
|
|
81
|
+
// that start or end with the U of "University" (USC, NYU, UCLA, BYU, UNC). It
|
|
82
|
+
// names no school, so every school's students and clubs read alike. Matched
|
|
83
|
+
// against the text as written: only capitals are a name ("usc" isn't one).
|
|
84
|
+
const SHORT_NAME = /\b(?:U[A-Z]{1,3}|[A-Z]{1,3}U)\b/g;
|
|
85
|
+
const AT_SHORT_NAME = /\s(?:@|[Aa][Tt])\s*(U[A-Z]{1,3}|[A-Z]{1,3}U)\b/;
|
|
86
|
+
// Short names that aren't a school's, whatever they look like: countries and
|
|
87
|
+
// unions of them, the AAU's youth sports clubs and a Unitarian (UU) society;
|
|
88
|
+
// and companies, a league and labor unions people write in capitals (UBS,
|
|
89
|
+
// UFC, UPMC, UnitedHealth Group and UnitedHealthcare, Under Armour, UL, United
|
|
90
|
+
// Launch Alliance, UTC; the UAW, USW, SEIU, UFCW and UFT). A short name the
|
|
91
|
+
// curated list knows as a company is one too (UPS). The price: a school whose
|
|
92
|
+
// short name is on this list (UA, UL, UTC) isn't read from it.
|
|
93
|
+
const NOT_A_SCHOOL = new Set(['US', 'USA', 'UK', 'UAE', 'EU', 'UN', 'UU', 'AAU',
|
|
94
|
+
'UBS', 'UFC', 'UPMC', 'UHG', 'UHC', 'UA', 'UL', 'ULA', 'UTC', 'UAW', 'USW', 'SEIU', 'UFCW', 'UFT']);
|
|
69
95
|
const GREEK = '(alpha|beta|gamma|delta|epsilon|zeta|eta|theta|iota|kappa|lambda|mu|nu|xi|omicron|pi|rho|sigma|tau|upsilon|phi|chi|psi|omega)';
|
|
70
96
|
const STUDENT_ORG = new RegExp(`\\b(fraternity|sorority|student (government|association|union|org(anization)?|senate)|honor society|campus)\\b|\\b${GREEK}\\s+${GREEK}\\b`);
|
|
71
|
-
|
|
97
|
+
// A club named with a school's words, either way round ("Marketing Club at the
|
|
98
|
+
// University of Utah", "University Consulting Club"). An association only
|
|
99
|
+
// counts before the school's words: "University Staff Association" is staff.
|
|
100
|
+
const SCHOOL_WORD_CLUB = /\b(club|chapter|society|association)\b.*\b(university|college|school|student)\b|\b(university|college|school|student)\b.*\b(club|chapter|society)\b/;
|
|
101
|
+
const CLUB = /\b(club|chapter|society|association)\b/;
|
|
102
|
+
// With a school's short name only a club or a society is a student group
|
|
103
|
+
// ("USC Marketing Club", "NYU Finance Society"), or an association named for
|
|
104
|
+
// a field of study ("USC Trojan Marketing Association"). A chapter is as often
|
|
105
|
+
// a union local's or a professional body's, and another association a
|
|
106
|
+
// university's staff ("KU Endowment Association").
|
|
107
|
+
const SHORT_NAME_CLUB = /\b(club|society)\b/;
|
|
108
|
+
const FIELD_ASSOCIATION = /\b(marketing|finance|financial|business|economics|accounting|consulting|investment|investing|entrepreneurship|engineering|computer science|data science|nursing|pre-?law|pre-?med|students?)\b.*\bassociation\b/;
|
|
109
|
+
// An alumni club or a parents' association is for grown-ups.
|
|
110
|
+
const GROWN_UP = /\b(alumni|alumnae|alumnus|alum|parents?)\b/;
|
|
72
111
|
const FORMER = /^\s*(ex[-\s]|former(ly)?\b|previously\b|prev\b|past\b|retired\b)/i;
|
|
73
112
|
|
|
113
|
+
/** Can this short name ("USC", "UBS") be a school's? Not one on NOT_A_SCHOOL, nor a company the curated list knows. */
|
|
114
|
+
function schoolShortName(name) {
|
|
115
|
+
if (NOT_A_SCHOOL.has(name) || /^US[A-Z]{2}$/.test(name)) return false; // USGA, USTA, USAA, USPS…
|
|
116
|
+
const listed = cleanCompany(name);
|
|
117
|
+
return !KNOWN_INDUSTRY.has(listed) || KNOWN_INDUSTRY.get(listed) === 'education';
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Does this part of a headline say they're studying now? "CS @ NYU", "Economics student". */
|
|
121
|
+
function studies(part) {
|
|
122
|
+
if (STUDENT.test(` ${part.toLowerCase()} `)) return true;
|
|
123
|
+
const at = MAJOR.test(part) && AT_SHORT_NAME.exec(part);
|
|
124
|
+
return !!at && schoolShortName(at[1]);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Is this part of a headline a role in a school club? "President, USC
|
|
129
|
+
* Marketing Club", "VP, NYU Finance Society", "President of the Marketing Club
|
|
130
|
+
* at the University of Utah". A club, chapter, society or association with a
|
|
131
|
+
* school's words, or a club or society with its short name, and not an alumni
|
|
132
|
+
* or parents' group.
|
|
133
|
+
*/
|
|
134
|
+
function isSchoolClub(part) {
|
|
135
|
+
const lower = part.toLowerCase();
|
|
136
|
+
if (!CLUB.test(lower) || GROWN_UP.test(lower)) return false;
|
|
137
|
+
if (SCHOOL_WORD_CLUB.test(lower)) return true;
|
|
138
|
+
if (!SHORT_NAME_CLUB.test(lower) && !FIELD_ASSOCIATION.test(lower)) return false;
|
|
139
|
+
return [...part.matchAll(SHORT_NAME)].some(([name]) => schoolShortName(name));
|
|
140
|
+
}
|
|
141
|
+
|
|
74
142
|
/** The parts of a headline, in order: "CEO @ X | Ex-Google | Speaker" → three parts. */
|
|
75
143
|
export function headlineParts(headline = '') {
|
|
76
144
|
return String(headline || '').split(/\s*(?:\||•|·|⋅|\/\/| – | — | - |;)\s*/).map((s) => s.trim()).filter(Boolean);
|
|
@@ -79,7 +147,7 @@ export function headlineParts(headline = '') {
|
|
|
79
147
|
function readPart(part) {
|
|
80
148
|
let s = ` ${part.toLowerCase()} `;
|
|
81
149
|
if (INTERN.test(s)) return LEVELS.intern;
|
|
82
|
-
if (
|
|
150
|
+
if (studies(part)) return LEVELS.student;
|
|
83
151
|
let best = null;
|
|
84
152
|
for (const [re, key] of TITLE_RULES) {
|
|
85
153
|
re.lastIndex = 0;
|
|
@@ -87,15 +155,14 @@ function readPart(part) {
|
|
|
87
155
|
s = s.replace(re, ' ');
|
|
88
156
|
if (key && (!best || LEVELS[key].level > best.level || (LEVELS[key].level === best.level && LEVELS[key].points > best.points))) best = LEVELS[key];
|
|
89
157
|
}
|
|
90
|
-
|
|
91
|
-
if (best && best.level >= 3 && (STUDENT_ORG.test(lower) || SCHOOL_CLUB.test(lower))) return { ...LEVELS.student, label: 'Student (club role)' };
|
|
158
|
+
if (best && best.level >= 3 && (STUDENT_ORG.test(part.toLowerCase()) || isSchoolClub(part))) return { ...LEVELS.student, label: 'Student (club role)' };
|
|
92
159
|
return best;
|
|
93
160
|
}
|
|
94
161
|
|
|
95
162
|
/**
|
|
96
163
|
* Every role a headline names: "CEO @ X | Ex-VP at Google | Speaker" gives a
|
|
97
164
|
* current CEO role at X and a former VP role at Google. A part that names only
|
|
98
|
-
* a company ("GTM @
|
|
165
|
+
* a company ("GTM @ Quillon") still says they work there.
|
|
99
166
|
*/
|
|
100
167
|
export function readRoles(headline = '', role = '') {
|
|
101
168
|
const out = [];
|
|
@@ -112,7 +179,7 @@ export function readRoles(headline = '', role = '') {
|
|
|
112
179
|
|
|
113
180
|
/** Is this person a student right now? Then no other title makes them powerful yet. */
|
|
114
181
|
export function isStudent(headline = '') {
|
|
115
|
-
return headlineParts(headline).some((p) => !FORMER.test(p) && (
|
|
182
|
+
return headlineParts(headline).some((p) => !FORMER.test(p) && (studies(p) || INTERN.test(` ${p.toLowerCase()} `)));
|
|
116
183
|
}
|
|
117
184
|
|
|
118
185
|
/** The person's first current title (a quick read; scoring weighs every role). */
|
|
@@ -125,109 +192,292 @@ export function readTitle(headline = '', role = '') {
|
|
|
125
192
|
// ── company ──────────────────────────────────────────────────────────────────
|
|
126
193
|
|
|
127
194
|
// Well-known companies, scored for what a person there can usually open up.
|
|
128
|
-
// These are defaults
|
|
129
|
-
//
|
|
195
|
+
// These are the defaults for everyone who runs the app, so one written rule
|
|
196
|
+
// decides who is on the list, not anyone's own ties to a company. A company is
|
|
197
|
+
// on it only if most US professionals would recognise it:
|
|
198
|
+
// - a household-name brand;
|
|
199
|
+
// - a Fortune 500 company, or a public company as large;
|
|
200
|
+
// - a top global VC, private equity, consulting, law or accounting firm;
|
|
201
|
+
// - a frontier AI lab;
|
|
202
|
+
// - a top national university.
|
|
203
|
+
// Scores follow one scale: 10 the largest tech platforms and the frontier AI
|
|
204
|
+
// labs, 9 elite (the most sought-after employers), 8 major, 7 well-known.
|
|
205
|
+
// Nothing on the list is below 7. Regional picks, picks from one person's
|
|
206
|
+
// career or network, and small startups are not on it: they are estimated
|
|
207
|
+
// from the network like any other company (4, or 5–6 when many of the
|
|
208
|
+
// network's people work there), and anyone can score them on Paths → Scores,
|
|
209
|
+
// where a score set always wins. When in doubt, a company stays off: the
|
|
210
|
+
// estimate is the neutral default.
|
|
211
|
+
//
|
|
212
|
+
// The rule is applied from named sources, the same way in every field, so
|
|
213
|
+
// anyone can check the list or extend it: the Fortune 500, the largest law
|
|
214
|
+
// firms, one ranking of universities and the others docs/brain/SCORING.md
|
|
215
|
+
// names. An addition is 8 at the size of the Fortune 100 (over about $45
|
|
216
|
+
// billion a year), as nearly every such company already here is, and 7 below
|
|
217
|
+
// it; where the list already scores its kind of firm otherwise, it follows
|
|
218
|
+
// those peers (private equity at 9 with Bain Capital, the largest law firms at
|
|
219
|
+
// 8 with the Big Four, universities by their rank). SCORING.md also says what
|
|
220
|
+
// applying the rule changed; lib/legacy-scores.js keeps the old scores, for
|
|
221
|
+
// the one-time offer to keep them (never for scoring).
|
|
222
|
+
//
|
|
223
|
+
// Aliases are matched from the start of the (cleaned) company name, as whole
|
|
224
|
+
// words, so they are kept narrow. A name that other companies also start with
|
|
225
|
+
// (Ford, Delta, Fidelity, Warner, Apollo, Columbia, Chase) matches only in the
|
|
226
|
+
// forms its company uses, ending there ($), and never as a short form that is
|
|
227
|
+
// also a word or a title: "GM" is a general manager. A dealership, a bottler
|
|
228
|
+
// or a franchise has staff of its own ("Toyota of …", "Coca-Cola
|
|
229
|
+
// Consolidated"), and so do companies that share a name or were spun off
|
|
230
|
+
// (Fidelity National Financial, Warner Music Group, J&J Snack Foods, Merck
|
|
231
|
+
// Millipore, Chase Brass). Only a name no other company starts with stays open
|
|
232
|
+
// (\b): Costco, Pfizer, "Deloitte Digital". tests/known-companies.test.mjs
|
|
233
|
+
// holds the names each must read and the names that must stay their own.
|
|
234
|
+
//
|
|
235
|
+
// The last field is the company's industry, one of lib/companies.js's
|
|
236
|
+
// INDUSTRIES keys (this file imports nothing, so they are plain strings;
|
|
237
|
+
// tests/companies.test.mjs checks each one). Most of these names carry no
|
|
238
|
+
// industry word, so without it Adobe, Pfizer or MIT would take whatever their
|
|
239
|
+
// people's headlines happen to say. Where a name does say something
|
|
240
|
+
// (YouTube → media, Tesla → industry), the field agrees with it.
|
|
241
|
+
|
|
242
|
+
// A brand's name on an arena or a stadium is not the brand's company: "State
|
|
243
|
+
// Farm Arena" and "AT&T Stadium" have staff of their own. Brands that name
|
|
244
|
+
// well-known venues are matched with this.
|
|
245
|
+
const VENUE = String.raw`(?! (performing arts )?(arena|stadium|center|centre|field|park|ballpark|dome|amphitheat(er|re)|theat(er|re)|pavilion|forum|coliseum|speedway)\b)`;
|
|
246
|
+
const sponsor = (names) => new RegExp(String.raw`^(${names})\b${VENUE}`);
|
|
247
|
+
// A university by its own names: the name alone or with "University", and its
|
|
248
|
+
// schools ("Harvard Business School", "Yale School of Management", "Stanford
|
|
249
|
+
// University School of Medicine"). Not a hospital, health plan, lab or
|
|
250
|
+
// company that shares its name (Harvard Pilgrim Health Care, Stanford Health
|
|
251
|
+
// Care, Vanderbilt University Medical Center, Brown University Health), nor
|
|
252
|
+
// another school that does (Princeton High School). `more` adds a form of its own.
|
|
253
|
+
const SCHOOLS = String.raw`( [a-z]+)?( graduate)? school of [a-z ,&]+| (business|law|medical|divinity|nursing|dental|journalism|graduate|extension|kennedy) school`;
|
|
254
|
+
const university = (names, more = '') => new RegExp(String.raw`^(${names})( university)?(${SCHOOLS}${more})?$`);
|
|
255
|
+
|
|
130
256
|
export const KNOWN_COMPANIES = [
|
|
131
|
-
// 10 — the largest platforms and the frontier AI labs
|
|
132
|
-
['Google', 10, /^(google|
|
|
133
|
-
['Apple', 10, /^apple( inc)
|
|
134
|
-
['Microsoft', 10, /^microsoft\b
|
|
135
|
-
['Amazon', 10, /^(amazon|aws|amazon web services)\b
|
|
136
|
-
['Meta', 10, /^(
|
|
137
|
-
['NVIDIA', 10, /^nvidia\b
|
|
138
|
-
['OpenAI', 10, /^(openai|open ai)\b
|
|
139
|
-
['Anthropic', 10, /^anthropic\b
|
|
140
|
-
['Tesla', 10, /^tesla( motors| inc)
|
|
141
|
-
// 9 — elite
|
|
142
|
-
|
|
143
|
-
['Netflix', 9, /^netflix\b
|
|
144
|
-
['YouTube', 9, /^youtube\b
|
|
145
|
-
['LinkedIn', 9, /^linkedin\b
|
|
146
|
-
['TikTok', 9, /^(tiktok|bytedance)\b
|
|
147
|
-
['SpaceX', 9, /^spacex\b
|
|
148
|
-
['Stripe', 9, /^stripe\b
|
|
149
|
-
['Palantir', 9, /^palantir\b
|
|
150
|
-
['
|
|
151
|
-
['
|
|
152
|
-
['
|
|
153
|
-
['
|
|
154
|
-
['
|
|
155
|
-
['
|
|
156
|
-
['
|
|
157
|
-
['
|
|
158
|
-
['
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
['Bain', 9, /^bain
|
|
162
|
-
['Procter & Gamble', 9, /^(p&g|procter)\b
|
|
163
|
-
['Sequoia', 9, /^sequoia( capital)
|
|
164
|
-
['Andreessen Horowitz', 9, /^(a16z|andreessen)\b
|
|
165
|
-
['Y Combinator', 9, /^(y combinator|yc)
|
|
166
|
-
//
|
|
167
|
-
|
|
168
|
-
['
|
|
169
|
-
['
|
|
170
|
-
|
|
171
|
-
['
|
|
172
|
-
['
|
|
173
|
-
['
|
|
174
|
-
['
|
|
175
|
-
['
|
|
176
|
-
['
|
|
177
|
-
['
|
|
178
|
-
['
|
|
179
|
-
['
|
|
180
|
-
['
|
|
181
|
-
['
|
|
182
|
-
['
|
|
183
|
-
['
|
|
184
|
-
['
|
|
185
|
-
['
|
|
186
|
-
['
|
|
187
|
-
['
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
['
|
|
191
|
-
['
|
|
192
|
-
['
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
['
|
|
199
|
-
['
|
|
200
|
-
['
|
|
201
|
-
|
|
202
|
-
['
|
|
203
|
-
['
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
['
|
|
257
|
+
// 10 — the largest tech platforms and the frontier AI labs
|
|
258
|
+
['Google', 10, /^(google|deepmind)\b|^alphabet$/, 'tech'],
|
|
259
|
+
['Apple', 10, /^apple( inc)?$/, 'tech'],
|
|
260
|
+
['Microsoft', 10, /^microsoft\b/, 'tech'],
|
|
261
|
+
['Amazon', 10, /^(amazon|aws|amazon web services)\b(?! (conservation|watch)\b)/, 'tech'],
|
|
262
|
+
['Meta', 10, /^meta( platforms)?$|^(facebook|instagram|whatsapp|reality labs)\b/, 'tech'],
|
|
263
|
+
['NVIDIA', 10, /^nvidia\b/, 'tech'],
|
|
264
|
+
['OpenAI', 10, /^(openai|open ai)\b/, 'tech'],
|
|
265
|
+
['Anthropic', 10, /^anthropic\b/, 'tech'],
|
|
266
|
+
['Tesla', 10, /^tesla( motors| inc)?$/, 'industry'],
|
|
267
|
+
// 9 — elite: the most sought-after employers in tech, finance, consulting,
|
|
268
|
+
// investing and consumer brands
|
|
269
|
+
['Netflix', 9, /^netflix\b/, 'entertainment'],
|
|
270
|
+
['YouTube', 9, /^youtube\b/, 'media'],
|
|
271
|
+
['LinkedIn', 9, /^linkedin\b/, 'tech'],
|
|
272
|
+
['TikTok', 9, /^(tiktok|bytedance)\b/, 'media'],
|
|
273
|
+
['SpaceX', 9, /^spacex\b/, 'defense'],
|
|
274
|
+
['Stripe', 9, /^stripe\b/, 'finance'],
|
|
275
|
+
['Palantir', 9, /^palantir\b/, 'tech'],
|
|
276
|
+
['Coca-Cola', 9, /^(the )?coca[-\s]cola( company| north america)?$/, 'consumer'],
|
|
277
|
+
['Disney', 9, /^(the walt |walt )?disney\b/, 'entertainment'],
|
|
278
|
+
['Goldman Sachs', 9, /^goldman sachs\b|^goldman$/, 'finance'],
|
|
279
|
+
['JPMorgan Chase', 9, /^(jp ?morgan|j\.p\. morgan|jpmorgan chase)\b|^chase( bank| home lending| card services| auto| for business)?$/, 'finance'],
|
|
280
|
+
['Morgan Stanley', 9, /^morgan stanley\b/, 'finance'],
|
|
281
|
+
['BlackRock', 9, /^blackrock\b/, 'finance'],
|
|
282
|
+
['McKinsey', 9, /^mckinsey\b/, 'consulting'],
|
|
283
|
+
['BCG', 9, /^(bcg|boston consulting group)\b/, 'consulting'],
|
|
284
|
+
['Bain', 9, /^bain( & company| and company)?$/, 'consulting'],
|
|
285
|
+
// A private equity firm, not Bain & Company: its own entry (so its own
|
|
286
|
+
// industry), at the 9 it had when it was an alias of Bain.
|
|
287
|
+
['Bain Capital', 9, /^bain capital$/, 'finance'],
|
|
288
|
+
['Procter & Gamble', 9, /^(p&g|procter)\b/, 'consumer'],
|
|
289
|
+
['Sequoia', 9, /^sequoia( capital)?$/, 'finance'],
|
|
290
|
+
['Andreessen Horowitz', 9, /^(a16z|andreessen)\b/, 'finance'],
|
|
291
|
+
['Y Combinator', 9, /^(y combinator|yc)$/, 'finance'],
|
|
292
|
+
// The private equity firms in the Fortune 500, peers of Bain Capital. Apollo
|
|
293
|
+
// only by its full name: Apollo Hospitals, Apollo.io and others share "Apollo".
|
|
294
|
+
['Blackstone', 9, /^blackstone( group)?$/, 'finance'], ['KKR', 9, /^(kkr|kohlberg kravis roberts)\b/, 'finance'],
|
|
295
|
+
['Apollo Global Management', 9, /^apollo( global( management)?| management)$/, 'finance'],
|
|
296
|
+
// 8 — major companies and institutions
|
|
297
|
+
['Salesforce', 8, /^salesforce\b/, 'tech'], ['Adobe', 8, /^adobe( systems)?$/, 'tech'], ['Oracle', 8, /^oracle( america| health| netsuite| cloud( infrastructure)?| financial services( software)?)?$/, 'tech'], ['IBM', 8, /^ibm\b/, 'tech'],
|
|
298
|
+
['Uber', 8, /^uber( technologies| eats)?$/, 'tech'], ['Airbnb', 8, /^airbnb\b/, 'tech'], ['DoorDash', 8, /^doordash\b/, 'tech'], ['Spotify', 8, /^spotify\b/, 'entertainment'],
|
|
299
|
+
['Snap', 8, /^(snap|snapchat)( inc)?$/, 'media'], ['Pinterest', 8, /^pinterest\b/, 'media'], ['Reddit', 8, /^reddit\b/, 'media'], ['X', 8, /^(x corp|twitter)\b/, 'media'],
|
|
300
|
+
['Roblox', 8, /^roblox\b/, 'entertainment'], ['Shopify', 8, /^shopify\b(?! (plus )?partners?\b)/, 'consumer'], ['Snowflake', 8, /^snowflake( computing)?$/, 'tech'], ['Datadog', 8, /^datadog\b/, 'tech'],
|
|
301
|
+
['CrowdStrike', 8, /^crowdstrike\b/, 'tech'], ['ServiceNow', 8, /^servicenow\b/, 'tech'], ['Canva', 8, /^canva\b/, 'tech'],
|
|
302
|
+
['Intuit', 8, /^intuit\b/, 'tech'], ['Cisco', 8, /^cisco\b/, 'tech'], ['Qualcomm', 8, /^qualcomm\b/, 'tech'], ['AMD', 8, /^amd\b/, 'tech'],
|
|
303
|
+
['Samsung', 8, /^samsung\b/, 'tech'], ['Sony', 8, /^sony\b/, 'entertainment'], ['Epic Games', 8, /^epic games\b/, 'entertainment'],
|
|
304
|
+
['Coinbase', 8, /^coinbase\b/, 'finance'], ['Robinhood', 8, /^robinhood\b/, 'finance'], ['Block', 8, /^(block|square|cash app)$/, 'finance'],
|
|
305
|
+
['Visa', 8, /^visa( inc)?$/, 'finance'], ['Mastercard', 8, /^mastercard\b/, 'finance'], ['American Express', 8, /^(american express|amex)\b/, 'finance'],
|
|
306
|
+
['Capital One', 8, sponsor('capital one'), 'finance'], ['BNY', 8, /^(bny|bny mellon|bank of new york)\b/, 'finance'], ['Citi', 8, /^(citi( private bank)?|citigroup|citibank)$/, 'finance'],
|
|
307
|
+
['Wells Fargo', 8, sponsor('wells fargo'), 'finance'], ['Bank of America', 8, new RegExp(String.raw`^bank of america\b${VENUE}|^merrill( lynch( wealth management)?| edge)?$`), 'finance'], ['Fidelity', 8, /^fidelity( investments| institutional| management (&|and) research( company)?)?$/, 'finance'],
|
|
308
|
+
['Deloitte', 8, /^deloitte\b/, 'consulting'], ['PwC', 8, /^(pwc|pricewaterhouse)\b/, 'consulting'], ['EY', 8, /^(ey|ernst & young)$/, 'consulting'], ['KPMG', 8, /^kpmg\b/, 'consulting'],
|
|
309
|
+
['Accenture', 8, /^accenture\b/, 'consulting'],
|
|
310
|
+
['Nike', 8, /^nike\b/, 'consumer'], ['PepsiCo', 8, /^pepsico\b|^pepsi( beverages( company)?| cola( company)?)?$/, 'consumer'], ['Walmart', 8, /^walmart\b/, 'consumer'], ['Starbucks', 8, /^starbucks\b/, 'consumer'],
|
|
311
|
+
['Unilever', 8, /^unilever\b/, 'consumer'], ['LVMH', 8, /^lvmh\b/, 'consumer'], ["L'Oréal", 8, /^l'?or[eé]al\b/, 'consumer'], ['Red Bull', 8, sponsor('red bull'), 'consumer'],
|
|
312
|
+
['NBCUniversal', 8, /^(nbcuniversal|nbc universal|nbc)\b/, 'entertainment'], ['Warner Bros. Discovery', 8, /^(warner (bros|brothers)( discovery| pictures| entertainment| television( studios)?| games| animation| studios)?|warner ?media|wbd)$/, 'entertainment'],
|
|
313
|
+
['Lockheed Martin', 8, /^lockheed\b/, 'defense'], ['Northrop Grumman', 8, /^northrop\b/, 'defense'], ['Boeing', 8, /^boeing\b/, 'defense'], ['RTX', 8, /^(rtx|raytheon)\b/, 'defense'],
|
|
314
|
+
['General Dynamics', 8, /^general dynamics\b/, 'defense'], ['Blue Origin', 8, /^blue origin\b/, 'defense'], ['Siemens', 8, /^siemens\b(?! (energy|healthineers|gamesa)\b)/, 'industry'],
|
|
315
|
+
// Mitsubishi's makers, not the group's bank, lender or landlord (MUFG, Mitsubishi HC Capital, Mitsubishi Estate).
|
|
316
|
+
['Mitsubishi', 8, /^mitsubishi\b(?! (ufj|hc capital|estate)\b)/, 'industry'], ['Toyota', 8, /^toyota( motor( north america| corporation| manufacturing( [a-z]+){0,2}| sales( usa)?)?| north america| financial services| research institute( of north america)?)?$/, 'industry'], ['Johnson & Johnson', 8, /^johnson (&|and) johnson\b|^j&j$/, 'health'],
|
|
317
|
+
['Pfizer', 8, /^pfizer\b/, 'health'], ['UnitedHealth', 8, /^(unitedhealth(care)?|united ?health ?care|united health group|optum(rx|care|health|insight|serve)?)\b/, 'health'], ['World Bank', 8, /^world bank\b/, 'finance'],
|
|
318
|
+
['NASA', 8, /^nasa\b(?! federal credit union\b)/, 'defense'], ['Stanford University', 8, university('stanford', '| gsb'), 'education'], ['Harvard University', 8, university('harvard', '| college'), 'education'], ['MIT', 8, /^(mit|massachusetts institute of technology)$/, 'education'],
|
|
319
|
+
// Added from the sources in docs/brain/SCORING.md, at 8: the Fortune 100
|
|
320
|
+
// (2025) and companies as large, whether based elsewhere, private or
|
|
321
|
+
// nonprofit. Health insurers, health systems and drugmakers. Aetna is part
|
|
322
|
+
// of CVS Health but keeps its own name, as people do. Merck KGaA is a
|
|
323
|
+
// different company from Merck.
|
|
324
|
+
['CVS Health', 8, /^(cvs|cvshealth)\b/, 'health'], ['Aetna', 8, /^aetna\b/, 'health'], ['Cigna', 8, /^cigna\b/, 'health'], ['Humana', 8, /^humana\b/, 'health'],
|
|
325
|
+
['Elevance Health', 8, /^elevance\b|^anthem( blue cross( (and |& )?blue shield)?| inc| health plans)?$/, 'health'],
|
|
326
|
+
['HCA Healthcare', 8, /^hca\b/, 'health'], ['Kaiser Permanente', 8, /^kaiser( (permanente|foundation (health plan|hospitals))\b|$)/, 'health'],
|
|
327
|
+
['Merck', 8, /^merck\b(?! (kgaa|group|millipore)\b)/, 'health'], ['AbbVie', 8, /^abbvie\b/, 'health'], ['Eli Lilly', 8, /^eli lilly\b|^lilly( usa)?$/, 'health'],
|
|
328
|
+
['Bristol Myers Squibb', 8, /^bristol[-\s]?myers\b/, 'health'], ['AstraZeneca', 8, /^astrazeneca\b/, 'health'],
|
|
329
|
+
['Bayer', 8, /^bayer( ag| u\.?s\.?| crop ?science| pharmaceuticals| healthcare| consumer health)?$/, 'health'],
|
|
330
|
+
// Retail, grocery, food and drink. Lowes Foods is a different grocer.
|
|
331
|
+
['Costco', 8, /^costco\b/, 'consumer'], ['The Home Depot', 8, sponsor('home depot'), 'consumer'], ['Kroger', 8, sponsor('kroger'), 'consumer'],
|
|
332
|
+
['IKEA', 8, /^ikea\b/, 'consumer'], ['Aldi', 8, /^aldi\b/, 'consumer'], ['7-Eleven', 8, /^7[-\s]?eleven\b/, 'consumer'],
|
|
333
|
+
['Walgreens', 8, /^walgreens?\b/, 'consumer'], ["Lowe's", 8, /^lowe['’]?s( companies| home improvement)?$/, 'consumer'],
|
|
334
|
+
['Albertsons', 8, /^albertsons( companies)?$/, 'consumer'], ['TJX', 8, /^(tjx( companies)?|tj ?maxx)$/, 'consumer'],
|
|
335
|
+
['Tyson Foods', 8, /^tyson( foods)?$/, 'consumer'], ['Nestlé', 8, /^nestl[eé](?![a-z])/, 'consumer'],
|
|
336
|
+
['Anheuser-Busch', 8, /^(anheuser[-\s]busch|ab[-\s]?inbev)\b/, 'consumer'], ['Mars', 8, /^mars( inc| incorporated| wrigley| petcare| snacking)?$/, 'consumer'],
|
|
337
|
+
// Smaller than the Fortune 100, but 8 like Starbucks, the other restaurant company here.
|
|
338
|
+
["McDonald's", 8, /^mcdonald['’]?s\b/, 'consumer'],
|
|
339
|
+
// Automakers, by the makers' own names only.
|
|
340
|
+
['General Motors', 8, /^general motors\b/, 'industry'], ['Ford', 8, /^ford( motor( company| credit( company)?)?)?$/, 'industry'],
|
|
341
|
+
['Volkswagen', 8, /^(volkswagen( group( of america)?| of america| ag)?|vw)$/, 'industry'],
|
|
342
|
+
['Mercedes-Benz', 8, /^mercedes[-\s]benz( group| usa| ag| u\.s)?$/, 'industry'], ['BMW', 8, /^bmw( group| ag| of north america| manufacturing)?$/, 'industry'],
|
|
343
|
+
['Honda', 8, /^(american )?honda( motor( company)?| north america| aircraft( company)?| r&d( americas)?)?$/, 'industry'],
|
|
344
|
+
['Hyundai', 8, /^hyundai( motor( company| america| group| north america)?)?$/, 'industry'], ['Kia', 8, /^kia( motors?| america| motors america| georgia)?$/, 'industry'],
|
|
345
|
+
['Nissan', 8, /^nissan( motor( company| corporation)?| north america| americas)?$/, 'industry'],
|
|
346
|
+
// Airlines, telecoms, energy and shipping. "Delta" alone is also Delta
|
|
347
|
+
// Dental and a sorority, so only the airline's full name matches.
|
|
348
|
+
['Delta Air Lines', 8, /^delta (air lines|airlines)\b/, 'consumer'], ['United Airlines', 8, /^united airlines\b/, 'consumer'],
|
|
349
|
+
['American Airlines', 8, sponsor('american airlines'), 'consumer'],
|
|
350
|
+
['Verizon', 8, sponsor('verizon'), 'tech'], ['AT&T', 8, sponsor('at ?& ?t'), 'tech'], ['T-Mobile', 8, sponsor(String.raw`t[-\s]?mobile`), 'tech'],
|
|
351
|
+
['Comcast', 8, sponsor('comcast|xfinity'), 'tech'],
|
|
352
|
+
['ExxonMobil', 8, /^exxon( ?mobil)?\b/, 'industry'], ['Chevron', 8, /^chevron\b(?! phillips\b)/, 'industry'], ['ConocoPhillips', 8, /^conocophillips\b/, 'industry'],
|
|
353
|
+
['Shell', 8, /^shell( oil( company)?| usa| energy| global solutions| trading)?$/, 'industry'], ['BP', 8, /^(bp( america| p\.l\.c)?|british petroleum)$/, 'industry'],
|
|
354
|
+
['UPS', 8, /^ups( supply chain solutions| airlines| healthcare)?$|^united parcel service\b/, 'industry'], ['FedEx', 8, sponsor('fedex|federal express'), 'industry'],
|
|
355
|
+
['DHL', 8, /^dhl\b/, 'industry'],
|
|
356
|
+
// Insurers and mortgage finance. Berkshire Hathaway by its own name only:
|
|
357
|
+
// its companies keep theirs (GEICO below; its HomeServices brokerages are
|
|
358
|
+
// franchises).
|
|
359
|
+
['Berkshire Hathaway', 8, /^berkshire hathaway$/, 'finance'],
|
|
360
|
+
['State Farm', 8, sponsor('state farm'), 'finance'], ['Allstate', 8, sponsor('allstate'), 'finance'], ['MetLife', 8, sponsor('metlife'), 'finance'],
|
|
361
|
+
['Progressive', 8, /^progressive( insurance| casualty insurance( company)?)?$/, 'finance'],
|
|
362
|
+
['Prudential Financial', 8, /^prudential( financial| insurance( company of america)?)?$/, 'finance'], ['New York Life', 8, /^new york life\b/, 'finance'],
|
|
363
|
+
['Nationwide', 8, /^nationwide( insurance| mutual( insurance( company)?)?| financial)?$/, 'finance'], ['Liberty Mutual', 8, /^liberty mutual\b/, 'finance'],
|
|
364
|
+
['USAA', 8, /^usaa\b/, 'finance'], ['Travelers', 8, /^travelers( insurance| companies)?$/, 'finance'],
|
|
365
|
+
['Fannie Mae', 8, /^(fannie mae|federal national mortgage association)\b/, 'finance'], ['Freddie Mac', 8, /^(freddie mac|federal home loan mortgage)\b/, 'finance'],
|
|
366
|
+
// Computers and machinery. HP only: Hewlett Packard Enterprise is another company.
|
|
367
|
+
['Dell', 8, /^dell( technologies| emc)?$/, 'tech'], ['HP', 8, /^(hp|hewlett[-\s]packard)( inc| company)?$/, 'tech'],
|
|
368
|
+
['Caterpillar', 8, /^caterpillar\b/, 'industry'], ['John Deere', 8, /^(john deere|deere( (&|and) company)?)\b/, 'industry'],
|
|
369
|
+
// Nine of the ten largest US law firms by revenue (the Am Law 100, 2025),
|
|
370
|
+
// 8 like the Big Four, the largest firms in accounting.
|
|
371
|
+
['Kirkland & Ellis', 8, /^kirkland (&|and) ellis\b/, 'consulting'], ['Latham & Watkins', 8, /^latham (&|and) watkins\b/, 'consulting'],
|
|
372
|
+
['DLA Piper', 8, /^dla piper\b/, 'consulting'], ['Baker McKenzie', 8, /^baker (& )?mckenzie\b/, 'consulting'], ['Skadden', 8, /^skadden\b/, 'consulting'],
|
|
373
|
+
['Gibson Dunn', 8, /^gibson dunn\b/, 'consulting'], ['Sidley Austin', 8, /^sidley( austin)?\b/, 'consulting'],
|
|
374
|
+
['White & Case', 8, /^white (&|and) case\b/, 'consulting'], ['Ropes & Gray', 8, /^ropes (&|and) gray\b/, 'consulting'],
|
|
375
|
+
// The top five of U.S. News's Best National Universities (2025 edition),
|
|
376
|
+
// with Harvard, Stanford and MIT above. Yale New Haven Health is a hospital
|
|
377
|
+
// system of its own.
|
|
378
|
+
['Princeton University', 8, university('princeton'), 'education'], ['Yale University', 8, university('yale', '| college'), 'education'],
|
|
379
|
+
// 7 — well-known companies and universities
|
|
380
|
+
['Discord', 7, /^discord\b/, 'tech'], ['Paramount', 7, /^paramount( global| pictures| skydance| streaming| television( studios)?| media networks|\+)?$/, 'entertainment'], ['Madison Square Garden', 7, /^(madison square garden|msg)\b/, 'entertainment'],
|
|
381
|
+
['Fanatics', 7, /^fanatics\b/, 'consumer'], ['Nielsen', 7, /^nielsen( holdings| company| media research| audience measurement)?$/, 'media'], ['The Trade Desk', 7, /^the trade desk\b/, 'media'], ['SiriusXM', 7, /^sirius ?xm\b/, 'entertainment'],
|
|
382
|
+
['Twitch', 7, /^twitch\b/, 'entertainment'], ['Electronic Arts', 7, /^(electronic arts|ea)$/, 'entertainment'], ['Activision Blizzard', 7, /^(activision( blizzard)?|blizzard( entertainment)?)$/, 'entertainment'],
|
|
383
|
+
['MrBeast', 7, /^(mrbeast|beast industries)$/, 'media'], ['Lyft', 7, /^lyft\b/, 'tech'], ['Instacart', 7, /^instacart\b/, 'tech'], ['Duolingo', 7, /^duolingo\b/, 'tech'],
|
|
384
|
+
['Intel', 7, /^intel( corporation)?$/, 'tech'], ['Workday', 7, /^workday\b/, 'tech'], ['PayPal', 7, /^paypal\b/, 'finance'], ['Credit Karma', 7, /^credit karma\b/, 'finance'],
|
|
385
|
+
['Chick-fil-A', 7, /^chick[-\s]fil[-\s]a\b/, 'consumer'], ['Target', 7, /^target( corporation)?$/, 'consumer'], ['Booz Allen Hamilton', 7, /^booz allen\b/, 'defense'],
|
|
386
|
+
['L3Harris', 7, /^l3harris\b/, 'defense'], ['Leidos', 7, /^leidos\b/, 'defense'],
|
|
387
|
+
['WPP', 7, /^wpp\b/, 'media'], ['Omnicom', 7, /^omnicom\b/, 'media'], ['Publicis', 7, /^publicis\b/, 'media'], ['Interpublic', 7, /^(interpublic|ipg)\b/, 'media'], ['Dentsu', 7, /^dentsu\b/, 'media'],
|
|
388
|
+
['Kellanova', 7, /^(kellanova|kellogg['’]?s|kellogg( company)?)$/, 'consumer'], ['Campbell', 7, /^campbell( soup( company)?| company|['’]?s)?$/, 'consumer'], ['Goodyear', 7, /^goodyear\b/, 'industry'],
|
|
389
|
+
['Duke University', 7, /^duke( university)?$/, 'education'], ['UC Berkeley', 7, /^(uc berkeley|university of california,? berkeley)\b/, 'education'],
|
|
390
|
+
// Added from the same sources: household names smaller than the Fortune
|
|
391
|
+
// 100. The best-known health systems, by their own names (Johns Hopkins
|
|
392
|
+
// Medicine apart from the university below).
|
|
393
|
+
['Mayo Clinic', 7, /^mayo clinic\b/, 'health'], ['Cleveland Clinic', 7, /^cleveland clinic\b/, 'health'],
|
|
394
|
+
['Johns Hopkins Medicine', 7, /^johns hopkins (medicine|hospital|health( system|care)|all children['’]?s|bayview|community physicians)\b/, 'health'],
|
|
395
|
+
['Dollar General', 7, /^dollar general\b/, 'consumer'], ['Dollar Tree', 7, /^dollar tree\b/, 'consumer'],
|
|
396
|
+
['Chipotle', 7, /^chipotle\b/, 'consumer'], ['Kraft Heinz', 7, /^kraft (heinz|foods)\b/, 'consumer'], ['General Mills', 7, /^general mills\b/, 'consumer'],
|
|
397
|
+
['Marriott', 7, /^marriott( international| hotels( (&|and) resorts)?)?$/, 'consumer'], ['Hilton', 7, /^hilton( worldwide( holdings)?| hotels( (&|and) resorts)?)?$/, 'consumer'],
|
|
398
|
+
['Southwest Airlines', 7, /^southwest airlines\b/, 'consumer'], ['Union Pacific', 7, /^union pacific\b/, 'industry'],
|
|
399
|
+
// Part of Berkshire Hathaway, named on its own.
|
|
400
|
+
['GEICO', 7, /^(geico|government employees insurance)\b/, 'finance'],
|
|
401
|
+
// Fox Corporation reads as "Fox" once "Corporation" is trimmed, like any company name.
|
|
402
|
+
['Fox', 7, /^fox( news( channel| media)?| sports| entertainment| broadcasting( company)?| television stations| business( network)?)?$/, 'entertainment'],
|
|
403
|
+
['The New York Times', 7, /^new york times\b|^nyt$/, 'media'],
|
|
404
|
+
['Bloomberg', 7, /^bloomberg( lp| l\.p| news| media| industry group| law| tax| government| intelligence)?$/, 'media'],
|
|
405
|
+
// Ranks 6 to 20 of U.S. News's Best National Universities (2025 edition),
|
|
406
|
+
// with Duke and UC Berkeley above. A school named apart from its university
|
|
407
|
+
// (Wharton, Kellogg, Booth) keeps its own name, as MIT Sloan does.
|
|
408
|
+
['Caltech', 7, /^(caltech|california institute of technology)$/, 'education'],
|
|
409
|
+
['Johns Hopkins University', 7, /^johns hopkins( university\b|$| (carey|bloomberg|whiting|krieger|peabody|sais)\b| school of\b)|^jhu$/, 'education'],
|
|
410
|
+
['Northwestern University', 7, university('northwestern'), 'education'], ['University of Pennsylvania', 7, university('university of pennsylvania|upenn|penn'), 'education'],
|
|
411
|
+
['Cornell University', 7, university('cornell', '| tech'), 'education'], ['University of Chicago', 7, university('university of chicago|uchicago'), 'education'],
|
|
412
|
+
['Brown University', 7, university('brown'), 'education'], ['Columbia University', 7, university('columbia university|columbia(?= (business|law|journalism) school$)'), 'education'],
|
|
413
|
+
['Dartmouth College', 7, university('dartmouth', '| college'), 'education'], ['UCLA', 7, /^(ucla|university of california,? los angeles)$|^ucla anderson\b/, 'education'],
|
|
414
|
+
['Rice University', 7, university('rice university'), 'education'], ['University of Notre Dame', 7, university('university of notre dame|notre dame'), 'education'],
|
|
415
|
+
['Vanderbilt University', 7, university('vanderbilt'), 'education'],
|
|
207
416
|
];
|
|
208
417
|
|
|
209
418
|
const JUNK_COMPANY = /^(self[-\s]?employed|freelance|various|multiple|confidential|n\/a|none|tbd|open to work|looking|seeking)$/i;
|
|
210
|
-
// "at scale", "at the intersection of…", "at heart"
|
|
211
|
-
|
|
419
|
+
// "at scale", "at the intersection of…", "at heart", "at home", "at best",
|
|
420
|
+
// "at best-in-class companies": phrases, not employers. The Home Depot, Home
|
|
421
|
+
// Instead and Home Chef are employers, and so are Best Buy, Best Western and
|
|
422
|
+
// Best Friends Animal Society.
|
|
423
|
+
const NOT_A_COMPANY = /^(scale$|heart|large|night|home(?! (depot|instead|chef)\b)|work|speed|best(?=$|[-\s]in[-\s]class|\s(of|practices?)\b)|the intersection|intersection|the crossroads|the forefront|the core|every stage|all levels|your service)\b/i;
|
|
424
|
+
// Where a company's name stops: a sentence's end ("Acme Corp. We build…") or a
|
|
425
|
+
// comma, but not the full stop of an initial or a short form ("J.P. Morgan",
|
|
426
|
+
// "J. Crew", "T. Rowe Price", "U.S. Bank", "St. Jude Children's Research
|
|
427
|
+
// Hospital"), which used to cut the name to "J.P" or nothing.
|
|
428
|
+
const NAME_END = /(?<!\b(?:[a-z]|st|mt|ft))\.\s|,\s/i;
|
|
429
|
+
// A company's legal form, trimmed so "Acme, Inc." and "Acme" are one company.
|
|
430
|
+
const LEGAL_FORM = /,?\s+(inc|incorporated|llc|ltd|l\.l\.c|corp|corporation|co|gmbh|plc|pbc)\.?$/i;
|
|
431
|
+
// Names that are another company with their legal form than without it: Chase
|
|
432
|
+
// Corporation (coatings) isn't Chase the bank, nor Merrill Corporation
|
|
433
|
+
// (financial printing) Bank of America's Merrill. Kept whole, so no alias takes them.
|
|
434
|
+
const WHOLE_NAME = /^(chase|merrill) corp(oration)?$/i;
|
|
435
|
+
// A name that says it is a school only matches a school on the list: the
|
|
436
|
+
// aliases match from the start of a name, so "Kellogg School of Management"
|
|
437
|
+
// read as Kellanova, "Warner University" as Warner Bros., "Campbell
|
|
438
|
+
// University" as Campbell's and "Chase College of Law" as JPMorgan Chase.
|
|
439
|
+
const SCHOOL_NAME = /\b(school|college|university)\b/;
|
|
440
|
+
// A credential, a program or gig work on a listed company's platform, or a fan
|
|
441
|
+
// of it, is not a job there: "AWS Certified Solutions Architect", "Google
|
|
442
|
+
// Developer Expert", "Uber Driver", "Airbnb Superhost", "Twitch Streamer",
|
|
443
|
+
// "TikTok Shop Seller", "LinkedIn Top Voice", "Google Alum", "Disney Fan".
|
|
444
|
+
// People write those as the first part of a headline, where a listed name
|
|
445
|
+
// reads as their employer; none of the listed companies' own names says them.
|
|
446
|
+
const NOT_ITS_STAFF = /\b(certified|certifications?|mvps?|champions?|experts?|ambassadors?|developers?|instructors?|trainers?|moderators?|superhosts?|hosts?|drivers?|dashers?|couriers?|shoppers?|sellers?|fba|kdp|dsp|franchisees?|streamers?|affiliates?|creators?|influencers?|athletes?|scholars?|shareholders?|alum|alumni|alumnus|alumna|alumnae|top voice|users? groups?|for startups|fans?|enthusiasts?|lovers?|nerds?|geeks?|junkies?|buffs?|addicts?|aficionados?|10,?000 (small businesses|women)|10ksb)\b/;
|
|
447
|
+
// A tech or media platform's partners, consultants and admins are other
|
|
448
|
+
// companies' staff ("Google Premier Partner", "Salesforce Consultant",
|
|
449
|
+
// "Workday Admin"). At a bank, a firm or a shop they work there ("Deloitte
|
|
450
|
+
// Partner", "Starbucks Partner"), so this reads only against those industries.
|
|
451
|
+
const PLATFORM_ROLE = /\b(partners?|consultants?|admins?|administrators?)\b/;
|
|
452
|
+
const PLATFORMS = new Set(['tech', 'media', 'entertainment']);
|
|
212
453
|
|
|
213
454
|
/** A company name as people write it → one clean, canonical name (or null). */
|
|
214
455
|
export function cleanCompany(raw) {
|
|
215
456
|
let s = String(raw || '')
|
|
216
457
|
.replace(/[\p{Extended_Pictographic}\u{FE0F}]/gu, '')
|
|
217
458
|
.split(/\s*(?:\||•|·| – | — | - |!|\(|\/| {2,})\s*/)[0]
|
|
218
|
-
.split(
|
|
219
|
-
.replace(/[\s,.:;'"]+$/, '')
|
|
220
|
-
|
|
221
|
-
|
|
459
|
+
.split(NAME_END)[0]
|
|
460
|
+
.replace(/[\s,.:;'"]+$/, '');
|
|
461
|
+
if (!WHOLE_NAME.test(s)) s = s.replace(LEGAL_FORM, '');
|
|
462
|
+
s = s.trim();
|
|
222
463
|
if (!s || s.length < 2 || JUNK_COMPANY.test(s) || NOT_A_COMPANY.test(s)) return null;
|
|
223
464
|
if (s.length > 50 || s.split(/\s+/).length > 6 || /\$|\d+%|\d+[kmb]\+/i.test(s)) return null;
|
|
224
465
|
const lower = s.toLowerCase();
|
|
225
466
|
const bare = lower.replace(/^the /, '');
|
|
226
|
-
|
|
467
|
+
const school = SCHOOL_NAME.test(lower);
|
|
468
|
+
const notStaff = NOT_ITS_STAFF.test(bare);
|
|
469
|
+
const platformRole = PLATFORM_ROLE.test(bare);
|
|
470
|
+
for (const [name, , alias, industry] of KNOWN_COMPANIES) {
|
|
471
|
+
if (school && industry !== 'education') continue;
|
|
472
|
+
if (lower === name.toLowerCase()) return name;
|
|
473
|
+
if (notStaff || (platformRole && PLATFORMS.has(industry))) continue;
|
|
474
|
+
if (alias.test(lower) || alias.test(bare)) return name;
|
|
475
|
+
}
|
|
227
476
|
return s;
|
|
228
477
|
}
|
|
229
478
|
|
|
230
479
|
const KNOWN_BY_NAME = new Map(KNOWN_COMPANIES.map(([name, score]) => [name, score]));
|
|
480
|
+
const KNOWN_INDUSTRY = new Map(KNOWN_COMPANIES.map(([name, , , industry]) => [name, industry]));
|
|
231
481
|
|
|
232
482
|
const isKnownCompany = (text) => {
|
|
233
483
|
const c = cleanCompany(text);
|
|
@@ -262,6 +512,149 @@ function companyIn(part = '') {
|
|
|
262
512
|
return m ? cleanCompany(m[1]) : null;
|
|
263
513
|
}
|
|
264
514
|
|
|
515
|
+
// ── industry ─────────────────────────────────────────────────────────────────
|
|
516
|
+
|
|
517
|
+
export const UNCLEAR = 'unknown';
|
|
518
|
+
|
|
519
|
+
/** The curated list's industry for a company, by its name or any alias of it. */
|
|
520
|
+
export function knownIndustry(name) {
|
|
521
|
+
if (!name) return null;
|
|
522
|
+
return KNOWN_INDUSTRY.get(name) || KNOWN_INDUSTRY.get(cleanCompany(name)) || null;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/** The answer most of them give, ignoring "unclear"; a tie stays unclear. */
|
|
526
|
+
function mostSaid(answers = []) {
|
|
527
|
+
const counts = new Map();
|
|
528
|
+
for (const a of answers) if (a && a !== UNCLEAR) counts.set(a, (counts.get(a) || 0) + 1);
|
|
529
|
+
let best = UNCLEAR;
|
|
530
|
+
let top = 0;
|
|
531
|
+
let tied = false;
|
|
532
|
+
for (const [key, n] of counts) {
|
|
533
|
+
if (n > top) { best = key; top = n; tied = false; } else if (n === top) tied = true;
|
|
534
|
+
}
|
|
535
|
+
return tied ? UNCLEAR : best;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* One industry per company, so its colour in Paths, its row in Scores and any
|
|
540
|
+
* sector lean agree. In order: the curated list's own industry; what the name
|
|
541
|
+
* says; otherwise what most of the people who work there now say in their
|
|
542
|
+
* headlines. A tie stays unclear rather than guessed, which also keeps the
|
|
543
|
+
* answer the same whichever order the rows arrive in (the server and Paths
|
|
544
|
+
* read them in different orders).
|
|
545
|
+
*
|
|
546
|
+
* `industryOf(company, headline)` returns an industry key; it is
|
|
547
|
+
* lib/companies.js's inference, passed in so this file needs no imports.
|
|
548
|
+
* Without it only the curated list can answer.
|
|
549
|
+
*/
|
|
550
|
+
export function companyIndustry(name, options) {
|
|
551
|
+
return industryAndSource(name, options).industry;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* companyIndustry, with where the answer came from: 'list' (the curated
|
|
556
|
+
* list), 'name' (the company's name) or 'people' (their headlines), or null
|
|
557
|
+
* when it is unclear. Your sector's broad picks count only the first two
|
|
558
|
+
* (leanToward): the people's vote is over job words every kind of company
|
|
559
|
+
* employs, so "Recruiter at Acme Widgets" alone would put Acme in consulting.
|
|
560
|
+
* @returns { industry, from }
|
|
561
|
+
*/
|
|
562
|
+
export function industryAndSource(name, { industryOf, headlines = [] } = {}) {
|
|
563
|
+
if (!name) return { industry: UNCLEAR, from: null };
|
|
564
|
+
const known = knownIndustry(name);
|
|
565
|
+
if (known) return { industry: known, from: 'list' };
|
|
566
|
+
if (!industryOf) return { industry: UNCLEAR, from: null };
|
|
567
|
+
const byName = industryOf(name, null);
|
|
568
|
+
if (byName && byName !== UNCLEAR) return { industry: byName, from: 'name' };
|
|
569
|
+
const voted = mostSaid(headlines.map((h) => industryOf(null, h)));
|
|
570
|
+
return { industry: voted, from: voted === UNCLEAR ? null : 'people' };
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/** The broad industries a company's directory sectors sit under, each once, in their order. */
|
|
574
|
+
const industriesUnder = (keys, groupOf) => [...new Set(keys.map(groupOf).filter(Boolean))];
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* Per company, from the people who work there now (each person once): how
|
|
578
|
+
* many there are, and the company's one industry. `rolesOf` lets a caller
|
|
579
|
+
* that also reads each row's roles (Paths) parse every headline once.
|
|
580
|
+
* `sectorsOf(name, headlines)` is Settings' sector directory
|
|
581
|
+
* (lib/sector-directory.js sectorMatcher), passed in like `industryOf`; with
|
|
582
|
+
* it, each company also gets the directory's sectors it matches, and with
|
|
583
|
+
* `groupOf(sector key)` (lib/sector-directory.js sectorGroup) the broad
|
|
584
|
+
* industries those sectors sit under.
|
|
585
|
+
* @returns { headcount: Map name → n, industries: Map name → industry key,
|
|
586
|
+
* industryFrom: Map name → 'list' | 'name' | 'people' | null (industryAndSource),
|
|
587
|
+
* sectors: Map name → sector keys, sectorIndustries: Map name → industry keys }
|
|
588
|
+
*/
|
|
589
|
+
export function networkCompanies(rows = [], { industryOf, sectorsOf, groupOf, rolesOf = rolesWithCompanies } = {}) {
|
|
590
|
+
const at = new Map();
|
|
591
|
+
const seen = new Set();
|
|
592
|
+
for (const r of rows) {
|
|
593
|
+
for (const role of rolesOf(r)) {
|
|
594
|
+
const key = `${role.company}|${r.profile_url || r.id}`;
|
|
595
|
+
if (!role.company || role.former || seen.has(key)) continue;
|
|
596
|
+
seen.add(key);
|
|
597
|
+
const c = at.get(role.company) || { n: 0, headlines: [] };
|
|
598
|
+
c.n++;
|
|
599
|
+
c.headlines.push(r.headline);
|
|
600
|
+
at.set(role.company, c);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
const headcount = new Map();
|
|
604
|
+
const industries = new Map();
|
|
605
|
+
const industryFrom = new Map();
|
|
606
|
+
const sectors = new Map();
|
|
607
|
+
const sectorIndustries = new Map();
|
|
608
|
+
for (const [name, c] of at) {
|
|
609
|
+
headcount.set(name, c.n);
|
|
610
|
+
const { industry, from } = industryAndSource(name, { industryOf, headlines: c.headlines });
|
|
611
|
+
industries.set(name, industry);
|
|
612
|
+
industryFrom.set(name, from);
|
|
613
|
+
if (!sectorsOf) continue;
|
|
614
|
+
const keys = sectorsOf(name, c.headlines);
|
|
615
|
+
sectors.set(name, keys);
|
|
616
|
+
if (groupOf) sectorIndustries.set(name, industriesUnder(keys, groupOf));
|
|
617
|
+
}
|
|
618
|
+
return { headcount, industries, industryFrom, sectors, sectorIndustries };
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// ── your sector ──────────────────────────────────────────────────────────────
|
|
622
|
+
|
|
623
|
+
// Points a company in one of your sectors gets on top of its score (Settings →
|
|
624
|
+
// Your sector). A point of company score is worth 0.055 × title points: +0.55
|
|
625
|
+
// for a founder, +0.41 for a director, +0.22 for an IC.
|
|
626
|
+
export const SECTOR_BONUS = { lean: 1, strong: 2 };
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* A company score leaned toward your sectors: `focus` is { sectors, strength:
|
|
630
|
+
* 'lean' | 'strong' } as saved in Settings, where a sector is a broad industry
|
|
631
|
+
* key or a key from the sector directory. A directory sector matches when it
|
|
632
|
+
* is among the company's `sectors` (the directory's matches, injected). A
|
|
633
|
+
* broad industry includes its sectors: it matches any industry the company's
|
|
634
|
+
* sectors sit under (`sectorIndustries`), so Healthcare & Biotech picks up a
|
|
635
|
+
* practice only the directory calls dental, and the company's one `industry`
|
|
636
|
+
* when the curated list or its name gave it (`industryFrom`). An industry
|
|
637
|
+
* voted by its people's headlines doesn't count: the vote is over job words
|
|
638
|
+
* every kind of company employs (recruiter, attorney, engineer, data), so a
|
|
639
|
+
* lone "Recruiter at Acme Widgets" would make Acme consulting. The
|
|
640
|
+
* directory's sectors, which don't read a job for work every company has, are
|
|
641
|
+
* what count for such a company. However many match, the bonus is added once;
|
|
642
|
+
* `sector` names the pick that matched, a directory sector before a broad
|
|
643
|
+
* industry. Capped at 10. The extra fields appear only when the score actually
|
|
644
|
+
* moved, so everything that reads a plain { score, source } keeps working.
|
|
645
|
+
*/
|
|
646
|
+
function leanToward(base, industry, industryFrom, sectors, sectorIndustries, focus) {
|
|
647
|
+
const bonus = SECTOR_BONUS[focus?.strength] || 0;
|
|
648
|
+
if (!bonus || !Array.isArray(focus.sectors)) return base;
|
|
649
|
+
const byIndustry = industryFrom !== 'people';
|
|
650
|
+
const pick = (sectors?.length && focus.sectors.find((k) => sectors.includes(k)))
|
|
651
|
+
|| focus.sectors.find((k) => (byIndustry && k === industry) || sectorIndustries?.includes(k));
|
|
652
|
+
if (!pick) return base;
|
|
653
|
+
const score = Math.min(10, base.score + bonus);
|
|
654
|
+
if (score === base.score) return base;
|
|
655
|
+
return { ...base, score, base: base.score, sector: pick, sectorBonus: score - base.score };
|
|
656
|
+
}
|
|
657
|
+
|
|
265
658
|
/**
|
|
266
659
|
* A company's score and where it came from.
|
|
267
660
|
* yours you set it
|
|
@@ -269,40 +662,108 @@ function companyIn(part = '') {
|
|
|
269
662
|
* network unknown to the list; nudged up when several of your people work there
|
|
270
663
|
* default unknown, and few of your people there
|
|
271
664
|
* none no company found for this person
|
|
665
|
+
* With a sector focus, a company in one of your sectors gets +1 or +2 on top
|
|
666
|
+
* ({ base, sector, sectorBonus } say so). A score you set is never changed.
|
|
667
|
+
* `industry` is the company's one industry (companyIndustry); a curated
|
|
668
|
+
* company's own is used when none is given. `industryFrom` says where it
|
|
669
|
+
* came from (industryAndSource): one from its people's headlines doesn't lean
|
|
670
|
+
* it (leanToward). `sectors` are the sector directory's keys it matches
|
|
671
|
+
* (lib/sector-directory.js), when known, and `sectorIndustries` the broad
|
|
672
|
+
* industries those sit under. companyScoreIn() passes all of these from a
|
|
673
|
+
* read of the network.
|
|
272
674
|
*/
|
|
273
|
-
export function companyScore(name, { overrides = new Map(), headcount = 0, industry } = {}) {
|
|
675
|
+
export function companyScore(name, { overrides = new Map(), headcount = 0, industry, industryFrom, sectors, sectorIndustries, focus } = {}) {
|
|
274
676
|
if (!name) return { score: 3, source: 'none' };
|
|
275
677
|
if (overrides.has(name)) return { score: overrides.get(name), source: 'yours' };
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
if (
|
|
279
|
-
|
|
678
|
+
const ind = industry ?? KNOWN_INDUSTRY.get(name);
|
|
679
|
+
let base;
|
|
680
|
+
if (KNOWN_BY_NAME.has(name)) base = { score: KNOWN_BY_NAME.get(name), source: 'known' };
|
|
681
|
+
else if (ind !== 'education' && headcount >= 15) base = { score: 6, source: 'network' };
|
|
682
|
+
else if (ind !== 'education' && headcount >= 5) base = { score: 5, source: 'network' };
|
|
683
|
+
else base = { score: 4, source: 'default' };
|
|
684
|
+
return focus ? leanToward(base, ind, industryFrom, sectors, sectorIndustries, focus) : base;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* A company's score from a read of the network (readNetwork): its headcount,
|
|
689
|
+
* industry and where that came from, and its sectors, as scoreNetwork scores
|
|
690
|
+
* it. Rescoring, Settings' preview and Paths → Scores all score a company
|
|
691
|
+
* through this, so they give it the same score.
|
|
692
|
+
*/
|
|
693
|
+
export function companyScoreIn(read, name, { overrides, focus } = {}) {
|
|
694
|
+
const c = name ? read?.companies.get(name) : null;
|
|
695
|
+
return companyScore(name, {
|
|
696
|
+
overrides, headcount: c?.headcount || 0, industry: c?.industry, industryFrom: c?.industryFrom,
|
|
697
|
+
sectors: c?.sectors, sectorIndustries: c?.sectorIndustries, focus,
|
|
698
|
+
});
|
|
280
699
|
}
|
|
281
700
|
|
|
282
701
|
// ── reach bonus ──────────────────────────────────────────────────────────────
|
|
283
702
|
|
|
703
|
+
// The top honours of every field, so recognition doesn't depend on which field
|
|
704
|
+
// someone is in: science, economics, peace and literature (Nobel), journalism
|
|
705
|
+
// (Pulitzer), broadcasting (Peabody), TV (Emmy), music (Grammy), film (Oscar),
|
|
706
|
+
// theatre (Tony), the web (Webby), advertising (Clio, Cannes Lions), food
|
|
707
|
+
// (James Beard), computing (Turing), mathematics (Fields), architecture
|
|
708
|
+
// (Pritzker), sport (the Olympics and Paralympics), and the MacArthur and
|
|
709
|
+
// Rhodes. An award's name alone can be a company's, a person's or its own
|
|
710
|
+
// organisation's ("Oscar Health", "Peabody Energy", Clio the legal software,
|
|
711
|
+
// "Tony", "Emmy's Bakery", "the Peabody Awards", "Pulitzer Center"), so those
|
|
712
|
+
// count only as claimed: "… Award", "…-winning", "… winner", "… nominee".
|
|
713
|
+
// docs/brain/SCORING.md says what the list replaced.
|
|
714
|
+
const HONOURS = [
|
|
715
|
+
String.raw`nobel (prize|laureate)`, String.raw`pulitzer( prize|[-\s](winning|winner|finalist))`,
|
|
716
|
+
String.raw`(emmy|grammy|oscar|tony|peabody|clio|james beard|turing)[-\s](award|winning|winner|nominated|nominee|honoree)`,
|
|
717
|
+
'academy award', 'webby', 'cannes lions', 'fields medal', 'pritzker prize', 'macarthur fellow', 'rhodes scholar',
|
|
718
|
+
String.raw`(olympi|paralympi)(an|c (gold|silver|bronze|medalist|champion))`,
|
|
719
|
+
].join('|');
|
|
720
|
+
|
|
284
721
|
const BONUSES = [
|
|
285
722
|
[/\b(angel investor|investor|venture capital(ist)?|vc|general partner|limited partner)\b(?! relations)/i, 1, 'investor'],
|
|
286
723
|
[/\by ?combinator\b|\(yc\)|\byc\b ?[wsfx]?\d{0,2}\b/i, 1, 'YC'],
|
|
287
724
|
[/\b30 under 30\b/i, 1, '30 under 30'],
|
|
288
725
|
[/\b\d[\d.,]*\s?(m|b|million|billion)\+?\s*(views|followers|subscribers|impressions|users|downloads|installs|listeners|students|customers|members|reach)\b|\$?\s?\d[\d.,]*\s?(m|b|million|billion)\+?\s*(in )?(sales|revenue|arr|gmv|raised|funding)\b|\braised \$?\s?\d[\d.,]*\s?(m|b|million|billion)\b/i, 0.75, 'reach in the millions'],
|
|
289
|
-
[
|
|
726
|
+
[new RegExp(String.raw`\b(tedx?|keynote|patents?|author of|best[-\s]?selling|award[-\s]winning|prize[-\s]winning|${HONOURS})\b`, 'i'), 0.5, 'recognition'],
|
|
290
727
|
];
|
|
291
728
|
|
|
729
|
+
/** The headline's reach signals before any halving: points (capped at 1.5) and reasons. */
|
|
730
|
+
function readReach(headline = '') {
|
|
731
|
+
let points = 0;
|
|
732
|
+
const reasons = [];
|
|
733
|
+
for (const [re, p, label] of BONUSES) if (re.test(headline || '')) { points += p; reasons.push(label); }
|
|
734
|
+
return { points: Math.min(1.5, points), reasons };
|
|
735
|
+
}
|
|
736
|
+
|
|
292
737
|
/**
|
|
293
738
|
* Bonus points from signals in the headline, capped at 1.5, with the reasons.
|
|
294
739
|
* Headlines are self-written: claims from someone at a company nobody knows
|
|
295
|
-
* (company score 4 or less) count half.
|
|
740
|
+
* (company score 4 or less) count half. `found` is the headline's readReach(),
|
|
741
|
+
* when it has been read already.
|
|
296
742
|
*/
|
|
297
|
-
export function reachBonus(headline = '', companyScoreValue = 10) {
|
|
298
|
-
let points =
|
|
299
|
-
const reasons = [];
|
|
300
|
-
for (const [re, p, label] of BONUSES) if (re.test(headline || '')) { points += p; reasons.push(label); }
|
|
301
|
-
points = Math.min(1.5, points);
|
|
743
|
+
export function reachBonus(headline = '', companyScoreValue = 10, found = readReach(headline)) {
|
|
744
|
+
let { points } = found;
|
|
745
|
+
const reasons = [...found.reasons];
|
|
302
746
|
if (points && companyScoreValue <= 4) { points /= 2; reasons.push('halved: unknown company'); }
|
|
303
747
|
return { points: round1(points), reasons };
|
|
304
748
|
}
|
|
305
749
|
|
|
750
|
+
// ── what stored scores were read with ────────────────────────────────────────
|
|
751
|
+
|
|
752
|
+
// Every table of words and rules above that reads a headline or a company
|
|
753
|
+
// name: the title ladder and its rules, the student, club and former rules,
|
|
754
|
+
// what isn't a company or a job there, and the reach bonuses. lib/rpc.js
|
|
755
|
+
// fingerprints them, with the curated list and the industries' words, into
|
|
756
|
+
// the stamp stored scores carry, so editing any of them rescores stored scores
|
|
757
|
+
// once, with no SCORING_VERSION to remember. How they're combined (the
|
|
758
|
+
// formula, the weights, the order of the steps) is still a SCORING_VERSION.
|
|
759
|
+
export const RULE_TABLES = {
|
|
760
|
+
levels: LEVELS, titles: TITLE_RULES, intern: INTERN, student: STUDENT, major: MAJOR, shortName: SHORT_NAME,
|
|
761
|
+
atShortName: AT_SHORT_NAME, notASchool: NOT_A_SCHOOL, studentOrg: STUDENT_ORG, schoolWordClub: SCHOOL_WORD_CLUB,
|
|
762
|
+
club: CLUB, shortNameClub: SHORT_NAME_CLUB, fieldAssociation: FIELD_ASSOCIATION, grownUp: GROWN_UP, former: FORMER,
|
|
763
|
+
junkCompany: JUNK_COMPANY, notACompany: NOT_A_COMPANY, nameEnd: NAME_END, legalForm: LEGAL_FORM, wholeName: WHOLE_NAME,
|
|
764
|
+
schoolName: SCHOOL_NAME, notItsStaff: NOT_ITS_STAFF, platformRole: PLATFORM_ROLE, platforms: PLATFORMS, bonuses: BONUSES,
|
|
765
|
+
};
|
|
766
|
+
|
|
306
767
|
// ── putting it together ──────────────────────────────────────────────────────
|
|
307
768
|
|
|
308
769
|
// S is the top ~3–4%: VP and up at strong companies, founders and C-suite at
|
|
@@ -320,28 +781,125 @@ const round1 = (n) => Math.round(n * 10) / 10;
|
|
|
320
781
|
export const FORMER_WEIGHT = 0.7;
|
|
321
782
|
export const STUDENT_CAP = 3;
|
|
322
783
|
|
|
784
|
+
/**
|
|
785
|
+
* What scoring reads from one person's row: every role with its company,
|
|
786
|
+
* whether they are a student now, and the headline's reach signals. Reading
|
|
787
|
+
* headlines is most of the work, so a network read once (readNetwork) can be
|
|
788
|
+
* scored several ways: Settings' preview scores it with two sector focuses.
|
|
789
|
+
*/
|
|
790
|
+
export function readPerson(row) {
|
|
791
|
+
return { roles: rolesWithCompanies(row), student: isStudent(row?.headline), reach: readReach(row?.headline) };
|
|
792
|
+
}
|
|
793
|
+
|
|
323
794
|
/**
|
|
324
795
|
* One person's score, before any bridge boost: their strongest role, where a
|
|
325
796
|
* role is worth title × company weight. Former roles count at 70% — an ex-SVP
|
|
326
797
|
* still knows the people an SVP knows. A current student is capped at 3 title
|
|
327
|
-
* points whatever else they list. `companyFor(name)` returns companyScore()
|
|
798
|
+
* points whatever else they list. `companyFor(name)` returns companyScore();
|
|
799
|
+
* `read` is the row's readPerson(), when it has been read already.
|
|
328
800
|
*/
|
|
329
|
-
|
|
330
|
-
|
|
801
|
+
/** A role's title points as they count: 70% for a former role, capped for a student. */
|
|
802
|
+
function rolePoints(role, student) {
|
|
803
|
+
const points = role.title.points * (role.former ? FORMER_WEIGHT : 1);
|
|
804
|
+
return student ? Math.min(points, STUDENT_CAP) : points;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
export function scorePerson(row, companyFor = (name) => companyScore(name), read = readPerson(row)) {
|
|
808
|
+
const { roles, student, reach } = read;
|
|
331
809
|
let best = null;
|
|
332
|
-
for (const role of
|
|
810
|
+
for (const role of roles) {
|
|
333
811
|
const co = companyFor(role.company);
|
|
334
|
-
|
|
335
|
-
if (student) points = Math.min(points, STUDENT_CAP);
|
|
812
|
+
const points = rolePoints(role, student);
|
|
336
813
|
const core = points * (0.45 + 0.055 * co.score);
|
|
337
814
|
if (!best || core > best.core) best = { role, co, points, core };
|
|
338
815
|
}
|
|
339
|
-
|
|
816
|
+
// Halved or not by the company's own score, before any sector lean: that you
|
|
817
|
+
// like a sector says nothing about whether a self-written claim is true.
|
|
818
|
+
const bonus = reachBonus(row?.headline, best.co.base ?? best.co.score, reach);
|
|
340
819
|
const power = round1(best.core + bonus.points);
|
|
341
820
|
const title = { ...best.role.title, former: best.role.former, student, points: round1(best.points) };
|
|
342
821
|
if (best.role.former) title.label = `Former ${title.label}`;
|
|
343
822
|
if (student && best.role.title.level > 0) title.label = `Student · ${title.label}`;
|
|
344
|
-
|
|
823
|
+
const out = { title, company: best.role.company, companyScore: best.co.score, companySource: best.co.source, bonus, power, tier: tierFor(power) };
|
|
824
|
+
// Only when your sector moved the company's score, so explainScore can show it.
|
|
825
|
+
if (best.co.sectorBonus) out.companySector = { key: best.co.sector, base: best.co.base, bonus: best.co.sectorBonus };
|
|
826
|
+
return out;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
// ── for the views ────────────────────────────────────────────────────────────
|
|
830
|
+
// Views that rank or describe people by where they work (the Queue's order,
|
|
831
|
+
// the person panel's notes) read the model's own company scores through these,
|
|
832
|
+
// so none keeps a list of famous names of its own.
|
|
833
|
+
|
|
834
|
+
// A company scored this high is a top company wherever a view says so: the
|
|
835
|
+
// curated list's major companies and up, or any company your own score or
|
|
836
|
+
// your sector puts there.
|
|
837
|
+
export const TOP_COMPANY = 8;
|
|
838
|
+
|
|
839
|
+
/**
|
|
840
|
+
* The company score a row's power was computed with: the row's stored
|
|
841
|
+
* company_prestige_score (a score you set, the curated list's or the
|
|
842
|
+
* network's estimate, and any sector lean), or companyScore()'s for a row not
|
|
843
|
+
* scored yet, which only the curated list can raise.
|
|
844
|
+
*/
|
|
845
|
+
export function rowCompanyScore(row, read) {
|
|
846
|
+
const stored = Number(row?.company_prestige_score);
|
|
847
|
+
return stored > 0 ? stored : scorePerson(row, undefined, read ?? readPerson(row)).companyScore;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
/**
|
|
851
|
+
* The company someone's score is built on, their strongest role's, with its
|
|
852
|
+
* score (rowCompanyScore) and whether that role is a former one. A scored row
|
|
853
|
+
* says which role it was by its title points (seniority_score): a former role
|
|
854
|
+
* counts at 70%, so it never has a current role's points. Two roles with the
|
|
855
|
+
* same points go to the one the stored working names (score_why, "… · Beta
|
|
856
|
+
* Labs (9/10, your score)"), since a score you set or your sector can make
|
|
857
|
+
* either the stronger; then to the one whose company the list scores as
|
|
858
|
+
* stored, else the first, as scorePerson keeps the first of equals. A row not
|
|
859
|
+
* scored yet, or whose headline no longer has those points, goes by
|
|
860
|
+
* scorePerson with the list alone.
|
|
861
|
+
* @returns { name, score, former } — name is null when no company was found
|
|
862
|
+
*/
|
|
863
|
+
export function scoredCompany(row, read = readPerson(row)) {
|
|
864
|
+
const stored = Number(row?.company_prestige_score);
|
|
865
|
+
if (stored > 0) {
|
|
866
|
+
const points = Number(row?.seniority_score);
|
|
867
|
+
const same = read.roles.filter((r) => Math.abs(round1(rolePoints(r, read.student)) - points) < 0.05);
|
|
868
|
+
const why = String(row?.score_why || '');
|
|
869
|
+
const role = same.find((r) => r.company && why.includes(` · ${r.company} (${stored}/10`))
|
|
870
|
+
|| same.find((r) => companyScore(r.company).score === stored) || same[0];
|
|
871
|
+
if (role) return { name: role.company, score: stored, former: role.former };
|
|
872
|
+
}
|
|
873
|
+
const s = scorePerson(row, undefined, read);
|
|
874
|
+
return { name: s.company, score: stored > 0 ? stored : s.companyScore, former: s.title.former };
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
/**
|
|
878
|
+
* The top companies (TOP_COMPANY and up) someone works at now and worked at
|
|
879
|
+
* before, for the person panel's notes: `now` is the company their score is
|
|
880
|
+
* built on when that is a current role, else their first current role's;
|
|
881
|
+
* `before` the best of their former roles' (not the one they're at now), each
|
|
882
|
+
* { name, score } or null. The company their score is built on gets the
|
|
883
|
+
* score it was scored with (scoredCompany); any other gets companyScore()'s,
|
|
884
|
+
* which only the curated list can raise.
|
|
885
|
+
*/
|
|
886
|
+
export function topCompanies(row) {
|
|
887
|
+
const read = readPerson(row);
|
|
888
|
+
const scored = scoredCompany(row, read);
|
|
889
|
+
const top = (name) => {
|
|
890
|
+
if (!name) return null;
|
|
891
|
+
const score = name === scored.name ? scored.score : companyScore(name).score;
|
|
892
|
+
return score >= TOP_COMPANY ? { name, score } : null;
|
|
893
|
+
};
|
|
894
|
+
const current = read.roles.filter((r) => !r.former && r.company);
|
|
895
|
+
const now = top(!scored.former && current.some((r) => r.company === scored.name) ? scored.name : current[0]?.company);
|
|
896
|
+
let before = null;
|
|
897
|
+
for (const r of read.roles) {
|
|
898
|
+
if (!r.former || r.company === now?.name) continue;
|
|
899
|
+
const t = top(r.company);
|
|
900
|
+
if (t && (!before || t.score > before.score)) before = t;
|
|
901
|
+
}
|
|
902
|
+
return { now, before };
|
|
345
903
|
}
|
|
346
904
|
|
|
347
905
|
/**
|
|
@@ -360,36 +918,87 @@ export function bridgeBoost(circle = []) {
|
|
|
360
918
|
return { boost: round1(boost), total, sCount, aCount, elitePct, circlePower, isCatalyst };
|
|
361
919
|
}
|
|
362
920
|
|
|
363
|
-
/**
|
|
364
|
-
|
|
365
|
-
|
|
921
|
+
/**
|
|
922
|
+
* A plain-English line for why someone scores what they do, e.g.
|
|
923
|
+
* "VP / Partner / GM (9) · Quillon (5/10: 4 + 1 your sector: Dental) · +0.7 strong circle".
|
|
924
|
+
* `sectorLabel(key)` names the sector that leaned the company (this file
|
|
925
|
+
* imports nothing, so the labels are passed in); without it the line says
|
|
926
|
+
* "your sector" alone.
|
|
927
|
+
*/
|
|
928
|
+
export function explainScore(s, boost = 0, { sectorLabel } = {}) {
|
|
929
|
+
const which = s.companySector && sectorLabel?.(s.companySector.key);
|
|
930
|
+
const co = s.companySector
|
|
931
|
+
? `${s.company} (${s.companyScore}/10: ${s.companySector.base} + ${s.companySector.bonus} your sector${which ? `: ${which}` : ''})`
|
|
932
|
+
: `${s.company || 'no company found'} (${s.companyScore}/10${s.companySource === 'yours' ? ', your score' : ''})`;
|
|
933
|
+
const bits = [`${s.title.label} (${s.title.points})`, co];
|
|
366
934
|
if (s.bonus.points) bits.push(`+${s.bonus.points} ${s.bonus.reasons.join(', ')}`);
|
|
367
935
|
if (boost) bits.push(`+${boost} strong circle`);
|
|
368
936
|
return bits.join(' · ');
|
|
369
937
|
}
|
|
370
938
|
|
|
371
939
|
/**
|
|
372
|
-
*
|
|
373
|
-
*
|
|
374
|
-
*
|
|
940
|
+
* A network read once, to be scored any number of ways: each row's
|
|
941
|
+
* readPerson(), and every company anyone names (in a current role or a former
|
|
942
|
+
* one) with what its score depends on besides your own choices: how many of
|
|
943
|
+
* your people work there now, and its one industry. With `sectorsOf` (the
|
|
944
|
+
* sector directory, as networkCompanies takes it), also the directory's
|
|
945
|
+
* sectors it matches, and with `groupOf` the broad industries those sit
|
|
946
|
+
* under, worked out here once however many ways the read is scored. Pass it
|
|
947
|
+
* to scoreNetwork() with the same rows.
|
|
948
|
+
* @returns { people: Map row → readPerson, headcount, industries,
|
|
949
|
+
* companies: Map name → { headcount, industry, industryFrom, sectors?, sectorIndustries? } }
|
|
375
950
|
*/
|
|
376
|
-
export function
|
|
377
|
-
|
|
378
|
-
const
|
|
379
|
-
const
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
headcount
|
|
951
|
+
export function readNetwork(rows = [], { industryOf, sectorsOf, groupOf } = {}) {
|
|
952
|
+
const people = new Map();
|
|
953
|
+
for (const r of rows) people.set(r, readPerson(r));
|
|
954
|
+
const { headcount, industries, industryFrom, sectors, sectorIndustries } = networkCompanies(rows, {
|
|
955
|
+
industryOf, sectorsOf, groupOf, rolesOf: (r) => people.get(r).roles,
|
|
956
|
+
});
|
|
957
|
+
const companies = new Map();
|
|
958
|
+
for (const [name, n] of headcount) {
|
|
959
|
+
companies.set(name, {
|
|
960
|
+
headcount: n, industry: industries.get(name), industryFrom: industryFrom.get(name),
|
|
961
|
+
...(sectorsOf && { sectors: sectors.get(name) }),
|
|
962
|
+
...(sectorsOf && groupOf && { sectorIndustries: sectorIndustries.get(name) }),
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
// A company only in someone's former roles has no current people to count or
|
|
966
|
+
// ask; its name (or the curated list) is all there is to go on.
|
|
967
|
+
for (const { roles } of people.values()) {
|
|
968
|
+
for (const { company } of roles) {
|
|
969
|
+
if (!company || companies.has(company)) continue;
|
|
970
|
+
const keys = sectorsOf ? sectorsOf(company, []) : null;
|
|
971
|
+
const { industry, from } = industryAndSource(company, { industryOf });
|
|
972
|
+
companies.set(company, {
|
|
973
|
+
headcount: 0, industry, industryFrom: from,
|
|
974
|
+
...(keys && { sectors: keys }),
|
|
975
|
+
...(keys && groupOf && { sectorIndustries: industriesUnder(keys, groupOf) }),
|
|
976
|
+
});
|
|
386
977
|
}
|
|
387
978
|
}
|
|
979
|
+
return { people, headcount, industries, companies };
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
/**
|
|
983
|
+
* Score a whole network. People first, with company scores that can use how
|
|
984
|
+
* many of your people work at each company and each company's one industry;
|
|
985
|
+
* then 1st-degree bridges get their circle boost from their scored circle.
|
|
986
|
+
* `focus` is the sector focus from Settings, or nothing. `read` is
|
|
987
|
+
* readNetwork() of these rows, when they have been read already. Returns one
|
|
988
|
+
* result per row id, plus the per-company facts it used and each company's
|
|
989
|
+
* score (companyScore(), by name).
|
|
990
|
+
*/
|
|
991
|
+
export function scoreNetwork(rows = [], {
|
|
992
|
+
overrides = new Map(), industryOf, sectorsOf, groupOf, focus, read = readNetwork(rows, { industryOf, sectorsOf, groupOf }),
|
|
993
|
+
} = {}) {
|
|
994
|
+
// Each company is scored once; everyone there shares the answer.
|
|
995
|
+
const companyScores = new Map();
|
|
996
|
+
const companyFor = (name) => {
|
|
997
|
+
if (!companyScores.has(name)) companyScores.set(name, companyScoreIn(read, name, { overrides, focus }));
|
|
998
|
+
return companyScores.get(name);
|
|
999
|
+
};
|
|
388
1000
|
const out = new Map();
|
|
389
|
-
for (const r of rows) {
|
|
390
|
-
const companyFor = (name) => companyScore(name, { overrides, headcount: headcount.get(name) || 0, industry: name ? industryOf?.(name, r.headline) : undefined });
|
|
391
|
-
out.set(r.id, { ...scorePerson(r, companyFor), boost: 0 });
|
|
392
|
-
}
|
|
1001
|
+
for (const r of rows) out.set(r.id, { ...scorePerson(r, companyFor, read.people.get(r)), boost: 0 });
|
|
393
1002
|
const circles = new Map();
|
|
394
1003
|
for (const r of rows) {
|
|
395
1004
|
if (r.degree !== 2 || !r.source_connection_id) continue;
|
|
@@ -406,5 +1015,5 @@ export function scoreNetwork(rows = [], { overrides = new Map(), industryOf } =
|
|
|
406
1015
|
s.power = round1(s.power + b.boost);
|
|
407
1016
|
s.tier = tierFor(s.power);
|
|
408
1017
|
}
|
|
409
|
-
return { scores: out, headcount };
|
|
1018
|
+
return { scores: out, headcount: read.headcount, industries: read.industries, companyScores };
|
|
410
1019
|
}
|