six-degrees 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (470) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/app-path-routes-manifest.json +10 -0
  3. package/.next/standalone/.next/build-manifest.json +3 -3
  4. package/.next/standalone/.next/prerender-manifest.json +39 -11
  5. package/.next/standalone/.next/required-server-files.json +4 -1
  6. package/.next/standalone/.next/routes-manifest.json +60 -0
  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 +10 -10
  18. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +10 -10
  19. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +9 -9
  20. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  21. package/.next/standalone/.next/server/app/api/admin-delete/route.js +3 -2
  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 +3 -2
  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 +3 -2
  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 +3 -2
  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/app-paths-manifest.json +3 -0
  30. package/.next/standalone/.next/server/app/api/company-scores/legacy/route/build-manifest.json +12 -0
  31. package/.next/standalone/.next/server/app/api/company-scores/legacy/route/server-reference-manifest.json +4 -0
  32. package/.next/standalone/.next/server/app/api/company-scores/legacy/route.js +9 -0
  33. package/.next/standalone/.next/server/app/api/company-scores/legacy/route.js.map +5 -0
  34. package/.next/standalone/.next/server/app/api/company-scores/legacy/route.js.nft.json +1 -0
  35. package/.next/standalone/.next/server/app/api/company-scores/legacy/route_client-reference-manifest.js +3 -0
  36. package/.next/standalone/.next/server/app/api/company-scores/route.js +3 -2
  37. package/.next/standalone/.next/server/app/api/company-scores/route.js.nft.json +1 -1
  38. package/.next/standalone/.next/server/app/api/connections/route.js +3 -2
  39. package/.next/standalone/.next/server/app/api/connections/route.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/api/data/export/route/app-paths-manifest.json +3 -0
  41. package/.next/standalone/.next/server/app/api/data/export/route/build-manifest.json +12 -0
  42. package/.next/standalone/.next/server/app/api/data/export/route/server-reference-manifest.json +4 -0
  43. package/.next/standalone/.next/server/app/api/data/export/route.js +7 -0
  44. package/.next/standalone/.next/server/app/api/data/export/route.js.map +5 -0
  45. package/.next/standalone/.next/server/app/api/data/export/route.js.nft.json +1 -0
  46. package/.next/standalone/.next/server/app/api/data/export/route_client-reference-manifest.js +3 -0
  47. package/.next/standalone/.next/server/app/api/data/import/route/app-paths-manifest.json +3 -0
  48. package/.next/standalone/.next/server/app/api/data/import/route/build-manifest.json +12 -0
  49. package/.next/standalone/.next/server/app/api/data/import/route/server-reference-manifest.json +4 -0
  50. package/.next/standalone/.next/server/app/api/data/import/route.js +8 -0
  51. package/.next/standalone/.next/server/app/api/data/import/route.js.map +5 -0
  52. package/.next/standalone/.next/server/app/api/data/import/route.js.nft.json +1 -0
  53. package/.next/standalone/.next/server/app/api/data/import/route_client-reference-manifest.js +3 -0
  54. package/.next/standalone/.next/server/app/api/data/restart/route/app-paths-manifest.json +3 -0
  55. package/.next/standalone/.next/server/app/api/data/restart/route/build-manifest.json +12 -0
  56. package/.next/standalone/.next/server/app/api/data/restart/route/server-reference-manifest.json +4 -0
  57. package/.next/standalone/.next/server/app/api/data/restart/route.js +8 -0
  58. package/.next/standalone/.next/server/app/api/data/restart/route.js.map +5 -0
  59. package/.next/standalone/.next/server/app/api/data/restart/route.js.nft.json +1 -0
  60. package/.next/standalone/.next/server/app/api/data/restart/route_client-reference-manifest.js +3 -0
  61. package/.next/standalone/.next/server/app/api/data/reveal/route/app-paths-manifest.json +3 -0
  62. package/.next/standalone/.next/server/app/api/data/reveal/route/build-manifest.json +12 -0
  63. package/.next/standalone/.next/server/app/api/data/reveal/route/server-reference-manifest.json +4 -0
  64. package/.next/standalone/.next/server/app/api/data/reveal/route.js +7 -0
  65. package/.next/standalone/.next/server/app/api/data/reveal/route.js.map +5 -0
  66. package/.next/standalone/.next/server/app/api/data/reveal/route.js.nft.json +1 -0
  67. package/.next/standalone/.next/server/app/api/data/reveal/route_client-reference-manifest.js +3 -0
  68. package/.next/standalone/.next/server/app/api/data/route/app-paths-manifest.json +3 -0
  69. package/.next/standalone/.next/server/app/api/data/route/build-manifest.json +12 -0
  70. package/.next/standalone/.next/server/app/api/data/route/server-reference-manifest.json +4 -0
  71. package/.next/standalone/.next/server/app/api/data/route.js +7 -0
  72. package/.next/standalone/.next/server/app/api/data/route.js.map +5 -0
  73. package/.next/standalone/.next/server/app/api/data/route.js.nft.json +1 -0
  74. package/.next/standalone/.next/server/app/api/data/route_client-reference-manifest.js +3 -0
  75. package/.next/standalone/.next/server/app/api/delete-cluster/route.js +3 -2
  76. package/.next/standalone/.next/server/app/api/delete-cluster/route.js.nft.json +1 -1
  77. package/.next/standalone/.next/server/app/api/ingest/route.js +2 -2
  78. package/.next/standalone/.next/server/app/api/ingest/route.js.nft.json +1 -1
  79. package/.next/standalone/.next/server/app/api/network/route.js +3 -2
  80. package/.next/standalone/.next/server/app/api/network/route.js.nft.json +1 -1
  81. package/.next/standalone/.next/server/app/api/notifications/route.js +3 -2
  82. package/.next/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  83. package/.next/standalone/.next/server/app/api/outreach/route.js +3 -2
  84. package/.next/standalone/.next/server/app/api/outreach/route.js.nft.json +1 -1
  85. package/.next/standalone/.next/server/app/api/queue/route.js +3 -2
  86. package/.next/standalone/.next/server/app/api/queue/route.js.nft.json +1 -1
  87. package/.next/standalone/.next/server/app/api/scraper/route.js +3 -2
  88. package/.next/standalone/.next/server/app/api/scraper/route.js.nft.json +1 -1
  89. package/.next/standalone/.next/server/app/api/settings/route/app-paths-manifest.json +3 -0
  90. package/.next/standalone/.next/server/app/api/settings/route/build-manifest.json +12 -0
  91. package/.next/standalone/.next/server/app/api/settings/route/server-reference-manifest.json +4 -0
  92. package/.next/standalone/.next/server/app/api/settings/route.js +9 -0
  93. package/.next/standalone/.next/server/app/api/settings/route.js.map +5 -0
  94. package/.next/standalone/.next/server/app/api/settings/route.js.nft.json +1 -0
  95. package/.next/standalone/.next/server/app/api/settings/route_client-reference-manifest.js +3 -0
  96. package/.next/standalone/.next/server/app/api/settings/sector-preview/route/app-paths-manifest.json +3 -0
  97. package/.next/standalone/.next/server/app/api/settings/sector-preview/route/build-manifest.json +12 -0
  98. package/.next/standalone/.next/server/app/api/settings/sector-preview/route/server-reference-manifest.json +4 -0
  99. package/.next/standalone/.next/server/app/api/settings/sector-preview/route.js +8 -0
  100. package/.next/standalone/.next/server/app/api/settings/sector-preview/route.js.map +5 -0
  101. package/.next/standalone/.next/server/app/api/settings/sector-preview/route.js.nft.json +1 -0
  102. package/.next/standalone/.next/server/app/api/settings/sector-preview/route_client-reference-manifest.js +3 -0
  103. package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route/app-paths-manifest.json +3 -0
  104. package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route/build-manifest.json +12 -0
  105. package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route/server-reference-manifest.json +4 -0
  106. package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route.js +8 -0
  107. package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route.js.map +5 -0
  108. package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route.js.nft.json +1 -0
  109. package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route_client-reference-manifest.js +3 -0
  110. package/.next/standalone/.next/server/app/api/setup-profile/route.js +3 -2
  111. package/.next/standalone/.next/server/app/api/setup-profile/route.js.nft.json +1 -1
  112. package/.next/standalone/.next/server/app/api/unlock/route.js +3 -2
  113. package/.next/standalone/.next/server/app/api/unlock/route.js.nft.json +1 -1
  114. package/.next/standalone/.next/server/app/api/update/route.js +1 -1
  115. package/.next/standalone/.next/server/app/api/update/route.js.nft.json +1 -1
  116. package/.next/standalone/.next/server/app/api/update-images/route.js +3 -2
  117. package/.next/standalone/.next/server/app/api/update-images/route.js.nft.json +1 -1
  118. package/.next/standalone/.next/server/app/api/users/route.js +3 -2
  119. package/.next/standalone/.next/server/app/api/users/route.js.nft.json +1 -1
  120. package/.next/standalone/.next/server/app/avatars/[file]/route.js +2 -1
  121. package/.next/standalone/.next/server/app/avatars/[file]/route.js.nft.json +1 -1
  122. package/.next/standalone/.next/server/app/favicon.ico/route.js +2 -1
  123. package/.next/standalone/.next/server/app/favicon.ico/route.js.nft.json +1 -1
  124. package/.next/standalone/.next/server/app/import/page.js.nft.json +1 -1
  125. package/.next/standalone/.next/server/app/import/page_client-reference-manifest.js +1 -1
  126. package/.next/standalone/.next/server/app/import.html +1 -1
  127. package/.next/standalone/.next/server/app/import.rsc +12 -12
  128. package/.next/standalone/.next/server/app/import.segments/_full.segment.rsc +12 -12
  129. package/.next/standalone/.next/server/app/import.segments/_tree.segment.rsc +1 -1
  130. package/.next/standalone/.next/server/app/import.segments/import/__PAGE__.segment.rsc +11 -11
  131. package/.next/standalone/.next/server/app/index.html +1 -1
  132. package/.next/standalone/.next/server/app/index.rsc +12 -12
  133. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +11 -11
  134. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +12 -12
  135. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  136. package/.next/standalone/.next/server/app/launch/page.js.nft.json +1 -1
  137. package/.next/standalone/.next/server/app/launch/page_client-reference-manifest.js +1 -1
  138. package/.next/standalone/.next/server/app/launch.html +2 -2
  139. package/.next/standalone/.next/server/app/launch.rsc +12 -12
  140. package/.next/standalone/.next/server/app/launch.segments/_full.segment.rsc +12 -12
  141. package/.next/standalone/.next/server/app/launch.segments/_tree.segment.rsc +1 -1
  142. package/.next/standalone/.next/server/app/launch.segments/launch/__PAGE__.segment.rsc +11 -11
  143. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  144. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  145. package/.next/standalone/.next/server/app/paths/page.js.nft.json +1 -1
  146. package/.next/standalone/.next/server/app/paths/page_client-reference-manifest.js +1 -1
  147. package/.next/standalone/.next/server/app/paths.html +1 -1
  148. package/.next/standalone/.next/server/app/paths.rsc +12 -12
  149. package/.next/standalone/.next/server/app/paths.segments/_full.segment.rsc +12 -12
  150. package/.next/standalone/.next/server/app/paths.segments/_tree.segment.rsc +1 -1
  151. package/.next/standalone/.next/server/app/paths.segments/paths/__PAGE__.segment.rsc +11 -11
  152. package/.next/standalone/.next/server/app/profile/page.js.nft.json +1 -1
  153. package/.next/standalone/.next/server/app/profile/page_client-reference-manifest.js +1 -1
  154. package/.next/standalone/.next/server/app/profile.html +1 -1
  155. package/.next/standalone/.next/server/app/profile.rsc +12 -12
  156. package/.next/standalone/.next/server/app/profile.segments/_full.segment.rsc +12 -12
  157. package/.next/standalone/.next/server/app/profile.segments/_tree.segment.rsc +1 -1
  158. package/.next/standalone/.next/server/app/profile.segments/profile/__PAGE__.segment.rsc +11 -11
  159. package/.next/standalone/.next/server/app/queue/page.js.nft.json +1 -1
  160. package/.next/standalone/.next/server/app/queue/page_client-reference-manifest.js +1 -1
  161. package/.next/standalone/.next/server/app/queue.html +1 -1
  162. package/.next/standalone/.next/server/app/queue.rsc +12 -12
  163. package/.next/standalone/.next/server/app/queue.segments/_full.segment.rsc +12 -12
  164. package/.next/standalone/.next/server/app/queue.segments/_tree.segment.rsc +1 -1
  165. package/.next/standalone/.next/server/app/queue.segments/queue/__PAGE__.segment.rsc +11 -11
  166. package/.next/standalone/.next/server/app/settings/page/app-paths-manifest.json +3 -0
  167. package/.next/standalone/.next/server/app/settings/page/build-manifest.json +20 -0
  168. package/.next/standalone/.next/server/app/settings/page/next-font-manifest.json +11 -0
  169. package/.next/standalone/.next/server/app/settings/page/react-loadable-manifest.json +1 -0
  170. package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +4 -0
  171. package/.next/standalone/.next/server/app/settings/page.js +13 -0
  172. package/.next/standalone/.next/server/app/settings/page.js.map +5 -0
  173. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -0
  174. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +3 -0
  175. package/.next/standalone/.next/server/app/settings.html +1 -0
  176. package/.next/standalone/.next/server/app/settings.meta +26 -0
  177. package/.next/standalone/.next/server/app/settings.rsc +24 -0
  178. package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +24 -0
  179. package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +4 -0
  180. package/.next/standalone/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +28 -0
  181. package/.next/standalone/.next/server/app/setup/page.js.nft.json +1 -1
  182. package/.next/standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  183. package/.next/standalone/.next/server/app/setup.html +1 -1
  184. package/.next/standalone/.next/server/app/setup.rsc +12 -12
  185. package/.next/standalone/.next/server/app/setup.segments/_full.segment.rsc +12 -12
  186. package/.next/standalone/.next/server/app/setup.segments/_tree.segment.rsc +1 -1
  187. package/.next/standalone/.next/server/app/setup.segments/setup/__PAGE__.segment.rsc +11 -11
  188. package/.next/standalone/.next/server/app-paths-manifest.json +10 -0
  189. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-rnla2._.js +3 -0
  190. package/.next/standalone/.next/server/chunks/[root-of-the-server]__00la_y0._.js +3 -0
  191. package/.next/standalone/.next/server/chunks/[root-of-the-server]__07t47cm._.js +3 -0
  192. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0cj5jqc._.js +3 -0
  193. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0e0fei3._.js +3 -0
  194. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0i5hdu4._.js +3 -0
  195. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ljcioj._.js +3 -0
  196. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ljnyho._.js +3 -0
  197. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o5-tcg._.js +3 -0
  198. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0qrnous._.js +3 -0
  199. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0t3v1a1._.js +3 -0
  200. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0uag-k5._.js +8 -0
  201. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0um1uey._.js +3 -0
  202. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0wmexuc._.js +3 -0
  203. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0xu34sz._.js +3 -0
  204. package/.next/standalone/.next/server/chunks/[root-of-the-server]__133knx1._.js +3 -0
  205. package/.next/standalone/.next/server/chunks/[root-of-the-server]__14ou7j5._.js +3 -0
  206. package/.next/standalone/.next/server/chunks/[root-of-the-server]__15-17hl._.js +3 -0
  207. package/.next/standalone/.next/server/chunks/[root-of-the-server]__16iu0hv._.js +3 -0
  208. package/.next/standalone/.next/server/chunks/[root-of-the-server]__19b7mig._.js +3 -0
  209. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1bbcwub._.js +8 -0
  210. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1eris29._.js +3 -0
  211. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1gjo_cq._.js +3 -0
  212. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1kbl21f._.js +3 -0
  213. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1qcm08_._.js +3 -0
  214. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1rphsdk._.js +3 -0
  215. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1t3rugi._.js +3 -0
  216. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1w25q0m._.js +3 -0
  217. package/.next/standalone/.next/server/chunks/_072jl0y._.js +3 -0
  218. package/.next/standalone/.next/server/chunks/_0lw8_i0._.js +3 -0
  219. package/.next/standalone/.next/server/chunks/_1_fynia._.js +21 -3
  220. package/.next/standalone/.next/server/chunks/_1fn30_l._.js +3 -0
  221. package/.next/standalone/.next/server/chunks/_1itd1o_._.js +2 -1
  222. package/.next/standalone/.next/server/chunks/_1iwkh8t._.js +1 -1
  223. package/.next/standalone/.next/server/chunks/_1mxn_lb._.js +3 -0
  224. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_company-scores_legacy_route_actions_1ncspu_.js +3 -0
  225. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_data_export_route_actions_1lkimty.js +3 -0
  226. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_data_import_route_actions_1eji0kd.js +3 -0
  227. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_data_restart_route_actions_00_-u6o.js +3 -0
  228. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_data_reveal_route_actions_0-moqtn.js +3 -0
  229. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_data_route_actions_0j0w5ep.js +3 -0
  230. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_settings_route_actions_1bpm9ac.js +3 -0
  231. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_settings_sector-preview_route_actions_0w79454.js +3 -0
  232. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_settings_sector-suggestions_route_actions_1eoqugp.js +3 -0
  233. package/.next/standalone/.next/server/chunks/lib_db-client_15le7cw.js +155 -0
  234. package/.next/standalone/.next/server/chunks/{lib_rpc_0mc73lk.js → lib_rpc_00tx2sm.js} +1 -1
  235. package/.next/standalone/.next/server/chunks/lib_rpc_02-qa4d.js +3 -0
  236. package/.next/standalone/.next/server/chunks/lib_rpc_02ndsg2.js +3 -0
  237. package/.next/standalone/.next/server/chunks/lib_rpc_08q5vp6.js +3 -0
  238. package/.next/standalone/.next/server/chunks/lib_rpc_0_8i3ft.js +3 -0
  239. package/.next/standalone/.next/server/chunks/lib_rpc_0c9d9ti.js +5 -2
  240. package/.next/standalone/.next/server/chunks/lib_rpc_0huvw-3.js +3 -0
  241. package/.next/standalone/.next/server/chunks/lib_rpc_0ip2vdh.js +3 -0
  242. package/.next/standalone/.next/server/chunks/lib_rpc_0jzisgh.js +3 -0
  243. package/.next/standalone/.next/server/chunks/lib_rpc_0yi9xvd.js +3 -0
  244. package/.next/standalone/.next/server/chunks/lib_rpc_1jyhd0o.js +3 -0
  245. package/.next/standalone/.next/server/chunks/lib_rpc_1kn59-i.js +3 -0
  246. package/.next/standalone/.next/server/chunks/lib_rpc_1q2jkza.js +3 -0
  247. package/.next/standalone/.next/server/chunks/lib_rpc_1vpm1sk.js +3 -0
  248. package/.next/standalone/.next/server/chunks/lib_rpc_1x6p_31.js +3 -0
  249. package/.next/standalone/.next/server/chunks/node_modules_next_1zc5q0a._.js +158 -0
  250. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0mocm6d._.js → [root-of-the-server]__0an6blh._.js} +3 -3
  251. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1m0255u._.js +3 -0
  252. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0bipr5s._.js → [root-of-the-server]__1z1j3ek._.js} +2 -2
  253. package/.next/standalone/.next/server/chunks/ssr/_07u8byv._.js +3 -0
  254. package/.next/standalone/.next/server/chunks/ssr/_08r_mlf._.js +3 -0
  255. package/.next/standalone/.next/server/chunks/ssr/{_0ggabjc._.js → _0942v8n._.js} +1 -1
  256. package/.next/standalone/.next/server/chunks/ssr/{_1djpk8t._.js → _0hl_t7r._.js} +2 -2
  257. package/.next/standalone/.next/server/chunks/ssr/_0tb83dw._.js +132 -0
  258. package/.next/standalone/.next/server/chunks/ssr/_1-87bs1._.js +3 -0
  259. package/.next/standalone/.next/server/chunks/ssr/_17gui6a._.js +1 -1
  260. package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_settings_page_actions_11x4dq5.js +3 -0
  261. package/.next/standalone/.next/server/chunks/ssr/app_launch_page_19t0dyd.js +2 -2
  262. package/.next/standalone/.next/server/chunks/ssr/app_page_1fazqbf.js +4 -4
  263. package/.next/standalone/.next/server/chunks/ssr/app_profile_page_0u3-l5y.js +1 -1
  264. package/.next/standalone/.next/server/chunks/ssr/app_queue_page_0c6znd6.js +5 -5
  265. package/.next/standalone/.next/server/chunks/ssr/app_settings_page_1f8mjj_.js +3 -0
  266. package/.next/standalone/.next/server/chunks/ssr/app_setup_page_1-282zx.js +2 -2
  267. package/.next/standalone/.next/server/chunks/ssr/lib_0i8v-s3._.js +1 -1
  268. package/.next/standalone/.next/server/chunks/ssr/node_modules_1f8t_rp._.js +23 -0
  269. package/.next/standalone/.next/server/chunks/ssr/node_modules_1nkgb45._.js +2 -2
  270. package/.next/standalone/.next/server/chunks/ssr/{node_modules_01xbj_9._.js → node_modules_1wax83z._.js} +3 -3
  271. package/.next/standalone/.next/server/chunks/ssr/{node_modules_next_dist_00nap8h._.js → node_modules_next_dist_1bunoni._.js} +2 -2
  272. package/.next/standalone/.next/server/edge/chunks/[root-of-the-server]__0pco7a6._.js +2 -2
  273. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  274. package/.next/standalone/.next/server/middleware-manifest.json +15 -7
  275. package/.next/standalone/.next/server/next-font-manifest.js +1 -1
  276. package/.next/standalone/.next/server/next-font-manifest.json +4 -0
  277. package/.next/standalone/.next/server/pages/404.html +1 -1
  278. package/.next/standalone/.next/server/pages/500.html +1 -1
  279. package/.next/standalone/.next/server/prefetch-hints.json +14 -0
  280. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  281. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  282. package/.next/standalone/.next/static/chunks/06x3jiiz96m5t.js +1 -0
  283. package/.next/standalone/.next/static/chunks/{34at5d2ifl62i.js → 0ngx9s6-zgshe.js} +5 -5
  284. package/.next/standalone/.next/static/chunks/0q-_khndfxubv.js +2 -0
  285. package/.next/standalone/.next/static/chunks/1c79veqedvubt.js +1 -0
  286. package/.next/standalone/.next/static/chunks/1chp3nx45meg5.js +44 -0
  287. package/.next/standalone/.next/static/chunks/1irqj0lf-fu3d.js +1 -0
  288. package/.next/standalone/.next/static/chunks/1uadqylrod5_v.js +1 -0
  289. package/.next/standalone/.next/static/chunks/23u0zclgwpc4d.js +1 -0
  290. package/.next/standalone/.next/static/chunks/289ktj97di6zg.js +1 -0
  291. package/.next/standalone/.next/static/chunks/2b3lmf8e6uj8m.js +16 -0
  292. package/.next/standalone/.next/static/chunks/2jz311xl1pp_8.js +1 -0
  293. package/.next/standalone/.next/static/chunks/3npq5yd7c4dmn.js +2 -0
  294. package/.next/standalone/.next/static/qqqcxvzBUizkECrT4swyK/_clientMiddlewareManifest.js +14 -0
  295. package/.next/standalone/CHANGELOG.md +298 -0
  296. package/.next/standalone/CONTRIBUTING.md +5 -2
  297. package/.next/standalone/README.md +138 -30
  298. package/.next/standalone/SECURITY.md +117 -10
  299. package/.next/standalone/app/api/company-scores/legacy/route.js +32 -0
  300. package/.next/standalone/app/api/company-scores/route.js +20 -11
  301. package/.next/standalone/app/api/data/export/route.js +49 -0
  302. package/.next/standalone/app/api/data/import/route.js +83 -0
  303. package/.next/standalone/app/api/data/restart/route.js +37 -0
  304. package/.next/standalone/app/api/data/reveal/route.js +14 -0
  305. package/.next/standalone/app/api/data/route.js +39 -0
  306. package/.next/standalone/app/api/ingest/route.js +15 -47
  307. package/.next/standalone/app/api/scraper/route.js +264 -125
  308. package/.next/standalone/app/api/settings/route.js +58 -0
  309. package/.next/standalone/app/api/settings/sector-preview/route.js +38 -0
  310. package/.next/standalone/app/api/settings/sector-suggestions/route.js +20 -0
  311. package/.next/standalone/app/api/update/route.js +188 -23
  312. package/.next/standalone/app/avatars/[file]/route.js +4 -2
  313. package/.next/standalone/app/components/CompanyScores.js +28 -7
  314. package/.next/standalone/app/components/EmptyState.js +8 -0
  315. package/.next/standalone/app/components/ForceGraph.js +39 -35
  316. package/.next/standalone/app/components/LegacyScoresCard.js +129 -0
  317. package/.next/standalone/app/components/Sidebar.js +11 -10
  318. package/.next/standalone/app/components/UpdatePanel.js +306 -119
  319. package/.next/standalone/app/components/UserProvider.js +4 -3
  320. package/.next/standalone/app/components/settings/DataSection.js +437 -0
  321. package/.next/standalone/app/components/settings/SectorSection.js +445 -0
  322. package/.next/standalone/app/components/ui.js +62 -0
  323. package/.next/standalone/app/launch/page.js +30 -20
  324. package/.next/standalone/app/page.js +13 -0
  325. package/.next/standalone/app/paths/page.js +12 -4
  326. package/.next/standalone/app/profile/page.js +41 -8
  327. package/.next/standalone/app/queue/page.js +8 -2
  328. package/.next/standalone/app/settings/page.js +96 -0
  329. package/.next/standalone/app/setup/page.js +32 -16
  330. package/.next/standalone/bin/args.mjs +44 -0
  331. package/.next/standalone/bin/six-degrees.mjs +9 -15
  332. package/.next/standalone/lib/companies.js +44 -119
  333. package/.next/standalone/lib/csv.js +8 -0
  334. package/.next/standalone/lib/data-export.js +170 -0
  335. package/.next/standalone/lib/data-folder.js +238 -0
  336. package/.next/standalone/lib/data-import.js +878 -0
  337. package/.next/standalone/lib/db-client.js +48 -3
  338. package/.next/standalone/lib/durable.js +73 -0
  339. package/.next/standalone/lib/gate.js +90 -1
  340. package/.next/standalone/lib/ingest.js +40 -0
  341. package/.next/standalone/lib/legacy-offer.js +185 -0
  342. package/.next/standalone/lib/legacy-scores.js +77 -0
  343. package/.next/standalone/lib/linkedin-limits.js +124 -1
  344. package/.next/standalone/lib/rpc.js +121 -13
  345. package/.next/standalone/lib/sample-stats.js +16 -0
  346. package/.next/standalone/lib/scan-progress.js +13 -0
  347. package/.next/standalone/lib/scan-state.js +53 -0
  348. package/.next/standalone/lib/scanner-python.js +685 -0
  349. package/.next/standalone/lib/scanner-setup.js +104 -0
  350. package/.next/standalone/lib/scoring.js +747 -138
  351. package/.next/standalone/lib/scraper-client.js +9 -5
  352. package/.next/standalone/lib/sector-directory.js +1532 -0
  353. package/.next/standalone/lib/sector-focus.js +163 -0
  354. package/.next/standalone/lib/sector-labels.js +81 -0
  355. package/.next/standalone/lib/settings-effects.js +59 -0
  356. package/.next/standalone/lib/settings.js +83 -0
  357. package/.next/standalone/lib/updater-job.js +838 -0
  358. package/.next/standalone/lib/updater.js +494 -0
  359. package/.next/standalone/middleware.js +25 -28
  360. package/.next/standalone/next.config.mjs +19 -3
  361. package/.next/standalone/package-lock.json +2 -2
  362. package/.next/standalone/package.json +1 -2
  363. package/.next/standalone/scripts/apply-update.sh +482 -0
  364. package/.next/standalone/scripts/build-app.mjs +251 -9
  365. package/.next/standalone/scripts/requirements.txt +106 -4
  366. package/.next/standalone/scripts/test-release-server.mjs +143 -0
  367. package/.next/standalone/server.js +1 -1
  368. package/.next/static/chunks/06x3jiiz96m5t.js +1 -0
  369. package/.next/static/chunks/{34at5d2ifl62i.js → 0ngx9s6-zgshe.js} +5 -5
  370. package/.next/static/chunks/0q-_khndfxubv.js +2 -0
  371. package/.next/static/chunks/1c79veqedvubt.js +1 -0
  372. package/.next/static/chunks/1chp3nx45meg5.js +44 -0
  373. package/.next/static/chunks/1irqj0lf-fu3d.js +1 -0
  374. package/.next/static/chunks/1uadqylrod5_v.js +1 -0
  375. package/.next/static/chunks/23u0zclgwpc4d.js +1 -0
  376. package/.next/static/chunks/289ktj97di6zg.js +1 -0
  377. package/.next/static/chunks/2b3lmf8e6uj8m.js +16 -0
  378. package/.next/static/chunks/2jz311xl1pp_8.js +1 -0
  379. package/.next/static/chunks/3npq5yd7c4dmn.js +2 -0
  380. package/.next/static/qqqcxvzBUizkECrT4swyK/_clientMiddlewareManifest.js +14 -0
  381. package/CHANGELOG.md +298 -0
  382. package/README.md +138 -30
  383. package/SECURITY.md +117 -10
  384. package/bin/args.mjs +44 -0
  385. package/bin/six-degrees.mjs +9 -15
  386. package/lib/companies.js +44 -119
  387. package/lib/csv.js +8 -0
  388. package/lib/data-export.js +170 -0
  389. package/lib/data-folder.js +238 -0
  390. package/lib/data-import.js +878 -0
  391. package/lib/db-client.js +48 -3
  392. package/lib/durable.js +73 -0
  393. package/lib/gate.js +90 -1
  394. package/lib/ingest.js +40 -0
  395. package/lib/legacy-offer.js +185 -0
  396. package/lib/legacy-scores.js +77 -0
  397. package/lib/linkedin-limits.js +124 -1
  398. package/lib/rpc.js +121 -13
  399. package/lib/sample-stats.js +16 -0
  400. package/lib/scan-progress.js +13 -0
  401. package/lib/scan-state.js +53 -0
  402. package/lib/scanner-python.js +685 -0
  403. package/lib/scanner-setup.js +104 -0
  404. package/lib/scoring.js +747 -138
  405. package/lib/scraper-client.js +9 -5
  406. package/lib/sector-directory.js +1532 -0
  407. package/lib/sector-focus.js +163 -0
  408. package/lib/sector-labels.js +81 -0
  409. package/lib/settings-effects.js +59 -0
  410. package/lib/settings.js +83 -0
  411. package/lib/updater-job.js +838 -0
  412. package/lib/updater.js +494 -0
  413. package/package.json +1 -2
  414. package/scripts/requirements.txt +106 -4
  415. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0jrmmj-._.js +0 -150
  416. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0qx548g._.js +0 -151
  417. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1an3leo._.js +0 -150
  418. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1m8zedk._.js +0 -155
  419. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1zor0ys._.js +0 -3
  420. package/.next/standalone/.next/server/chunks/_0_rp-01._.js +0 -3
  421. package/.next/standalone/.next/server/chunks/_0hzdk79._.js +0 -3
  422. package/.next/standalone/.next/server/chunks/_0ow7u5r._.js +0 -3
  423. package/.next/standalone/.next/server/chunks/_0t0zc2d._.js +0 -3
  424. package/.next/standalone/.next/server/chunks/_0wa631d._.js +0 -3
  425. package/.next/standalone/.next/server/chunks/_13ccnao._.js +0 -3
  426. package/.next/standalone/.next/server/chunks/_13es063._.js +0 -3
  427. package/.next/standalone/.next/server/chunks/_142872j._.js +0 -3
  428. package/.next/standalone/.next/server/chunks/_18wsb_a._.js +0 -8
  429. package/.next/standalone/.next/server/chunks/_1e-4h5h._.js +0 -3
  430. package/.next/standalone/.next/server/chunks/_1gvvd7n._.js +0 -158
  431. package/.next/standalone/.next/server/chunks/_1moda66._.js +0 -3
  432. package/.next/standalone/.next/server/chunks/_1p6ghzv._.js +0 -3
  433. package/.next/standalone/.next/server/chunks/_1xmmi_v._.js +0 -3
  434. package/.next/standalone/.next/server/chunks/_1z-yrlf._.js +0 -3
  435. package/.next/standalone/.next/server/chunks/ssr/_0bf3x4q._.js +0 -3
  436. package/.next/standalone/.next/server/chunks/ssr/app_paths_page_1ol90m7.js +0 -4
  437. package/.next/standalone/.next/static/ZGTiDrZ5NHGETgGdlhssu/_clientMiddlewareManifest.js +0 -6
  438. package/.next/standalone/.next/static/chunks/0jk64vo17avsb.js +0 -1
  439. package/.next/standalone/.next/static/chunks/0s65dsrthylo6.js +0 -2
  440. package/.next/standalone/.next/static/chunks/0uyfdhi8dek85.js +0 -16
  441. package/.next/standalone/.next/static/chunks/220-ne82aus0c.js +0 -44
  442. package/.next/standalone/.next/static/chunks/229haorf0f19b.js +0 -2
  443. package/.next/standalone/.next/static/chunks/2g54uafnwuakc.js +0 -1
  444. package/.next/standalone/.next/static/chunks/2spwqs6b6dx13.js +0 -1
  445. package/.next/standalone/.next/static/chunks/38dlepuubiox0.js +0 -1
  446. package/.next/standalone/scripts/score-connections.sql +0 -80
  447. package/.next/standalone/scripts/score_new_connections.sql +0 -77
  448. package/.next/standalone/site/app.js +0 -84
  449. package/.next/standalone/site/index.html +0 -221
  450. package/.next/standalone/site/style.css +0 -155
  451. package/.next/standalone/site/video/install-mac-poster.jpg +0 -0
  452. package/.next/standalone/site/video/install-mac.mp4 +0 -0
  453. package/.next/standalone/site/video/linux-poster.jpg +0 -0
  454. package/.next/standalone/site/video/linux.mp4 +0 -0
  455. package/.next/standalone/site/video/scan-poster.jpg +0 -0
  456. package/.next/standalone/site/video/scan.mp4 +0 -0
  457. package/.next/static/ZGTiDrZ5NHGETgGdlhssu/_clientMiddlewareManifest.js +0 -6
  458. package/.next/static/chunks/0jk64vo17avsb.js +0 -1
  459. package/.next/static/chunks/0s65dsrthylo6.js +0 -2
  460. package/.next/static/chunks/0uyfdhi8dek85.js +0 -16
  461. package/.next/static/chunks/220-ne82aus0c.js +0 -44
  462. package/.next/static/chunks/229haorf0f19b.js +0 -2
  463. package/.next/static/chunks/2g54uafnwuakc.js +0 -1
  464. package/.next/static/chunks/2spwqs6b6dx13.js +0 -1
  465. package/.next/static/chunks/38dlepuubiox0.js +0 -1
  466. package/scripts/score_new_connections.sql +0 -77
  467. /package/.next/standalone/.next/static/{ZGTiDrZ5NHGETgGdlhssu → qqqcxvzBUizkECrT4swyK}/_buildManifest.js +0 -0
  468. /package/.next/standalone/.next/static/{ZGTiDrZ5NHGETgGdlhssu → qqqcxvzBUizkECrT4swyK}/_ssgManifest.js +0 -0
  469. /package/.next/static/{ZGTiDrZ5NHGETgGdlhssu → qqqcxvzBUizkECrT4swyK}/_buildManifest.js +0 -0
  470. /package/.next/static/{ZGTiDrZ5NHGETgGdlhssu → qqqcxvzBUizkECrT4swyK}/_ssgManifest.js +0 -0
@@ -4,6 +4,7 @@ import { homedir } from 'node:os';
4
4
  import path from 'node:path';
5
5
  import { SCHEMA_SQL } from '../db/schema.js';
6
6
  import { APP_VERSION } from './app-version.js';
7
+ import { applyPendingImport, recordImport } from './data-import.js';
7
8
 
8
9
  // The database lives OUTSIDE the app directory so an installed copy never
9
10
  // writes into its own package. Override with SIX_DEGREES_HOME.
@@ -36,10 +37,42 @@ export const BOOLEAN_COLUMNS = {
36
37
 
37
38
  let _db = null;
38
39
 
40
+ // Set once this server process has opened the database, by any copy of this
41
+ // module. The bundler can give a route its own copy, with its own _db, and a
42
+ // copy that opens late must not finish an import staged while another copy's
43
+ // handle was already open: the swap would happen under that handle. There is
44
+ // one globalThis per process, like lib/scan-state.js.
45
+ const OPENED = Symbol.for('six-degrees.db-opened');
46
+
47
+ /** The database file: SIX_DEGREES_DB, or six-degrees.sqlite in the data folder. */
48
+ export function dbFile() {
49
+ return process.env.SIX_DEGREES_DB || path.join(dataDir(), 'six-degrees.sqlite');
50
+ }
51
+
39
52
  export function getDb() {
40
53
  if (_db) return _db;
41
54
 
42
- const file = process.env.SIX_DEGREES_DB || path.join(dataDir(), 'six-degrees.sqlite');
55
+ const file = dbFile();
56
+
57
+ // A network imported from another computer (Settings → Your data) is swapped
58
+ // in here, at the start, before anything opens the database: it can't be
59
+ // swapped under a handle that is already open, this process's or another's
60
+ // (it waits while another copy of Six Degrees has the database open). What
61
+ // was here is kept in backups/ first. If a step fails, what is in place opens
62
+ // as usual and the next start carries on (lib/data-import.js).
63
+ let imported = null;
64
+ if (!globalThis[OPENED]) {
65
+ try {
66
+ imported = applyPendingImport({ dir: dataDir(), dbFile: file });
67
+ if (imported) {
68
+ console.log(`Six Degrees: finished the import (${imported.people} people). What was here before: ${imported.keptDatabase || 'nothing to keep'}.`);
69
+ }
70
+ } catch (err) {
71
+ console.error(`Six Degrees: could not finish the import (will try again next start): ${err.message}`);
72
+ }
73
+ }
74
+ globalThis[OPENED] = true;
75
+
43
76
  _db = new DatabaseSync(file);
44
77
 
45
78
  // Before this version changes anything: if a different version opened this
@@ -53,12 +86,24 @@ export function getDb() {
53
86
 
54
87
  // Idempotent: every statement is CREATE ... IF NOT EXISTS, so this doubles
55
88
  // as first-run setup and as a no-op on every later boot.
56
- _db.exec(SCHEMA_SQL);
57
- addMissingColumns(_db);
89
+ applySchema(_db);
90
+
91
+ if (imported) {
92
+ try { recordImport(_db, imported); } catch { /* only the Settings page's note is lost */ }
93
+ }
58
94
 
59
95
  return _db;
60
96
  }
61
97
 
98
+ /**
99
+ * Every table, index and column this version uses, made if missing. An import
100
+ * is rebuilt into exactly this (lib/data-import.js), so only rows travel.
101
+ */
102
+ export function applySchema(db) {
103
+ db.exec(SCHEMA_SQL);
104
+ addMissingColumns(db);
105
+ }
106
+
62
107
  // CREATE TABLE IF NOT EXISTS never adds a column to a table that already
63
108
  // exists, so columns added after first release are added here, once.
64
109
  const ADDED_COLUMNS = [
@@ -0,0 +1,73 @@
1
+ // Writes that have to be on the disk before the next step may count on them.
2
+ //
3
+ // A rename is atomic, but it isn't durable. After a power cut the new name can
4
+ // be on the disk while the bytes it names aren't, so a copy that "exists" can
5
+ // be empty. SQLite doesn't sync what VACUUM INTO writes either (its docs say
6
+ // so). So a file is synced before it is renamed into place, and its folder
7
+ // after, so the rename itself is on the disk before anything relies on it.
8
+ //
9
+ // On a Mac, Node's fsync is F_FULLFSYNC (libuv), which also empties the drive's
10
+ // own write cache; a plain fsync there doesn't.
11
+
12
+ import fs from 'node:fs';
13
+ import path from 'node:path';
14
+
15
+ // A folder that isn't there has no rename to make durable, and some file
16
+ // systems can't open or sync one: nothing more can be done then.
17
+ const FOLDER_CANT_SYNC = new Set(['ENOENT', 'EINVAL', 'ENOTSUP', 'ENOSYS', 'EISDIR', 'EPERM', 'EBADF']);
18
+
19
+ function syncFile(file) {
20
+ const fd = fs.openSync(file, 'r');
21
+ try {
22
+ fs.fsyncSync(fd);
23
+ } finally {
24
+ fs.closeSync(fd);
25
+ }
26
+ }
27
+
28
+ function syncFolder(folder) {
29
+ let fd;
30
+ try {
31
+ fd = fs.openSync(folder, 'r');
32
+ } catch (err) {
33
+ if (FOLDER_CANT_SYNC.has(err.code)) return;
34
+ throw err;
35
+ }
36
+ try {
37
+ fs.fsyncSync(fd);
38
+ } catch (err) {
39
+ if (!FOLDER_CANT_SYNC.has(err.code)) throw err;
40
+ } finally {
41
+ fs.closeSync(fd);
42
+ }
43
+ }
44
+
45
+ /** Replace `file` with `data` so that after a power cut it holds either the old contents or the new, never part of either. */
46
+ function writeFileDurably(file, data) {
47
+ const tmp = `${file}.${process.pid}.tmp`;
48
+ try {
49
+ const fd = fs.openSync(tmp, 'w');
50
+ try {
51
+ fs.writeFileSync(fd, data);
52
+ fs.fsyncSync(fd);
53
+ } finally {
54
+ fs.closeSync(fd);
55
+ }
56
+ fs.renameSync(tmp, file);
57
+ } catch (err) {
58
+ fs.rmSync(tmp, { force: true });
59
+ throw err;
60
+ }
61
+ syncFolder(path.dirname(file));
62
+ }
63
+
64
+ /** The same, for a JSON document, laid out the way the scanner writes its own files. */
65
+ function writeJsonDurably(file, value) {
66
+ writeFileDurably(file, `${JSON.stringify(value, null, 2)}\n`);
67
+ }
68
+
69
+ /**
70
+ * Called through this object rather than directly, so a test can see what was
71
+ * synced and in which order (tests/data-transfer.test.mjs).
72
+ */
73
+ export const durable = { syncFile, syncFolder, writeFileDurably, writeJsonDurably };
@@ -7,6 +7,11 @@
7
7
  // irreversibly destroy or rewrite data; /api/scraper spawns processes and
8
8
  // /api/update changes the code that will run next, both of which are more power
9
9
  // than any of the others, so they sit behind the same gate.
10
+ //
11
+ // The four under /api/data are Settings → Your data: export hands over the
12
+ // whole network in one file, import replaces it, restart stops the server, and
13
+ // reveal starts a process. Reading /api/data itself (sizes, the folder's path)
14
+ // changes nothing and stays open, like every other read.
10
15
  export const DESTRUCTIVE_ROUTES = [
11
16
  '/api/admin-delete',
12
17
  '/api/admin-update',
@@ -14,6 +19,10 @@ export const DESTRUCTIVE_ROUTES = [
14
19
  '/api/setup-profile',
15
20
  '/api/scraper',
16
21
  '/api/update',
22
+ '/api/data/export',
23
+ '/api/data/import',
24
+ '/api/data/restart',
25
+ '/api/data/reveal',
17
26
  ];
18
27
 
19
28
  const LOOPBACK = new Set(['127.0.0.1', '::1', 'localhost']);
@@ -29,11 +38,21 @@ const LOOPBACK = new Set(['127.0.0.1', '::1', 'localhost']);
29
38
  * Sec-Fetch-Site is set by the browser and cannot be forged by script, so it is
30
39
  * the reliable signal. Origin is the fallback for browsers that omit it.
31
40
  * Non-browser callers (curl, scripts/scrape.py) send neither and are unaffected.
41
+ *
42
+ * 'same-site' is not enough on its own: a site ignores the port, so a page
43
+ * served from any other port on 127.0.0.1 (another local app, a dev server, a
44
+ * tool's web UI) is "same-site" with this one. Such a write has to show an
45
+ * Origin that is exactly this host and port. The app's own pages are always
46
+ * 'same-origin', so nothing legitimate is refused.
32
47
  */
33
48
  export function isCrossSiteWrite({ method, secFetchSite, origin, host }) {
34
49
  if (!method || !['POST', 'PUT', 'PATCH', 'DELETE'].includes(method.toUpperCase())) return false;
35
50
 
36
- if (secFetchSite) return !['same-origin', 'same-site', 'none'].includes(secFetchSite);
51
+ if (secFetchSite) {
52
+ if (secFetchSite === 'same-origin' || secFetchSite === 'none') return false;
53
+ if (secFetchSite !== 'same-site') return true;
54
+ if (!origin) return true; // same-site but no Origin to pin the port: refuse
55
+ }
37
56
 
38
57
  if (origin) {
39
58
  try {
@@ -46,6 +65,38 @@ export function isCrossSiteWrite({ method, secFetchSite, origin, host }) {
46
65
  return false; // no browser headers at all: not a browser
47
66
  }
48
67
 
68
+ /**
69
+ * Was this request sent to this app under someone else's name?
70
+ *
71
+ * DNS rebinding: a page on any website can make its own domain resolve to
72
+ * 127.0.0.1. The browser then treats the app as that page's own origin, so
73
+ * Sec-Fetch-Site says 'same-origin', the write check above lets it through,
74
+ * and CORS lets the page read every answer, the whole network included. What
75
+ * gives it away is the Host header: it carries the attacker's name, because
76
+ * that is the address the page used.
77
+ *
78
+ * When the server is bound to loopback, every real caller (this app's pages,
79
+ * the Mac app's window, the scanner, curl) addresses it as 127.0.0.1,
80
+ * localhost or [::1], so any other Host is refused. Bound anywhere else, the
81
+ * operator chose that on purpose and ADMIN_TOKEN guards what matters.
82
+ */
83
+ export function isRebound({ bind, host }) {
84
+ if (!boundToLoopback(bind)) return false;
85
+ if (!host) return false; // a client that sends no Host names nothing to judge
86
+ let hostname;
87
+ try {
88
+ hostname = new URL(`http://${host}`).hostname.toLowerCase();
89
+ } catch {
90
+ return true; // an unparseable Host is not a real caller
91
+ }
92
+ return !LOOPBACK_HOSTNAMES.has(hostname);
93
+ }
94
+
95
+ // How URL() writes the loopback names (it keeps IPv6 in brackets). 0.0.0.0 is
96
+ // here because the scanner's own allow-list accepts it for APP_URL; a rebinding
97
+ // page can only ever send its own domain, never this.
98
+ const LOOPBACK_HOSTNAMES = new Set(['127.0.0.1', 'localhost', '[::1]', '0.0.0.0']);
99
+
49
100
  export function isDestructive(pathname) {
50
101
  return DESTRUCTIVE_ROUTES.some((p) => pathname === p || pathname.startsWith(`${p}/`));
51
102
  }
@@ -61,6 +112,44 @@ function sameSecret(a, b) {
61
112
  return diff === 0;
62
113
  }
63
114
 
115
+ /**
116
+ * Everything middleware.js decides about a request, as one answer: null to let
117
+ * it through, or { status, error } to refuse it. In order: a request addressed
118
+ * to another name (421), a write from another site (403), then, for the routes
119
+ * in DESTRUCTIVE_ROUTES, the gate (gateDecision).
120
+ *
121
+ * One function, so the one route middleware.js leaves alone (the import, whose
122
+ * body must not be buffered first: see middleware.js) makes exactly the same
123
+ * checks itself, before it reads anything.
124
+ *
125
+ * @param {{ method: string, pathname: string, headers: { get(name: string): string | null },
126
+ * env: { SIX_DEGREES_BIND?: string, HOSTNAME?: string, ADMIN_TOKEN?: string } }} request
127
+ */
128
+ export function requestRefusal({ method, pathname, headers, env = {} }) {
129
+ const header = (name) => headers?.get?.(name) ?? null;
130
+ const bind = env.SIX_DEGREES_BIND || env.HOSTNAME;
131
+
132
+ if (isRebound({ bind, host: header('host') })) {
133
+ return { status: 421, error: 'This app only answers to 127.0.0.1 and localhost.' };
134
+ }
135
+ if (isCrossSiteWrite({
136
+ method,
137
+ secFetchSite: header('sec-fetch-site'),
138
+ origin: header('origin'),
139
+ host: header('host'),
140
+ })) {
141
+ return { status: 403, error: 'Cross-site requests are not accepted. This app only answers to pages it serves.' };
142
+ }
143
+ if (!isDestructive(pathname)) return null;
144
+
145
+ const decision = gateDecision({
146
+ bind,
147
+ token: env.ADMIN_TOKEN,
148
+ bearer: (header('authorization') || '').replace(/^Bearer\s+/i, ''),
149
+ });
150
+ return decision.allow ? null : { status: decision.status, error: decision.reason };
151
+ }
152
+
64
153
  /**
65
154
  * @param {{ bind?: string, token?: string, bearer?: string }} ctx
66
155
  * @returns {{ allow: boolean, status?: number, reason: string }}
@@ -60,3 +60,43 @@ export function toIsoDate(text) {
60
60
  if (month < 0 || day < 1 || day > 31) return null;
61
61
  return `${m[3]}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
62
62
  }
63
+
64
+ // Tiers that make a new connection worth a notification of its own.
65
+ const HIGH_VALUE = new Set(['S', 'A']);
66
+
67
+ /**
68
+ * The notifications a refresh of your own connections (1st degree) leaves:
69
+ * how many are new, and one for each new person the model scored S or A.
70
+ *
71
+ * `added` are the records this request inserted: new means new to the
72
+ * database, not missing from a lookup (a re-scan of people already saved
73
+ * used to count everyone past the first hundred as new). `tierOf(url)` is
74
+ * the tier each was stored with by the rescore that follows the insert, so
75
+ * "high-value" is the model's judgement (lib/scoring.js), not a list of
76
+ * words or famous names matched anywhere in the headline, which read
77
+ * "Metadata Engineer" as Meta and "Engineer at Applewood Bakery" as Apple.
78
+ * `checked` is how many records the refresh sent.
79
+ */
80
+ export function refreshNotifications({ added = [], checked = 0, tierOf = () => null, userId = null } = {}) {
81
+ const user_id = userId || null;
82
+ if (!added.length) {
83
+ return [{
84
+ user_id, type: 'refresh_summary', title: 'Network up to date',
85
+ message: `Checked ${checked} connections — no new additions`, icon: '✓',
86
+ }];
87
+ }
88
+ const out = [{
89
+ user_id,
90
+ type: 'refresh_summary',
91
+ title: `${added.length} new connection${added.length > 1 ? 's' : ''} found!`,
92
+ message: added.slice(0, 3).map((r) => r.name).join(', ') + (added.length > 3 ? ` +${added.length - 3} more` : ''),
93
+ icon: '🔄',
94
+ }];
95
+ for (const r of added.filter((a) => HIGH_VALUE.has(tierOf(a.profile_url))).slice(0, 5)) {
96
+ out.push({
97
+ user_id, type: 'new_elite_connection', title: `High-value connection: ${r.name}`,
98
+ message: r.headline?.substring(0, 60), icon: '👑',
99
+ });
100
+ }
101
+ return out;
102
+ }
@@ -0,0 +1,185 @@
1
+ // Paths → Scores: the one-time offer to keep the curated list's old scores as
2
+ // your own, after the list was made neutral (lib/legacy-scores.js has the old
3
+ // scores and why each changed).
4
+ //
5
+ // Who is offered what. The offer opens when rescoreAll() first replaces scores
6
+ // that were computed with the old list (lib/rpc.js), so a database that never
7
+ // had them, a fresh one, never sees it. It then lists each old entry that
8
+ // someone in this network works at now, whose built-in score is different now,
9
+ // and that you haven't scored yourself. A CSV import and the sample aren't
10
+ // scored on the server, so the page doesn't ask while one is open.
11
+ //
12
+ // Keeping writes the old score under every name scoring uses for the entry's
13
+ // companies, as Paths → Scores sets a score (setCompanyScores), then rescores
14
+ // everyone once for the whole batch. Keep or No thanks, it is never offered
15
+ // again, and an offer whose first look finds nothing to offer closes itself.
16
+
17
+ import { getDb } from './db-client.js';
18
+ import { readNetwork, companyScore, KNOWN_COMPANIES } from './scoring.js';
19
+ import { industryKeyOf } from './companies.js';
20
+ import { scoringRows, companyOverrides, setCompanyScores, rescoreAll } from './rpc.js';
21
+ import { LEGACY_SCORES, LEGACY_OFFER_KEY } from './legacy-scores.js';
22
+
23
+ const LISTED = new Set(KNOWN_COMPANIES.map(([name]) => name));
24
+ const OLD = LEGACY_SCORES.map(([name, score, names, industry]) => ({ name, score, names, industry }));
25
+ const OLD_BY_NAME = new Map(OLD.map((e) => [e.name, e]));
26
+ // As lib/scoring.js cleanCompany reads it: a name that says it is a school
27
+ // only matched a school on the list. Copied rather than imported, because this
28
+ // is how the old list matched and must stay so whatever the live rule becomes.
29
+ const SCHOOL_NAME = /\b(school|college|university)\b/;
30
+
31
+ // Most names an answer may carry: the old list had 34 changed entries.
32
+ const MAX_ANSWER = 100;
33
+
34
+ /**
35
+ * The old list's entry that scored a company, found by the name scoring gives
36
+ * the company now (cleanCompany's), or null. A company still on the list keeps
37
+ * its name, so its old entry counts only if it was rescored. A removed entry's
38
+ * names are found by its own names (lib/legacy-scores.js), the way
39
+ * cleanCompany found them: "University of Central Florida" is its own name
40
+ * now, and was UCF's. Another company the old alias also caught ("Hard Rock
41
+ * Hotel" for Hard Rock Digital) isn't that entry's.
42
+ */
43
+ export function legacyEntryFor(name) {
44
+ if (!name) return null;
45
+ if (LISTED.has(name)) return OLD_BY_NAME.get(name) || null;
46
+ const lower = name.toLowerCase();
47
+ const bare = lower.replace(/^the /, '');
48
+ const school = SCHOOL_NAME.test(lower);
49
+ for (const entry of OLD) {
50
+ if (LISTED.has(entry.name) || (school && entry.industry !== 'education')) continue;
51
+ if (entry.names.test(lower) || entry.names.test(bare) || lower === entry.name.toLowerCase()) return entry;
52
+ }
53
+ return null;
54
+ }
55
+
56
+ /**
57
+ * 'open', 'kept', 'declined', 'none' (it opened, and there was nothing in
58
+ * this network to offer), or null when this database was never offered.
59
+ */
60
+ export function legacyOfferState(db = getDb()) {
61
+ return db.prepare('SELECT value FROM app_meta WHERE key = ?').get(LEGACY_OFFER_KEY)?.value ?? null;
62
+ }
63
+
64
+ /**
65
+ * What Paths → Scores offers, or null: nothing when the offer isn't open
66
+ * (never was, or was answered) or no company in the network lost an old score
67
+ * that you haven't replaced with one of your own. That last closes it for
68
+ * good ('none'): the network the old list scored is the one read here, so
69
+ * nothing could turn up later, and every visit to Paths → Scores would
70
+ * otherwise read the whole network again to find nothing.
71
+ *
72
+ * The companies are the ones Paths → Scores lists, those someone works at now,
73
+ * so a kept score shows there as yours and Auto can hand it back. (A name only
74
+ * in former roles would be a score nothing lists; its people's former role
75
+ * counts at 70% and moves them little.) Each is compared by its built-in score
76
+ * now (the list or the estimate, before any sector lean) with the old entry's.
77
+ *
78
+ * @returns null | { companies: [{ name, was, now, estimated, people, names }] },
79
+ * one per old entry, most people first: the score it had (`was`), the
80
+ * built-in score its main name has now (`now`, `estimated` when that comes
81
+ * from the network rather than the list), how many people work there now,
82
+ * and every name it covers, as scoring writes them.
83
+ */
84
+ export function legacyOffer(db = getDb()) {
85
+ if (legacyOfferState(db) !== 'open') return null;
86
+ const rows = scoringRows(db);
87
+ if (!rows.length) return closeEmpty(db);
88
+ const overrides = companyOverrides(db);
89
+ const read = readNetwork(rows, { industryOf: industryKeyOf });
90
+
91
+ const offered = new Map(); // old entry name → what it offers
92
+ const entryOf = new Map(); // company name → that offer
93
+ for (const [name, { headcount, industry }] of read.companies) {
94
+ if (!headcount || overrides.has(name)) continue; // nobody there now, or yours already
95
+ const entry = legacyEntryFor(name);
96
+ if (!entry) continue;
97
+ const now = companyScore(name, { headcount, industry });
98
+ if (now.score === entry.score) continue; // the estimate landed where the list was
99
+ const o = offered.get(entry.name) || { name: entry.name, was: entry.score, names: [], people: new Set() };
100
+ o.names.push({ name, now, headcount });
101
+ offered.set(entry.name, o);
102
+ entryOf.set(name, o);
103
+ }
104
+ if (!offered.size) return closeEmpty(db);
105
+
106
+ // People, each once, across all of an entry's names.
107
+ for (const r of rows) {
108
+ for (const role of read.people.get(r).roles) {
109
+ if (!role.former && entryOf.has(role.company)) entryOf.get(role.company).people.add(r.profile_url || r.id);
110
+ }
111
+ }
112
+ const companies = [...offered.values()].map((o) => {
113
+ // Its main name: where most of its people are (then the first by name).
114
+ const main = o.names.reduce((a, b) => (b.headcount > a.headcount || (b.headcount === a.headcount && b.name < a.name) ? b : a));
115
+ return {
116
+ name: o.name,
117
+ was: o.was,
118
+ now: main.now.score,
119
+ estimated: main.now.source !== 'known',
120
+ people: o.people.size,
121
+ names: o.names.map((n) => n.name).sort(),
122
+ };
123
+ });
124
+ companies.sort((a, b) => b.people - a.people || a.name.localeCompare(b.name));
125
+ return { companies };
126
+ }
127
+
128
+ /** Close an open offer that has nothing to offer; null, as legacyOffer() says it. */
129
+ function closeEmpty(db) {
130
+ db.prepare("UPDATE app_meta SET value = 'none' WHERE key = ? AND value = 'open'").run(LEGACY_OFFER_KEY);
131
+ return null;
132
+ }
133
+
134
+ /**
135
+ * The names an answer keeps, from the body the page sends: {keep: [names]},
136
+ * where an empty list is No thanks. Throws an Error fit to show on anything
137
+ * else.
138
+ */
139
+ export function parseLegacyAnswer(body) {
140
+ const keep = body?.keep;
141
+ if (!Array.isArray(keep) || keep.length > MAX_ANSWER || !keep.every((n) => typeof n === 'string' && n.length <= 200)) {
142
+ throw new Error('Send the companies to keep as a list of names (an empty list for No thanks).');
143
+ }
144
+ return keep;
145
+ }
146
+
147
+ /**
148
+ * Answer the offer: keep, as your own, the old scores of the entries named in
149
+ * `keep` (names from the offer; any other name is ignored), or keep none (No
150
+ * thanks). Either way it is never offered again. The scores and the answer
151
+ * are written together, all or nothing; then everyone is rescored once for
152
+ * the whole batch, and only if something was kept. Answering an offer that
153
+ * isn't open (another window already did) changes nothing.
154
+ *
155
+ * `rescore` is lib/rpc.js rescoreAll; tests pass a counting stand-in.
156
+ * @returns { kept: [{ name, score, names }], scored }
157
+ * @throws when the rescore fails, after the answer is saved: the message says
158
+ * so, `.kept` says what was kept, and the next load of the map rescores.
159
+ */
160
+ export function answerLegacyOffer(keep = [], { db = getDb(), rescore = rescoreAll } = {}) {
161
+ if (legacyOfferState(db) !== 'open') return { kept: [], scored: 0 };
162
+ const wanted = new Set(keep);
163
+ const kept = (legacyOffer(db)?.companies || [])
164
+ .filter((c) => wanted.has(c.name))
165
+ .map((c) => ({ name: c.name, score: c.was, names: c.names }));
166
+ db.exec('BEGIN');
167
+ try {
168
+ setCompanyScores(db, kept.flatMap((c) => c.names.map((n) => [n, c.score])));
169
+ db.prepare('UPDATE app_meta SET value = ? WHERE key = ?').run(kept.length ? 'kept' : 'declined', LEGACY_OFFER_KEY);
170
+ db.exec('COMMIT');
171
+ } catch (err) {
172
+ db.exec('ROLLBACK');
173
+ throw err;
174
+ }
175
+ if (!kept.length) return { kept, scored: 0 };
176
+ try {
177
+ return { kept, scored: rescore().scored };
178
+ } catch (err) {
179
+ // The kept scores are saved and the stored ones are not yet computed with
180
+ // them. Without a model stamp they count as stale, so the next load of the
181
+ // map redoes it (rpc.js rescoreIfStale), as a Settings save's retry does.
182
+ try { db.prepare("DELETE FROM app_meta WHERE key = 'scoring_version'").run(); } catch { /* then the next change to any score rescores */ }
183
+ throw Object.assign(new Error(`Kept, but rescoring your network failed (${err.message}). It will try again the next time the map loads.`), { kept });
184
+ }
185
+ }
@@ -0,0 +1,77 @@
1
+ // The curated company list's scores from before it was made neutral, kept for
2
+ // one thing only: the one-time offer on Paths → Scores to keep them as your
3
+ // own (lib/legacy-offer.js). Nothing scores with these. lib/scoring.js never
4
+ // imports this file, and the model must never read it.
5
+ //
6
+ // The list used to mix well-known companies with picks from one person's
7
+ // career and region (Snap at 9, UCF at 5, a group of 6s from one network),
8
+ // and those picks were everyone's defaults. It now follows a written rule
9
+ // (lib/scoring.js, docs/brain/SCORING.md). Each row here is an entry that the
10
+ // rule removed or rescored, with the score exactly as it was:
11
+ //
12
+ // [name, the score it had, the names it is offered for, its industry]
13
+ //
14
+ // The names and industry are kept because a removed entry no longer
15
+ // canonicalises: "University of Central Florida" used to be read as "UCF" and
16
+ // is now a name of its own. The names are the entry's own company's, as they
17
+ // read once cleaned, and end there ($) unless every company starting so is
18
+ // that company's (UCF's colleges, AdventHealth's hospitals, Havas's
19
+ // agencies). The old aliases were wider and caught other companies too
20
+ // ("Snap-on" and "Specs Optical" read as Snap, "Hard Rock Hotel" as Hard Rock
21
+ // Digital): the old list scored those by mistake, and keeping must not give
22
+ // them its score. Rows are in the old list's order, since the first to match
23
+ // won.
24
+
25
+ export const LEGACY_SCORES = [
26
+ // Rescored: scored like its peers Pinterest, Reddit and X.
27
+ ['Snap', 9, /^(snap|snapchat)( inc)?$/, 'media'],
28
+ // Removed: private companies that are neither household names nor Fortune
29
+ // 500-sized, and small startups.
30
+ ['Anduril', 9, /^anduril( industries)?$/, 'defense'],
31
+ ['Polymarket', 9, /^polymarket$/, 'finance'],
32
+ ['Kalshi', 9, /^kalshi$/, 'finance'],
33
+ ['Databricks', 8, /^databricks$/, 'tech'],
34
+ ['Figma', 8, /^figma$/, 'tech'],
35
+ ['Scale AI', 8, /^(scale ai|scale\.ai|scale\.com)$/, 'tech'],
36
+ ['Whatnot', 8, /^whatnot$/, 'consumer'],
37
+ // Removed: executive search firms, known to executives rather than to most
38
+ // professionals, and two of the field's five big firms (not its largest).
39
+ ['Egon Zehnder', 8, /^egon zehnder( international)?$/, 'consulting'],
40
+ ['Heidrick & Struggles', 8, /^heidrick( (&|and) struggles)?$/, 'consulting'],
41
+ // Rescored: a household name, but a company of a few hundred people, so the
42
+ // list's floor (7) rather than "major" (8).
43
+ ['MrBeast', 8, /^(mrbeast|beast industries)$/, 'media'],
44
+ // Removed: startups and mid-size companies known inside one field.
45
+ ['Whop', 7, /^whop$/, 'tech'],
46
+ ['Hims & Hers', 7, /^hims( (&|and) hers( health)?)?$/, 'health'],
47
+ ['Riot Games', 7, /^riot games$/, 'entertainment'],
48
+ ['Notion', 7, /^notion( labs)?$/, 'tech'],
49
+ ['Vercel', 7, /^vercel$/, 'tech'],
50
+ ['Plaid', 7, /^plaid$/, 'finance'],
51
+ ['Brex', 7, /^brex$/, 'finance'],
52
+ ['Ramp', 7, /^ramp$/, 'finance'],
53
+ ['Mercury', 7, /^mercury$/, 'finance'],
54
+ ['Chime', 7, /^chime$/, 'finance'],
55
+ // Removed: the only military branch on the list (no Army, Navy, Air Force or
56
+ // Marines), so a regional pick rather than a rule.
57
+ ['U.S. Space Force', 7, /^((u\.?s\.?|united states) )?space force$/, 'defense'],
58
+ // Removed: the "notable" 6s, drawn from one network and one region, and the
59
+ // one "local institution".
60
+ ['Grindr', 6, /^grindr$/, 'tech'],
61
+ ['Genius Sports', 6, /^genius sports( group)?$/, 'entertainment'],
62
+ ['Later', 6, /^later$/, 'media'],
63
+ ['Hard Rock Digital', 6, /^hard rock digital$/, 'entertainment'],
64
+ ['Vanta', 6, /^vanta$/, 'tech'],
65
+ ['Kaseya', 6, /^kaseya$/, 'tech'],
66
+ ['Havas', 6, /^havas\b/, 'media'],
67
+ ['VaynerMedia', 6, /^vayner ?media$/, 'media'],
68
+ ['AdventHealth', 6, /^adventhealth\b/, 'health'],
69
+ ['University of Florida', 6, /^(uf|university of florida)$/, 'education'],
70
+ ['The Athletic', 6, /^the athletic( media( company)?)?$/, 'media'],
71
+ ['UCF', 5, /^(ucf|university of central florida)\b/, 'education'],
72
+ ];
73
+
74
+ // Where the offer's state lives in app_meta. lib/rpc.js rescoreAll() sets it
75
+ // to 'open' the first time it replaces scores that came from the old list;
76
+ // answering sets it to 'kept' or 'declined', and nothing sets it back.
77
+ export const LEGACY_OFFER_KEY = 'legacy_scores_offer';