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.
Files changed (212) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +3 -3
  3. package/.next/standalone/.next/prerender-manifest.json +13 -13
  4. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  5. package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
  6. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  7. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  9. package/.next/standalone/.next/server/app/_not-found/page.js +1 -1
  10. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  11. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  12. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  13. package/.next/standalone/.next/server/app/_not-found.rsc +18 -17
  14. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +18 -17
  15. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +14 -13
  16. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  17. package/.next/standalone/.next/server/app/api/admin-delete/route.js.nft.json +1 -1
  18. package/.next/standalone/.next/server/app/api/admin-update/route.js.nft.json +1 -1
  19. package/.next/standalone/.next/server/app/api/bridges/route.js.nft.json +1 -1
  20. package/.next/standalone/.next/server/app/api/bulk-import/route.js.nft.json +1 -1
  21. package/.next/standalone/.next/server/app/api/company-scores/legacy/route.js.nft.json +1 -1
  22. package/.next/standalone/.next/server/app/api/company-scores/route.js.nft.json +1 -1
  23. package/.next/standalone/.next/server/app/api/connections/route.js.nft.json +1 -1
  24. package/.next/standalone/.next/server/app/api/data/export/route.js.nft.json +1 -1
  25. package/.next/standalone/.next/server/app/api/data/import/route.js.nft.json +1 -1
  26. package/.next/standalone/.next/server/app/api/data/restart/route.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/api/data/reveal/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/api/data/route.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/api/delete-cluster/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/api/ingest/route.js.nft.json +1 -1
  31. package/.next/standalone/.next/server/app/api/network/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/outreach/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/api/queue/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/api/scraper/route.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/api/settings/route.js.nft.json +1 -1
  37. package/.next/standalone/.next/server/app/api/settings/sector-preview/route.js.nft.json +1 -1
  38. package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route.js.nft.json +1 -1
  39. package/.next/standalone/.next/server/app/api/setup-profile/route.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/api/unlock/route.js.nft.json +1 -1
  41. package/.next/standalone/.next/server/app/api/update/route.js.nft.json +1 -1
  42. package/.next/standalone/.next/server/app/api/update-images/route.js.nft.json +1 -1
  43. package/.next/standalone/.next/server/app/api/users/route.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/avatars/[file]/route.js.nft.json +1 -1
  45. package/.next/standalone/.next/server/app/import/page.js +1 -1
  46. package/.next/standalone/.next/server/app/import/page.js.nft.json +1 -1
  47. package/.next/standalone/.next/server/app/import/page_client-reference-manifest.js +1 -1
  48. package/.next/standalone/.next/server/app/import.html +1 -1
  49. package/.next/standalone/.next/server/app/import.rsc +22 -21
  50. package/.next/standalone/.next/server/app/import.segments/_full.segment.rsc +22 -21
  51. package/.next/standalone/.next/server/app/import.segments/_tree.segment.rsc +2 -2
  52. package/.next/standalone/.next/server/app/import.segments/import/__PAGE__.segment.rsc +16 -15
  53. package/.next/standalone/.next/server/app/index.html +1 -1
  54. package/.next/standalone/.next/server/app/index.rsc +20 -19
  55. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +16 -15
  56. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +20 -19
  57. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  58. package/.next/standalone/.next/server/app/launch/page.js +1 -1
  59. package/.next/standalone/.next/server/app/launch/page.js.nft.json +1 -1
  60. package/.next/standalone/.next/server/app/launch/page_client-reference-manifest.js +1 -1
  61. package/.next/standalone/.next/server/app/launch.html +2 -2
  62. package/.next/standalone/.next/server/app/launch.rsc +22 -21
  63. package/.next/standalone/.next/server/app/launch.segments/_full.segment.rsc +22 -21
  64. package/.next/standalone/.next/server/app/launch.segments/_tree.segment.rsc +2 -2
  65. package/.next/standalone/.next/server/app/launch.segments/launch/__PAGE__.segment.rsc +16 -15
  66. package/.next/standalone/.next/server/app/page.js +1 -1
  67. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  68. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  69. package/.next/standalone/.next/server/app/paths/page.js +1 -1
  70. package/.next/standalone/.next/server/app/paths/page.js.nft.json +1 -1
  71. package/.next/standalone/.next/server/app/paths/page_client-reference-manifest.js +1 -1
  72. package/.next/standalone/.next/server/app/paths.html +1 -1
  73. package/.next/standalone/.next/server/app/paths.rsc +22 -21
  74. package/.next/standalone/.next/server/app/paths.segments/_full.segment.rsc +22 -21
  75. package/.next/standalone/.next/server/app/paths.segments/_tree.segment.rsc +2 -2
  76. package/.next/standalone/.next/server/app/paths.segments/paths/__PAGE__.segment.rsc +16 -15
  77. package/.next/standalone/.next/server/app/profile/page.js +1 -1
  78. package/.next/standalone/.next/server/app/profile/page.js.nft.json +1 -1
  79. package/.next/standalone/.next/server/app/profile/page_client-reference-manifest.js +1 -1
  80. package/.next/standalone/.next/server/app/profile.html +1 -1
  81. package/.next/standalone/.next/server/app/profile.rsc +22 -21
  82. package/.next/standalone/.next/server/app/profile.segments/_full.segment.rsc +22 -21
  83. package/.next/standalone/.next/server/app/profile.segments/_tree.segment.rsc +2 -2
  84. package/.next/standalone/.next/server/app/profile.segments/profile/__PAGE__.segment.rsc +16 -15
  85. package/.next/standalone/.next/server/app/queue/page.js +1 -1
  86. package/.next/standalone/.next/server/app/queue/page.js.nft.json +1 -1
  87. package/.next/standalone/.next/server/app/queue/page_client-reference-manifest.js +1 -1
  88. package/.next/standalone/.next/server/app/queue.html +1 -1
  89. package/.next/standalone/.next/server/app/queue.rsc +22 -21
  90. package/.next/standalone/.next/server/app/queue.segments/_full.segment.rsc +22 -21
  91. package/.next/standalone/.next/server/app/queue.segments/_tree.segment.rsc +2 -2
  92. package/.next/standalone/.next/server/app/queue.segments/queue/__PAGE__.segment.rsc +16 -15
  93. package/.next/standalone/.next/server/app/scores/page.js +1 -1
  94. package/.next/standalone/.next/server/app/scores/page.js.nft.json +1 -1
  95. package/.next/standalone/.next/server/app/scores/page_client-reference-manifest.js +1 -1
  96. package/.next/standalone/.next/server/app/scores.html +1 -1
  97. package/.next/standalone/.next/server/app/scores.rsc +22 -21
  98. package/.next/standalone/.next/server/app/scores.segments/_full.segment.rsc +22 -21
  99. package/.next/standalone/.next/server/app/scores.segments/_tree.segment.rsc +2 -2
  100. package/.next/standalone/.next/server/app/scores.segments/scores/__PAGE__.segment.rsc +16 -15
  101. package/.next/standalone/.next/server/app/settings/page.js +1 -1
  102. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  103. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  104. package/.next/standalone/.next/server/app/settings.html +1 -1
  105. package/.next/standalone/.next/server/app/settings.rsc +22 -21
  106. package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +22 -21
  107. package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  108. package/.next/standalone/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +16 -15
  109. package/.next/standalone/.next/server/app/setup/page.js +1 -1
  110. package/.next/standalone/.next/server/app/setup/page.js.nft.json +1 -1
  111. package/.next/standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  112. package/.next/standalone/.next/server/app/setup.html +1 -1
  113. package/.next/standalone/.next/server/app/setup.rsc +22 -21
  114. package/.next/standalone/.next/server/app/setup.segments/_full.segment.rsc +22 -21
  115. package/.next/standalone/.next/server/app/setup.segments/_tree.segment.rsc +2 -2
  116. package/.next/standalone/.next/server/app/setup.segments/setup/__PAGE__.segment.rsc +16 -15
  117. package/.next/standalone/.next/server/chunks/_1_fynia._.js +6 -6
  118. package/.next/standalone/.next/server/chunks/_1itd1o_._.js +1 -1
  119. package/.next/standalone/.next/server/chunks/lib_db-client_15le7cw.js +1 -1
  120. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0d3vjsq._.js +3 -0
  121. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0m4vk0b._.js +3 -0
  122. package/.next/standalone/.next/server/chunks/ssr/{_15xuwkv._.js → _012mop3._.js} +2 -2
  123. package/.next/standalone/.next/server/chunks/ssr/_1163x49._.js +3 -0
  124. package/.next/standalone/.next/server/chunks/ssr/_1av11ws._.js +3 -0
  125. package/.next/standalone/.next/server/chunks/ssr/_1ig7em7._.js +3 -0
  126. package/.next/standalone/.next/server/chunks/ssr/_20w06i-._.js +1 -1
  127. package/.next/standalone/.next/server/chunks/ssr/app_page_1fazqbf.js +4 -4
  128. package/.next/standalone/.next/server/chunks/ssr/app_setup_page_1-282zx.js +1 -1
  129. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  130. package/.next/standalone/.next/server/middleware-manifest.json +5 -5
  131. package/.next/standalone/.next/server/pages/404.html +1 -1
  132. package/.next/standalone/.next/server/pages/500.html +1 -1
  133. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  134. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  135. package/.next/standalone/.next/static/chunks/{3e8zg6ctvvb8p.js → 0_3ubivay59bq.js} +4 -4
  136. package/.next/standalone/.next/static/chunks/{2p6o90z0vgcwr.css → 0bo1dxn5gwzpk.css} +1 -1
  137. package/.next/standalone/.next/static/chunks/0f4ugd6v3ph0-.js +1 -0
  138. package/.next/standalone/.next/static/chunks/10ogurbcuf3nq.js +1 -0
  139. package/.next/standalone/.next/static/chunks/1489t7jrjojk_.js +1 -0
  140. package/.next/standalone/.next/static/chunks/{0ax_9zzboyv6d.js → 17-rhp8sg4m1b.js} +2 -2
  141. package/.next/standalone/.next/static/chunks/1zunq4tk-u3ng.js +1 -0
  142. package/.next/standalone/.next/static/chunks/1zwjy1mmpkdrg.js +6 -0
  143. package/.next/standalone/.next/static/chunks/2_wcobfqry2uj.js +1 -0
  144. package/.next/standalone/.next/static/chunks/2dwtoszeq91us.js +1 -0
  145. package/.next/standalone/.next/static/chunks/2pyo4j84prgp6.js +18 -0
  146. package/.next/standalone/.next/static/chunks/2wwmljnf7yn_5.js +1 -0
  147. package/.next/{static/chunks/2xjho0lroiwlq.js → standalone/.next/static/chunks/3kllnkb3hof1s.js} +1 -1
  148. package/.next/standalone/CHANGELOG.md +54 -0
  149. package/.next/standalone/app/api/scraper/route.js +27 -2
  150. package/.next/standalone/app/components/ChainView.js +17 -10
  151. package/.next/standalone/app/components/FilterPanel.js +37 -5
  152. package/.next/standalone/app/components/ForceGraph.js +106 -13
  153. package/.next/standalone/app/components/OrbitGraph.js +105 -30
  154. package/.next/standalone/app/components/ScanStatusBar.js +82 -0
  155. package/.next/standalone/app/globals.css +4 -0
  156. package/.next/standalone/app/layout.js +3 -0
  157. package/.next/standalone/app/page.js +8 -4
  158. package/.next/standalone/app/setup/page.js +13 -1
  159. package/.next/standalone/lib/dot-rings.js +21 -0
  160. package/.next/standalone/lib/reach.js +22 -1
  161. package/.next/standalone/lib/scraper-client.js +1 -0
  162. package/.next/standalone/package-lock.json +2 -2
  163. package/.next/standalone/package.json +1 -1
  164. package/.next/standalone/scripts/scrape.py +229 -5
  165. package/.next/static/chunks/{3e8zg6ctvvb8p.js → 0_3ubivay59bq.js} +4 -4
  166. package/.next/static/chunks/{2p6o90z0vgcwr.css → 0bo1dxn5gwzpk.css} +1 -1
  167. package/.next/static/chunks/0f4ugd6v3ph0-.js +1 -0
  168. package/.next/static/chunks/10ogurbcuf3nq.js +1 -0
  169. package/.next/static/chunks/1489t7jrjojk_.js +1 -0
  170. package/.next/static/chunks/{0ax_9zzboyv6d.js → 17-rhp8sg4m1b.js} +2 -2
  171. package/.next/static/chunks/1zunq4tk-u3ng.js +1 -0
  172. package/.next/static/chunks/1zwjy1mmpkdrg.js +6 -0
  173. package/.next/static/chunks/2_wcobfqry2uj.js +1 -0
  174. package/.next/static/chunks/2dwtoszeq91us.js +1 -0
  175. package/.next/static/chunks/2pyo4j84prgp6.js +18 -0
  176. package/.next/static/chunks/2wwmljnf7yn_5.js +1 -0
  177. package/.next/{standalone/.next/static/chunks/2xjho0lroiwlq.js → static/chunks/3kllnkb3hof1s.js} +1 -1
  178. package/CHANGELOG.md +54 -0
  179. package/lib/dot-rings.js +21 -0
  180. package/lib/reach.js +22 -1
  181. package/lib/scraper-client.js +1 -0
  182. package/package.json +1 -1
  183. package/scripts/scrape.py +229 -5
  184. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0h1_f_2._.js +0 -3
  185. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__19ip1qq._.js +0 -3
  186. package/.next/standalone/.next/server/chunks/ssr/_06w_gl4._.js +0 -3
  187. package/.next/standalone/.next/server/chunks/ssr/_13kzrwv._.js +0 -3
  188. package/.next/standalone/.next/server/chunks/ssr/_1hgd5dp._.js +0 -3
  189. package/.next/standalone/.next/server/chunks/ssr/node_modules_1f8t_rp._.js +0 -23
  190. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_client_components_server-async-storage_0t1dato.js +0 -3
  191. package/.next/standalone/.next/static/chunks/10784r7w7t5gw.js +0 -18
  192. package/.next/standalone/.next/static/chunks/18xu0fbgo_qz-.js +0 -1
  193. package/.next/standalone/.next/static/chunks/28bu26ausqzmn.js +0 -1
  194. package/.next/standalone/.next/static/chunks/2jz311xl1pp_8.js +0 -1
  195. package/.next/standalone/.next/static/chunks/2ot61qsv6_0eq.js +0 -1
  196. package/.next/standalone/.next/static/chunks/2pnbm1z6j7xab.js +0 -1
  197. package/.next/standalone/.next/static/chunks/3ol1xux1-ofm4.js +0 -6
  198. package/.next/standalone/.next/static/chunks/3zs_6-nn0dapb.js +0 -1
  199. package/.next/static/chunks/10784r7w7t5gw.js +0 -18
  200. package/.next/static/chunks/18xu0fbgo_qz-.js +0 -1
  201. package/.next/static/chunks/28bu26ausqzmn.js +0 -1
  202. package/.next/static/chunks/2jz311xl1pp_8.js +0 -1
  203. package/.next/static/chunks/2ot61qsv6_0eq.js +0 -1
  204. package/.next/static/chunks/2pnbm1z6j7xab.js +0 -1
  205. package/.next/static/chunks/3ol1xux1-ofm4.js +0 -6
  206. package/.next/static/chunks/3zs_6-nn0dapb.js +0 -1
  207. /package/.next/standalone/.next/static/{LlDgA9g8ToBkDLlo9Qtov → 814ehZBeFO2rUKle7mzX-}/_buildManifest.js +0 -0
  208. /package/.next/standalone/.next/static/{LlDgA9g8ToBkDLlo9Qtov → 814ehZBeFO2rUKle7mzX-}/_clientMiddlewareManifest.js +0 -0
  209. /package/.next/standalone/.next/static/{LlDgA9g8ToBkDLlo9Qtov → 814ehZBeFO2rUKle7mzX-}/_ssgManifest.js +0 -0
  210. /package/.next/static/{LlDgA9g8ToBkDLlo9Qtov → 814ehZBeFO2rUKle7mzX-}/_buildManifest.js +0 -0
  211. /package/.next/static/{LlDgA9g8ToBkDLlo9Qtov → 814ehZBeFO2rUKle7mzX-}/_clientMiddlewareManifest.js +0 -0
  212. /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