six-degrees 0.4.1 → 0.4.3
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/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +13 -13
- 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 +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 +18 -17
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +18 -17
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +14 -13
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/api/admin-delete/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/admin-update/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/bridges/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/bulk-import/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/company-scores/legacy/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/company-scores/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/connections/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/export/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/import/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/restart/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/reveal/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/delete-cluster/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/ingest/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/outreach/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/queue/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/scraper/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/settings/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/settings/sector-preview/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/setup-profile/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/unlock/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/update/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/update-images/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/users/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/avatars/[file]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/import/page.js +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 +22 -21
- package/.next/standalone/.next/server/app/import.segments/_full.segment.rsc +22 -21
- package/.next/standalone/.next/server/app/import.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/import.segments/import/__PAGE__.segment.rsc +16 -15
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +20 -19
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +16 -15
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +20 -19
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/launch/page.js +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 +22 -21
- package/.next/standalone/.next/server/app/launch.segments/_full.segment.rsc +22 -21
- package/.next/standalone/.next/server/app/launch.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/launch.segments/launch/__PAGE__.segment.rsc +16 -15
- package/.next/standalone/.next/server/app/page.js +1 -1
- 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 +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 +22 -21
- package/.next/standalone/.next/server/app/paths.segments/_full.segment.rsc +22 -21
- package/.next/standalone/.next/server/app/paths.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/paths.segments/paths/__PAGE__.segment.rsc +16 -15
- package/.next/standalone/.next/server/app/profile/page.js +1 -1
- 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 +22 -21
- package/.next/standalone/.next/server/app/profile.segments/_full.segment.rsc +22 -21
- package/.next/standalone/.next/server/app/profile.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/profile.segments/profile/__PAGE__.segment.rsc +16 -15
- package/.next/standalone/.next/server/app/queue/page.js +1 -1
- 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 +22 -21
- package/.next/standalone/.next/server/app/queue.segments/_full.segment.rsc +22 -21
- package/.next/standalone/.next/server/app/queue.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/queue.segments/queue/__PAGE__.segment.rsc +16 -15
- package/.next/standalone/.next/server/app/scores/page.js +1 -1
- package/.next/standalone/.next/server/app/scores/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/scores/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/scores.html +1 -1
- package/.next/standalone/.next/server/app/scores.rsc +22 -21
- package/.next/standalone/.next/server/app/scores.segments/_full.segment.rsc +22 -21
- package/.next/standalone/.next/server/app/scores.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/scores.segments/scores/__PAGE__.segment.rsc +16 -15
- package/.next/standalone/.next/server/app/settings/page.js +1 -1
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/settings.html +1 -1
- package/.next/standalone/.next/server/app/settings.rsc +22 -21
- package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +22 -21
- package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +16 -15
- package/.next/standalone/.next/server/app/setup/page.js +1 -1
- 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 +22 -21
- package/.next/standalone/.next/server/app/setup.segments/_full.segment.rsc +22 -21
- package/.next/standalone/.next/server/app/setup.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/setup.segments/setup/__PAGE__.segment.rsc +16 -15
- package/.next/standalone/.next/server/chunks/_1_fynia._.js +6 -6
- package/.next/standalone/.next/server/chunks/_1itd1o_._.js +1 -1
- package/.next/standalone/.next/server/chunks/lib_db-client_15le7cw.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0d3vjsq._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0m4vk0b._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{_15xuwkv._.js → _012mop3._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_1163x49._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1av11ws._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1ig7em7._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_20w06i-._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_page_1fazqbf.js +4 -4
- package/.next/standalone/.next/server/chunks/ssr/app_setup_page_1-282zx.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/middleware-manifest.json +5 -5
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- 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/{3e8zg6ctvvb8p.js → 0_3ubivay59bq.js} +4 -4
- package/.next/standalone/.next/static/chunks/{2p6o90z0vgcwr.css → 0bo1dxn5gwzpk.css} +1 -1
- package/.next/standalone/.next/static/chunks/0f4ugd6v3ph0-.js +1 -0
- package/.next/standalone/.next/static/chunks/10ogurbcuf3nq.js +1 -0
- package/.next/standalone/.next/static/chunks/1489t7jrjojk_.js +1 -0
- package/.next/standalone/.next/static/chunks/{0ax_9zzboyv6d.js → 17-rhp8sg4m1b.js} +2 -2
- package/.next/standalone/.next/static/chunks/1zunq4tk-u3ng.js +1 -0
- package/.next/standalone/.next/static/chunks/1zwjy1mmpkdrg.js +6 -0
- package/.next/standalone/.next/static/chunks/2_wcobfqry2uj.js +1 -0
- package/.next/standalone/.next/static/chunks/2dwtoszeq91us.js +1 -0
- package/.next/standalone/.next/static/chunks/2pyo4j84prgp6.js +18 -0
- package/.next/standalone/.next/static/chunks/2wwmljnf7yn_5.js +1 -0
- package/.next/{static/chunks/2xjho0lroiwlq.js → standalone/.next/static/chunks/3kllnkb3hof1s.js} +1 -1
- package/.next/standalone/CHANGELOG.md +54 -0
- package/.next/standalone/app/api/scraper/route.js +27 -2
- package/.next/standalone/app/components/ChainView.js +17 -10
- package/.next/standalone/app/components/FilterPanel.js +37 -5
- package/.next/standalone/app/components/ForceGraph.js +106 -13
- package/.next/standalone/app/components/OrbitGraph.js +105 -30
- package/.next/standalone/app/components/ScanStatusBar.js +82 -0
- package/.next/standalone/app/globals.css +4 -0
- package/.next/standalone/app/layout.js +3 -0
- package/.next/standalone/app/page.js +8 -4
- package/.next/standalone/app/setup/page.js +13 -1
- package/.next/standalone/lib/dot-rings.js +21 -0
- package/.next/standalone/lib/reach.js +22 -1
- package/.next/standalone/lib/scraper-client.js +1 -0
- package/.next/standalone/package-lock.json +2 -2
- package/.next/standalone/package.json +1 -1
- package/.next/standalone/scripts/scrape.py +229 -5
- package/.next/static/chunks/{3e8zg6ctvvb8p.js → 0_3ubivay59bq.js} +4 -4
- package/.next/static/chunks/{2p6o90z0vgcwr.css → 0bo1dxn5gwzpk.css} +1 -1
- package/.next/static/chunks/0f4ugd6v3ph0-.js +1 -0
- package/.next/static/chunks/10ogurbcuf3nq.js +1 -0
- package/.next/static/chunks/1489t7jrjojk_.js +1 -0
- package/.next/static/chunks/{0ax_9zzboyv6d.js → 17-rhp8sg4m1b.js} +2 -2
- package/.next/static/chunks/1zunq4tk-u3ng.js +1 -0
- package/.next/static/chunks/1zwjy1mmpkdrg.js +6 -0
- package/.next/static/chunks/2_wcobfqry2uj.js +1 -0
- package/.next/static/chunks/2dwtoszeq91us.js +1 -0
- package/.next/static/chunks/2pyo4j84prgp6.js +18 -0
- package/.next/static/chunks/2wwmljnf7yn_5.js +1 -0
- package/.next/{standalone/.next/static/chunks/2xjho0lroiwlq.js → static/chunks/3kllnkb3hof1s.js} +1 -1
- package/CHANGELOG.md +54 -0
- package/lib/dot-rings.js +21 -0
- package/lib/reach.js +22 -1
- package/lib/scraper-client.js +1 -0
- package/package.json +1 -1
- package/scripts/scrape.py +229 -5
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0h1_f_2._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__19ip1qq._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_06w_gl4._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_13kzrwv._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_1hgd5dp._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/node_modules_1f8t_rp._.js +0 -23
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_client_components_server-async-storage_0t1dato.js +0 -3
- package/.next/standalone/.next/static/chunks/10784r7w7t5gw.js +0 -18
- package/.next/standalone/.next/static/chunks/18xu0fbgo_qz-.js +0 -1
- package/.next/standalone/.next/static/chunks/28bu26ausqzmn.js +0 -1
- package/.next/standalone/.next/static/chunks/2jz311xl1pp_8.js +0 -1
- package/.next/standalone/.next/static/chunks/2ot61qsv6_0eq.js +0 -1
- package/.next/standalone/.next/static/chunks/2pnbm1z6j7xab.js +0 -1
- package/.next/standalone/.next/static/chunks/3ol1xux1-ofm4.js +0 -6
- package/.next/standalone/.next/static/chunks/3zs_6-nn0dapb.js +0 -1
- package/.next/static/chunks/10784r7w7t5gw.js +0 -18
- package/.next/static/chunks/18xu0fbgo_qz-.js +0 -1
- package/.next/static/chunks/28bu26ausqzmn.js +0 -1
- package/.next/static/chunks/2jz311xl1pp_8.js +0 -1
- package/.next/static/chunks/2ot61qsv6_0eq.js +0 -1
- package/.next/static/chunks/2pnbm1z6j7xab.js +0 -1
- package/.next/static/chunks/3ol1xux1-ofm4.js +0 -6
- package/.next/static/chunks/3zs_6-nn0dapb.js +0 -1
- /package/.next/standalone/.next/static/{LlDgA9g8ToBkDLlo9Qtov → 814ehZBeFO2rUKle7mzX-}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{LlDgA9g8ToBkDLlo9Qtov → 814ehZBeFO2rUKle7mzX-}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{LlDgA9g8ToBkDLlo9Qtov → 814ehZBeFO2rUKle7mzX-}/_ssgManifest.js +0 -0
- /package/.next/static/{LlDgA9g8ToBkDLlo9Qtov → 814ehZBeFO2rUKle7mzX-}/_buildManifest.js +0 -0
- /package/.next/static/{LlDgA9g8ToBkDLlo9Qtov → 814ehZBeFO2rUKle7mzX-}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{LlDgA9g8ToBkDLlo9Qtov → 814ehZBeFO2rUKle7mzX-}/_ssgManifest.js +0 -0
package/scripts/scrape.py
CHANGED
|
@@ -27,11 +27,12 @@ in one session — LinkedIn detects automation and flags your account.
|
|
|
27
27
|
import argparse
|
|
28
28
|
import json
|
|
29
29
|
import os
|
|
30
|
+
import re
|
|
30
31
|
import signal
|
|
31
32
|
import sys
|
|
32
33
|
import time
|
|
33
34
|
import requests
|
|
34
|
-
from datetime import datetime
|
|
35
|
+
from datetime import datetime, timedelta
|
|
35
36
|
from pathlib import Path
|
|
36
37
|
from urllib.parse import parse_qs, urlparse
|
|
37
38
|
|
|
@@ -1274,6 +1275,17 @@ PAGE_PAUSE = 20 # seconds before each next page of results
|
|
|
1274
1275
|
CHUNK_COOLDOWN = 60 # extra seconds after every SAVE_EVERY_PAGES pages
|
|
1275
1276
|
LEGACY_PAGES_READ = 10 # how far every read before 0.1.6 went, at most
|
|
1276
1277
|
|
|
1278
|
+
# Experimental all-day pacing for Auto-Bridge (--experimental; item 44, Graph
|
|
1279
|
+
# Study §8). Openly slow, never disguised: fixed waits, no random "human" timing.
|
|
1280
|
+
# A sitting of SESSION_PAGES searches, then a long rest; searches only in the
|
|
1281
|
+
# daytime on this computer's clock; and at the daily budget it waits for the
|
|
1282
|
+
# budget to free up instead of stopping. Pages are saved one at a time.
|
|
1283
|
+
EXPERIMENT = {"on": False, "pages": 0, "wire": 0}
|
|
1284
|
+
SESSION_PAGES = 10 # searches in one sitting
|
|
1285
|
+
SESSION_REST = 45 * 60 # the rest after each sitting
|
|
1286
|
+
DRIP_HOURS = (9, 19) # searches only from 09:00 to 19:00, local time
|
|
1287
|
+
WIRE_SAMPLES = 5 # raw LinkedIn responses kept per run, for research
|
|
1288
|
+
|
|
1277
1289
|
|
|
1278
1290
|
# Someone whose connections are hidden can never produce a 2nd-degree row. The
|
|
1279
1291
|
# "who still needs bridging" query is "everyone with no 2nd-degree rows", so
|
|
@@ -1701,6 +1713,12 @@ def _change_progress(change):
|
|
|
1701
1713
|
print(f" (could not note how far this read got: {exc})")
|
|
1702
1714
|
|
|
1703
1715
|
|
|
1716
|
+
# How long each list is, from LinkedIn's "About N results" line on the first
|
|
1717
|
+
# page read (RESULT_COUNT_JS), kept until the read's progress is written: the
|
|
1718
|
+
# app's rings round a dot show pages read against it. Approximate, like the line.
|
|
1719
|
+
_LIST_TOTALS = {}
|
|
1720
|
+
|
|
1721
|
+
|
|
1704
1722
|
def record_bridge_progress(profile_url, name, pages, more, urn=None):
|
|
1705
1723
|
"""Note that someone's connections have been read through page `pages`.
|
|
1706
1724
|
|
|
@@ -1717,6 +1735,9 @@ def record_bridge_progress(profile_url, name, pages, more, urn=None):
|
|
|
1717
1735
|
"at": datetime.now().isoformat(timespec="seconds")}
|
|
1718
1736
|
if urn or old.get("urn"):
|
|
1719
1737
|
entry["urn"] = urn or old.get("urn")
|
|
1738
|
+
total = _LIST_TOTALS.get(profile_url) or old.get("total")
|
|
1739
|
+
if total:
|
|
1740
|
+
entry["total"] = int(total)
|
|
1720
1741
|
mine[profile_url] = entry
|
|
1721
1742
|
_change_progress(change)
|
|
1722
1743
|
|
|
@@ -1778,6 +1799,193 @@ def stop_requested():
|
|
|
1778
1799
|
return _stop_requested
|
|
1779
1800
|
|
|
1780
1801
|
|
|
1802
|
+
# LinkedIn's own data (experimental, with --experimental). While a circle is
|
|
1803
|
+
# read, the responses the browser already gets from LinkedIn's own API are kept
|
|
1804
|
+
# and read for the same people the page shows: the same traffic, no extra
|
|
1805
|
+
# requests. The page text stays in charge. This fills blanks for people both
|
|
1806
|
+
# found and gives the list's real total; people only in LinkedIn's data are
|
|
1807
|
+
# logged, not added, since a results page can carry other boxes ("People you
|
|
1808
|
+
# may know"). Each page logs how the two compare, and a few raw responses are
|
|
1809
|
+
# kept in the data folder (wire-samples/) to tune it from real ones.
|
|
1810
|
+
_WIRE = {"page": None, "responses": []}
|
|
1811
|
+
|
|
1812
|
+
|
|
1813
|
+
def _wire_tap(page):
|
|
1814
|
+
if _WIRE["page"] is page:
|
|
1815
|
+
return
|
|
1816
|
+
_WIRE["page"], _WIRE["responses"] = page, []
|
|
1817
|
+
|
|
1818
|
+
def keep(response):
|
|
1819
|
+
url = response.url
|
|
1820
|
+
if "/voyager/api/" in url and ("search" in url.lower() or "graphql" in url):
|
|
1821
|
+
_WIRE["responses"].append(response)
|
|
1822
|
+
page.on("response", keep)
|
|
1823
|
+
|
|
1824
|
+
|
|
1825
|
+
def mutual_count_text(text):
|
|
1826
|
+
"""LinkedIn's mutual line as a number, as the page reader's mutualCount reads it; None otherwise."""
|
|
1827
|
+
line = re.sub(r"\s+", " ", str(text or "")).strip()
|
|
1828
|
+
if not re.search(r"\bmutual connections?$", line, re.I) or len(line) > 300:
|
|
1829
|
+
return None
|
|
1830
|
+
num = lambda v: int(str(v).replace(",", ""))
|
|
1831
|
+
names = lambda v: len([p for p in re.split(r"\s*,\s*|\s+and\s+", v) if p])
|
|
1832
|
+
m = re.match(r"^([\d,]+) mutual connections?$", line, re.I)
|
|
1833
|
+
if m:
|
|
1834
|
+
return num(m.group(1))
|
|
1835
|
+
m = re.match(r"^(.*?)\s*\band ([\d,]+) others? mutual connections?$", line, re.I)
|
|
1836
|
+
if m:
|
|
1837
|
+
return max(1, names(m.group(1))) + num(m.group(2))
|
|
1838
|
+
if re.search(r"\bis a mutual connection$", line, re.I):
|
|
1839
|
+
return 1
|
|
1840
|
+
m = re.match(r"^(.+?)\s+are mutual connections$", line, re.I)
|
|
1841
|
+
return names(m.group(1)) if m else None
|
|
1842
|
+
|
|
1843
|
+
|
|
1844
|
+
def voyager_people(payload):
|
|
1845
|
+
"""Everyone in a LinkedIn API response who looks like a search result: anything with a
|
|
1846
|
+
title and a link to a profile, wherever it sits in the response."""
|
|
1847
|
+
out, seen = [], set()
|
|
1848
|
+
text = lambda v: (v.get("text") if isinstance(v, dict) else v if isinstance(v, str) else "") or ""
|
|
1849
|
+
|
|
1850
|
+
def photo(x):
|
|
1851
|
+
try:
|
|
1852
|
+
for a in (x.get("image") or {}).get("attributes") or []:
|
|
1853
|
+
vi = ((a.get("detailData") or {}).get("nonEntityProfilePicture") or {}).get("vectorImage") or a.get("vectorImage")
|
|
1854
|
+
if vi and vi.get("rootUrl") and vi.get("artifacts"):
|
|
1855
|
+
best = max(vi["artifacts"], key=lambda f: f.get("width") or 0)
|
|
1856
|
+
return vi["rootUrl"] + (best.get("fileIdentifyingUrlPathSegment") or "")
|
|
1857
|
+
except Exception:
|
|
1858
|
+
pass
|
|
1859
|
+
return ""
|
|
1860
|
+
|
|
1861
|
+
def walk(x):
|
|
1862
|
+
if isinstance(x, dict):
|
|
1863
|
+
nav = x.get("navigationUrl")
|
|
1864
|
+
if isinstance(nav, str) and "/in/" in nav and isinstance(x.get("title"), dict):
|
|
1865
|
+
url = nav.split("?")[0].split("#")[0].rstrip("/") + "/"
|
|
1866
|
+
name = text(x.get("title")).strip()
|
|
1867
|
+
if name and name != "LinkedIn Member" and url not in seen:
|
|
1868
|
+
seen.add(url)
|
|
1869
|
+
mutual = None
|
|
1870
|
+
for ins in x.get("insightsResolutionResults") or []:
|
|
1871
|
+
mutual = mutual_count_text(text(((ins or {}).get("simpleInsight") or {}).get("title")))
|
|
1872
|
+
if mutual:
|
|
1873
|
+
break
|
|
1874
|
+
out.append({"name": name, "headline": text(x.get("primarySubtitle")).strip(),
|
|
1875
|
+
"profileUrl": url, "imageUrl": photo(x), "mutualCount": mutual})
|
|
1876
|
+
for v in x.values():
|
|
1877
|
+
walk(v)
|
|
1878
|
+
elif isinstance(x, list):
|
|
1879
|
+
for v in x:
|
|
1880
|
+
walk(v)
|
|
1881
|
+
walk(payload)
|
|
1882
|
+
return out
|
|
1883
|
+
|
|
1884
|
+
|
|
1885
|
+
def voyager_total(payload):
|
|
1886
|
+
"""The list's length, when a response says it (totalResultCount); None otherwise."""
|
|
1887
|
+
stack = [payload]
|
|
1888
|
+
while stack:
|
|
1889
|
+
x = stack.pop()
|
|
1890
|
+
if isinstance(x, dict):
|
|
1891
|
+
v = x.get("totalResultCount")
|
|
1892
|
+
if isinstance(v, int) and v > 0:
|
|
1893
|
+
return v
|
|
1894
|
+
stack.extend(x.values())
|
|
1895
|
+
elif isinstance(x, list):
|
|
1896
|
+
stack.extend(x)
|
|
1897
|
+
return None
|
|
1898
|
+
|
|
1899
|
+
|
|
1900
|
+
def _wire_sample(data):
|
|
1901
|
+
if EXPERIMENT["wire"] >= WIRE_SAMPLES:
|
|
1902
|
+
return
|
|
1903
|
+
try:
|
|
1904
|
+
raw = json.dumps(data)
|
|
1905
|
+
if len(raw) > 500_000:
|
|
1906
|
+
return
|
|
1907
|
+
folder = _home() / "wire-samples"
|
|
1908
|
+
folder.mkdir(parents=True, exist_ok=True)
|
|
1909
|
+
(folder / f"{int(time.time() * 1000)}.json").write_text(raw)
|
|
1910
|
+
EXPERIMENT["wire"] += 1
|
|
1911
|
+
except Exception:
|
|
1912
|
+
pass
|
|
1913
|
+
|
|
1914
|
+
|
|
1915
|
+
def _wire_merge(page, profile_url, page_results):
|
|
1916
|
+
"""Fill blanks in this page's people from LinkedIn's own data, and log how the two compare."""
|
|
1917
|
+
got, _WIRE["responses"] = _WIRE["responses"], []
|
|
1918
|
+
people, total = [], None
|
|
1919
|
+
for response in got:
|
|
1920
|
+
try:
|
|
1921
|
+
data = response.json()
|
|
1922
|
+
except Exception:
|
|
1923
|
+
continue
|
|
1924
|
+
_wire_sample(data)
|
|
1925
|
+
people.extend(voyager_people(data))
|
|
1926
|
+
total = total or voyager_total(data)
|
|
1927
|
+
if total:
|
|
1928
|
+
_LIST_TOTALS[profile_url] = int(total)
|
|
1929
|
+
own = (profile_url or "").rstrip("/").split("/")[-1]
|
|
1930
|
+
by_url = {c.get("profileUrl"): c for c in page_results}
|
|
1931
|
+
only_wire = 0
|
|
1932
|
+
for w in people:
|
|
1933
|
+
if own and own in w["profileUrl"]:
|
|
1934
|
+
continue
|
|
1935
|
+
c = by_url.get(w["profileUrl"])
|
|
1936
|
+
if c is None:
|
|
1937
|
+
only_wire += 1
|
|
1938
|
+
continue
|
|
1939
|
+
for k in ("name", "headline", "imageUrl"):
|
|
1940
|
+
if not c.get(k) and w.get(k):
|
|
1941
|
+
c[k] = w[k]
|
|
1942
|
+
if c.get("mutualCount") is None and w.get("mutualCount") is not None:
|
|
1943
|
+
c["mutualCount"] = w["mutualCount"]
|
|
1944
|
+
print(f" LinkedIn's own data: {len(people)} people, {only_wire} not in the page text"
|
|
1945
|
+
+ (f", list of about {total:,}" if total else ""), flush=True)
|
|
1946
|
+
return page_results
|
|
1947
|
+
|
|
1948
|
+
|
|
1949
|
+
def _drip_wait(label, seconds):
|
|
1950
|
+
"""Wait `seconds` for an experimental rest, saying when it ends, a line a minute. False if stopped."""
|
|
1951
|
+
seconds = max(1, int(seconds))
|
|
1952
|
+
until = datetime.fromtimestamp(time.time() + seconds).strftime("%H:%M")
|
|
1953
|
+
print(f" {label}. Carrying on at {until}.", flush=True)
|
|
1954
|
+
return interruptible_sleep(seconds, on_tick=lambda left: print(f" {label}: {max(1, left // 60)} min to go", flush=True), step=60)
|
|
1955
|
+
|
|
1956
|
+
|
|
1957
|
+
def _seconds_until_search_frees(now=None):
|
|
1958
|
+
"""How long until the oldest search in the last 24 hours drops out of the daily budget."""
|
|
1959
|
+
now = now if now is not None else time.time()
|
|
1960
|
+
recent = sorted(t for t in _read_activity()["searches"] if now - DAY_SECONDS < t <= now)
|
|
1961
|
+
return (recent[0] + DAY_SECONDS - now + 5) if recent else 5
|
|
1962
|
+
|
|
1963
|
+
|
|
1964
|
+
def _drip_before_search():
|
|
1965
|
+
"""Experimental pacing, before a search: rest after a sitting, sleep through the
|
|
1966
|
+
night, and wait for a used daily budget to free up. True to go ahead, False if stopped."""
|
|
1967
|
+
while True:
|
|
1968
|
+
if EXPERIMENT["pages"] >= SESSION_PAGES:
|
|
1969
|
+
EXPERIMENT["pages"] = 0
|
|
1970
|
+
if not _drip_wait(f"Resting after {SESSION_PAGES} pages", SESSION_REST):
|
|
1971
|
+
return False
|
|
1972
|
+
continue
|
|
1973
|
+
now = datetime.fromtimestamp(time.time())
|
|
1974
|
+
if not (DRIP_HOURS[0] <= now.hour < DRIP_HOURS[1]):
|
|
1975
|
+
start = now.replace(hour=DRIP_HOURS[0], minute=0, second=0, microsecond=0)
|
|
1976
|
+
if now.hour >= DRIP_HOURS[1]:
|
|
1977
|
+
start += timedelta(days=1)
|
|
1978
|
+
if not _drip_wait("Resting overnight", (start - now).total_seconds()):
|
|
1979
|
+
return False
|
|
1980
|
+
continue
|
|
1981
|
+
left, kind = searches_left()
|
|
1982
|
+
if left <= 0 and kind == "daily":
|
|
1983
|
+
if not _drip_wait("Today's searches are used", _seconds_until_search_frees()):
|
|
1984
|
+
return False
|
|
1985
|
+
continue
|
|
1986
|
+
return True
|
|
1987
|
+
|
|
1988
|
+
|
|
1781
1989
|
def interruptible_sleep(seconds, on_tick=None, step=5):
|
|
1782
1990
|
"""Sleep, but notice a stop request. Returns False if we were interrupted."""
|
|
1783
1991
|
waited = 0
|
|
@@ -2393,6 +2601,12 @@ def _scrape_one_bridge(page, bridge_name, bridge_id, profile_url, max_pages=LINK
|
|
|
2393
2601
|
# 1st, 2nd and 3rd+ — all of them; the app drops your own connections when
|
|
2394
2602
|
# it saves. It used to be announced as a "3rd+ filter", which it never was.
|
|
2395
2603
|
search_url = f"https://www.linkedin.com/search/results/people/?network=%5B%22F%22%2C%22S%22%2C%22O%22%5D&connectionOf=%5B%22{urn}%22%5D"
|
|
2604
|
+
if EXPERIMENT["on"] and not _drip_before_search():
|
|
2605
|
+
reach["more"] = True
|
|
2606
|
+
print(" Stopped before their list opened; the next run carries on from there.")
|
|
2607
|
+
return [], "stopped", reach
|
|
2608
|
+
if EXPERIMENT["on"]:
|
|
2609
|
+
_wire_tap(page)
|
|
2396
2610
|
left, kind = searches_left()
|
|
2397
2611
|
if left <= 0:
|
|
2398
2612
|
reach["budget"] = kind
|
|
@@ -2478,6 +2692,7 @@ def _scrape_one_bridge(page, bridge_name, bridge_id, profile_url, max_pages=LINK
|
|
|
2478
2692
|
except Exception:
|
|
2479
2693
|
total = None
|
|
2480
2694
|
if total:
|
|
2695
|
+
_LIST_TOTALS[profile_url] = int(total)
|
|
2481
2696
|
pages = -(-total // 10)
|
|
2482
2697
|
print(f" About {total:,} results in their list: {pages} pages"
|
|
2483
2698
|
+ (f", and LinkedIn shows the first {LINKEDIN_MAX_PAGES}." if pages > LINKEDIN_MAX_PAGES else "."))
|
|
@@ -2537,6 +2752,9 @@ def _scrape_one_bridge(page, bridge_name, bridge_id, profile_url, max_pages=LINK
|
|
|
2537
2752
|
break
|
|
2538
2753
|
|
|
2539
2754
|
page_results = [c for c in page_results if slug not in c.get("profileUrl", "")]
|
|
2755
|
+
if EXPERIMENT["on"]:
|
|
2756
|
+
page_results = _wire_merge(page, profile_url, page_results)
|
|
2757
|
+
EXPERIMENT["pages"] += 1
|
|
2540
2758
|
chunk.extend(page_results)
|
|
2541
2759
|
chunk_pages += 1
|
|
2542
2760
|
reach["last"] = pg
|
|
@@ -2559,7 +2777,7 @@ def _scrape_one_bridge(page, bridge_name, bridge_id, profile_url, max_pages=LINK
|
|
|
2559
2777
|
# own path: through the page-limit branch, a missing Next button would
|
|
2560
2778
|
# have marked the list finished.
|
|
2561
2779
|
left, kind = searches_left()
|
|
2562
|
-
if left <= 0:
|
|
2780
|
+
if left <= 0 and not (EXPERIMENT["on"] and kind == "daily"):
|
|
2563
2781
|
# Looking for Next costs nothing. No Next on a live page of results
|
|
2564
2782
|
# means the list is finished — not paused at the next page forever.
|
|
2565
2783
|
if _find_next(page) is None and not _window_closed(page) and _result_links(page):
|
|
@@ -2572,10 +2790,10 @@ def _scrape_one_bridge(page, bridge_name, bridge_id, profile_url, max_pages=LINK
|
|
|
2572
2790
|
|
|
2573
2791
|
# Rest before the next search, and longer after every SAVE_EVERY_PAGES.
|
|
2574
2792
|
rest = PAGE_PAUSE
|
|
2575
|
-
if (pg - start_page + 1) % SAVE_EVERY_PAGES == 0:
|
|
2793
|
+
if not EXPERIMENT["on"] and (pg - start_page + 1) % SAVE_EVERY_PAGES == 0:
|
|
2576
2794
|
rest += CHUNK_COOLDOWN
|
|
2577
2795
|
print(f" {SAVE_EVERY_PAGES} pages read; resting {rest}s before the next search.")
|
|
2578
|
-
if not interruptible_sleep(rest) or _window_closed(page):
|
|
2796
|
+
if not interruptible_sleep(rest) or _window_closed(page) or (EXPERIMENT["on"] and not _drip_before_search()):
|
|
2579
2797
|
reach["more"] = True
|
|
2580
2798
|
print(f" Stopped before page {pg + 1}; the next run carries on from there.")
|
|
2581
2799
|
break
|
|
@@ -2600,7 +2818,7 @@ def _scrape_one_bridge(page, bridge_name, bridge_id, profile_url, max_pages=LINK
|
|
|
2600
2818
|
|
|
2601
2819
|
# Save as it goes, so a stop, a crash or the search limit costs at most
|
|
2602
2820
|
# the last few pages rather than the whole list.
|
|
2603
|
-
if on_save and chunk_pages >= SAVE_EVERY_PAGES:
|
|
2821
|
+
if on_save and chunk_pages >= (1 if EXPERIMENT["on"] else SAVE_EVERY_PAGES):
|
|
2604
2822
|
on_save(_dedupe(chunk), reach["last"], True, reach["urn"])
|
|
2605
2823
|
chunk, chunk_pages = [], 0
|
|
2606
2824
|
|
|
@@ -3742,6 +3960,9 @@ Examples:
|
|
|
3742
3960
|
parser.add_argument("--bridge", type=str, help="Name of one bridge person to scrape")
|
|
3743
3961
|
parser.add_argument("--rescrape", type=str, help="Delete + re-scrape a bridge's cluster from scratch")
|
|
3744
3962
|
parser.add_argument("--company", type=str, help="Scan everyone the app can see at one company")
|
|
3963
|
+
parser.add_argument("--experimental", action="store_true",
|
|
3964
|
+
help="Auto-Bridge, experimental: all-day pacing (sittings, rests, daytime only, "
|
|
3965
|
+
"waits for the daily budget) and reading LinkedIn's own data beside the page text")
|
|
3745
3966
|
parser.add_argument("--auto-bridge", action="store_true",
|
|
3746
3967
|
help="Map every bridge in turn, highest tier first")
|
|
3747
3968
|
parser.add_argument("--retry-private", action="store_true",
|
|
@@ -3773,6 +3994,9 @@ Examples:
|
|
|
3773
3994
|
"(every scan also does this at its end)")
|
|
3774
3995
|
parser.add_argument("--headless", action="store_true", help="Run browser in headless mode")
|
|
3775
3996
|
args = parser.parse_args()
|
|
3997
|
+
if getattr(args, "experimental", False):
|
|
3998
|
+
EXPERIMENT["on"] = True
|
|
3999
|
+
print("Experimental Auto-Bridge: all-day pacing, and LinkedIn's own data read beside the page text.")
|
|
3776
4000
|
args.max_pages = max(1, min(LINKEDIN_MAX_PAGES, args.max_pages))
|
|
3777
4001
|
|
|
3778
4002
|
# A failed save ends the run with its reason, not a traceback (TRAPS §32).
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
module.exports=[9272,a=>{a.v({className:"geist_2ae47f08-module__h69qWW__className",variable:"geist_2ae47f08-module__h69qWW__variable"})},79474,a=>{a.v({className:"geist_mono_eb58308d-module__w_p2Lq__className",variable:"geist_mono_eb58308d-module__w_p2Lq__variable"})},61143,a=>{"use strict";a.s(["default",()=>b]);let b=(0,a.i(11857).registerClientReference)(function(){throw Error("Attempted to call the default export of [project]/app/components/StaleServerBanner.js from the server, but it's on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.")},"[project]/app/components/StaleServerBanner.js","default")},42041,a=>{"use strict";var b=a.i(61143);a.n(b)},62925,a=>{"use strict";var b=a.i(7997),c=a.i(9272);let d={className:c.default.className,style:{fontFamily:"'Geist', 'Geist Fallback'",fontStyle:"normal"}};null!=c.default.variable&&(d.variable=c.default.variable);var e=a.i(79474);let f={className:e.default.className,style:{fontFamily:"'Geist Mono', 'Geist Mono Fallback'",fontStyle:"normal"}};null!=e.default.variable&&(f.variable=e.default.variable);var g=a.i(68354),h=a.i(42041);a.s(["default",0,function({children:a}){return(0,b.jsx)("html",{lang:"en",className:`${d.variable} ${f.variable}`,children:(0,b.jsx)("body",{style:{margin:0,padding:0,background:"#0a0a1a"},children:(0,b.jsxs)(g.default,{children:[a,(0,b.jsx)(h.default,{})]})})})},"metadata",0,{title:"Six Degrees",description:"Maps your LinkedIn network: who you know, who they know, and the shortest path to someone you haven't met. Runs on your own computer; nothing is uploaded.",viewport:"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"}],62925)},19325,function(a){a.n(a.i(62925))},69067,a=>{"use strict";a.s(["default",()=>c,"useUser",()=>d]);var b=a.i(11857);let c=(0,b.registerClientReference)(function(){throw Error("Attempted to call the default export of [project]/app/components/UserProvider.js from the server, but it's on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.")},"[project]/app/components/UserProvider.js","default"),d=(0,b.registerClientReference)(function(){throw Error("Attempted to call useUser() from the server but useUser is on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.")},"[project]/app/components/UserProvider.js","useUser")},68354,a=>{"use strict";var b=a.i(69067);a.n(b)}];
|
|
2
|
-
|
|
3
|
-
//# sourceMappingURL=%5Broot-of-the-server%5D__0h1_f_2._.js.map
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
module.exports=[18622,(a,b,c)=>{b.exports=a.x("next/dist/compiled/next-server/app-page-turbo.runtime.prod.js",()=>require("next/dist/compiled/next-server/app-page-turbo.runtime.prod.js"))},20635,(a,b,c)=>{b.exports=a.x("next/dist/server/app-render/action-async-storage.external.js",()=>require("next/dist/server/app-render/action-async-storage.external.js"))},24725,(a,b,c)=>{b.exports=a.x("next/dist/server/app-render/after-task-async-storage.external.js",()=>require("next/dist/server/app-render/after-task-async-storage.external.js"))},43285,(a,b,c)=>{b.exports=a.x("next/dist/server/app-render/dynamic-access-async-storage.external.js",()=>require("next/dist/server/app-render/dynamic-access-async-storage.external.js"))},56704,(a,b,c)=>{b.exports=a.x("next/dist/server/app-render/work-async-storage.external.js",()=>require("next/dist/server/app-render/work-async-storage.external.js"))},32319,(a,b,c)=>{b.exports=a.x("next/dist/server/app-render/work-unit-async-storage.external.js",()=>require("next/dist/server/app-render/work-unit-async-storage.external.js"))},59043,(a,b,c)=>{b.exports=a.x("next/dist/server/runtime-reacts.external.js",()=>require("next/dist/server/runtime-reacts.external.js"))},17372,a=>{"use strict";var b=a.i(87924),c=a.i(72131);a.s(["default",0,function(){let[a,d]=(0,c.useState)(null);return((0,c.useEffect)(()=>{let a=!0;return fetch("/api/update").then(a=>a.json()).then(b=>{a&&b?.restartNeeded&&d(b)}).catch(()=>{}),()=>{a=!1}},[]),a)?(0,b.jsxs)("div",{role:"status",style:{position:"fixed",left:0,right:0,bottom:0,zIndex:9999,background:"linear-gradient(90deg, #FFD700, #FF6B35)",color:"#1a1200",padding:"9px 16px",fontSize:13,fontWeight:600,lineHeight:1.5,display:"flex",alignItems:"center",justifyContent:"center",gap:10,flexWrap:"wrap",textAlign:"center"},children:[(0,b.jsxs)("span",{children:["You have newer code on disk (",a.sha,") than this server is running (",a.bootSha,"). Restart it to see the changes — ",(0,b.jsx)("code",{style:{background:"rgba(0,0,0,0.16)",padding:"1px 6px",borderRadius:4},children:"Ctrl-C"})," in the terminal, then ",(0,b.jsx)("code",{style:{background:"rgba(0,0,0,0.16)",padding:"1px 6px",borderRadius:4},children:"npm run dev"}),"."]}),(0,b.jsx)("button",{onClick:()=>d(null),style:{border:"none",background:"rgba(0,0,0,0.18)",color:"#1a1200",borderRadius:6,padding:"3px 10px",fontWeight:700,cursor:"pointer"},children:"Dismiss"})]}):null}])},75857,58834,a=>{"use strict";var b=a.i(87924),c=a.i(72131),d=a.i(73167),e=a.i(42510);let f="true"===process.env.NEXT_PUBLIC_DEMO_MODE,g={id:"synthetic-demo-user",name:"You",headline:"Sample network"},h=null;async function i(){if(h)return h;let a=await fetch("/demo-data.json",{cache:"no-cache"});if(!a.ok)throw Error("demo-data.json not found — run: node scripts/gen-synthetic.mjs");let b=await a.json();return h={degree1:b.degree1||[],degree2:b.degree2||[]}}async function j(){let a=await i();return(0,e.saveSessionNetwork)({degree1:a.degree1,degree2:a.degree2,source:"sample"}),{degree1:a.degree1.length,degree2:a.degree2.length}}a.s(["DEMO_USER",0,g,"IS_DEMO",0,f,"loadDemoNetwork",0,i,"loadSampleIntoSession",0,j],58834);let k=(0,c.createContext)({userId:null,userName:null,userProfile:null,ready:!1});a.s(["default",0,function({children:a}){let[h,i]=(0,c.useState)(null),[j,l]=(0,c.useState)(null),[m,n]=(0,c.useState)(null),[o,p]=(0,c.useState)(!1);return(0,c.useEffect)(()=>{if(f){i(g.id),l(g.name),n({name:g.name,headline:g.headline,role:"",company:"",industry:"",sectors:[],goals:[],linkedin_url:""}),p(!0);return}if((0,e.hasCsvNetwork)()){i(e.CSV_USER.id),l(e.CSV_USER.name),n({name:e.CSV_USER.name,headline:"Imported from your LinkedIn CSV",role:"",company:"",industry:"",sectors:[],goals:[],linkedin_url:""}),p(!0);return}fetch("/api/users?me=1").then(a=>a.json()).then(a=>{let b=a.user;if(!b)throw Error("no profile");(0,d.setUser)(b.id,b.name),i(b.id),l(b.name),n({name:b.name,headline:b.headline||"",role:b.role||"",company:b.company||"",industry:b.industry||"",sectors:b.sectors||[],goals:b.goals||[],linkedin_url:b.linkedin_url||""})}).catch(()=>{(0,d.hasUser)()&&(i((0,d.getUserId)()),l((0,d.getUserName)()))}).finally(()=>p(!0))},[]),(0,b.jsx)(k.Provider,{value:{userId:h,userName:j,userProfile:m,ready:o,login:(a,b)=>{(0,d.setUser)(a,b),i(a),l(b),fetch(`/api/users?id=${a}`).then(a=>a.json()).then(a=>{a.user&&n({name:a.user.name,headline:a.user.headline||"",role:a.user.role||"",company:a.user.company||"",industry:a.user.industry||"",sectors:a.user.sectors||[],goals:a.user.goals||[],linkedin_url:a.user.linkedin_url||""})}).catch(()=>{})}},children:a})},"useUser",0,function(){return(0,c.useContext)(k)}],75857)},42602,(a,b,c)=>{"use strict";b.exports=a.r(18622)},9270,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored.contexts.AppRouterContext},36313,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored.contexts.HooksClientContext},18341,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored.contexts.ServerInsertedHtml},35112,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored["react-ssr"].ReactDOM},87924,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored["react-ssr"].ReactJsxRuntime},38783,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored["react-ssr"].ReactServerDOMTurbopackClient},72131,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored["react-ssr"].React}];
|
|
2
|
-
|
|
3
|
-
//# sourceMappingURL=%5Broot-of-the-server%5D__19ip1qq._.js.map
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
module.exports=[16874,a=>{"use strict";var b=a.i(87924);let c="1px solid rgba(255,255,255,0.1)";function d({children:a,style:c}){return(0,b.jsx)("div",{style:{fontSize:13.5,color:"#8b9a9a",lineHeight:1.6,marginTop:4,...c},children:a})}a.s(["Body",0,d,"Btn",0,function({children:a,onClick:d,disabled:e,primary:f,tone:g,type:h="button"}){return(0,b.jsx)("button",{type:h,onClick:d,disabled:e,style:{padding:"9px 18px",borderRadius:7,fontSize:13.5,fontWeight:650,color:e?"#667":"#fff",border:c,background:e?"rgba(255,255,255,0.05)":"bad"===g?"rgba(255,80,80,0.15)":f?"linear-gradient(135deg, #9B59B6, #3498DB)":"rgba(255,255,255,0.08)",cursor:e?"not-allowed":"pointer"},children:a})},"FONT",0,'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',"LINE",0,c,"Mono",0,function({children:a}){return(0,b.jsx)("code",{style:{background:"rgba(255,255,255,0.08)",padding:"1px 5px",borderRadius:4,wordBreak:"break-all"},children:a})},"Section",0,function({id:a,title:e,intro:f,children:g}){return(0,b.jsxs)("section",{id:a,style:{padding:"24px 0",borderBottom:c,scrollMarginTop:16},children:[(0,b.jsx)("h2",{style:{fontSize:16,fontWeight:650,margin:"0 0 6px"},children:e}),f&&(0,b.jsx)(d,{style:{marginTop:0,marginBottom:12},children:f}),g]})},"Status",0,function({children:a,tone:c}){return(0,b.jsxs)("div",{role:"status",style:{marginTop:10,padding:"10px 14px",borderRadius:8,fontSize:13.5,lineHeight:1.6,color:"ok"===c?"#00ff88":"bad"===c?"#ff7676":"#e8e8ee",background:"ok"===c?"rgba(0,255,136,0.08)":"bad"===c?"rgba(255,80,80,0.08)":"rgba(255,255,255,0.05)",border:`1px solid ${"ok"===c?"rgba(0,255,136,0.3)":"bad"===c?"rgba(255,80,80,0.3)":"rgba(255,255,255,0.12)"}`},children:["ok"===c&&"✓ ",a]})}])},9282,a=>{"use strict";var b=a.i(30889);function c(a){let c=(0,b.readTitle)(a);return{key:c.key,label:c.label,level:c.level}}function d(a){return(0,b.currentCompany)(a)}let e=[{key:"defense",label:"Government, Defense & Aerospace",color:"#7f8c8d",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},{key:"finance",label:"Finance & Investing",color:"#2ecc71",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},{key:"health",label:"Healthcare & Biotech",color:"#e74c3c",words:/\b(health|healthcare|medical|medicine|hospital|clinic|pharma|pharmaceutical|biotech|nurse|nursing|physician|doctor|dental|therapy|therapist|wellness)\b/i},{key:"education",label:"Education",color:"#f1c40f",words:/\b(university|college|school|education|student|professor|teacher|academy|phd|faculty|alumni)\b/i},{key:"media",label:"Marketing & Media",color:"#e67e22",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},{key:"entertainment",label:"Entertainment & Gaming",color:"#9b59b6",words:/\b(entertainment|film|movie|music|records|gaming|games?|esports|studio|studios|netflix|disney|spotify|hollywood|theater|theatre|sports)\b/i},{key:"tech",label:"Tech, Software & AI",color:"#3498db",words:/\b(software|saas|engineer|engineering|developer|tech|technology|technologies|ai|artificial intelligence|machine learning|ml|data|cloud|cyber|security|devops|product manager|google|microsoft|apple|meta|amazon|nvidia|openai|anthropic|palantir|salesforce|oracle|ibm|intel|app|apps|startup|platform|labs?)\b/i},{key:"consulting",label:"Consulting, Legal & Services",color:"#1abc9c",words:/\b(consulting|consultant|consultancy|deloitte|accenture|mckinsey|kpmg|pwc|ey|bain|bcg|legal|law|lawyer|attorney|paralegal|recruiting|recruiter|staffing|talent)\b/i},{key:"realestate",label:"Real Estate & Construction",color:"#d35400",words:/\b(real estate|realtor|realty|property|properties|construction|architecture|architect|mortgage|homes|housing|builders?)\b/i},{key:"consumer",label:"Retail, Consumer & Hospitality",color:"#ff79c6",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},{key:"industry",label:"Manufacturing, Energy & Logistics",color:"#95a5a6",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},{key:"nonprofit",label:"Nonprofit & Community",color:"#bdc3c7",words:/\b(nonprofit|non-profit|foundation|charity|ngo|church|ministry|volunteer)\b/i}],f={key:"unknown",label:"Unclear",color:"#555c66"},g=Object.fromEntries([...e,f].map(a=>[a.key,a])),h=a=>g[a]||f;function i(a,b){for(let c of[a,b]){if(!c)continue;let a=e.find(a=>a.words.test(c));if(a)return a}return f}let j=(a,b)=>i(a,b).key,k=a=>c(a).level>=4;a.s(["INDUSTRIES",0,e,"UNKNOWN_INDUSTRY",0,f,"buildCompanyIndex",0,function(a=[]){let c=new Map,d=a=>(c.has(a)||c.set(a,(0,b.rolesWithCompanies)(a)),c.get(a)),{industries:e}=(0,b.networkCompanies)(a,{industryOf:j,rolesOf:d}),f=new Map;for(let b of a){let a=b.profile_url||`id:${b.id}`,c=f.get(a);(!c||(b.degree||9)<(c.degree||9))&&f.set(a,b)}let g=new Map;for(let a of f.values()){let b=d(a).find(a=>!a.former)?.company;if(!b)continue;let c=g.get(b);c||(c={name:b,people:[],d1:0,d2:0,d3:0,S:0,A:0,senior:0},g.set(b,c)),c.people.push(a),1===a.degree?c.d1++:2===a.degree?c.d2++:c.d3++,"S"===a.tier&&c.S++,"A"===a.tier&&c.A++,k(a.headline)&&c.senior++}for(let a of g.values())a.industry=h(e.get(a.name));return g},"companyLinks",0,function(a=[],b=[]){let c=new Map;for(let b of a){let a=d(b);a&&c.set(b.id,a)}let e=new Map;for(let a of b){let b=c.get(a.source_connection_id),f=d(a);if(!b||!f||b===f)continue;let[g,h]=b<f?[b,f]:[f,b],i=`${g}\u0000${h}`,j=e.get(i);j||(j={a:g,b:h,weight:0,via:new Set},e.set(i,j)),j.weight++,j.via.add(a.source_connection_id)}return[...e.values()]},"companyOf",0,d,"getSeniority",0,c,"industryByKey",0,h,"industryOf",0,i,"isSenior",0,k,"waysInto",0,function(a,b=[],c=[]){let e=b.filter(b=>d(b)===a),f=new Map;for(let b of c)d(b)===a&&f.set(b.source_connection_id,(f.get(b.source_connection_id)||0)+1);let g=new Map(b.map(a=>[a.id,a]));return{direct:e,bridges:[...f.entries()].map(([a,b])=>({bridge:g.get(a),n:b})).filter(a=>a.bridge).sort((a,b)=>b.n-a.n||(Number(b.bridge.power_score)||0)-(Number(a.bridge.power_score)||0))}}])},54919,a=>{"use strict";var b=a.i(9282);let c=new Map(b.INDUSTRIES.map(a=>[a.key,a.color])),d=new Map([...b.INDUSTRIES.map(a=>[a.key,Object.freeze({key:a.key,label:a.label,color:a.color,group:a.key,kind:"industry"})]),...Object.entries({software:["Software & SaaS","tech","function"],"ai-data":["AI & Data","tech","function"],cybersecurity:["Cybersecurity","tech","function"],telecom:["Telecom","tech","industry"],fintech:["Fintech & Payments","finance","industry"],banking:["Banking & Lending","finance","industry"],"vc-pe":["Venture Capital & Private Equity","finance","industry"],wealth:["Wealth & Asset Management","finance","industry"],insurance:["Insurance","finance","industry"],accounting:["Accounting & Tax","finance","function"],crypto:["Crypto & Web3","finance","industry"],hospitals:["Hospitals & Clinics","health","industry"],dental:["Dental","health","industry"],"pharma-biotech":["Pharma & Biotech","health","industry"],"medical-devices":["Medical Devices","health","industry"],"mental-health":["Mental Health","health","industry"],"fitness-wellness":["Fitness & Wellness","health","industry"],veterinary:["Veterinary & Animal Care","health","industry"],k12:["K-12 Education","education","industry"],"higher-ed":["Higher Education","education","industry"],"marketing-advertising":["Marketing & Advertising","media","function"],"pr-comms":["PR & Communications","media","function"],"media-publishing":["Media & Publishing","media","industry"],"creator-economy":["Creator Economy","media","industry"],"film-tv-music":["Film, TV & Music","entertainment","industry"],"gaming-esports":["Gaming & Esports","entertainment","industry"],sports:["Sports","entertainment","industry"],"ecommerce-retail":["E-commerce & Retail","consumer","industry"],"consumer-goods":["Consumer Goods","consumer","industry"],restaurants:["Restaurants & Food Service","consumer","industry"],"hospitality-travel":["Hospitality & Travel","consumer","industry"],"beauty-personal-care":["Beauty & Personal Care","consumer","industry"],logistics:["Logistics & Supply Chain","industry","industry"],manufacturing:["Manufacturing","industry","industry"],energy:["Energy & Utilities","industry","industry"],automotive:["Automotive","industry","industry"],agriculture:["Agriculture & Farming","industry","industry"],"aerospace-defense":["Aerospace & Defense","defense","industry"],military:["Military","defense","industry"],government:["Government & Public Sector","defense","industry"],legal:["Legal","consulting","function"],"management-consulting":["Management Consulting","consulting","function"],"hr-recruiting":["HR & Recruiting","consulting","function"],"security-services":["Security Services","consulting","function"],"real-estate":["Real Estate","realestate","industry"],"construction-trades":["Construction & Trades","realestate","industry"],charities:["Nonprofits & Charities","nonprofit","industry"],"social-work":["Social Work & Human Services","nonprofit","industry"],faith:["Faith & Ministry","nonprofit","industry"]}).map(([a,[d,e,f]])=>[a,Object.freeze({key:a,label:d,color:c.get(e)||b.UNKNOWN_INDUSTRY.color,group:e,kind:f})])]);a.s(["sectorByKey",0,function(a){return d.get(a)||{key:a,label:b.UNKNOWN_INDUSTRY.label,color:b.UNKNOWN_INDUSTRY.color,group:null,kind:null}}])}];
|
|
2
|
-
|
|
3
|
-
//# sourceMappingURL=_06w_gl4._.js.map
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
module.exports=[36839,a=>{"use strict";var b=a.i(87924),c=a.i(75857);a.s(["default",0,function({children:a}){let{userId:d,ready:e}=(0,c.useUser)();return e&&d?a:(0,b.jsx)("div",{style:{minHeight:"100vh",display:"flex",alignItems:"center",justifyContent:"center",background:"#0a0a1a",color:"#fff",padding:24,textAlign:"center",fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'},children:e?(0,b.jsxs)("div",{style:{maxWidth:420},children:[(0,b.jsx)("div",{style:{fontSize:18,fontWeight:700,marginBottom:8},children:"Can’t reach Six Degrees’ local server"}),(0,b.jsx)("div",{style:{color:"#888",fontSize:14,lineHeight:1.6},children:"The page loaded but the app behind it is not answering. If you started it from a terminal, check that window for an error, then reload this page."})]}):(0,b.jsx)("div",{style:{fontSize:18,fontWeight:600},children:"Loading…"})})}])},23310,a=>{"use strict";async function b(a){let b=a?`/api/network?userId=${encodeURIComponent(a)}`:"/api/network",c=await fetch(b);if(!c.ok)throw Error(`Failed to load network (${c.status})`);let d=await c.json();return{degree1:d.degree1||[],degree2:d.degree2||[],degree3:d.degree3||[]}}a.s(["loadNetwork",0,b])},56377,a=>{"use strict";let b=/^[A-Za-z0-9_-]+\.(webp|jpg|jpeg|png)$/,c="/avatars/";a.s(["localPhoto",0,function(a){return"string"==typeof a&&a.startsWith(c)&&b.test(a.slice(c.length))?a:null}])},37732,a=>{"use strict";async function b(){let a=await fetch("/api/scraper");if(!a.ok)throw Error("Could not reach the app.");return a.json()}async function c(a,b,d={}){let e=await fetch("/api/scraper",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({action:a,...b?{name:b}:{},...d})}),f=await e.json().catch(()=>({}));if(!e.ok)throw Error(f.error||"Could not start the scanner.");return f}async function d(a){let b=await fetch(`/api/scraper?resume=${encodeURIComponent(a)}`);return b.ok?(await b.json()).resume??null:null}Symbol.for("six-degrees.scan-state");let e=Object.freeze({skips:[],read:[]});async function f(){try{let a=await fetch("/api/scraper?reach=1");if(!a.ok)return e;let b=await a.json();return{skips:Array.isArray(b.skips)?b.skips:[],read:Array.isArray(b.read)?b.read:[]}}catch{return e}}async function g(a){let b=await fetch(`/api/scraper?person=${encodeURIComponent(a)}`);return b.ok?b.json():{person:null}}async function h(){await fetch("/api/scraper",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({action:"cancel"})})}let i=Object.freeze({known:!1,running:!1,pending:!1,action:null,target:null,startedAt:null,progress:null,log:[],finished:[]}),j=new Set,k=i,l=JSON.stringify(k),m=null,n=null,o=0,p=!1,q=new Set;function r(a){let b=JSON.stringify(a);if(b!==l)for(let c of(l=b,k=Object.freeze(a),[...j]))c()}async function s(){(!("u">typeof document&&document.hidden)||k.running)&&await function(){if(n&&n.epoch===o)return n.promise;let a=o,b=fetch("/api/scraper?job=1").then(a=>a.ok?a.json():null).then(b=>{a===o&&function(a){if(!a||"object"!=typeof a)return;let b=!0===a.running;b&&null!=a.startedAt&&q.add(a.startedAt);let c=(Array.isArray(a.recent)?a.recent:[]).filter(a=>q.has(a.startedAt)).map(c=>({...c,log:!b&&c.startedAt===a.startedAt&&Array.isArray(a.log)?a.log:[]}));r({known:!0,running:b||p,pending:p&&!b,action:b?a.action??null:p?k.action:null,target:b?a.target??null:p?k.target:null,startedAt:b?a.startedAt??null:null,progress:b?a.progress??null:null,log:b&&Array.isArray(a.log)?a.log:[],finished:c})}(b)}).catch(()=>{}).finally(()=>{n?.promise===b&&(n=null)});return n={epoch:a,promise:b},b}(),clearTimeout(m),m=j.size?setTimeout(s,k.running?1500:5e3):null}function t(a){return j.add(a),1===j.size&&s(),()=>{j.delete(a),j.size||(clearTimeout(m),m=null)}}async function u(a,{name:b,id:d,onLog:e,...f}={}){let g;o++,p=!0,r({...k,known:!0,running:!0,pending:!0,action:a,target:b||d?{id:d??null,name:b??null}:null,startedAt:null,progress:null,log:[]});try{g=await c(a,b,{...d?{id:d}:{},...f})}catch(a){throw p=!1,o++,k.pending&&r({...k,running:!1,pending:!1,action:null,target:null,log:[]}),s(),a}p=!1,o++;let h=g?.startedAt??null;null!=h&&q.add(h);let i=new Promise(a=>{let b=t(()=>{k.running&&k.startedAt===h&&e?.(k.log);let c=null==h?k.running?null:{exitCode:null,failure:null,log:[]}:k.finished.find(a=>a.startedAt===h);c&&(b(),c.log.length&&e?.(c.log),a(c))});s()});return{startedAt:h,ended:i}}function v(a){return!!a?.target&&["bridge","rescrape","resume"].includes(a.action)}a.s(["NO_SCAN_NOTES",0,e,"SCANNER_UNKNOWN",0,i,"beginScrape",0,u,"busyReason",0,function(a=k){if(!a?.running)return null;let b=a.target?.name;switch(a.action){case"bridge":case"rescrape":case"resume":return b?`${b}’s circle is being scanned`:"A circle is being scanned";case"company":return b?`${b} is being scanned`:"A company is being scanned";case"auto-bridge":case"auto-bridge-retry":case"resume-all":return"Your bridges are being mapped, one by one";case"full":case"refresh":return"Your own connections are being scanned";default:switch(a.action){case"install":return"The scanner's packages are being installed";case"setup":return"The scanner is being set up";case"login":return"The LinkedIn sign-in window is open";case"photos":return"Profile photos are being saved";default:return"A scan is running"}}},"isCircleScan",0,v,"loadScanNotes",0,f,"notReadyMessage",0,function(a){if(!a)return"Could not reach the app.";let b=a.checks||{};return b.scriptsFound?b.dependencies?b.chrome?null:"Google Chrome is not installed.":b.python||b.download?"The scanner is not set up yet — open Scan to set it up.":"No Python 3.10 to 3.14 is installed on this machine.":"The scanner files are missing from this install."},"pickedPerson",0,g,"resumePoint",0,d,"runScrape",0,function(a,b={}){return u(a,b).then(a=>a.ended)},"scannerNow",0,function(){return k},"scansCircleOf",0,function(a,b){if(!v(a)||!b)return!1;let{id:c,name:d}=a.target;return c&&b.id?c===b.id:!!d&&d===b.name},"scraperStatus",0,b,"stopScrape",0,h,"watchScanner",0,t],37732)},69789,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0});var d={actionAsyncStorage:function(){return f.actionAsyncStorage},workAsyncStorage:function(){return g.workAsyncStorage},workUnitAsyncStorage:function(){return h.workUnitAsyncStorage}};for(var e in d)Object.defineProperty(c,e,{enumerable:!0,get:d[e]});let f=a.r(20635),g=a.r(56704),h=a.r(32319);("function"==typeof c.default||"object"==typeof c.default&&null!==c.default)&&void 0===c.default.__esModule&&(Object.defineProperty(c.default,"__esModule",{value:!0}),Object.assign(c.default,c),b.exports=c.default)}];
|
|
2
|
-
|
|
3
|
-
//# sourceMappingURL=_13kzrwv._.js.map
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
module.exports=[36839,a=>{"use strict";var b=a.i(87924),c=a.i(75857);a.s(["default",0,function({children:a}){let{userId:d,ready:e}=(0,c.useUser)();return e&&d?a:(0,b.jsx)("div",{style:{minHeight:"100vh",display:"flex",alignItems:"center",justifyContent:"center",background:"#0a0a1a",color:"#fff",padding:24,textAlign:"center",fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'},children:e?(0,b.jsxs)("div",{style:{maxWidth:420},children:[(0,b.jsx)("div",{style:{fontSize:18,fontWeight:700,marginBottom:8},children:"Can’t reach Six Degrees’ local server"}),(0,b.jsx)("div",{style:{color:"#888",fontSize:14,lineHeight:1.6},children:"The page loaded but the app behind it is not answering. If you started it from a terminal, check that window for an error, then reload this page."})]}):(0,b.jsx)("div",{style:{fontSize:18,fontWeight:600},children:"Loading…"})})}])},56377,a=>{"use strict";let b=/^[A-Za-z0-9_-]+\.(webp|jpg|jpeg|png)$/,c="/avatars/";a.s(["localPhoto",0,function(a){return"string"==typeof a&&a.startsWith(c)&&b.test(a.slice(c.length))?a:null}])},91443,90231,a=>{"use strict";var b=a.i(56377);let c={S:0,A:1,B:2,C:3,D:4};function d(a){let b=parseFloat(a?.power_score);return Number.isFinite(b)?b:0}function e(a){return Math.round(10*d(a))/10}function f(a){return String(a?.profile_url||"").split(/[?#]/)[0].replace(/\/+$/,"")||`id:${a?.id}`}let g=(a,b)=>String(a??"").localeCompare(String(b??""),void 0,{numeric:!0});function h(a,b){return(c[a.tier]??9)-(c[b.tier]??9)||d(b)-d(a)||String(a.name||"").localeCompare(String(b.name||""))||g(a.id,b.id)}function i(a,b){return a.bridge&&b.bridge?h(a.bridge,b.bridge):a.bridge?-1:b.bridge?1:g(a.id,b.id)}function j(a,b){return b.score-a.score||b.waysIn-a.waysIn||i(a.routes[0]||k,b.routes[0]||k)||String(a.person.name||"").localeCompare(String(b.person.name||""))||(a.key<b.key?-1:+(a.key>b.key))}let k={id:null,bridge:null};function l(a){let b=String(a||"").trim().split(/\s+/).filter(Boolean);return b.length<2?b[0]||"":`${b[0]} ${b[b.length-1].charAt(0)}.`}function m(a,b){let c=String(a||"");return b<=1?c?"…":"":c.length>b?`${c.slice(0,b-1)}…`:c}function n(a,b){return a?b.scanned.has(a.id)||a.profile_url&&b.read?.has(f(a))?"scanned":a.profile_url&&b.hidden.has(f(a))?"hidden":"todo":"todo"}function o(a,b){if(!a||1!==a.degree||null==a.unlocked_from_bridge_id)return null;let c=n(a,b);return"todo"===c?"ready":c}a.s(["TIERS",0,["S","A","B","C","D"],"compareBridges",0,h,"compareWaysIn",0,function(a,b){return b.waysIn-a.waysIn||j(a,b)},"keyFor",0,f,"routeIndex",0,function(a=[]){let b=new Map;for(let c of a||[]){let a=f(c),d=b.get(a);d||(d=new Set,b.set(a,d)),d.add(c.source_connection_id??null)}return b},"routesFor",0,function(a,b,c){return a?[...b?.get(f(a))??(null!=a.source_connection_id?new Set([a.source_connection_id]):new Set)].map(a=>({id:a,bridge:c?.get(a)??null})).sort(i):[]},"score",0,d,"score1",0,e,"separationPeople",0,function(a=[],c=[]){let d=new Map;for(let a of c||[])d.set(a.id,a);let h=new Map,k=new Map;for(let c of a||[]){let a=f(c);k.set(c.id,a);let d=h.get(a);d?function(a,c){let d=e(a)-e(c);if(d)return d>0;let f=!!(0,b.localPhoto)(a.profile_image_url);return!!(0,b.localPhoto)(c.profile_image_url)!==f?f:0>g(a.id,c.id)}(c,d.person)&&(d.person=c):(d={key:a,person:c,rowIds:[],routeIds:new Set},h.set(a,d)),d.rowIds.push(c.id),d.routeIds.add(c.source_connection_id??null)}let l={people:h.size,byTier:{S:0,A:0,B:0,C:0,D:0},multi:0,bridges:0,unresolvedIds:0,peopleOnlyUnresolved:0,onlyUnresolvedVia:0},m=new Set,n=new Set,o=new Set,p=[];for(let a of h.values()){let b=[...a.routeIds].map(a=>({id:a,bridge:d.get(a)??null}));b.sort(i);let c=b.filter(a=>a.bridge);for(let a of b)(a.bridge?m:n).add(a.id);if(!c.length)for(let a of(l.peopleOnlyUnresolved++,b))o.add(a.id);let{person:f}=a,g=f.tier;void 0!==l.byTier[g]&&l.byTier[g]++,b.length>1&&l.multi++,p.push({key:a.key,person:f,score:e(f),tier:g,routes:b,waysIn:b.length,rowIds:a.rowIds,haystack:[f.name,f.headline,f.company,f.role,...c.map(a=>a.bridge.name)].filter(Boolean).join("\n").toLowerCase(),rank:0,tied:!1})}return l.bridges=m.size,l.unresolvedIds=n.size,l.onlyUnresolvedVia=o.size,p.sort(j),function(a){for(let b=0;b<a.length;b++){let c=a[b],d=a[b-1],e=d&&d.score===c.score&&d.waysIn===c.waysIn;c.rank=e?d.rank:b+1,e&&(c.tied=!0,d.tied=!0)}}(p),{people:p,rowToKey:k,summary:l}},"shortName",0,l,"summitLayout",0,function(a=[],b=800,c=!1){let d=c?36:26,e=Math.round(.42*b),f=Math.round(.58*b),g=a.map((a,c)=>{var e;return{key:a.key,p:a,x:f,y:16+c*d,r:(e=a.waysIn)>=4?8.5:3===e?7:2===e?5.5:4,label:m(`#${a.rank}${a.tied?"=":""} ${a.person.name||""}`,Math.floor((b-f-60)/6.2))}}),i=new Map,j="\0unresolved";for(let a of g)for(let b of a.p.routes){let c=b.bridge?b.id:j,d=i.get(c);d||(d={id:b.bridge?b.id:null,bridge:b.bridge,unresolved:!b.bridge,ys:[],count:0},i.set(c,d)),d.count++,d.ys.push(a.y)}let k=[...i.values()].map(a=>({...a,bary:a.ys.reduce((a,b)=>a+b,0)/a.ys.length}));k.sort((a,b)=>a.bary-b.bary||a.unresolved-b.unresolved||(a.bridge&&b.bridge?h(a.bridge,b.bridge):0));let n=g.length?g[g.length-1].y:16,o=k.length>1?Math.max(18,(n-16)/(k.length-1)):0,p=Math.max(4,Math.floor((e-10-42)/6));k.forEach((a,b)=>{if(a.x=e,a.y=1===k.length?(16+n)/2:16+b*o,a.r=5,a.unresolved)a.label=m(`can't be named (${a.count})`,p);else{let b=a.bridge.name||"";a.label=b.length<=p?b:m(l(b),p)}delete a.ys});let q=new Map(k.map(a=>[a.unresolved?j:a.id,a])),r=[];for(let a of g){let b=new Set;a.p.routes.forEach((c,d)=>{let e=c.bridge?c.id:j;if(b.has(e))return;b.add(e);let f=q.get(e);r.push({key:a.key,bridgeId:f.id,primary:0===d&&!f.unresolved,unresolved:f.unresolved,tier:f.bridge?.tier??null,x1:f.x,y1:f.y,x2:a.x,y2:a.y})})}let s=Math.max(n,k.length?k[k.length-1].y:0),t=g.length?Math.ceil(s+16):0,u={x:20,y:g.length?(16+Math.max(n,s))/2:16,r:14},v=k.map(a=>({id:a.id,x1:u.x,y1:u.y,x2:a.x,y2:a.y}));return{width:b,height:t,you:u,people:g,bridges:k,links:r,spokes:v}}],90231),a.s(["circleScanCost",0,function(a=100){let b=Math.max(1,Math.min(100,Math.round(a)||100));return{profileViews:1,searches:b,minutes:b>=100?55:Math.max(5,Math.round(.55*b))}},"circleState",0,n,"reachIndex",0,function(a=[],b=[],c={}){let d=new Set;for(let a of b||[])a?.source_connection_id!=null&&d.add(a.source_connection_id);let e=new Set;for(let a of c?.skips||[])a?.profileUrl&&e.add(f({profile_url:a.profileUrl}));let g=new Set;for(let a of c?.read||[])a&&g.add(f({profile_url:a}));let h=new Map;for(let b of a||[]){let a=b?.unlocked_from_bridge_id;if(null==a||1!==b.degree)continue;let c=h.get(a);c||h.set(a,c=[]),c.push(b)}return{scanned:d,read:g,hidden:e,reachedFrom:h}},"reachState",0,o,"readyByCircle",0,function(a){let b=new Map;for(let[c,d]of a.reachedFrom){let e=d.filter(b=>"ready"===o(b,a)).length;e&&b.set(c,e)}return b},"readyToScan",0,function(a,b){return(a||[]).filter(a=>"ready"===o(a,b)).sort(h)}],91443)},37732,a=>{"use strict";async function b(){let a=await fetch("/api/scraper");if(!a.ok)throw Error("Could not reach the app.");return a.json()}async function c(a,b,d={}){let e=await fetch("/api/scraper",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({action:a,...b?{name:b}:{},...d})}),f=await e.json().catch(()=>({}));if(!e.ok)throw Error(f.error||"Could not start the scanner.");return f}async function d(a){let b=await fetch(`/api/scraper?resume=${encodeURIComponent(a)}`);return b.ok?(await b.json()).resume??null:null}Symbol.for("six-degrees.scan-state");let e=Object.freeze({skips:[],read:[]});async function f(){try{let a=await fetch("/api/scraper?reach=1");if(!a.ok)return e;let b=await a.json();return{skips:Array.isArray(b.skips)?b.skips:[],read:Array.isArray(b.read)?b.read:[]}}catch{return e}}async function g(a){let b=await fetch(`/api/scraper?person=${encodeURIComponent(a)}`);return b.ok?b.json():{person:null}}async function h(){await fetch("/api/scraper",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({action:"cancel"})})}let i=Object.freeze({known:!1,running:!1,pending:!1,action:null,target:null,startedAt:null,progress:null,log:[],finished:[]}),j=new Set,k=i,l=JSON.stringify(k),m=null,n=null,o=0,p=!1,q=new Set;function r(a){let b=JSON.stringify(a);if(b!==l)for(let c of(l=b,k=Object.freeze(a),[...j]))c()}async function s(){(!("u">typeof document&&document.hidden)||k.running)&&await function(){if(n&&n.epoch===o)return n.promise;let a=o,b=fetch("/api/scraper?job=1").then(a=>a.ok?a.json():null).then(b=>{a===o&&function(a){if(!a||"object"!=typeof a)return;let b=!0===a.running;b&&null!=a.startedAt&&q.add(a.startedAt);let c=(Array.isArray(a.recent)?a.recent:[]).filter(a=>q.has(a.startedAt)).map(c=>({...c,log:!b&&c.startedAt===a.startedAt&&Array.isArray(a.log)?a.log:[]}));r({known:!0,running:b||p,pending:p&&!b,action:b?a.action??null:p?k.action:null,target:b?a.target??null:p?k.target:null,startedAt:b?a.startedAt??null:null,progress:b?a.progress??null:null,log:b&&Array.isArray(a.log)?a.log:[],finished:c})}(b)}).catch(()=>{}).finally(()=>{n?.promise===b&&(n=null)});return n={epoch:a,promise:b},b}(),clearTimeout(m),m=j.size?setTimeout(s,k.running?1500:5e3):null}function t(a){return j.add(a),1===j.size&&s(),()=>{j.delete(a),j.size||(clearTimeout(m),m=null)}}async function u(a,{name:b,id:d,onLog:e,...f}={}){let g;o++,p=!0,r({...k,known:!0,running:!0,pending:!0,action:a,target:b||d?{id:d??null,name:b??null}:null,startedAt:null,progress:null,log:[]});try{g=await c(a,b,{...d?{id:d}:{},...f})}catch(a){throw p=!1,o++,k.pending&&r({...k,running:!1,pending:!1,action:null,target:null,log:[]}),s(),a}p=!1,o++;let h=g?.startedAt??null;null!=h&&q.add(h);let i=new Promise(a=>{let b=t(()=>{k.running&&k.startedAt===h&&e?.(k.log);let c=null==h?k.running?null:{exitCode:null,failure:null,log:[]}:k.finished.find(a=>a.startedAt===h);c&&(b(),c.log.length&&e?.(c.log),a(c))});s()});return{startedAt:h,ended:i}}function v(a){return!!a?.target&&["bridge","rescrape","resume"].includes(a.action)}a.s(["NO_SCAN_NOTES",0,e,"SCANNER_UNKNOWN",0,i,"beginScrape",0,u,"busyReason",0,function(a=k){if(!a?.running)return null;let b=a.target?.name;switch(a.action){case"bridge":case"rescrape":case"resume":return b?`${b}’s circle is being scanned`:"A circle is being scanned";case"company":return b?`${b} is being scanned`:"A company is being scanned";case"auto-bridge":case"auto-bridge-retry":case"resume-all":return"Your bridges are being mapped, one by one";case"full":case"refresh":return"Your own connections are being scanned";default:switch(a.action){case"install":return"The scanner's packages are being installed";case"setup":return"The scanner is being set up";case"login":return"The LinkedIn sign-in window is open";case"photos":return"Profile photos are being saved";default:return"A scan is running"}}},"isCircleScan",0,v,"loadScanNotes",0,f,"notReadyMessage",0,function(a){if(!a)return"Could not reach the app.";let b=a.checks||{};return b.scriptsFound?b.dependencies?b.chrome?null:"Google Chrome is not installed.":b.python||b.download?"The scanner is not set up yet — open Scan to set it up.":"No Python 3.10 to 3.14 is installed on this machine.":"The scanner files are missing from this install."},"pickedPerson",0,g,"resumePoint",0,d,"runScrape",0,function(a,b={}){return u(a,b).then(a=>a.ended)},"scannerNow",0,function(){return k},"scansCircleOf",0,function(a,b){if(!v(a)||!b)return!1;let{id:c,name:d}=a.target;return c&&b.id?c===b.id:!!d&&d===b.name},"scraperStatus",0,b,"stopScrape",0,h,"watchScanner",0,t],37732)},76939,a=>{"use strict";a.s(["TIER_COLORS",0,{S:"#f6c344",A:"#a78bfa",B:"#60a5fa",C:"#94a3b8",D:"#6b7280"},"TIER_COLORS_CLASSIC",0,{S:"#FFD700",A:"#9B59B6",B:"#3498DB",C:"#95A5A6",D:"#BDC3C7"},"TIER_ORDER",0,["S","A","B","C","D"],"TIER_RING_RADIUS",0,{S:150,A:230,B:310,C:390,D:470},"initialsFor",0,function(a){let b=String(a||"").trim().split(/\s+/).filter(Boolean);return 0===b.length?"?":(b[0].charAt(0)+(b.length>1?b[b.length-1].charAt(0):"")).toUpperCase()}])},69789,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0});var d={actionAsyncStorage:function(){return f.actionAsyncStorage},workAsyncStorage:function(){return g.workAsyncStorage},workUnitAsyncStorage:function(){return h.workUnitAsyncStorage}};for(var e in d)Object.defineProperty(c,e,{enumerable:!0,get:d[e]});let f=a.r(20635),g=a.r(56704),h=a.r(32319);("function"==typeof c.default||"object"==typeof c.default&&null!==c.default)&&void 0===c.default.__esModule&&(Object.defineProperty(c.default,"__esModule",{value:!0}),Object.assign(c.default,c),b.exports=c.default)}];
|
|
2
|
-
|
|
3
|
-
//# sourceMappingURL=_1hgd5dp._.js.map
|