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/companies.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readTitle, currentCompany } from './scoring.js';
|
|
1
|
+
import { readTitle, currentCompany, rolesWithCompanies, networkCompanies } from './scoring.js';
|
|
2
2
|
// Companies and industries across your network — what the Paths analyzer is
|
|
3
3
|
// built on. Plain functions, no React, so tests/companies.test.mjs runs them.
|
|
4
4
|
//
|
|
@@ -6,99 +6,6 @@ import { readTitle, currentCompany } from './scoring.js';
|
|
|
6
6
|
// industry is inferred from the company name and headline, and labelled
|
|
7
7
|
// "inferred" wherever it is shown. Unclear stays unclear rather than guessed.
|
|
8
8
|
|
|
9
|
-
// Merge company name variants
|
|
10
|
-
export function normalizeCompany(name) {
|
|
11
|
-
const n = (name || '').trim().toLowerCase();
|
|
12
|
-
// Social / Media
|
|
13
|
-
if (n.includes('snap') && (n.includes('inc') || n === 'snap' || n === 'snapchat')) return 'Snap';
|
|
14
|
-
if (n.includes('meta') && (n.includes('platform') || n === 'meta' || n.includes('facebook'))) return 'Meta';
|
|
15
|
-
if (n.includes('tiktok') || n.includes('bytedance')) return 'TikTok';
|
|
16
|
-
if (n.includes('pinterest')) return 'Pinterest';
|
|
17
|
-
// Big Tech
|
|
18
|
-
if (n === 'google' || n.includes('alphabet') || (n.includes('google') && n.includes('llc'))) return 'Google';
|
|
19
|
-
if (n === 'apple' || n === 'apple inc' || n === 'apple inc.') return 'Apple';
|
|
20
|
-
if (n.includes('amazon') && !n.includes('amazon ')) return 'Amazon';
|
|
21
|
-
if (n.includes('microsoft')) return 'Microsoft';
|
|
22
|
-
// AI
|
|
23
|
-
if (n.includes('anthropic')) return 'Anthropic';
|
|
24
|
-
if (n.includes('openai') || n === 'open ai') return 'OpenAI';
|
|
25
|
-
if (n.includes('deepmind')) return 'DeepMind';
|
|
26
|
-
if (n.includes('cohere')) return 'Cohere';
|
|
27
|
-
if (n.includes('mistral')) return 'Mistral';
|
|
28
|
-
if (n.includes('hugging') && n.includes('face')) return 'Hugging Face';
|
|
29
|
-
// Fintech / Crypto
|
|
30
|
-
if (n.includes('stripe')) return 'Stripe';
|
|
31
|
-
if (n.includes('coinbase')) return 'Coinbase';
|
|
32
|
-
if (n.includes('polymarket')) return 'Polymarket';
|
|
33
|
-
if (n.includes('blackrock')) return 'BlackRock';
|
|
34
|
-
if (n.includes('robinhood')) return 'Robinhood';
|
|
35
|
-
// Growth / Commerce
|
|
36
|
-
if (n.includes('whatnot')) return 'Whatnot';
|
|
37
|
-
if (n.includes('shopify')) return 'Shopify';
|
|
38
|
-
if (n.includes('palantir')) return 'Palantir';
|
|
39
|
-
if (n.includes('anduril')) return 'Anduril';
|
|
40
|
-
if (n.includes('netflix')) return 'Netflix';
|
|
41
|
-
if (n.includes('spotify')) return 'Spotify';
|
|
42
|
-
if (n.includes('tesla')) return 'Tesla';
|
|
43
|
-
if (n.includes('spacex')) return 'SpaceX';
|
|
44
|
-
if (n.includes('nvidia')) return 'NVIDIA';
|
|
45
|
-
// Defense / Aerospace
|
|
46
|
-
if (n.includes('lockheed')) return 'Lockheed Martin';
|
|
47
|
-
if (n.includes('northrop')) return 'Northrop Grumman';
|
|
48
|
-
if (n.includes('boeing')) return 'Boeing';
|
|
49
|
-
if (n.includes('l3harris') || n.includes('l3 harris')) return 'L3Harris';
|
|
50
|
-
if (n.includes('leidos')) return 'Leidos';
|
|
51
|
-
if (n.includes('nasa')) return 'NASA';
|
|
52
|
-
if (n.includes('sandia')) return 'Sandia National Labs';
|
|
53
|
-
if (n.includes('blue origin')) return 'Blue Origin';
|
|
54
|
-
if (n.includes('raytheon')) return 'Raytheon';
|
|
55
|
-
if (n.includes('general dynamics')) return 'General Dynamics';
|
|
56
|
-
if (n.includes('bae systems')) return 'BAE Systems';
|
|
57
|
-
// Finance
|
|
58
|
-
if (n.includes('goldman')) return 'Goldman Sachs';
|
|
59
|
-
if (n.includes('jpmorgan') || n.includes('jp morgan')) return 'JPMorgan Chase';
|
|
60
|
-
if (n.includes('wells fargo')) return 'Wells Fargo';
|
|
61
|
-
if (n.includes('citi') && !n.includes('citizen')) return 'Citi';
|
|
62
|
-
if (n.includes('mastercard')) return 'Mastercard';
|
|
63
|
-
if (n.includes('visa') && n.length < 15) return 'Visa';
|
|
64
|
-
if (n.includes('paypal')) return 'PayPal';
|
|
65
|
-
if (n.includes('bny') || n.includes('bank of new york') || n.includes('mellon')) return 'BNY Mellon';
|
|
66
|
-
if (n.includes('deloitte')) return 'Deloitte';
|
|
67
|
-
if (n.includes('bain')) return 'Bain & Company';
|
|
68
|
-
if (n.includes('bloomberg')) return 'Bloomberg';
|
|
69
|
-
if (n.includes('usaa')) return 'USAA';
|
|
70
|
-
if (n.includes('navy federal')) return 'Navy Federal';
|
|
71
|
-
if (n.includes('raymond james')) return 'Raymond James';
|
|
72
|
-
if (n.includes('geico')) return 'GEICO';
|
|
73
|
-
// Enterprise / Industrial
|
|
74
|
-
if (n.includes('oracle')) return 'Oracle';
|
|
75
|
-
if (n.includes('ibm')) return 'IBM';
|
|
76
|
-
if (n.includes('servicenow')) return 'ServiceNow';
|
|
77
|
-
if (n.includes('siemens')) return 'Siemens';
|
|
78
|
-
if (n.includes('texas instruments')) return 'Texas Instruments';
|
|
79
|
-
if (n.includes('qualcomm')) return 'Qualcomm';
|
|
80
|
-
if (n.includes('databricks')) return 'Databricks';
|
|
81
|
-
if (n.includes('together.ai') || n.includes('together ai')) return 'Together AI';
|
|
82
|
-
if (n.includes('zoox')) return 'Zoox';
|
|
83
|
-
// Entertainment
|
|
84
|
-
if (n.includes('nbcuniversal') || n.includes('nbc universal')) return 'NBCUniversal';
|
|
85
|
-
if (n.includes('universal destinations')) return 'Universal Destinations';
|
|
86
|
-
// Other
|
|
87
|
-
if (n.includes('humana')) return 'Humana';
|
|
88
|
-
if (n.includes('booz allen')) return 'Booz Allen Hamilton';
|
|
89
|
-
if (n.includes('abbott')) return 'Abbott';
|
|
90
|
-
if (n.includes('walmart')) return 'Walmart';
|
|
91
|
-
if (n.includes('publix')) return 'Publix';
|
|
92
|
-
if (n.includes('mitsubishi')) return 'Mitsubishi Power';
|
|
93
|
-
if (n.includes('oscar health')) return 'Oscar Health';
|
|
94
|
-
if (n.includes('ford') && n.length < 15) return 'Ford';
|
|
95
|
-
if (n.includes('coca') && n.includes('cola')) return 'Coca-Cola';
|
|
96
|
-
if (n.includes('nike')) return 'Nike';
|
|
97
|
-
if (n.includes('disney')) return 'Disney';
|
|
98
|
-
if (n.includes('ucf') || n.includes('university of central florida')) return 'UCF';
|
|
99
|
-
return (name || '').trim();
|
|
100
|
-
}
|
|
101
|
-
|
|
102
9
|
// Seniority for grouping and filters, read by lib/scoring.js so Paths and the
|
|
103
10
|
// power score never disagree about someone's level (0 intern/student … 6 C-suite).
|
|
104
11
|
export function getSeniority(headline) {
|
|
@@ -106,25 +13,36 @@ export function getSeniority(headline) {
|
|
|
106
13
|
return { key: t.key, label: t.label, level: t.level };
|
|
107
14
|
}
|
|
108
15
|
|
|
109
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* The company someone works at now: a company scan's, else their current
|
|
18
|
+
* role's, named as scoring names it (lib/scoring.js cleanCompany), so Paths
|
|
19
|
+
* groups people under the companies the score is built on. It used to merge
|
|
20
|
+
* names by a second, hand-picked list of its own, which also merged by
|
|
21
|
+
* mistake: Oxford, Hartford and Bradford were Ford, and Bainbridge was Bain.
|
|
22
|
+
*/
|
|
110
23
|
export function companyOf(c) {
|
|
111
|
-
|
|
112
|
-
return co ? normalizeCompany(co) || co : null;
|
|
24
|
+
return currentCompany(c);
|
|
113
25
|
}
|
|
114
26
|
|
|
115
27
|
// Order matters: the first industry whose words match wins, checked against the
|
|
116
|
-
// company name first (it is the stronger signal), then the headline.
|
|
28
|
+
// company name first (it is the stronger signal), then the headline. The words
|
|
29
|
+
// are the same for everyone: no one school, region or company of one person's,
|
|
30
|
+
// no job every kind of company has (a "Head of Growth" can work anywhere, so
|
|
31
|
+
// it is "growth marketing" here), and no word that is also plain English ("AR
|
|
32
|
+
// Specialist" is accounts receivable). docs/brain/SCORING.md lists the words
|
|
33
|
+
// taken out. Editing them changes the stored scores' list stamp (lib/rpc.js),
|
|
34
|
+
// since a company's one industry can decide its estimate and your sector.
|
|
117
35
|
export const INDUSTRIES = [
|
|
118
36
|
{ key: 'defense', label: 'Government, Defense & Aerospace', color: '#7f8c8d',
|
|
119
|
-
words: /\b(government|federal|state of|county|city of|defen[cs]e|army|navy|air force|marine corps|military|dod|nasa|aerospace|lockheed|northrop|boeing|raytheon|l3harris|leidos|
|
|
120
|
-
{ key: 'finance', label: 'Finance
|
|
121
|
-
words: /\b(bank|banking|capital|ventures?|vc|investments?|investor|fund|private equity|asset management|wealth|trading|trader|hedge|crypto|blockchain|web3|defi|fintech|stripe|coinbase|blackrock|robinhood|
|
|
37
|
+
words: /\b(government|federal|state of|county|city of|defen[cs]e|army|navy|air force|marine corps|military|dod|nasa|aerospace|lockheed|northrop|boeing|raytheon|l3harris|leidos|blue origin|spacex|public sector)\b/i },
|
|
38
|
+
{ key: 'finance', label: 'Finance & Investing', color: '#2ecc71',
|
|
39
|
+
words: /\b(bank|banking|capital|ventures?|vc|investments?|investor|fund|private equity|growth equity|asset management|wealth|trading|trader|hedge|crypto|blockchain|web3|defi|fintech|stripe|coinbase|blackrock|robinhood|goldman|morgan stanley|jpmorgan|chase|bny|mellon|wells fargo|citi|citigroup|capital one|fidelity|vanguard|schwab|visa|mastercard|amex|american express|paypal|insurance|accounting|cpa|financial)\b/i },
|
|
122
40
|
{ key: 'health', label: 'Healthcare & Biotech', color: '#e74c3c',
|
|
123
41
|
words: /\b(health|healthcare|medical|medicine|hospital|clinic|pharma|pharmaceutical|biotech|nurse|nursing|physician|doctor|dental|therapy|therapist|wellness)\b/i },
|
|
124
42
|
{ key: 'education', label: 'Education', color: '#f1c40f',
|
|
125
|
-
words: /\b(university|college|school|education|student|professor|teacher|academy|
|
|
126
|
-
{ key: 'media', label: 'Marketing
|
|
127
|
-
words: /\b(marketing|brand|branding|advertising|agency|media|content|social media|creative|creator|influencer|pr|public relations|communications|
|
|
43
|
+
words: /\b(university|college|school|education|student|professor|teacher|academy|phd|faculty|alumni)\b/i },
|
|
44
|
+
{ key: 'media', label: 'Marketing & Media', color: '#e67e22',
|
|
45
|
+
words: /\b(marketing|brand|branding|advertising|agency|media|content|social media|creative|creator|influencer|pr|public relations|communications|tiktok|pinterest|youtube|growth marketing)\b/i },
|
|
128
46
|
{ key: 'entertainment', label: 'Entertainment & Gaming', color: '#9b59b6',
|
|
129
47
|
words: /\b(entertainment|film|movie|music|records|gaming|games?|esports|studio|studios|netflix|disney|spotify|hollywood|theater|theatre|sports)\b/i },
|
|
130
48
|
{ key: 'tech', label: 'Tech, Software & AI', color: '#3498db',
|
|
@@ -134,7 +52,7 @@ export const INDUSTRIES = [
|
|
|
134
52
|
{ key: 'realestate', label: 'Real Estate & Construction', color: '#d35400',
|
|
135
53
|
words: /\b(real estate|realtor|realty|property|properties|construction|architecture|architect|mortgage|homes|housing|builders?)\b/i },
|
|
136
54
|
{ key: 'consumer', label: 'Retail, Consumer & Hospitality', color: '#ff79c6',
|
|
137
|
-
words: /\b(retail|e-?commerce|shopify|consumer|cpg|fashion|apparel|beauty|cosmetics|food|beverage|restaurant|hospitality|hotel|travel|tourism|coca-cola|nike|
|
|
55
|
+
words: /\b(retail|e-?commerce|shopify|consumer|cpg|fashion|apparel|beauty|cosmetics|food|beverage|restaurant|hospitality|hotel|travel|tourism|coca-cola|nike|store|shop)\b/i },
|
|
138
56
|
{ key: 'industry', label: 'Manufacturing, Energy & Logistics', color: '#95a5a6',
|
|
139
57
|
words: /\b(manufacturing|energy|oil|gas|solar|utilities|automotive|tesla|siemens|ge|general electric|honeywell|3m|logistics|supply chain|shipping|freight|fedex|ups|industrial|mining|chemicals?)\b/i },
|
|
140
58
|
{ key: 'nonprofit', label: 'Nonprofit & Community', color: '#bdc3c7',
|
|
@@ -154,14 +72,30 @@ export function industryOf(company, headline) {
|
|
|
154
72
|
return UNKNOWN_INDUSTRY;
|
|
155
73
|
}
|
|
156
74
|
|
|
75
|
+
/** industryOf as a key: what lib/scoring.js is handed, since it imports nothing. */
|
|
76
|
+
export const industryKeyOf = (company, headline) => industryOf(company, headline).key;
|
|
77
|
+
|
|
157
78
|
export const isSenior = (headline) => getSeniority(headline).level >= 4;
|
|
158
79
|
|
|
159
80
|
/**
|
|
160
81
|
* Every company in the network with the people there.
|
|
161
82
|
* @returns Map name -> { name, industry, people, d1, d2, d3, S, A, senior }
|
|
162
83
|
* People are kept once (by profile URL), at their closest degree.
|
|
84
|
+
*
|
|
85
|
+
* A company's industry is the one scoring uses (lib/scoring.js
|
|
86
|
+
* networkCompanies over the same rows): the curated list's, else the name's,
|
|
87
|
+
* else what most of its people say. So a company's colour here is the sector
|
|
88
|
+
* Settings → Your sector leans on, and each company is the one scoring
|
|
89
|
+
* scores: both name it with cleanCompany().
|
|
163
90
|
*/
|
|
164
91
|
export function buildCompanyIndex(rows = []) {
|
|
92
|
+
// Each headline is read once, for the industries and for companyOf below.
|
|
93
|
+
const parsed = new Map();
|
|
94
|
+
const rolesOf = (r) => {
|
|
95
|
+
if (!parsed.has(r)) parsed.set(r, rolesWithCompanies(r));
|
|
96
|
+
return parsed.get(r);
|
|
97
|
+
};
|
|
98
|
+
const { industries } = networkCompanies(rows, { industryOf: industryKeyOf, rolesOf });
|
|
165
99
|
const best = new Map();
|
|
166
100
|
for (const r of rows) {
|
|
167
101
|
const key = r.profile_url || `id:${r.id}`;
|
|
@@ -170,11 +104,12 @@ export function buildCompanyIndex(rows = []) {
|
|
|
170
104
|
}
|
|
171
105
|
const index = new Map();
|
|
172
106
|
for (const r of best.values()) {
|
|
173
|
-
|
|
107
|
+
// companyOf(r), from the roles already read.
|
|
108
|
+
const name = rolesOf(r).find((x) => !x.former)?.company;
|
|
174
109
|
if (!name) continue;
|
|
175
110
|
let co = index.get(name);
|
|
176
111
|
if (!co) {
|
|
177
|
-
co = { name, people: [], d1: 0, d2: 0, d3: 0, S: 0, A: 0, senior: 0
|
|
112
|
+
co = { name, people: [], d1: 0, d2: 0, d3: 0, S: 0, A: 0, senior: 0 };
|
|
178
113
|
index.set(name, co);
|
|
179
114
|
}
|
|
180
115
|
co.people.push(r);
|
|
@@ -182,19 +117,9 @@ export function buildCompanyIndex(rows = []) {
|
|
|
182
117
|
if (r.tier === 'S') co.S++;
|
|
183
118
|
if (r.tier === 'A') co.A++;
|
|
184
119
|
if (isSenior(r.headline)) co.senior++;
|
|
185
|
-
const ind = industryOf(name, r.headline).key;
|
|
186
|
-
co.votes[ind] = (co.votes[ind] || 0) + 1;
|
|
187
|
-
}
|
|
188
|
-
for (const co of index.values()) {
|
|
189
|
-
// The company name decides when it can; otherwise the most common headline industry.
|
|
190
|
-
const byName = industryOf(co.name, null);
|
|
191
|
-
if (byName !== UNKNOWN_INDUSTRY) co.industry = byName;
|
|
192
|
-
else {
|
|
193
|
-
const [top] = Object.entries(co.votes).filter(([k]) => k !== 'unknown').sort((a, b) => b[1] - a[1]);
|
|
194
|
-
co.industry = top ? industryByKey(top[0]) : UNKNOWN_INDUSTRY;
|
|
195
|
-
}
|
|
196
|
-
delete co.votes;
|
|
197
120
|
}
|
|
121
|
+
// Every company here is someone's current one, so networkCompanies read it too.
|
|
122
|
+
for (const co of index.values()) co.industry = industryByKey(industries.get(co.name));
|
|
198
123
|
return index;
|
|
199
124
|
}
|
|
200
125
|
|
package/lib/csv.js
CHANGED
|
@@ -181,6 +181,14 @@ export function hasCsvNetwork() {
|
|
|
181
181
|
try { return !!sessionStorage.getItem(STORAGE_KEY); } catch { return false; }
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
+
/** 'csv' or 'sample' when this tab holds one, else null — without rescoring it as loadCsvNetwork() does. */
|
|
185
|
+
export function csvNetworkSource() {
|
|
186
|
+
try {
|
|
187
|
+
const raw = sessionStorage.getItem(STORAGE_KEY);
|
|
188
|
+
return raw ? JSON.parse(raw).source || 'csv' : null;
|
|
189
|
+
} catch { return null; }
|
|
190
|
+
}
|
|
191
|
+
|
|
184
192
|
export function loadCsvNetwork() {
|
|
185
193
|
try {
|
|
186
194
|
const raw = sessionStorage.getItem(STORAGE_KEY);
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// One file that carries your network to another computer.
|
|
2
|
+
//
|
|
3
|
+
// The file is itself a SQLite database: a consistent copy of yours, made with
|
|
4
|
+
// VACUUM INTO the way the automatic backups are (safe even mid-WAL), plus two
|
|
5
|
+
// tables of its own. sd_export_manifest says what it is: the format, which
|
|
6
|
+
// version made it, when, and how many of each thing. sd_export_files holds the
|
|
7
|
+
// photos and the scanner's files that travel, each with its SHA-256. One file,
|
|
8
|
+
// no archive format, no new dependency. lib/data-import.js reads it back.
|
|
9
|
+
//
|
|
10
|
+
// What travels is an allow-list (lib/data-folder.js). Your LinkedIn sign-in
|
|
11
|
+
// (chrome-profile/) never does: the new computer signs in again.
|
|
12
|
+
|
|
13
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
14
|
+
import { readFileSync, lstatSync, statSync } from 'node:fs';
|
|
15
|
+
import { createHash } from 'node:crypto';
|
|
16
|
+
import path from 'node:path';
|
|
17
|
+
import { AVATAR_FILE, TRAVELLING_FILES, namesInFolder } from './data-folder.js';
|
|
18
|
+
|
|
19
|
+
/** Bump when the file's layout changes in a way an older import can't read. */
|
|
20
|
+
export const EXPORT_FORMAT = 1;
|
|
21
|
+
export const EXPORT_EXTENSION = '.sixdegrees';
|
|
22
|
+
|
|
23
|
+
/** The two tables an export adds. An import accepts exactly these columns. */
|
|
24
|
+
export const EXPORT_TABLES = {
|
|
25
|
+
sd_export_manifest: 'CREATE TABLE sd_export_manifest (key TEXT PRIMARY KEY, value TEXT NOT NULL)',
|
|
26
|
+
sd_export_files: 'CREATE TABLE sd_export_files (path TEXT PRIMARY KEY, bytes BLOB NOT NULL, sha256 TEXT NOT NULL)',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export function sha256(bytes) {
|
|
30
|
+
return createHash('sha256').update(bytes).digest('hex');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** A path as an SQL string literal, for the statements that can't take a parameter (VACUUM INTO, ATTACH). */
|
|
34
|
+
export function sqlString(value) {
|
|
35
|
+
return `'${String(value).replace(/'/g, "''")}'`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** The app's tables, read from the schema, so a table added later is counted and carried without anyone remembering to. */
|
|
39
|
+
export function appTables(schemaSql) {
|
|
40
|
+
return [...schemaSql.matchAll(/CREATE TABLE IF NOT EXISTS (\w+)/g)].map((m) => m[1]);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** The app's named indexes, read from the schema the same way. */
|
|
44
|
+
export function appIndexes(schemaSql) {
|
|
45
|
+
return [...schemaSql.matchAll(/CREATE (?:UNIQUE )?INDEX IF NOT EXISTS (\w+)/g)].map((m) => m[1]);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** People, not rows: someone reachable through two bridges has a row for each. */
|
|
49
|
+
export function countPeople(db, schema = 'main') {
|
|
50
|
+
try {
|
|
51
|
+
return db.prepare(`SELECT count(DISTINCT profile_url) AS n FROM ${schema}.linkedin_connections`).get().n;
|
|
52
|
+
} catch {
|
|
53
|
+
return 0;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** "Six Degrees backup 2026-09-25.sixdegrees", in this computer's own date. */
|
|
58
|
+
export function exportFileName(now = new Date()) {
|
|
59
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
60
|
+
return `Six Degrees backup ${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}${EXPORT_EXTENSION}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** The photo files the network's rows point at (`/avatars/<name>`), by name. */
|
|
64
|
+
export function referencedPhotos(db) {
|
|
65
|
+
return new Set(db.prepare(`SELECT DISTINCT substr(profile_image_url, 10) AS name FROM linkedin_connections
|
|
66
|
+
WHERE substr(profile_image_url, 1, 9) = '/avatars/'`).all().map((r) => r.name));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The files in the data folder that travel: the scanner's own JSON files and,
|
|
71
|
+
* if asked, the photos (only those named in `photos`, when it is given). Only
|
|
72
|
+
* plain files are taken, never a link, and avatars/ is read only if it is a
|
|
73
|
+
* real folder, so nothing outside the data folder can be pulled in through one.
|
|
74
|
+
*/
|
|
75
|
+
export function travellingFiles(dir, { includePhotos = true, photos = null } = {}) {
|
|
76
|
+
const plain = (p) => {
|
|
77
|
+
try { return lstatSync(p).isFile(); } catch { return false; }
|
|
78
|
+
};
|
|
79
|
+
const out = [];
|
|
80
|
+
for (const name of TRAVELLING_FILES) {
|
|
81
|
+
const abs = path.join(dir, name);
|
|
82
|
+
if (plain(abs)) out.push({ rel: name, abs });
|
|
83
|
+
}
|
|
84
|
+
if (includePhotos) {
|
|
85
|
+
for (const name of namesInFolder(path.join(dir, 'avatars')).sort()) {
|
|
86
|
+
if (!AVATAR_FILE.test(name) || (photos && !photos.has(name))) continue;
|
|
87
|
+
const abs = path.join(dir, 'avatars', name);
|
|
88
|
+
if (plain(abs)) out.push({ rel: `avatars/${name}`, abs });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Write an export of the open database `db` and the data folder `dir` to
|
|
96
|
+
* `outFile`, which must not exist yet.
|
|
97
|
+
* @returns {{ bytes, people, photos, files, counts }}
|
|
98
|
+
*/
|
|
99
|
+
export function buildExport(db, { dir, outFile, includePhotos = true, appVersion, schemaSql, now = new Date() }) {
|
|
100
|
+
db.exec(`VACUUM INTO ${sqlString(outFile)}`);
|
|
101
|
+
|
|
102
|
+
const out = new DatabaseSync(outFile);
|
|
103
|
+
let summary;
|
|
104
|
+
try {
|
|
105
|
+
// One self-contained file: no -wal beside it to be forgotten in a copy.
|
|
106
|
+
out.exec('PRAGMA journal_mode = DELETE');
|
|
107
|
+
out.exec(`${Object.values(EXPORT_TABLES).join(';\n')};`);
|
|
108
|
+
out.exec('BEGIN');
|
|
109
|
+
|
|
110
|
+
// Without the photos, a row pointing at /avatars/<file> would point at a
|
|
111
|
+
// file the other computer doesn't have: some views draw an empty circle
|
|
112
|
+
// for it. With no photo, they show initials, and the next scan of that
|
|
113
|
+
// person saves a new one. A photo not saved yet (LinkedIn's own link,
|
|
114
|
+
// which expires in a few weeks) is left as it is, as it is here.
|
|
115
|
+
if (!includePhotos) {
|
|
116
|
+
out.exec("UPDATE linkedin_connections SET profile_image_url = NULL WHERE profile_image_url LIKE '/avatars/%'");
|
|
117
|
+
}
|
|
118
|
+
// This computer's own history, not the network's: the other computer
|
|
119
|
+
// records its own import when it finishes it.
|
|
120
|
+
out.exec("DELETE FROM app_meta WHERE key = 'last_import'");
|
|
121
|
+
|
|
122
|
+
const put = out.prepare('INSERT INTO sd_export_files (path, bytes, sha256) VALUES (?, ?, ?)');
|
|
123
|
+
let photos = 0;
|
|
124
|
+
let files = 0;
|
|
125
|
+
// Only the photos of people still in the network. avatars/ keeps the
|
|
126
|
+
// photo of someone whose circle was deleted since; the copy has no row
|
|
127
|
+
// for them, so their face has no reason to travel.
|
|
128
|
+
const inNetwork = includePhotos ? referencedPhotos(out) : null;
|
|
129
|
+
for (const f of travellingFiles(dir, { includePhotos, photos: inNetwork })) {
|
|
130
|
+
let bytes;
|
|
131
|
+
try { bytes = readFileSync(f.abs); } catch { continue; } // replaced since the listing
|
|
132
|
+
put.run(f.rel, bytes, sha256(bytes));
|
|
133
|
+
files++;
|
|
134
|
+
if (f.rel.startsWith('avatars/')) photos++;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const counts = {};
|
|
138
|
+
for (const t of appTables(schemaSql)) {
|
|
139
|
+
try { counts[t] = out.prepare(`SELECT count(*) AS n FROM "${t}"`).get().n; } catch { counts[t] = 0; }
|
|
140
|
+
}
|
|
141
|
+
let scoringVersion = '';
|
|
142
|
+
try {
|
|
143
|
+
scoringVersion = out.prepare("SELECT value FROM app_meta WHERE key = 'scoring_version'").get()?.value ?? '';
|
|
144
|
+
} catch { /* never scored */ }
|
|
145
|
+
const people = countPeople(out);
|
|
146
|
+
|
|
147
|
+
const manifest = {
|
|
148
|
+
format: EXPORT_FORMAT,
|
|
149
|
+
app_version: appVersion,
|
|
150
|
+
scoring_version: scoringVersion,
|
|
151
|
+
exported_at: now.toISOString(),
|
|
152
|
+
include_photos: includePhotos ? 1 : 0,
|
|
153
|
+
people,
|
|
154
|
+
photos,
|
|
155
|
+
files,
|
|
156
|
+
counts: JSON.stringify(counts),
|
|
157
|
+
};
|
|
158
|
+
const meta = out.prepare('INSERT INTO sd_export_manifest (key, value) VALUES (?, ?)');
|
|
159
|
+
for (const [key, value] of Object.entries(manifest)) meta.run(key, String(value));
|
|
160
|
+
|
|
161
|
+
out.exec('COMMIT');
|
|
162
|
+
summary = { people, photos, files, counts };
|
|
163
|
+
} catch (err) {
|
|
164
|
+
try { out.exec('ROLLBACK'); } catch { /* nothing open */ }
|
|
165
|
+
throw err;
|
|
166
|
+
} finally {
|
|
167
|
+
out.close();
|
|
168
|
+
}
|
|
169
|
+
return { ...summary, bytes: statSync(outFile).size };
|
|
170
|
+
}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
// The data folder: what is in it, and which parts of it may leave this computer.
|
|
2
|
+
//
|
|
3
|
+
// Everything Six Degrees keeps lives in one folder (SIX_DEGREES_HOME, default
|
|
4
|
+
// ~/.six-degrees). Parts of it belong to this computer only: chrome-profile/ is
|
|
5
|
+
// a live, signed-in LinkedIn session (SECURITY.md), and venv/ and python/ (the
|
|
6
|
+
// scanner's own Python, when Set up the scanner downloaded one) are built for this
|
|
7
|
+
// machine's chip. So what an export carries is an allow-list, never "the folder
|
|
8
|
+
// minus a few things": a file that appears in the folder later stays behind
|
|
9
|
+
// until someone decides it should travel.
|
|
10
|
+
|
|
11
|
+
import { readdirSync, lstatSync, existsSync, rmSync } from 'node:fs';
|
|
12
|
+
import { spawn } from 'node:child_process';
|
|
13
|
+
import path from 'node:path';
|
|
14
|
+
|
|
15
|
+
/** A photo the /avatars route serves: a plain file name, no folders. The route uses this same pattern. */
|
|
16
|
+
export const AVATAR_FILE = /^[A-Za-z0-9_-]+\.(webp|jpg|jpeg|png)$/;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The scanner's notes about the network itself: without them, people whose
|
|
20
|
+
* circles are hidden or half-read would be retried from scratch on the new
|
|
21
|
+
* computer (TRAPS §15, §34). They describe one network's people, so an import
|
|
22
|
+
* replaces them along with the network: this computer's notes would say which
|
|
23
|
+
* pages of the old network's people were read, and the scanner would skip
|
|
24
|
+
* those pages for the new one.
|
|
25
|
+
*/
|
|
26
|
+
export const NETWORK_FILES = [
|
|
27
|
+
'bridge-progress.json',
|
|
28
|
+
'bridge-skips.json',
|
|
29
|
+
'bridge-unclear.json',
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The LinkedIn account's budget and cooldown. They belong to the account, not
|
|
34
|
+
* to a network or a computer, and leaving them behind would reset the month's
|
|
35
|
+
* count (TRAPS §35). So they travel, and an import merges them with the ones
|
|
36
|
+
* already here instead of replacing them (lib/linkedin-limits.js
|
|
37
|
+
* mergeBudgetFiles): searches made on either computer still count.
|
|
38
|
+
*/
|
|
39
|
+
export const BUDGET_FILES = [
|
|
40
|
+
'scan-limits.json',
|
|
41
|
+
'linkedin-activity.json',
|
|
42
|
+
'linkedin-cooldown.json',
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
/** Everything of the scanner's that an export carries. */
|
|
46
|
+
export const TRAVELLING_FILES = [...NETWORK_FILES, ...BUDGET_FILES];
|
|
47
|
+
|
|
48
|
+
/** Where a checked import waits for the next start (lib/data-import.js). */
|
|
49
|
+
export const PENDING_DIR = 'import-pending';
|
|
50
|
+
|
|
51
|
+
/** backups/ names: the copy kept before an import replaced the network. */
|
|
52
|
+
export const IMPORT_BACKUP_PREFIX = 'before-import-';
|
|
53
|
+
|
|
54
|
+
// Private working folders, made with mkdtemp inside the data folder so nothing
|
|
55
|
+
// is written anywhere else. A run that dies part-way leaves one behind; they
|
|
56
|
+
// are swept once they are an hour old (so a job still running is never hit),
|
|
57
|
+
// at each server's start (lib/data-import.js applyPendingImport, from getDb)
|
|
58
|
+
// and before the next run of the same kind.
|
|
59
|
+
export const EXPORT_WORK_PREFIX = '.export-';
|
|
60
|
+
export const UPLOAD_WORK_PREFIX = '.import-upload-';
|
|
61
|
+
// Set up the scanner's download and what it unpacked (lib/scanner-python.js):
|
|
62
|
+
// 25 to 150 MB, so a server stopped part-way shouldn't leave it for good.
|
|
63
|
+
export const SETUP_WORK_PREFIX = '.python-setup-';
|
|
64
|
+
const WORK_PREFIXES = [EXPORT_WORK_PREFIX, UPLOAD_WORK_PREFIX, SETUP_WORK_PREFIX];
|
|
65
|
+
const SPENT_PENDING = /^import-pending\.(done|cancelled)-/;
|
|
66
|
+
|
|
67
|
+
function plainFileSize(file) {
|
|
68
|
+
try {
|
|
69
|
+
const s = lstatSync(file);
|
|
70
|
+
return s.isFile() ? s.size : 0;
|
|
71
|
+
} catch {
|
|
72
|
+
return 0;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* The names in a folder that really is one here. A link, even to a folder, is
|
|
78
|
+
* never followed: nothing outside the data folder may be counted, or carried
|
|
79
|
+
* off in an export, through one.
|
|
80
|
+
*/
|
|
81
|
+
export function namesInFolder(folder) {
|
|
82
|
+
try {
|
|
83
|
+
return lstatSync(folder).isDirectory() ? readdirSync(folder) : [];
|
|
84
|
+
} catch {
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Count and total size of the plain files directly in a folder whose names pass `keep`. */
|
|
90
|
+
function filesIn(folder, keep = () => true) {
|
|
91
|
+
let count = 0;
|
|
92
|
+
let bytes = 0;
|
|
93
|
+
const names = namesInFolder(folder);
|
|
94
|
+
for (const name of names) {
|
|
95
|
+
if (!keep(name)) continue;
|
|
96
|
+
try {
|
|
97
|
+
const s = lstatSync(path.join(folder, name));
|
|
98
|
+
if (s.isFile()) { count++; bytes += s.size; }
|
|
99
|
+
} catch { /* gone since the listing */ }
|
|
100
|
+
}
|
|
101
|
+
return { count, bytes };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Everything under a folder, without following links. Capped, so a huge folder can't stall the page. */
|
|
105
|
+
function treeBytes(folder, budget = { left: 50000 }) {
|
|
106
|
+
let bytes = 0;
|
|
107
|
+
let names = [];
|
|
108
|
+
try { names = readdirSync(folder); } catch { return 0; }
|
|
109
|
+
for (const name of names) {
|
|
110
|
+
if (budget.left-- <= 0) break;
|
|
111
|
+
const p = path.join(folder, name);
|
|
112
|
+
try {
|
|
113
|
+
const s = lstatSync(p);
|
|
114
|
+
if (s.isDirectory()) bytes += treeBytes(p, budget);
|
|
115
|
+
else if (s.isFile()) bytes += s.size;
|
|
116
|
+
} catch { /* gone since the listing */ }
|
|
117
|
+
}
|
|
118
|
+
return bytes;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** 'auto' (before a new version), 'import' (before an import) or 'manual' (anything else, made by hand). */
|
|
122
|
+
export function backupKind(name, autoPrefix) {
|
|
123
|
+
if (autoPrefix && name.startsWith(autoPrefix)) return 'auto';
|
|
124
|
+
if (name.startsWith(IMPORT_BACKUP_PREFIX)) return 'import';
|
|
125
|
+
return 'manual';
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Every copy in backups/, newest first. A folder is the photos and files kept beside an import's copy. */
|
|
129
|
+
export function listBackups(folder, { autoPrefix } = {}) {
|
|
130
|
+
let names = [];
|
|
131
|
+
try { names = readdirSync(folder); } catch { return []; }
|
|
132
|
+
const out = [];
|
|
133
|
+
for (const name of names) {
|
|
134
|
+
if (name.startsWith('.') || name.endsWith('.partial')) continue;
|
|
135
|
+
const p = path.join(folder, name);
|
|
136
|
+
let s;
|
|
137
|
+
try { s = lstatSync(p); } catch { continue; }
|
|
138
|
+
if (!s.isFile() && !s.isDirectory()) continue;
|
|
139
|
+
out.push({
|
|
140
|
+
name,
|
|
141
|
+
bytes: s.isFile() ? s.size : treeBytes(p),
|
|
142
|
+
modifiedAt: s.mtimeMs,
|
|
143
|
+
kind: backupKind(name, autoPrefix),
|
|
144
|
+
folder: s.isDirectory(),
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
return out.sort((a, b) => b.modifiedAt - a.modifiedAt || b.name.localeCompare(a.name));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* What the Settings page shows about the folder. Sizes come from the file
|
|
152
|
+
* system only; nothing is read. chrome-profile/ is reported as there or not,
|
|
153
|
+
* and never looked inside. `inNetwork` (the photo names the network's rows
|
|
154
|
+
* point at) gives photosInCopy: the ones a copy would carry, since photos of
|
|
155
|
+
* people no longer in the network stay behind (lib/data-export.js).
|
|
156
|
+
*/
|
|
157
|
+
export function folderReport({ dir, dbFile, autoPrefix, inNetwork = null }) {
|
|
158
|
+
const avatars = path.join(dir, 'avatars');
|
|
159
|
+
return {
|
|
160
|
+
dataDir: dir,
|
|
161
|
+
dbFile,
|
|
162
|
+
databaseBytes: ['', '-wal', '-shm'].reduce((n, s) => n + plainFileSize(dbFile + s), 0),
|
|
163
|
+
photos: filesIn(avatars, (name) => AVATAR_FILE.test(name)),
|
|
164
|
+
photosInCopy: inNetwork ? filesIn(avatars, (name) => AVATAR_FILE.test(name) && inNetwork.has(name)) : null,
|
|
165
|
+
backups: listBackups(path.join(path.dirname(dbFile), 'backups'), { autoPrefix }),
|
|
166
|
+
linkedinSignIn: existsSync(path.join(dir, 'chrome-profile')),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** Remove working folders a run that died left behind, once they are old enough to be nobody's. */
|
|
171
|
+
export function sweepLeftovers(dir, { now = Date.now(), maxAgeMs = 3600 * 1000 } = {}) {
|
|
172
|
+
let names = [];
|
|
173
|
+
try { names = readdirSync(dir); } catch { return 0; }
|
|
174
|
+
let removed = 0;
|
|
175
|
+
for (const name of names) {
|
|
176
|
+
const work = WORK_PREFIXES.some((prefix) => name.startsWith(prefix));
|
|
177
|
+
if (!work && !SPENT_PENDING.test(name)) continue;
|
|
178
|
+
const p = path.join(dir, name);
|
|
179
|
+
try {
|
|
180
|
+
const s = lstatSync(p);
|
|
181
|
+
if (!s.isDirectory()) continue;
|
|
182
|
+
// A spent import folder is only ever renamed away to be deleted: always safe.
|
|
183
|
+
if (work && now - s.mtimeMs < maxAgeMs) continue;
|
|
184
|
+
rmSync(p, { recursive: true, force: true });
|
|
185
|
+
removed++;
|
|
186
|
+
} catch { /* someone else got there first */ }
|
|
187
|
+
}
|
|
188
|
+
return removed;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** The program that opens a folder in the file browser here, or null where there is none. */
|
|
192
|
+
export function folderOpener(platform = process.platform) {
|
|
193
|
+
if (platform === 'darwin') return 'open';
|
|
194
|
+
if (platform === 'linux') return 'xdg-open';
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Open the data folder in Finder (or the Linux file browser). The path is the
|
|
200
|
+
* data folder itself, never anything from a request. Resolves { ok, message }.
|
|
201
|
+
*/
|
|
202
|
+
export function revealFolder(dir, { platform = process.platform, spawnImpl = spawn, waitMs = 3000 } = {}) {
|
|
203
|
+
const cmd = folderOpener(platform);
|
|
204
|
+
if (!cmd) return Promise.resolve({ ok: false, message: 'There is no file browser to open it with on this computer.' });
|
|
205
|
+
return new Promise((resolve) => {
|
|
206
|
+
let timer = null;
|
|
207
|
+
let settled = false;
|
|
208
|
+
const done = (result) => {
|
|
209
|
+
if (settled) return;
|
|
210
|
+
settled = true;
|
|
211
|
+
clearTimeout(timer);
|
|
212
|
+
resolve(result);
|
|
213
|
+
};
|
|
214
|
+
let child;
|
|
215
|
+
try {
|
|
216
|
+
child = spawnImpl(cmd, [dir], { stdio: 'ignore', detached: true });
|
|
217
|
+
} catch (err) {
|
|
218
|
+
done({ ok: false, message: `It couldn't be opened (${err.message}).` });
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
child.on('error', (err) => done({
|
|
222
|
+
ok: false,
|
|
223
|
+
message: err.code === 'ENOENT'
|
|
224
|
+
? `This computer has no ${cmd}, so there is no file browser to open it with.`
|
|
225
|
+
: `It couldn't be opened (${err.message}).`,
|
|
226
|
+
}));
|
|
227
|
+
// `open` hands the folder to Finder and exits at once, and so does xdg-open
|
|
228
|
+
// (or it fails at once, on a machine with no desktop). Still running after a
|
|
229
|
+
// few seconds means a file browser is up and holding it.
|
|
230
|
+
child.on('exit', (code) => done(code === 0
|
|
231
|
+
? { ok: true }
|
|
232
|
+
: { ok: false, message: `The file browser didn't open (${cmd} stopped with code ${code}).` }));
|
|
233
|
+
timer = setTimeout(() => {
|
|
234
|
+
try { child.unref(); } catch { /* already gone */ }
|
|
235
|
+
done({ ok: true });
|
|
236
|
+
}, waitMs);
|
|
237
|
+
});
|
|
238
|
+
}
|