six-degrees 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (427) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/app-path-routes-manifest.json +1 -0
  3. package/.next/standalone/.next/build-manifest.json +3 -3
  4. package/.next/standalone/.next/prerender-manifest.json +35 -7
  5. package/.next/standalone/.next/required-server-files.json +6 -1
  6. package/.next/standalone/.next/routes-manifest.json +18 -1
  7. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  8. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
  11. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  12. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  13. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  14. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  15. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  16. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  17. package/.next/standalone/.next/server/app/_not-found.rsc +11 -11
  18. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +11 -11
  19. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +10 -10
  20. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  21. package/.next/standalone/.next/server/app/api/admin-delete/route.js +1 -1
  22. package/.next/standalone/.next/server/app/api/admin-delete/route.js.nft.json +1 -1
  23. package/.next/standalone/.next/server/app/api/admin-update/route.js +1 -1
  24. package/.next/standalone/.next/server/app/api/admin-update/route.js.nft.json +1 -1
  25. package/.next/standalone/.next/server/app/api/bridges/route.js +1 -1
  26. package/.next/standalone/.next/server/app/api/bridges/route.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/api/bulk-import/route.js +1 -1
  28. package/.next/standalone/.next/server/app/api/bulk-import/route.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/api/company-scores/legacy/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/api/company-scores/route.js.nft.json +1 -1
  31. package/.next/standalone/.next/server/app/api/connections/route.js +1 -1
  32. package/.next/standalone/.next/server/app/api/connections/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/data/export/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/api/data/import/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/api/data/restart/route.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/api/data/reveal/route.js.nft.json +1 -1
  37. package/.next/standalone/.next/server/app/api/data/route.js.nft.json +1 -1
  38. package/.next/standalone/.next/server/app/api/delete-cluster/route.js +1 -1
  39. package/.next/standalone/.next/server/app/api/delete-cluster/route.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/api/ingest/route.js +1 -1
  41. package/.next/standalone/.next/server/app/api/ingest/route.js.nft.json +1 -1
  42. package/.next/standalone/.next/server/app/api/network/route.js +1 -1
  43. package/.next/standalone/.next/server/app/api/network/route.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/api/notifications/route.js +1 -1
  45. package/.next/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  46. package/.next/standalone/.next/server/app/api/outreach/route.js +3 -3
  47. package/.next/standalone/.next/server/app/api/outreach/route.js.nft.json +1 -1
  48. package/.next/standalone/.next/server/app/api/queue/route.js +1 -1
  49. package/.next/standalone/.next/server/app/api/queue/route.js.nft.json +1 -1
  50. package/.next/standalone/.next/server/app/api/scraper/route.js +1 -1
  51. package/.next/standalone/.next/server/app/api/scraper/route.js.nft.json +1 -1
  52. package/.next/standalone/.next/server/app/api/settings/route.js.nft.json +1 -1
  53. package/.next/standalone/.next/server/app/api/settings/sector-preview/route.js.nft.json +1 -1
  54. package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route.js.nft.json +1 -1
  55. package/.next/standalone/.next/server/app/api/setup-profile/route.js +1 -1
  56. package/.next/standalone/.next/server/app/api/setup-profile/route.js.nft.json +1 -1
  57. package/.next/standalone/.next/server/app/api/unlock/route.js +1 -1
  58. package/.next/standalone/.next/server/app/api/unlock/route.js.nft.json +1 -1
  59. package/.next/standalone/.next/server/app/api/update/route.js.nft.json +1 -1
  60. package/.next/standalone/.next/server/app/api/update-images/route.js +1 -1
  61. package/.next/standalone/.next/server/app/api/update-images/route.js.nft.json +1 -1
  62. package/.next/standalone/.next/server/app/api/users/route.js +1 -1
  63. package/.next/standalone/.next/server/app/api/users/route.js.nft.json +1 -1
  64. package/.next/standalone/.next/server/app/avatars/[file]/route.js.nft.json +1 -1
  65. package/.next/standalone/.next/server/app/import/page.js.nft.json +1 -1
  66. package/.next/standalone/.next/server/app/import/page_client-reference-manifest.js +1 -1
  67. package/.next/standalone/.next/server/app/import.html +1 -1
  68. package/.next/standalone/.next/server/app/import.rsc +13 -13
  69. package/.next/standalone/.next/server/app/import.segments/_full.segment.rsc +13 -13
  70. package/.next/standalone/.next/server/app/import.segments/_tree.segment.rsc +2 -2
  71. package/.next/standalone/.next/server/app/import.segments/import/__PAGE__.segment.rsc +12 -12
  72. package/.next/standalone/.next/server/app/index.html +1 -1
  73. package/.next/standalone/.next/server/app/index.rsc +13 -13
  74. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +12 -12
  75. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +13 -13
  76. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  77. package/.next/standalone/.next/server/app/launch/page.js.nft.json +1 -1
  78. package/.next/standalone/.next/server/app/launch/page_client-reference-manifest.js +1 -1
  79. package/.next/standalone/.next/server/app/launch.html +2 -2
  80. package/.next/standalone/.next/server/app/launch.rsc +13 -13
  81. package/.next/standalone/.next/server/app/launch.segments/_full.segment.rsc +13 -13
  82. package/.next/standalone/.next/server/app/launch.segments/_tree.segment.rsc +2 -2
  83. package/.next/standalone/.next/server/app/launch.segments/launch/__PAGE__.segment.rsc +12 -12
  84. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  85. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  86. package/.next/standalone/.next/server/app/paths/page.js.nft.json +1 -1
  87. package/.next/standalone/.next/server/app/paths/page_client-reference-manifest.js +1 -1
  88. package/.next/standalone/.next/server/app/paths.html +1 -1
  89. package/.next/standalone/.next/server/app/paths.rsc +13 -13
  90. package/.next/standalone/.next/server/app/paths.segments/_full.segment.rsc +13 -13
  91. package/.next/standalone/.next/server/app/paths.segments/_tree.segment.rsc +2 -2
  92. package/.next/standalone/.next/server/app/paths.segments/paths/__PAGE__.segment.rsc +12 -12
  93. package/.next/standalone/.next/server/app/profile/page.js.nft.json +1 -1
  94. package/.next/standalone/.next/server/app/profile/page_client-reference-manifest.js +1 -1
  95. package/.next/standalone/.next/server/app/profile.html +1 -1
  96. package/.next/standalone/.next/server/app/profile.rsc +13 -13
  97. package/.next/standalone/.next/server/app/profile.segments/_full.segment.rsc +13 -13
  98. package/.next/standalone/.next/server/app/profile.segments/_tree.segment.rsc +2 -2
  99. package/.next/standalone/.next/server/app/profile.segments/profile/__PAGE__.segment.rsc +12 -12
  100. package/.next/standalone/.next/server/app/queue/page.js.nft.json +1 -1
  101. package/.next/standalone/.next/server/app/queue/page_client-reference-manifest.js +1 -1
  102. package/.next/standalone/.next/server/app/queue.html +1 -1
  103. package/.next/standalone/.next/server/app/queue.rsc +13 -13
  104. package/.next/standalone/.next/server/app/queue.segments/_full.segment.rsc +13 -13
  105. package/.next/standalone/.next/server/app/queue.segments/_tree.segment.rsc +2 -2
  106. package/.next/standalone/.next/server/app/queue.segments/queue/__PAGE__.segment.rsc +12 -12
  107. package/.next/standalone/.next/server/app/scores/page/app-paths-manifest.json +3 -0
  108. package/.next/standalone/.next/server/app/scores/page/build-manifest.json +20 -0
  109. package/.next/standalone/.next/server/app/scores/page/next-font-manifest.json +11 -0
  110. package/.next/standalone/.next/server/app/scores/page/react-loadable-manifest.json +1 -0
  111. package/.next/standalone/.next/server/app/scores/page/server-reference-manifest.json +4 -0
  112. package/.next/standalone/.next/server/app/scores/page.js +13 -0
  113. package/.next/standalone/.next/server/app/scores/page.js.map +5 -0
  114. package/.next/standalone/.next/server/app/scores/page.js.nft.json +1 -0
  115. package/.next/standalone/.next/server/app/scores/page_client-reference-manifest.js +3 -0
  116. package/.next/standalone/.next/server/app/scores.html +1 -0
  117. package/.next/standalone/.next/server/app/scores.meta +26 -0
  118. package/.next/standalone/.next/server/app/scores.rsc +24 -0
  119. package/.next/standalone/.next/server/app/scores.segments/_full.segment.rsc +24 -0
  120. package/.next/standalone/.next/server/app/scores.segments/_tree.segment.rsc +4 -0
  121. package/.next/standalone/.next/server/app/scores.segments/scores/__PAGE__.segment.rsc +28 -0
  122. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  123. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  124. package/.next/standalone/.next/server/app/settings.html +1 -1
  125. package/.next/standalone/.next/server/app/settings.rsc +13 -13
  126. package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +13 -13
  127. package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  128. package/.next/standalone/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +12 -12
  129. package/.next/standalone/.next/server/app/setup/page.js.nft.json +1 -1
  130. package/.next/standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  131. package/.next/standalone/.next/server/app/setup.html +1 -1
  132. package/.next/standalone/.next/server/app/setup.rsc +13 -13
  133. package/.next/standalone/.next/server/app/setup.segments/_full.segment.rsc +13 -13
  134. package/.next/standalone/.next/server/app/setup.segments/_tree.segment.rsc +2 -2
  135. package/.next/standalone/.next/server/app/setup.segments/setup/__PAGE__.segment.rsc +12 -12
  136. package/.next/standalone/.next/server/app-paths-manifest.json +1 -0
  137. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-rnla2._.js +1 -1
  138. package/.next/standalone/.next/server/chunks/[root-of-the-server]__00la_y0._.js +1 -1
  139. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0i5hdu4._.js → [root-of-the-server]__03ydxxh._.js} +2 -2
  140. package/.next/standalone/.next/server/chunks/[root-of-the-server]__07t47cm._.js +1 -1
  141. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0bat8nx._.js +8 -0
  142. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0cj5jqc._.js +1 -1
  143. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0e0fei3._.js +1 -1
  144. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ljcioj._.js +1 -1
  145. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ljnyho._.js +1 -1
  146. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o5-tcg._.js +1 -1
  147. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0qrnous._.js +1 -1
  148. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0t3v1a1._.js +1 -1
  149. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0uag-k5._.js +1 -1
  150. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0um1uey._.js +1 -1
  151. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0wmexuc._.js +1 -1
  152. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0xu34sz._.js +1 -1
  153. package/.next/standalone/.next/server/chunks/[root-of-the-server]__133knx1._.js +1 -1
  154. package/.next/standalone/.next/server/chunks/[root-of-the-server]__14ou7j5._.js +1 -1
  155. package/.next/standalone/.next/server/chunks/[root-of-the-server]__15-17hl._.js +1 -1
  156. package/.next/standalone/.next/server/chunks/[root-of-the-server]__16iu0hv._.js +1 -1
  157. package/.next/standalone/.next/server/chunks/[root-of-the-server]__19b7mig._.js +1 -1
  158. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1bbcwub._.js +2 -2
  159. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1eris29._.js +1 -1
  160. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1gjo_cq._.js +1 -1
  161. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1kbl21f._.js +1 -1
  162. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1qcm08_._.js +1 -1
  163. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1rphsdk._.js +1 -1
  164. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1w25q0m._.js +1 -1
  165. package/.next/standalone/.next/server/chunks/_02twlca._.js +10 -0
  166. package/.next/standalone/.next/server/chunks/_072jl0y._.js +1 -1
  167. package/.next/standalone/.next/server/chunks/_0lw8_i0._.js +1 -1
  168. package/.next/standalone/.next/server/chunks/_1_fynia._.js +8 -6
  169. package/.next/standalone/.next/server/chunks/_1fn30_l._.js +1 -1
  170. package/.next/standalone/.next/server/chunks/_1itd1o_._.js +1 -1
  171. package/.next/standalone/.next/server/chunks/_1iwkh8t._.js +1 -1
  172. package/.next/standalone/.next/server/chunks/lib_db-client_15le7cw.js +13 -8
  173. package/.next/standalone/.next/server/chunks/{lib_rpc_02ndsg2.js → lib_rpc_03i8xef.js} +1 -1
  174. package/.next/standalone/.next/server/chunks/{lib_rpc_08q5vp6.js → lib_rpc_08fc3rp.js} +1 -1
  175. package/.next/standalone/.next/server/chunks/{lib_rpc_00tx2sm.js → lib_rpc_08r2t_q.js} +1 -1
  176. package/.next/standalone/.next/server/chunks/{lib_rpc_02-qa4d.js → lib_rpc_0btpv1t.js} +1 -1
  177. package/.next/standalone/.next/server/chunks/lib_rpc_0c9d9ti.js +5 -5
  178. package/.next/standalone/.next/server/chunks/lib_rpc_0cm2ev3.js +3 -0
  179. package/.next/standalone/.next/server/chunks/lib_rpc_0iovswz.js +3 -0
  180. package/.next/standalone/.next/server/chunks/lib_rpc_0is8flp.js +3 -0
  181. package/.next/standalone/.next/server/chunks/lib_rpc_0v8ajtd.js +3 -0
  182. package/.next/standalone/.next/server/chunks/lib_rpc_0xgmvvt.js +3 -0
  183. package/.next/standalone/.next/server/chunks/lib_rpc_144mzbf.js +3 -0
  184. package/.next/standalone/.next/server/chunks/lib_rpc_1bggi0n.js +3 -0
  185. package/.next/standalone/.next/server/chunks/lib_rpc_1dkcu9f.js +3 -0
  186. package/.next/standalone/.next/server/chunks/lib_rpc_1o0jb6w.js +3 -0
  187. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0an6blh._.js → [root-of-the-server]__0zzcbqg._.js} +2 -2
  188. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1194jps._.js +3 -0
  189. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1z1j3ek._.js → [root-of-the-server]__19ip1qq._.js} +2 -2
  190. package/.next/standalone/.next/server/chunks/ssr/_01f38ty._.js +3 -0
  191. package/.next/standalone/.next/server/chunks/ssr/_06w_gl4._.js +3 -0
  192. package/.next/standalone/.next/server/chunks/ssr/_0hw_qnj._.js +3 -0
  193. package/.next/standalone/.next/server/chunks/ssr/_13kzrwv._.js +3 -0
  194. package/.next/standalone/.next/server/chunks/ssr/_15xuwkv._.js +3 -0
  195. package/.next/standalone/.next/server/chunks/ssr/_1cyxocj._.js +3 -0
  196. package/.next/standalone/.next/server/chunks/ssr/_1gpbbw9._.js +3 -0
  197. package/.next/standalone/.next/server/chunks/ssr/_1hgd5dp._.js +3 -0
  198. package/.next/standalone/.next/server/chunks/ssr/_1vpfyd-._.js +3 -0
  199. package/.next/standalone/.next/server/chunks/ssr/_20w06i-._.js +3 -0
  200. package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_scores_page_actions_0x-w58n.js +3 -0
  201. package/.next/standalone/.next/server/chunks/ssr/app_components_settings_SectorPicker_1c-29qw.js +3 -0
  202. package/.next/standalone/.next/server/chunks/ssr/app_launch_page_19t0dyd.js +1 -1
  203. package/.next/standalone/.next/server/chunks/ssr/app_page_1fazqbf.js +11 -9
  204. package/.next/standalone/.next/server/chunks/ssr/app_paths_page_1ol90m7.js +4 -0
  205. package/.next/standalone/.next/server/chunks/ssr/app_profile_page_0u3-l5y.js +1 -1
  206. package/.next/standalone/.next/server/chunks/ssr/app_queue_page_0c6znd6.js +5 -5
  207. package/.next/standalone/.next/server/chunks/ssr/app_scores_page_09wepvv.js +3 -0
  208. package/.next/standalone/.next/server/chunks/ssr/app_settings_page_1f8mjj_.js +1 -1
  209. package/.next/standalone/.next/server/chunks/ssr/app_setup_page_1-282zx.js +2 -2
  210. package/.next/standalone/.next/server/chunks/ssr/lib_1bn38gs._.js +3 -0
  211. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_0x3i8za._.js +131 -0
  212. package/.next/standalone/.next/server/chunks/ssr/{node_modules_next_dist_1bunoni._.js → node_modules_next_dist_1v8aef8._.js} +2 -2
  213. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_client_components_server-async-storage_0t1dato.js +3 -0
  214. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  215. package/.next/standalone/.next/server/middleware-manifest.json +5 -5
  216. package/.next/standalone/.next/server/next-font-manifest.js +1 -1
  217. package/.next/standalone/.next/server/next-font-manifest.json +4 -0
  218. package/.next/standalone/.next/server/pages/404.html +1 -1
  219. package/.next/standalone/.next/server/pages/500.html +1 -1
  220. package/.next/standalone/.next/server/prefetch-hints.json +14 -0
  221. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  222. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  223. package/.next/standalone/.next/static/chunks/0ax_9zzboyv6d.js +44 -0
  224. package/.next/standalone/.next/static/chunks/0cuuo2jwc9h94.js +1 -0
  225. package/.next/standalone/.next/static/chunks/0l1568qff0efj.js +1 -0
  226. package/.next/standalone/.next/static/chunks/0muwt9bykzr-1.js +1 -0
  227. package/.next/standalone/.next/static/chunks/0w76iraw7ij3k.js +18 -0
  228. package/.next/standalone/.next/static/chunks/135n87ydxjrra.js +2 -0
  229. package/.next/standalone/.next/static/chunks/18xu0fbgo_qz-.js +1 -0
  230. package/.next/standalone/.next/static/chunks/28bu26ausqzmn.js +1 -0
  231. package/.next/standalone/.next/static/chunks/{40nvbgfn7-072.css → 2p6o90z0vgcwr.css} +1 -1
  232. package/.next/standalone/.next/static/chunks/2pnbm1z6j7xab.js +1 -0
  233. package/.next/standalone/.next/static/chunks/2xjho0lroiwlq.js +1 -0
  234. package/.next/standalone/.next/static/chunks/3e8zg6ctvvb8p.js +33 -0
  235. package/.next/standalone/.next/static/chunks/3qh78g2aqhprd.js +2 -0
  236. package/.next/standalone/CHANGELOG.md +286 -0
  237. package/.next/standalone/CONTRIBUTING.md +15 -4
  238. package/.next/standalone/README.md +50 -29
  239. package/.next/standalone/SECURITY.md +10 -0
  240. package/.next/standalone/app/api/company-scores/legacy/route.js +1 -1
  241. package/.next/standalone/app/api/company-scores/route.js +1 -1
  242. package/.next/standalone/app/api/ingest/route.js +23 -2
  243. package/.next/standalone/app/api/outreach/route.js +107 -44
  244. package/.next/standalone/app/api/scraper/route.js +146 -17
  245. package/.next/standalone/app/api/settings/sector-preview/route.js +3 -2
  246. package/.next/standalone/app/api/settings/sector-suggestions/route.js +1 -1
  247. package/.next/standalone/app/api/update-images/route.js +41 -7
  248. package/.next/standalone/app/avatars/[file]/route.js +6 -1
  249. package/.next/standalone/app/components/Avatar.js +5 -2
  250. package/.next/standalone/app/components/ChainView.js +659 -469
  251. package/.next/standalone/app/components/CompanyScores.js +16 -10
  252. package/.next/standalone/app/components/EmptyState.js +1 -1
  253. package/.next/standalone/app/components/FieldStep.js +94 -0
  254. package/.next/standalone/app/components/ForceGraph.js +183 -768
  255. package/.next/standalone/app/components/GridView.js +9 -7
  256. package/.next/standalone/app/components/LegacyScoresCard.js +1 -1
  257. package/.next/standalone/app/components/LinkedInLimits.js +22 -7
  258. package/.next/standalone/app/components/ListView.js +6 -4
  259. package/.next/standalone/app/components/MappingProgress.js +5 -6
  260. package/.next/standalone/app/components/OnboardingGate.js +1 -1
  261. package/.next/standalone/app/components/OrbitGraph.js +33 -1
  262. package/.next/standalone/app/components/OutlinkQuest.js +5 -4
  263. package/.next/standalone/app/components/PathsAnalyzer.js +1 -1
  264. package/.next/standalone/app/components/RingsView.js +9 -8
  265. package/.next/standalone/app/components/SeparationView.js +96 -14
  266. package/.next/standalone/app/components/Sidebar.js +366 -287
  267. package/.next/standalone/app/components/TheirCircle.js +300 -0
  268. package/.next/standalone/app/components/settings/SectorPicker.js +202 -0
  269. package/.next/standalone/app/components/settings/SectorSection.js +21 -195
  270. package/.next/standalone/app/components/settings/TierSection.js +99 -0
  271. package/.next/standalone/app/components/useRequests.js +12 -0
  272. package/.next/standalone/app/components/useScanner.js +13 -0
  273. package/.next/standalone/app/components/views.js +3 -5
  274. package/.next/standalone/app/globals.css +7 -0
  275. package/.next/standalone/app/launch/page.js +6 -3
  276. package/.next/standalone/app/page.js +183 -74
  277. package/.next/standalone/app/paths/page.js +20 -14
  278. package/.next/standalone/app/profile/page.js +3 -3
  279. package/.next/standalone/app/queue/page.js +56 -60
  280. package/.next/standalone/app/scores/page.js +72 -0
  281. package/.next/standalone/app/settings/page.js +15 -3
  282. package/.next/standalone/app/setup/page.js +423 -260
  283. package/.next/standalone/db/schema.js +2 -1
  284. package/.next/standalone/eslint.config.mjs +3 -3
  285. package/.next/standalone/install.sh +3 -3
  286. package/.next/standalone/lib/chain-layout.js +139 -0
  287. package/.next/standalone/lib/circle.js +169 -0
  288. package/.next/standalone/lib/companies.js +1 -1
  289. package/.next/standalone/lib/csv.js +21 -10
  290. package/.next/standalone/lib/data-export.js +2 -1
  291. package/.next/standalone/lib/data-folder.js +4 -2
  292. package/.next/standalone/lib/db-client.js +1 -0
  293. package/.next/standalone/lib/galaxy.js +12 -0
  294. package/.next/standalone/lib/ingest.js +27 -8
  295. package/.next/standalone/lib/legacy-offer.js +7 -6
  296. package/.next/standalone/lib/legacy-scores.js +2 -2
  297. package/.next/standalone/lib/linkedin-limits.js +30 -8
  298. package/.next/standalone/lib/photos.js +47 -0
  299. package/.next/standalone/lib/quest.js +8 -3
  300. package/.next/standalone/lib/rarity.js +80 -0
  301. package/.next/standalone/lib/reach.js +113 -0
  302. package/.next/standalone/lib/requests-client.js +113 -0
  303. package/.next/standalone/lib/rpc.js +26 -15
  304. package/.next/standalone/lib/scan-state.js +1 -0
  305. package/.next/standalone/lib/scanner-setup.js +20 -1
  306. package/.next/standalone/lib/score-guess.js +60 -0
  307. package/.next/standalone/lib/scoring.js +1761 -69
  308. package/.next/standalone/lib/scraper-client.js +235 -17
  309. package/.next/standalone/lib/sector-directory.js +1 -1
  310. package/.next/standalone/lib/sector-focus.js +17 -3
  311. package/.next/standalone/lib/separation.js +4 -2
  312. package/.next/standalone/lib/settings-client.js +21 -0
  313. package/.next/standalone/lib/settings-effects.js +19 -1
  314. package/.next/standalone/lib/settings.js +7 -2
  315. package/.next/standalone/next.config.mjs +25 -3
  316. package/.next/standalone/package-lock.json +2 -2
  317. package/.next/standalone/package.json +2 -2
  318. package/.next/standalone/public/demo-data.json +1 -1
  319. package/.next/standalone/scripts/build-app.mjs +11 -2
  320. package/.next/standalone/scripts/check-node.mjs +1 -1
  321. package/.next/standalone/scripts/gen-synthetic.mjs +1 -1
  322. package/.next/standalone/scripts/image_store.py +77 -21
  323. package/.next/standalone/scripts/scrape.py +305 -29
  324. package/.next/standalone/server.js +1 -1
  325. package/.next/static/chunks/0ax_9zzboyv6d.js +44 -0
  326. package/.next/static/chunks/0cuuo2jwc9h94.js +1 -0
  327. package/.next/static/chunks/0l1568qff0efj.js +1 -0
  328. package/.next/static/chunks/0muwt9bykzr-1.js +1 -0
  329. package/.next/static/chunks/0w76iraw7ij3k.js +18 -0
  330. package/.next/static/chunks/135n87ydxjrra.js +2 -0
  331. package/.next/static/chunks/18xu0fbgo_qz-.js +1 -0
  332. package/.next/static/chunks/28bu26ausqzmn.js +1 -0
  333. package/.next/static/chunks/{40nvbgfn7-072.css → 2p6o90z0vgcwr.css} +1 -1
  334. package/.next/static/chunks/2pnbm1z6j7xab.js +1 -0
  335. package/.next/static/chunks/2xjho0lroiwlq.js +1 -0
  336. package/.next/static/chunks/3e8zg6ctvvb8p.js +33 -0
  337. package/.next/static/chunks/3qh78g2aqhprd.js +2 -0
  338. package/CHANGELOG.md +286 -0
  339. package/README.md +50 -29
  340. package/SECURITY.md +10 -0
  341. package/db/schema.js +2 -1
  342. package/lib/chain-layout.js +139 -0
  343. package/lib/circle.js +169 -0
  344. package/lib/companies.js +1 -1
  345. package/lib/csv.js +21 -10
  346. package/lib/data-export.js +2 -1
  347. package/lib/data-folder.js +4 -2
  348. package/lib/db-client.js +1 -0
  349. package/lib/galaxy.js +12 -0
  350. package/lib/ingest.js +27 -8
  351. package/lib/legacy-offer.js +7 -6
  352. package/lib/legacy-scores.js +2 -2
  353. package/lib/linkedin-limits.js +30 -8
  354. package/lib/photos.js +47 -0
  355. package/lib/quest.js +8 -3
  356. package/lib/rarity.js +80 -0
  357. package/lib/reach.js +113 -0
  358. package/lib/requests-client.js +113 -0
  359. package/lib/rpc.js +26 -15
  360. package/lib/scan-state.js +1 -0
  361. package/lib/scanner-setup.js +20 -1
  362. package/lib/score-guess.js +60 -0
  363. package/lib/scoring.js +1761 -69
  364. package/lib/scraper-client.js +235 -17
  365. package/lib/sector-directory.js +1 -1
  366. package/lib/sector-focus.js +17 -3
  367. package/lib/separation.js +4 -2
  368. package/lib/settings-client.js +21 -0
  369. package/lib/settings-effects.js +19 -1
  370. package/lib/settings.js +7 -2
  371. package/package.json +2 -2
  372. package/public/demo-data.json +1 -1
  373. package/scripts/check-node.mjs +1 -1
  374. package/scripts/image_store.py +77 -21
  375. package/scripts/scrape.py +305 -29
  376. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1t3rugi._.js +0 -3
  377. package/.next/standalone/.next/server/chunks/lib_rpc_0_8i3ft.js +0 -3
  378. package/.next/standalone/.next/server/chunks/lib_rpc_0huvw-3.js +0 -3
  379. package/.next/standalone/.next/server/chunks/lib_rpc_0ip2vdh.js +0 -3
  380. package/.next/standalone/.next/server/chunks/lib_rpc_0jzisgh.js +0 -3
  381. package/.next/standalone/.next/server/chunks/lib_rpc_0yi9xvd.js +0 -3
  382. package/.next/standalone/.next/server/chunks/lib_rpc_1jyhd0o.js +0 -3
  383. package/.next/standalone/.next/server/chunks/lib_rpc_1kn59-i.js +0 -3
  384. package/.next/standalone/.next/server/chunks/lib_rpc_1q2jkza.js +0 -3
  385. package/.next/standalone/.next/server/chunks/lib_rpc_1vpm1sk.js +0 -3
  386. package/.next/standalone/.next/server/chunks/lib_rpc_1x6p_31.js +0 -3
  387. package/.next/standalone/.next/server/chunks/ssr/_07u8byv._.js +0 -3
  388. package/.next/standalone/.next/server/chunks/ssr/_08r_mlf._.js +0 -3
  389. package/.next/standalone/.next/server/chunks/ssr/_0942v8n._.js +0 -3
  390. package/.next/standalone/.next/server/chunks/ssr/_0hl_t7r._.js +0 -3
  391. package/.next/standalone/.next/server/chunks/ssr/_0tb83dw._.js +0 -132
  392. package/.next/standalone/.next/server/chunks/ssr/_1-87bs1._.js +0 -3
  393. package/.next/standalone/.next/server/chunks/ssr/_17gui6a._.js +0 -3
  394. package/.next/standalone/.next/server/chunks/ssr/lib_0i8v-s3._.js +0 -3
  395. package/.next/standalone/.next/server/chunks/ssr/node_modules_16vay6l._.js +0 -3
  396. package/.next/standalone/.next/server/chunks/ssr/node_modules_1kciwn4._.js +0 -3
  397. package/.next/standalone/.next/static/chunks/06x3jiiz96m5t.js +0 -1
  398. package/.next/standalone/.next/static/chunks/08k1crid-05dt.js +0 -1
  399. package/.next/standalone/.next/static/chunks/0ngx9s6-zgshe.js +0 -33
  400. package/.next/standalone/.next/static/chunks/0q-_khndfxubv.js +0 -2
  401. package/.next/standalone/.next/static/chunks/1c79veqedvubt.js +0 -1
  402. package/.next/standalone/.next/static/chunks/1chp3nx45meg5.js +0 -44
  403. package/.next/standalone/.next/static/chunks/1irqj0lf-fu3d.js +0 -1
  404. package/.next/standalone/.next/static/chunks/1uadqylrod5_v.js +0 -1
  405. package/.next/standalone/.next/static/chunks/23u0zclgwpc4d.js +0 -1
  406. package/.next/standalone/.next/static/chunks/289ktj97di6zg.js +0 -1
  407. package/.next/standalone/.next/static/chunks/2b3lmf8e6uj8m.js +0 -16
  408. package/.next/standalone/.next/static/chunks/3npq5yd7c4dmn.js +0 -2
  409. package/.next/standalone/app/components/BridgeRing.js +0 -753
  410. package/.next/static/chunks/06x3jiiz96m5t.js +0 -1
  411. package/.next/static/chunks/08k1crid-05dt.js +0 -1
  412. package/.next/static/chunks/0ngx9s6-zgshe.js +0 -33
  413. package/.next/static/chunks/0q-_khndfxubv.js +0 -2
  414. package/.next/static/chunks/1c79veqedvubt.js +0 -1
  415. package/.next/static/chunks/1chp3nx45meg5.js +0 -44
  416. package/.next/static/chunks/1irqj0lf-fu3d.js +0 -1
  417. package/.next/static/chunks/1uadqylrod5_v.js +0 -1
  418. package/.next/static/chunks/23u0zclgwpc4d.js +0 -1
  419. package/.next/static/chunks/289ktj97di6zg.js +0 -1
  420. package/.next/static/chunks/2b3lmf8e6uj8m.js +0 -16
  421. package/.next/static/chunks/3npq5yd7c4dmn.js +0 -2
  422. /package/.next/standalone/.next/static/{qqqcxvzBUizkECrT4swyK → WseE_5HuU-1q0ywC9CP3U}/_buildManifest.js +0 -0
  423. /package/.next/standalone/.next/static/{qqqcxvzBUizkECrT4swyK → WseE_5HuU-1q0ywC9CP3U}/_clientMiddlewareManifest.js +0 -0
  424. /package/.next/standalone/.next/static/{qqqcxvzBUizkECrT4swyK → WseE_5HuU-1q0ywC9CP3U}/_ssgManifest.js +0 -0
  425. /package/.next/static/{qqqcxvzBUizkECrT4swyK → WseE_5HuU-1q0ywC9CP3U}/_buildManifest.js +0 -0
  426. /package/.next/static/{qqqcxvzBUizkECrT4swyK → WseE_5HuU-1q0ywC9CP3U}/_clientMiddlewareManifest.js +0 -0
  427. /package/.next/static/{qqqcxvzBUizkECrT4swyK → WseE_5HuU-1q0ywC9CP3U}/_ssgManifest.js +0 -0
@@ -7,6 +7,8 @@
7
7
  //
8
8
  // Now they all call this, which talks to /api/scraper on the app itself.
9
9
 
10
+ import { runningNow } from './scan-state.js';
11
+
10
12
  export async function scraperStatus() {
11
13
  const r = await fetch('/api/scraper');
12
14
  if (!r.ok) throw new Error('Could not reach the app.');
@@ -25,24 +27,41 @@ export async function startScrape(action, name, extra = {}) {
25
27
  }
26
28
 
27
29
  /**
28
- * Run one scrape and report progress until it ends.
29
- * onLog receives the whole log each tick; returns the final status.
30
+ * Where Resume would carry on with one of your connections, for their profile
31
+ * card: { nextPage, pagesRead, legacy }, or null when there is nothing left to
32
+ * read (never mapped, read to the end, hidden).
30
33
  */
31
- export function runScrape(action, { name, onLog, ...extra } = {}) {
32
- return new Promise((resolve, reject) => {
33
- startScrape(action, name, extra).then(() => {
34
- const poll = setInterval(async () => {
35
- try {
36
- const s = await scraperStatus();
37
- onLog?.(s.log || []);
38
- if (!s.running) {
39
- clearInterval(poll);
40
- resolve(s);
41
- }
42
- } catch { /* transient; keep polling */ }
43
- }, 1500);
44
- }).catch(reject);
45
- });
34
+ export async function resumePoint(id) {
35
+ const r = await fetch(`/api/scraper?resume=${encodeURIComponent(id)}`);
36
+ if (!r.ok) return null;
37
+ return (await r.json()).resume ?? null;
38
+ }
39
+
40
+ /** Nothing noted: before the first answer, and for the sample or a CSV. */
41
+ export const NO_SCAN_NOTES = Object.freeze({ skips: [], read: [] });
42
+
43
+ /**
44
+ * What the scanner noted, for lib/reach.js: `skips`, the hidden lists
45
+ * ([{ profileUrl, … }]), and `read`, the profile URLs of every list it has
46
+ * read. Never throws: with no answer (the app restarting, say) nothing is
47
+ * noted, so a hidden list shows as ready rather than anyone going missing.
48
+ */
49
+ export async function loadScanNotes() {
50
+ try {
51
+ const r = await fetch('/api/scraper?reach=1');
52
+ if (!r.ok) return NO_SCAN_NOTES;
53
+ const d = await r.json();
54
+ return { skips: Array.isArray(d.skips) ? d.skips : [], read: Array.isArray(d.read) ? d.read : [] };
55
+ } catch {
56
+ return NO_SCAN_NOTES;
57
+ }
58
+ }
59
+
60
+ /** One of your connections for the Scan page's "Scan one circle" box: { person, circle }. */
61
+ export async function pickedPerson(id) {
62
+ const r = await fetch(`/api/scraper?person=${encodeURIComponent(id)}`);
63
+ if (!r.ok) return { person: null };
64
+ return r.json();
46
65
  }
47
66
 
48
67
  /** What to tell someone when the scanner is not usable yet. */
@@ -67,3 +86,202 @@ export async function stopScrape() {
67
86
  body: JSON.stringify({ action: 'cancel' }),
68
87
  });
69
88
  }
89
+
90
+ // One scan at a time, and every button knows it.
91
+ //
92
+ // The scanner runs one job and refuses a second. The buttons that start one
93
+ // didn't know that: each kept its own idea of whether a scan was running, so a
94
+ // scan started on one profile card left every other card's button live, and
95
+ // pressing it said "Scan failed — connections may be private" about a scan
96
+ // that had never started. Now they all read the scanner's own answer, asked
97
+ // here once for the whole page however many buttons are showing: every 1.5 s
98
+ // while a job runs, every 5 s while none does (so one started on the Scan page
99
+ // or in another window greys them out too), and not at all while no button is
100
+ // on screen. The idle look is skipped while the page is hidden.
101
+
102
+ const BUSY_MS = 1500;
103
+ const QUIET_MS = 5000;
104
+
105
+ /** Before the first answer, and on the server, where nothing is asked. */
106
+ export const SCANNER_UNKNOWN = Object.freeze({
107
+ known: false, running: false, pending: false, action: null, target: null,
108
+ startedAt: null, progress: null, log: [], finished: [],
109
+ });
110
+
111
+ const listeners = new Set();
112
+ let now = SCANNER_UNKNOWN;
113
+ let said = JSON.stringify(now);
114
+ let timer = null;
115
+ let asking = null;
116
+ let epoch = 0; // moves when this page starts a job: answers asked before then are stale
117
+ let starting = false; // this page's start request is on its way
118
+ const followed = new Set(); // jobs (by startedAt) this page has seen running, or started
119
+
120
+ function publish(next) {
121
+ const text = JSON.stringify(next);
122
+ if (text === said) return;
123
+ said = text;
124
+ now = Object.freeze(next);
125
+ for (const listener of [...listeners]) listener();
126
+ }
127
+
128
+ /**
129
+ * An answer from GET /api/scraper?job=1. `finished` is how the jobs this page
130
+ * watched ended, newest first, so a card can say its scan is done even if it
131
+ * was closed while the scan ran, and never shows the end of a scan from before
132
+ * the page opened.
133
+ */
134
+ function take(s) {
135
+ if (!s || typeof s !== 'object') return;
136
+ const running = s.running === true;
137
+ if (running && s.startedAt != null) followed.add(s.startedAt);
138
+ const finished = (Array.isArray(s.recent) ? s.recent : [])
139
+ .filter((j) => followed.has(j.startedAt))
140
+ .map((j) => ({ ...j, log: !running && j.startedAt === s.startedAt && Array.isArray(s.log) ? s.log : [] }));
141
+ // While our own start request is on its way, the server may not have begun
142
+ // the job yet and says nothing runs: the buttons stay grey regardless.
143
+ publish({
144
+ known: true,
145
+ running: running || starting,
146
+ pending: starting && !running,
147
+ action: running ? s.action ?? null : starting ? now.action : null,
148
+ target: running ? s.target ?? null : starting ? now.target : null,
149
+ startedAt: running ? s.startedAt ?? null : null,
150
+ progress: running ? s.progress ?? null : null,
151
+ log: running && Array.isArray(s.log) ? s.log : [],
152
+ finished,
153
+ });
154
+ }
155
+
156
+ function ask() {
157
+ if (asking && asking.epoch === epoch) return asking.promise;
158
+ const asked = epoch;
159
+ const promise = fetch('/api/scraper?job=1')
160
+ .then((r) => (r.ok ? r.json() : null))
161
+ .then((s) => { if (asked === epoch) take(s); })
162
+ .catch(() => { /* the app restarting, say: keep the last answer */ })
163
+ .finally(() => { if (asking?.promise === promise) asking = null; });
164
+ asking = { epoch: asked, promise };
165
+ return promise;
166
+ }
167
+
168
+ function schedule() {
169
+ clearTimeout(timer);
170
+ timer = listeners.size ? setTimeout(tick, now.running ? BUSY_MS : QUIET_MS) : null;
171
+ }
172
+
173
+ async function tick() {
174
+ const hidden = typeof document !== 'undefined' && document.hidden;
175
+ if (!hidden || now.running) await ask();
176
+ schedule();
177
+ }
178
+
179
+ /** Call `listener` whenever the answer changes. Returns the way to stop. */
180
+ export function watchScanner(listener) {
181
+ listeners.add(listener);
182
+ if (listeners.size === 1) tick();
183
+ return () => {
184
+ listeners.delete(listener);
185
+ if (!listeners.size) {
186
+ clearTimeout(timer);
187
+ timer = null;
188
+ }
189
+ };
190
+ }
191
+
192
+ /** The latest answer: { known, running, pending, action, target, startedAt, progress, log, finished }. */
193
+ export function scannerNow() {
194
+ return now;
195
+ }
196
+
197
+ /**
198
+ * Start a job. Every Scan button greys out at once, not a poll later.
199
+ * Resolves once the scanner has taken it, with `ended`: a promise of how it
200
+ * ends ({ exitCode, failure, log, … }). onLog gets the whole log as it grows.
201
+ * `id` is who a scan of a person is of, so their card can find it again.
202
+ */
203
+ export async function beginScrape(action, { name, id, onLog, ...extra } = {}) {
204
+ epoch++;
205
+ starting = true;
206
+ publish({
207
+ ...now, known: true, running: true, pending: true, action,
208
+ target: name || id ? { id: id ?? null, name: name ?? null } : null,
209
+ startedAt: null, progress: null, log: [],
210
+ });
211
+ let started;
212
+ try {
213
+ started = await startScrape(action, name, { ...(id ? { id } : {}), ...extra });
214
+ } catch (err) {
215
+ // Refused, often because something else runs: ask what, straight away.
216
+ // Until the answer comes, only our own claim is taken back.
217
+ starting = false;
218
+ epoch++;
219
+ if (now.pending) publish({ ...now, running: false, pending: false, action: null, target: null, log: [] });
220
+ tick();
221
+ throw err;
222
+ }
223
+ starting = false;
224
+ epoch++;
225
+ const mine = started?.startedAt ?? null;
226
+ if (mine != null) followed.add(mine);
227
+ const ended = new Promise((resolve) => {
228
+ const stop = watchScanner(() => {
229
+ if (now.running && now.startedAt === mine) onLog?.(now.log);
230
+ // A server from before jobs had a start time (one left running while
231
+ // the app updated): the first answer with nothing running is the end.
232
+ const end = mine == null
233
+ ? (!now.running ? { exitCode: null, failure: null, log: [] } : null)
234
+ : now.finished.find((j) => j.startedAt === mine);
235
+ if (!end) return;
236
+ stop();
237
+ if (end.log.length) onLog?.(end.log);
238
+ resolve(end);
239
+ });
240
+ tick();
241
+ });
242
+ return { startedAt: mine, ended };
243
+ }
244
+
245
+ /** Start a job and follow it to its end: resolves with how it ended. */
246
+ export function runScrape(action, opts = {}) {
247
+ return beginScrape(action, opts).then((s) => s.ended);
248
+ }
249
+
250
+ /** Is this job, running or finished, a read of one person's circle? */
251
+ export function isCircleScan(job) {
252
+ return Boolean(job?.target) && ['bridge', 'rescrape', 'resume'].includes(job.action);
253
+ }
254
+
255
+ /**
256
+ * Is this job, running or finished, a read of this person's circle? By id when
257
+ * both have one: two connections can share a name. Else by name.
258
+ */
259
+ export function scansCircleOf(job, person) {
260
+ if (!isCircleScan(job) || !person) return false;
261
+ const { id, name } = job.target;
262
+ if (id && person.id) return id === person.id;
263
+ return Boolean(name) && name === person.name;
264
+ }
265
+
266
+ /** Why the Scan buttons are greyed out, in a few words; null when nothing runs. */
267
+ export function busyReason(job = now) {
268
+ if (!job?.running) return null;
269
+ const who = job.target?.name;
270
+ switch (job.action) {
271
+ case 'bridge':
272
+ case 'rescrape':
273
+ case 'resume':
274
+ return who ? `${who}’s circle is being scanned` : 'A circle is being scanned';
275
+ case 'company':
276
+ return who ? `${who} is being scanned` : 'A company is being scanned';
277
+ case 'auto-bridge':
278
+ case 'auto-bridge-retry':
279
+ case 'resume-all':
280
+ return 'Your bridges are being mapped, one by one';
281
+ case 'full':
282
+ case 'refresh':
283
+ return 'Your own connections are being scanned';
284
+ default:
285
+ return runningNow(job.action);
286
+ }
287
+ }
@@ -1460,7 +1460,7 @@ export function sectorMatcher() {
1460
1460
  export { sectorByKey, sectorLabel } from './sector-labels.js';
1461
1461
 
1462
1462
  /**
1463
- * Everything Settings → Your sector can pick, in the order a choice is stored:
1463
+ * Everything Scores → Your sector can pick, in the order a choice is stored:
1464
1464
  * each broad industry, then its sectors.
1465
1465
  */
1466
1466
  export const SECTOR_KEYS = Object.freeze(INDUSTRIES.flatMap((g) => [g.key, ...DIRECTORY.filter((s) => s.group === g.key).map((s) => s.key)]));
@@ -48,6 +48,19 @@ export function parseSectorFocus(value) {
48
48
  /** How Settings declares it (lib/settings.js). */
49
49
  export const SECTOR_FOCUS_SETTING = { default: NO_FOCUS, parse: parseSectorFocus };
50
50
 
51
+ /**
52
+ * Whether the Scan page has asked for your field, before the first scan
53
+ * (app/components/FieldStep.js): true once it was answered or skipped, so it
54
+ * is asked once. Kept with the data, like the choice itself.
55
+ */
56
+ export const FIELD_ASKED_SETTING = {
57
+ default: false,
58
+ parse(value) {
59
+ if (typeof value !== 'boolean') throw new Error('Whether your field was asked is true or false.');
60
+ return value;
61
+ },
62
+ };
63
+
51
64
  /**
52
65
  * A short, stable string for a focus, stamped beside the stored scores
53
66
  * (app_meta 'scoring_focus'). If the saved focus stops matching it (a
@@ -128,10 +141,11 @@ export function tierMoves(rows, tierBefore, tierAfter, { examples = 5, companyOf
128
141
  */
129
142
  export function previewSectorFocus(rows = [], {
130
143
  overrides = new Map(), industryOf, sectorsOf = sectorMatcher(), groupOf = sectorGroup, from = NO_FOCUS, to = NO_FOCUS, examples = 5,
131
- read = readNetwork(rows, { industryOf, sectorsOf, groupOf }),
144
+ tierScale, read = readNetwork(rows, { industryOf, sectorsOf, groupOf }),
132
145
  } = {}) {
133
- const before = scoreNetwork(rows, { overrides, focus: from, read });
134
- const after = scoreNetwork(rows, { overrides, focus: to, read });
146
+ // Both on the tier scale saved now (Scores → Tiers), as rescoring grades them.
147
+ const before = scoreNetwork(rows, { overrides, focus: from, tierScale, read });
148
+ const after = scoreNetwork(rows, { overrides, focus: to, tierScale, read });
135
149
 
136
150
  // Companies whose score moves, biggest first (by your people there now).
137
151
  // Every company anyone names counts, a former employer too: its score moves
@@ -10,6 +10,8 @@
10
10
  // This is the ONE merge: the Separation view and the Sidebar's path box both
11
11
  // read it, so the list and the panel can never disagree about who reaches whom.
12
12
 
13
+ import { localPhoto } from './photos.js';
14
+
13
15
  const TIER_RANK = { S: 0, A: 1, B: 2, C: 3, D: 4 };
14
16
  export const TIERS = ['S', 'A', 'B', 'C', 'D'];
15
17
 
@@ -62,11 +64,11 @@ function compareRoutes(a, b) {
62
64
  return byId(a.id, b.id);
63
65
  }
64
66
 
65
- /** The better copy of a person: higher shown score, then one with a photo, then the lower id. */
67
+ /** The better copy of a person: higher shown score, then one with a saved photo, then the lower id. */
66
68
  function betterRow(a, b) {
67
69
  const d = score1(a) - score1(b);
68
70
  if (d) return d > 0;
69
- const pa = !!a.profile_image_url, pb = !!b.profile_image_url;
71
+ const pa = !!localPhoto(a.profile_image_url), pb = !!localPhoto(b.profile_image_url);
70
72
  if (pa !== pb) return pa;
71
73
  return byId(a.id, b.id) < 0;
72
74
  }
@@ -0,0 +1,21 @@
1
+ // Saving settings from the browser: POST /api/settings with only the settings
2
+ // that change (lib/settings.js keeps the rest as stored). Scores → Your sector
3
+ // and Tiers save through it, and so does the Scan page's question about your
4
+ // field before the first scan (app/components/FieldStep.js).
5
+
6
+ /**
7
+ * @returns { settings, effects, error }: `settings` as saved, also when the
8
+ * save landed but the work it set in motion failed (lib/settings-effects.js);
9
+ * `effects` what that work reported; `error` a message fit to show, or null.
10
+ */
11
+ export async function saveSettings(patch) {
12
+ try {
13
+ const r = await fetch('/api/settings', {
14
+ method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ settings: patch }),
15
+ });
16
+ const d = await r.json();
17
+ return { settings: d.settings || null, effects: d.effects || null, error: r.ok ? null : d.error || 'Could not save.' };
18
+ } catch {
19
+ return { settings: null, effects: null, error: 'Could not reach the app. Reload this page to see what is saved.' };
20
+ }
21
+ }
@@ -4,12 +4,30 @@
4
4
  // circle). A setting with no entry here is simply saved.
5
5
 
6
6
  import { rescoreAll } from './rpc.js';
7
- import { sameFocus, NO_FOCUS } from './sector-focus.js';
7
+ import { sameFocus, NO_FOCUS, tierMoves } from './sector-focus.js';
8
8
 
9
9
  // Each entry: `changed(before, after)` says whether a change matters (by
10
10
  // default, any change to the stored value), and `run(db, after, before)` does
11
11
  // the work and returns what to tell the page.
12
12
  const EFFECTS = {
13
+ tierScale: {
14
+ // Tiers are graded when everyone is scored, so a new scale rescores
15
+ // everyone. The answer counts who changed tier, from the stored tiers
16
+ // before and after, with the sector focus's own counter (tierMoves): a
17
+ // person reachable through two bridges is two rows but one person, counted
18
+ // once at their closest degree.
19
+ run(db) {
20
+ const rows = () => db.prepare('SELECT id, degree, profile_url, tier FROM linkedin_connections').all();
21
+ try {
22
+ const before = new Map(rows().map((r) => [r.id, r.tier]));
23
+ const { scored } = rescoreAll();
24
+ const { people, up, down } = tierMoves(rows(), (r) => before.get(r.id), (r) => r.tier, { examples: 0 });
25
+ return { scored, people, moved: up + down, up, down };
26
+ } catch (err) {
27
+ throw new Error(`Saved, but rescoring your network failed (${err.message}). It will try again the next time the map loads.`);
28
+ }
29
+ },
30
+ },
13
31
  sectorFocus: {
14
32
  // Only a change to what scoring uses: a strength with no sectors picked
15
33
  // scores exactly like nothing picked, so it saves without a rescore.
@@ -10,11 +10,16 @@
10
10
  // error. A key that isn't declared is refused, so a stale page or a stray
11
11
  // request can't plant values nothing reads.
12
12
 
13
- import { SECTOR_FOCUS_SETTING } from './sector-focus.js';
13
+ import { SECTOR_FOCUS_SETTING, FIELD_ASKED_SETTING } from './sector-focus.js';
14
+ import { TIER_SCALE_SETTING } from './scoring.js';
14
15
 
15
16
  export const SETTINGS = {
16
- // Settings → Your sector: industries whose companies count for more (lib/sector-focus.js).
17
+ // Scores → Your sector: industries whose companies count for more (lib/sector-focus.js).
17
18
  sectorFocus: SECTOR_FOCUS_SETTING,
19
+ // The Scan page's question before the first scan, your field: answered or skipped (lib/sector-focus.js).
20
+ fieldAsked: FIELD_ASKED_SETTING,
21
+ // Scores → Tiers: graded on your network's curve, or on the fixed scale (lib/scoring.js).
22
+ tierScale: TIER_SCALE_SETTING,
18
23
  };
19
24
 
20
25
  const KEY = 'settings';
@@ -21,14 +21,22 @@ const nextConfig = {
21
21
  // server, never inside it: Electron alone is ~250 MB.
22
22
  //
23
23
  // scripts/pin-python-packages.mjs is a developer's tool that asks PyPI
24
- // about packages; nothing the app runs uses it, so it doesn't ship.
24
+ // about packages, and scripts/check-site-version.mjs a release check of the
25
+ // website; nothing the app runs uses either, so they don't ship.
25
26
  outputFileTracingExcludes: {
26
27
  '*': ['dist/*.app/**', 'dist/*.dmg', 'dist/staging/**', 'dist/electron-stage/**', 'docs/**', 'tests/**',
27
- '.git', '.git/**', '*.log', 'scripts/dmg/**', 'scripts/pin-python-packages.mjs', 'desktop/**',
28
+ '.git', '.git/**', '*.log', 'scripts/dmg/**', 'scripts/pin-python-packages.mjs', 'scripts/check-site-version.mjs', 'desktop/**',
28
29
  'node_modules/electron/**', 'node_modules/@electron/**',
29
30
  // The download website (site/, published by pages.yml from the repo) is
30
31
  // never read by the app; tracing swept its 17 MB of videos in.
31
- 'site/**'],
32
+ 'site/**',
33
+ // A contributor's own data: a LinkedIn export, a database and its
34
+ // -wal/-shm, a saved copy of a network, the scanner's signed-in browser
35
+ // profile. .gitignore keeps them out of a commit; the trace never reads
36
+ // it. In contains mode these match at any depth, node_modules included,
37
+ // and nothing the app depends on has such a file (checked when added).
38
+ // A dependency that ships one would lose it, so smoke-test every route.
39
+ '*.csv', '*.sqlite*', '*.sixdegrees', 'chrome-profile/**'],
32
40
  },
33
41
  // Emits .next/standalone with a server and only the dependencies actually
34
42
  // reached, so the published package can run without node_modules being
@@ -39,6 +47,20 @@ const nextConfig = {
39
47
  // to be required at runtime.
40
48
  serverExternalPackages: ['node:sqlite'],
41
49
 
50
+ // Pictures come only from this app: saved photos through /avatars, and its
51
+ // own files. Views never put LinkedIn's image links in a page
52
+ // (lib/photos.js), and this has the browser refuse one if a view ever does,
53
+ // so browsing can't go online for a picture. The same pages are what the Mac
54
+ // app's window shows and what a browser gets from npx or a checkout, so this
55
+ // one header covers all three. img-src only: scripts, styles and fonts stay
56
+ // as Next needs them.
57
+ async headers() {
58
+ return [{
59
+ source: '/(.*)',
60
+ headers: [{ key: 'Content-Security-Policy', value: "img-src 'self' data: blob:" }],
61
+ }];
62
+ },
63
+
42
64
  // experimental.proxyClientMaxBodySize stays at Next's 10 MB on purpose. Next
43
65
  // copies the body of every request middleware.js sees into memory, up to that
44
66
  // size, before middleware decides anything, so raising it for the one big
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "six-degrees",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "six-degrees",
9
- "version": "0.3.0",
9
+ "version": "0.4.0",
10
10
  "license": "MIT",
11
11
  "os": [
12
12
  "darwin",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "six-degrees",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Map your professional network as a galaxy — leverage tiers, bridges into new circles, and the shortest path to anyone. Runs locally on your own data.",
5
5
  "license": "MIT",
6
6
  "author": "Blake Burford",
@@ -11,7 +11,7 @@
11
11
  "bugs": {
12
12
  "url": "https://github.com/blakeb056/six-degrees/issues"
13
13
  },
14
- "homepage": "https://blakeb056.github.io/six-degrees/",
14
+ "homepage": "https://sixdegreesapp.com/",
15
15
  "keywords": [
16
16
  "linkedin",
17
17
  "network",