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
@@ -12,12 +12,14 @@
12
12
  //
13
13
  // What this removes: installing Node, cloning, npm install, and typing a
14
14
  // command. The Node runtime is bundled, so the app has no prerequisites at all
15
- // for the CSV path — download, double-click, drop in your export.
15
+ // for the CSV path — download, double-click, drop in your export. The Electron
16
+ // app also carries the scanner's own Python with its packages installed
17
+ // (bundlePython, DESKTOP.md D2), so scanning needs no install step either.
16
18
  //
17
- // What it does NOT remove: Python and Google Chrome, which the scraper needs.
18
- // Those stay runtime prerequisites and the Scan page already detects both.
19
- // Bundling a Chromium instead would make the scraper MORE detectable, not less,
20
- // which defeats the point of driving the user's real browser.
19
+ // What it does NOT remove: Google Chrome, which the scanner drives. It stays a
20
+ // runtime prerequisite and the Scan page already detects it. Bundling a
21
+ // Chromium instead would make the scanner MORE detectable, not less, which
22
+ // defeats the point of driving the user's real browser.
21
23
  //
22
24
  // Unsigned, because notarisation needs a paid Apple Developer account. macOS
23
25
  // will refuse the first launch; the user allows it once in System Settings.
@@ -25,13 +27,24 @@
25
27
  // separate "app is damaged" failure that unsigned arm64 binaries otherwise hit.
26
28
 
27
29
  import { execFileSync } from 'node:child_process';
28
- import { mkdirSync, rmSync, cpSync, writeFileSync, existsSync, chmodSync, readFileSync, symlinkSync, lstatSync, renameSync, readlinkSync } from 'node:fs';
30
+ import { createHash } from 'node:crypto';
31
+ import {
32
+ mkdirSync, rmSync, cpSync, writeFileSync, existsSync, chmodSync, readFileSync, symlinkSync, lstatSync, renameSync,
33
+ readlinkSync, readdirSync, openSync, readSync, closeSync, realpathSync,
34
+ } from 'node:fs';
29
35
  import path from 'node:path';
30
36
  import os from 'node:os';
31
37
  import { fileURLToPath } from 'node:url';
38
+ import {
39
+ standaloneBuild, downloadVerified, machoSlices, machoSlice, megabytes, IMPORTS,
40
+ } from '../lib/scanner-python.js';
32
41
 
33
42
  const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
34
43
  const pkg = JSON.parse(readFileSync(path.join(ROOT, 'package.json'), 'utf8'));
44
+ // The oldest macOS the app runs on: the bundled Node 24 needs 13.5 (Electron
45
+ // itself needs 13). Written into Info.plist, and every program in the bundled
46
+ // Python is checked against it (checkPython).
47
+ const MACOS_MIN = '13.5';
35
48
 
36
49
  const NODE_VERSION = process.env.BUNDLE_NODE || 'v24.21.0';
37
50
  const ARCH = process.env.BUNDLE_ARCH || (os.arch() === 'x64' ? 'x64' : 'arm64');
@@ -134,9 +147,13 @@ try {
134
147
  for (const rel of untracked) console.log(` ${rel}`);
135
148
  }
136
149
  } catch { /* not a git checkout: nothing to compare against */ }
137
- // The scraper is not part of the standalone output but the Scan page runs it.
150
+ // The files the app runs from scripts/, copied by name so the app never
151
+ // depends on what Next's tracing happens to bring along (TRAPS §27): the
152
+ // scanner the Scan page runs, and apply-update.sh, the in-app updater's helper.
153
+ // The helper ships inside the app, so the version that swaps the app is the one
154
+ // the user is running, never a script fetched from the main branch.
138
155
  for (const rel of ['scripts/scrape.py', 'scripts/image_store.py', 'scripts/requirements.txt',
139
- 'scripts/score_new_connections.sql', 'scripts/audit-avatars.mjs']) {
156
+ 'scripts/audit-avatars.mjs', 'scripts/apply-update.sh']) {
140
157
  const from = path.join(ROOT, rel);
141
158
  if (existsSync(from)) {
142
159
  mkdirSync(path.join(SERVER_DIR, path.dirname(rel)), { recursive: true });
@@ -171,6 +188,8 @@ export SIX_DEGREES_BIND=127.0.0.1
171
188
  export NEXT_TELEMETRY_DISABLED=1
172
189
  export SIX_DEGREES_ROOT="$HERE/app"
173
190
  export SIX_DEGREES_INSTALL=mac-app
191
+ # This app's own bundle, for the in-app updater (lib/updater.js runningBundle).
192
+ export SIX_DEGREES_APP="$(cd "$HERE/../.." && pwd)"
174
193
  export HOSTNAME=127.0.0.1
175
194
 
176
195
  # Walk up from 6363 so a second copy does not fight the first.
@@ -308,7 +327,7 @@ async function buildElectronShell() {
308
327
  darwinDarkModeSupport: true,
309
328
  // The bundled Node 24 needs macOS 13.5 (Electron itself needs 13), so say
310
329
  // so, and macOS explains it instead of the app failing to start.
311
- extendInfo: { LSMinimumSystemVersion: '13.5' },
330
+ extendInfo: { LSMinimumSystemVersion: MACOS_MIN },
312
331
  osxSign: false, // signed ad hoc below, after everything is in place
313
332
  });
314
333
  console.log(` Electron ${electronVersion} (${ARCH})`);
@@ -316,14 +335,237 @@ async function buildElectronShell() {
316
335
  renameSync(path.join(built, `${APP_NAME}.app`), APP);
317
336
  rmSync(STAGE, { recursive: true, force: true });
318
337
 
338
+ // Put straight into the finished app, so its links are made where they stay
339
+ // and nothing copies them again (TRAPS §37).
340
+ await bundlePython(path.join(APP, 'Contents', 'Resources'));
341
+
319
342
  step('Signing (ad-hoc)');
320
343
  // Electron's framework and helpers must all carry the same kind of signature,
321
344
  // or macOS refuses to load them; re-signing the whole bundle ad hoc does that.
345
+ // --deep reaches nested bundles, not loose programs and libraries in
346
+ // Resources: the Python's were signed one by one first (signPython), and this
347
+ // seals them into the app's signature with everything else.
322
348
  run('codesign', ['--force', '--deep', '--sign', '-', APP]);
323
349
  run('codesign', ['--verify', '--deep', '--strict', APP]);
324
350
  console.log(' signed ad-hoc and verified — no Apple account needed, still unnotarised');
325
351
  }
326
352
 
353
+ // ---- the scanner's Python (Electron app only; DESKTOP.md D2) ------------------
354
+ // A standalone CPython from python-build-standalone, with the scanner's packages
355
+ // installed, at Contents/Resources/python. desktop/main.mjs names it to the
356
+ // server (SIX_DEGREES_PYTHON), which runs the scanner on it before any other
357
+ // Python: no install step, and scanning works offline.
358
+ //
359
+ // Pinned twice: the Python by the release and SHA-256 in lib/scanner-python.js,
360
+ // the packages by the hash of every file in scripts/requirements.txt. Downloads
361
+ // are cached in ~/.cache/six-degrees-build like Node's, and checked every build.
362
+ // Built on a Mac with the same chip (release.yml has one job per chip): pip runs
363
+ // this Python to install into it.
364
+ async function bundlePython(resources) {
365
+ const build = standaloneBuild(`darwin-${ARCH}`);
366
+ const xy = build.version.split('.').slice(0, 2).join('.');
367
+ const py = path.join(resources, 'python');
368
+ const lib = path.join(py, 'lib', `python${xy}`);
369
+
370
+ step(`Fetching Python ${build.version} (${ARCH})`);
371
+ const archive = path.join(CACHE, build.file);
372
+ if (existsSync(archive) && sha256File(archive) === build.sha256) {
373
+ console.log(' using the cached download (its SHA-256 matches the pinned one)');
374
+ } else {
375
+ rmSync(archive, { force: true });
376
+ await downloadVerified(build.url, archive, { sha256: build.sha256, size: build.size, idleMs: 60000 });
377
+ console.log(` downloaded ${megabytes(build.size)} MB; its SHA-256 matches the pinned one`);
378
+ }
379
+ rmSync(py, { recursive: true, force: true });
380
+ run('tar', ['-xzf', archive, '-C', resources]);
381
+ const python = path.join(py, 'bin', 'python3');
382
+ // Only this Python's own packages and settings: nothing from the build
383
+ // machine's user site-packages, PYTHONPATH or pip configuration.
384
+ const env = {
385
+ HOME: os.homedir(), TMPDIR: os.tmpdir(), PATH: '/usr/bin:/bin:/usr/sbin:/sbin', LANG: 'en_US.UTF-8',
386
+ PYTHONNOUSERSITE: '1', PYTHONDONTWRITEBYTECODE: '1',
387
+ };
388
+ try {
389
+ execFileSync(python, ['-c', 'pass'], { env, stdio: 'ignore' });
390
+ } catch {
391
+ console.error(`\n ✗ The ${ARCH} Python can't run on this Mac. Build on a Mac with that chip (release.yml does), or install Rosetta for an Intel build.\n`);
392
+ process.exit(1);
393
+ }
394
+
395
+ step('Installing the scanner\'s packages into it');
396
+ run(python, ['-s', '-m', 'pip', '--isolated', 'install', '--disable-pip-version-check', '--no-input',
397
+ '--no-warn-script-location', '--no-compile', '--index-url', 'https://pypi.org/simple',
398
+ '--cache-dir', path.join(CACHE, 'pip'), '--require-hashes', '--only-binary', ':all:',
399
+ '-r', path.join(ROOT, 'scripts', 'requirements.txt')], { env });
400
+ run(python, ['-s', '-m', 'pip', '--isolated', 'check', '--disable-pip-version-check'], { env });
401
+
402
+ const full = duKb(py);
403
+ trimPython(py, lib, xy);
404
+ const trimmed = duKb(py);
405
+ const shared = shareDriverNode(lib, resources);
406
+ signPython(py);
407
+ checkPython(py, lib, env);
408
+ console.log(` Python ${build.version} and the scanner's packages: ${Math.round(full / 1024)} MB installed, `
409
+ + `${Math.round(trimmed / 1024)} MB after trimming, ${Math.round(duKb(py) / 1024)} MB in the app`
410
+ + (shared ? ' (Playwright runs on the app\'s own Node)' : ''));
411
+ }
412
+
413
+ function sha256File(file) {
414
+ return createHash('sha256').update(readFileSync(file)).digest('hex');
415
+ }
416
+
417
+ function duKb(dir) {
418
+ return Number.parseInt(execFileSync('du', ['-sk', dir]).toString(), 10) || 0;
419
+ }
420
+
421
+ // Every file under `dir`, not following links.
422
+ function* walk(dir) {
423
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
424
+ const at = path.join(dir, entry.name);
425
+ if (entry.isDirectory()) yield* walk(at);
426
+ else yield { at, entry };
427
+ }
428
+ }
429
+
430
+ // The Mach-O programs and libraries under `dir`, as { file, slices, head }.
431
+ function machoFiles(dir) {
432
+ const found = [];
433
+ for (const { at, entry } of walk(dir)) {
434
+ if (!entry.isFile()) continue;
435
+ const head = readAt(at, 0, 65536);
436
+ const slices = machoSlices(head);
437
+ if (slices.length) found.push({ file: at, slices });
438
+ }
439
+ return found;
440
+ }
441
+
442
+ function readAt(file, offset, length) {
443
+ const fd = openSync(file, 'r');
444
+ try {
445
+ const buf = Buffer.alloc(length);
446
+ const n = readSync(fd, buf, 0, length, offset);
447
+ return buf.subarray(0, n);
448
+ } finally {
449
+ closeSync(fd);
450
+ }
451
+ }
452
+
453
+ // What the scanner never uses, taken out to keep the download small. None of
454
+ // it is imported by scrape.py or its packages (checkPython proves the imports
455
+ // still load, and CI runs the scanner's pure functions on this Python):
456
+ // - bin/ keeps only the interpreter. pip's console scripts (idna, normalizer,
457
+ // playwright) name the build machine's path in their first line, and pip
458
+ // itself goes below: nothing installs into the app once it is built.
459
+ // - IDLE, tkinter and Tcl/Tk, turtle, lib2to3, ensurepip, the C headers and
460
+ // build files, man pages, test suites, and bytecode: without
461
+ // __pycache__ a module is compiled as it is imported (about 0.3 s for the
462
+ // scanner's), and ownPythonEnv never lets it write any into the app.
463
+ // - libpython's shared library: the interpreter has Python built in, so it is
464
+ // kept only if something inside links to it.
465
+ function trimPython(py, lib, xy) {
466
+ step('Trimming what the scanner never uses');
467
+ const keepInBin = new Set([`python${xy}`, 'python3', 'python']);
468
+ for (const name of readdirSync(path.join(py, 'bin'))) {
469
+ if (!keepInBin.has(name)) rmSync(path.join(py, 'bin', name), { recursive: true, force: true });
470
+ }
471
+ const remove = [
472
+ 'include', 'share', 'lib/pkgconfig',
473
+ ...readdirSync(path.join(py, 'lib')).filter((n) => /^(libtcl|libtk|tcl|tk|itcl|thread)/.test(n)).map((n) => `lib/${n}`),
474
+ ...['idlelib', 'tkinter', 'turtledemo', 'turtle.py', 'lib2to3', 'ensurepip', 'test', 'idle_test', `config-${xy}-darwin`]
475
+ .map((n) => path.relative(py, path.join(lib, n))),
476
+ ...readdirSync(path.join(lib, 'lib-dynload')).filter((n) => n.startsWith('_tkinter.')).map((n) => path.relative(py, path.join(lib, 'lib-dynload', n))),
477
+ ...readdirSync(path.join(lib, 'site-packages')).filter((n) => n === 'pip' || /^pip-.*\.dist-info$/.test(n))
478
+ .map((n) => path.relative(py, path.join(lib, 'site-packages', n))),
479
+ ];
480
+ for (const rel of remove) rmSync(path.join(py, rel), { recursive: true, force: true });
481
+ for (const dir of [...findDirs(py, (n) => n === '__pycache__'), ...findDirs(path.join(lib, 'site-packages'), (n) => n === 'tests')]) {
482
+ rmSync(dir, { recursive: true, force: true });
483
+ }
484
+ const libpython = path.join(py, 'lib', `libpython${xy}.dylib`);
485
+ if (existsSync(libpython)) {
486
+ const users = machoFiles(py).filter((m) => m.file !== libpython
487
+ && m.slices.some((s) => (machoSlice(readAt(m.file, s.offset, 65536)) || { dylibs: [] }).dylibs.some((d) => d.endsWith(`/libpython${xy}.dylib`))));
488
+ if (users.length) console.log(` keeping libpython${xy}.dylib: ${path.relative(py, users[0].file)} links to it`);
489
+ else rmSync(libpython);
490
+ }
491
+ }
492
+
493
+ function findDirs(dir, match) {
494
+ const found = [];
495
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
496
+ if (!entry.isDirectory()) continue;
497
+ const at = path.join(dir, entry.name);
498
+ if (match(entry.name)) found.push(at);
499
+ else found.push(...findDirs(at, match));
500
+ }
501
+ return found;
502
+ }
503
+
504
+ // Playwright's driver carries its own Node (about 120 MB). When that is byte
505
+ // for byte the Node the app already bundles for its server (Playwright 1.63
506
+ // ships Node 24.21.0, the version build-app pins), the driver's becomes a link
507
+ // to the app's: the same program, about 40 MB less to download. When they
508
+ // differ, the driver keeps its own. Resolves whether it shared.
509
+ function shareDriverNode(lib, resources) {
510
+ const driverNode = path.join(lib, 'site-packages', 'playwright', 'driver', 'node');
511
+ const appNode = path.join(resources, 'node');
512
+ if (!existsSync(driverNode) || !existsSync(appNode)) return false;
513
+ if (sha256File(driverNode) !== sha256File(appNode)) {
514
+ console.log(' Playwright\'s Node differs from the app\'s, so the driver keeps its own');
515
+ return false;
516
+ }
517
+ rmSync(driverNode);
518
+ symlinkSync(path.relative(path.dirname(driverNode), appNode), driverNode);
519
+ if (realpathSync(driverNode) !== realpathSync(appNode)) throw new Error('The driver\'s link to the app\'s Node is wrong.');
520
+ return true;
521
+ }
522
+
523
+ // Every program and library in the Python, signed ad hoc like the rest of the
524
+ // app. On Apple Silicon unsigned code isn't allowed to run at all.
525
+ function signPython(py) {
526
+ const files = machoFiles(py).map((m) => m.file);
527
+ for (let i = 0; i < files.length; i += 64) {
528
+ // Its only chatter is "replacing existing signature", once a file.
529
+ mustRun('codesign', ['--force', '--sign', '-', ...files.slice(i, i + 64)], process.env, 'Signing the app\'s Python failed');
530
+ }
531
+ console.log(` signed ${files.length} programs and libraries ad hoc`);
532
+ }
533
+
534
+ // The Python as the app will run it: every program is for this chip and this
535
+ // app's oldest macOS, the imports load (the compiled parts too: TRAPS §30), and
536
+ // Playwright's driver starts. Any failure fails the build.
537
+ function checkPython(py, lib, env) {
538
+ step('Checking the Python inside the app');
539
+ const newer = (v) => {
540
+ const [a, b] = [String(v).split('.').map(Number), MACOS_MIN.split('.').map(Number)];
541
+ return (a[0] - b[0] || (a[1] || 0) - (b[1] || 0)) > 0;
542
+ };
543
+ for (const m of machoFiles(py)) {
544
+ const slice = m.slices.find((s) => s.arch === ARCH);
545
+ const rel = path.relative(py, m.file);
546
+ if (!slice) throw new Error(`${rel} isn't built for ${ARCH}.`);
547
+ const info = machoSlice(readAt(m.file, slice.offset, 65536));
548
+ if (info?.minos && newer(info.minos)) throw new Error(`${rel} needs macOS ${info.minos}; the app promises ${MACOS_MIN}.`);
549
+ }
550
+ const python = path.join(py, 'bin', 'python3');
551
+ console.log(` ${mustRun(python, ['-s', '-c', `${IMPORTS}; import sys; from importlib.metadata import version as v; `
552
+ + 'print("Python", sys.version.split()[0], "· Playwright", v("playwright"), "· requests", v("requests"), "· Pillow", v("pillow"))'],
553
+ env, 'The scanner\'s packages don\'t load in the app\'s Python')}`);
554
+ const driver = path.join(lib, 'site-packages', 'playwright', 'driver');
555
+ console.log(` Playwright's driver starts: ${mustRun(path.join(driver, 'node'), [path.join(driver, 'package', 'cli.js'), '--version'],
556
+ env, 'Playwright\'s driver doesn\'t start')}`);
557
+ }
558
+
559
+ // What a check printed, or the build stops with what went wrong.
560
+ function mustRun(file, args, env, what) {
561
+ try {
562
+ return execFileSync(file, args, { env, stdio: ['ignore', 'pipe', 'pipe'] }).toString().trim();
563
+ } catch (err) {
564
+ console.error(`\n ✗ ${what}:\n${String(err.stderr || err.message).trim().split('\n').slice(-8).map((l) => ` ${l}`).join('\n')}\n`);
565
+ process.exit(1);
566
+ }
567
+ }
568
+
327
569
  // The app icon, from an SVG: sharp (already here, Next depends on it) draws it
328
570
  // at 1024px, sips makes the sizes macOS wants, iconutil packs them.
329
571
  async function makeIcns(svg, out) {
@@ -1,4 +1,106 @@
1
- playwright>=1.40.0
2
- requests>=2.31.0
3
- Pillow>=10.0.0
4
- tzdata>=2024.1; sys_platform == "win32"
1
+ # The scanner's Python packages (scripts/scrape.py), pinned to exact versions,
2
+ # with the SHA-256 of every file pip may install for each. The same file goes
3
+ # into the Python inside the Mac app when it is built (scripts/build-app.mjs),
4
+ # and into the scanner's own environment when the Scan page sets one up.
5
+ #
6
+ # To change a version, edit its line and run
7
+ # node scripts/pin-python-packages.mjs
8
+ # which rewrites the hashes from PyPI. Every dependency is listed: once hashes
9
+ # are used, pip refuses anything that isn't.
10
+
11
+ --only-binary :all:
12
+ --require-hashes
13
+
14
+ # Drives the user's own Google Chrome (channel="chrome"), so no browser is downloaded.
15
+ # Its wheel carries Playwright's driver, which it needs.
16
+ playwright==1.63.0 \
17
+ --hash=sha256:354e15b29503565fc598b89f16fbe070459343bef9d7498a93e304864000c6a7 \
18
+ --hash=sha256:84c540759e8e7f01e690e197e04060f48899d37cea322299f255843273d3385d \
19
+ --hash=sha256:ad21bc07516b187965a7521c5cf0df0bd657b17482eaad74335272d35a2b07de \
20
+ --hash=sha256:c89fc4736502a1f0fac2c8ca5d10c0cbc1c669f1f4774a2d8507a43140e4d53f \
21
+ --hash=sha256:fd1aa00631d44d55e56e0975bf3f3f285fac4a9fd2813183f0c12a488f1a1b24
22
+
23
+ # Playwright's own dependencies: greenlet, pyee, and pyee's typing-extensions.
24
+ greenlet==3.5.6 \
25
+ --hash=sha256:1c20ea32a73d17b9b60e3371240e17b0068120c98a5ec01a224a7dd8c89733ba \
26
+ --hash=sha256:45bfd2b51e38aaa5f9849f114d9c7c1d75f69187c849b3549cd64c465283abfa \
27
+ --hash=sha256:8cddea1b8339451c2fb3388e138347b6126744f33b611bdb55b7357361cfef46 \
28
+ --hash=sha256:95e7c44d072db623a1aab04ce488cf9533294a77ed9d072cd503a3596f4106ac \
29
+ --hash=sha256:975736b002ed080d124cf81a79cb7e05cb26d6b3f5c7a7b651c0fcce70353aa1 \
30
+ --hash=sha256:9a09d59bef1db94f384b5bcc2d523694d338f3df6b757aeeaf7baca5d0c0be88 \
31
+ --hash=sha256:a5876d0a60355af98d535c47f6cd6eb0f8a432396dab26845d380b92f8412422 \
32
+ --hash=sha256:a6a4b98a9132e0f45c9fc245a63894cfd8c45fb7a0d6bffc5eab3ec327cf7324 \
33
+ --hash=sha256:b7d501d5eb5d4f67207df364752ad697465b834268744be7581c18d81d35d41d \
34
+ --hash=sha256:c59acfa8eb73a1e0d484392dc002bdf001fd4ce73394e0132df3d1ab6093d7cb \
35
+ --hash=sha256:c75116c9de79949de23006e2d9b35ee82874c594fcf5c0311b439acaa14b8441 \
36
+ --hash=sha256:e85880b538e59a59f55117b81f208a6660ad5ac328aad9305f812d9b8bc67a0f \
37
+ --hash=sha256:eed88b64a5e5da72d6a71cdc5aaeefaa5ced9b748f8d19f89800b339961dad39 \
38
+ --hash=sha256:f96f0e30b5a95c7631b12bfe214cbc90ec8fe8cfa36920596c10514a65743519 \
39
+ --hash=sha256:f98e8215e172f567ce80eeaed9107fb4d32b6c44f26983d9b8334658136a205a
40
+
41
+ pyee==13.0.1 \
42
+ --hash=sha256:af2f8fede4171ef667dfded53f96e2ed0d6e6bd7ee3bb46437f77e3b57689228
43
+
44
+ typing-extensions==4.16.0 \
45
+ --hash=sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8
46
+
47
+ requests==2.34.2 \
48
+ --hash=sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0
49
+
50
+ # requests' own dependencies: certifi, charset-normalizer, idna, urllib3.
51
+ certifi==2026.7.22 \
52
+ --hash=sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775
53
+
54
+ charset-normalizer==3.5.1 \
55
+ --hash=sha256:15f024313246a4ed976c60f440bb8d257815513a681d212ff74fd46f7d715a90 \
56
+ --hash=sha256:2f06b7eae9dbe77fe1d644ca244dad508de8d302870a43f3c559b521270938a0 \
57
+ --hash=sha256:41876ee62a3dddf48ff1121ad8f0798032aa03f2fd35f21f34a4cab14f18d8d2 \
58
+ --hash=sha256:4582c27e8c889d64811987b5967fbd3ae0c823fe1fd933b543d55ac20bb475fa \
59
+ --hash=sha256:5b6d1386bf0096d26d3a863dc0a487a5b4eb9aa93cf5ba69683d29dde6b9d60f \
60
+ --hash=sha256:62b55f6722735a6c472f88361cde6640608773d9443cebdbb51abf436a1fcdd3 \
61
+ --hash=sha256:6df0ec430f9a831772c23ca5a224cba36517a58a84bb32c32bb59a9fa67c47f6 \
62
+ --hash=sha256:94d78ecec2605a8d0398b0f365d5f12a63248438516f5dac536a5eff7337df4a \
63
+ --hash=sha256:96eefc178f8636b9c760c5829345307fd81cfae9ab1e80997dbddeb0f54ee9a3 \
64
+ --hash=sha256:a6dac12ff6b846103483683f60c5f8fee205121adc58ffd87e90a90a3af69e99 \
65
+ --hash=sha256:aa2bb0b37202dca27175591f761108b5d34096ade1191ffe4808bdf6b1571488 \
66
+ --hash=sha256:b9af956078716df40d985fb0dfeb2c2120c5ca92ba4ff4b388acfd01cdc14d08 \
67
+ --hash=sha256:c428c6c31eb5f4277d7f8eccaf767fbd548ddd5ce3c8b4f4cbbfab3d96b5904c \
68
+ --hash=sha256:c7b742bf31c88566b4bb6335a7f393bb322e580b6bb98df7bd0c25e6e3519ce8 \
69
+ --hash=sha256:cee5dd7c6fb5dd52a0fe2a740f9bc6e3593f5f8b1788bde49de02086f30182b2 \
70
+ --hash=sha256:d1ee1e296209fdce05b81b663250eefa02213a2da7b41bf26f7829b8ba3545aa \
71
+ --hash=sha256:e90251c0c7bdd54a100a0dce3c07b7e637278c93af29dbf78ebb89a58c4bac7d \
72
+ --hash=sha256:e9fbdce1e47394b09bc9f26ab117dfc8d6491977a11d86f592bb42c779db2fda \
73
+ --hash=sha256:eda059b6bc8bc0812d626fd91a7ce01bf583df0a61296eff390fd94141a34e30
74
+
75
+ idna==3.20 \
76
+ --hash=sha256:ab7ae7122974553370f0bdb919e1a960b2cd1bc1ef0276416d896db81c14582c
77
+
78
+ urllib3==2.8.0 \
79
+ --hash=sha256:0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3
80
+
81
+ # Profile photos (scripts/image_store.py).
82
+ pillow==12.3.0 \
83
+ --hash=sha256:00808c5e14ef63ac5161091d242999076604ff74b883423a11e5d7bbb38bf756 \
84
+ --hash=sha256:0847a763afefb695bc912d7c131e7e0632d4edc1d8698f58ddabec8e46b8b6d3 \
85
+ --hash=sha256:23d27a3e0307ec2244cc51e7287b919aa68d097504ebe19df4e76a98a3eea5bd \
86
+ --hash=sha256:251bf95b67017e27b13d82f5b326234ca62d70f9cf4c2b9032de2358a3b12c7b \
87
+ --hash=sha256:37d6d0a00072fd2948eb22bce7e1475f34569d90c87c59f7a2ec59541b77f7a6 \
88
+ --hash=sha256:5594fc43d548a7ed94949d139aa1341b270f1863f11cfd37f5a6c8b778a6b67f \
89
+ --hash=sha256:6c0016e7b354317c4e9e525b937ac8596c38d2d232b419529b9cd7a1cd46e39a \
90
+ --hash=sha256:78cb2c6865a35ab8ff8b75fd122f6033b92a62c82801110e48ddd6c936a45d91 \
91
+ --hash=sha256:7a743ff716f746fc19a9557f60dab1600d4613255f8a7aeb3cdde4db7eb15a66 \
92
+ --hash=sha256:ba09209fbe443b4acccebe845d8a138b89a8f4fbaeedd44953490b5315d5e965 \
93
+ --hash=sha256:ba54cfebe86920a559a7c4d6b9050791c20513650a1952ebe3368c7dc70306f8 \
94
+ --hash=sha256:bcb46e2f9feff8d06323983bd83ed00c201fdcab3d74973e7072a889b3979fcd \
95
+ --hash=sha256:bcc33feacfaefce60c12fd500a277533bdc02b10a19f7f6d348763d8140bbba7 \
96
+ --hash=sha256:d69141514cc30b774ceea5e3ed3a6635c8d8a96edf664689b890f4089111fb35 \
97
+ --hash=sha256:d9c7f76c0673154f044e9d78c8655fb4213f6ca31a836df48b40fe5d187717b9 \
98
+ --hash=sha256:e158cb00350dc278f3b91551101aa7d12415a66ebf2c91d8d5ac14e56ddd3ad0 \
99
+ --hash=sha256:e9aeb04d6aef139de265b29683e119b638208f88cf73cdd1658aa07221165321 \
100
+ --hash=sha256:f0606c8bf2cdefea14a43530f7657cbbb7ecf1c4222512492ef4a4434a9501ec \
101
+ --hash=sha256:f7401aebd7f581d7f83a439d87d474999317ee099218e5ad25d125290990ba65 \
102
+ --hash=sha256:ffd0c5368496f41b0944be820fcb7a838aa6e623d250b01acf2643939c3f99d7
103
+
104
+ # Windows only: Python there has no time zone database (docs/brain/DESKTOP.md D3).
105
+ tzdata==2026.4 ; sys_platform == "win32" \
106
+ --hash=sha256:c2169a8b0a7a5e9674da5a135ccdfb2b3e671b333ed9fed17b41f73c34476e81
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env node
2
+ // A pretend GitHub release, served on 127.0.0.1, for testing the Mac app's
3
+ // in-app update without GitHub: tests/updater-job.test.mjs, a browser check of
4
+ // Settings → Updates, and a CI step that updates one build to the next.
5
+ //
6
+ // node scripts/test-release-server.mjs --dir DIR [--version X.Y.Z] [--port 3303]
7
+ //
8
+ // It serves every .dmg in DIR as that version's release assets, with a
9
+ // SHA256SUMS made from them, the way release.yml publishes a release:
10
+ //
11
+ // GET /repos/<owner/repo>/releases/latest the release, as GitHub describes it
12
+ // GET /<owner/repo>/releases/download/v<version>/<name> an asset
13
+ //
14
+ // Point the app at it with SIX_DEGREES_TEST_RELEASES=<the address it prints>.
15
+ // The app honours that only for a 127.0.0.1 address (lib/updater.js
16
+ // releaseSource). It is never set by the app, and nothing here ships in it.
17
+
18
+ import { createServer } from 'node:http';
19
+ import { createHash } from 'node:crypto';
20
+ import fs from 'node:fs';
21
+ import path from 'node:path';
22
+ import { fileURLToPath } from 'node:url';
23
+
24
+ /**
25
+ * Start the server. `release` (also settable later with setRelease) is:
26
+ * version '0.2.2'
27
+ * assets [{ name, data: Buffer } | { name, file: path }]
28
+ * sums the SHA256SUMS text; computed from the assets when left out, and
29
+ * null for a release published without one
30
+ * stall an asset name: send half of it, then nothing (a dead connection)
31
+ * prerelease, draft, tag as GitHub would say them
32
+ * Every request is recorded in `requests` (method and path).
33
+ */
34
+ export async function startTestReleaseServer({ slug, port = 0, release = null } = {}) {
35
+ if (!/^[\w.-]+\/[\w.-]+$/.test(String(slug))) throw new Error(`Not an owner/repo: ${slug}`);
36
+ let current = release;
37
+ const requests = [];
38
+ const open = new Set();
39
+
40
+ const bytesOf = (asset) => (asset.data ? asset.data : fs.readFileSync(asset.file));
41
+ const sizeOf = (asset) => (asset.data ? asset.data.length : fs.statSync(asset.file).size);
42
+ const sumsOf = (r) => {
43
+ if (r.sums === null) return null;
44
+ if (typeof r.sums === 'string') return r.sums;
45
+ return r.assets.map((a) => `${createHash('sha256').update(bytesOf(a)).digest('hex')} ${a.name}`).join('\n') + '\n';
46
+ };
47
+ const tagOf = (r) => r.tag || `v${r.version}`;
48
+
49
+ const server = createServer((req, res) => {
50
+ try {
51
+ handle(req, res);
52
+ } catch (err) {
53
+ // A file that went missing, say: answer like a failing server, don't die.
54
+ if (!res.headersSent) res.writeHead(500, { 'Content-Type': 'text/plain' });
55
+ res.end(String(err.message));
56
+ }
57
+ });
58
+ function handle(req, res) {
59
+ requests.push(`${req.method} ${req.url}`);
60
+ const r = current;
61
+ const url = new URL(req.url, 'http://127.0.0.1');
62
+ const send = (status, body, type = 'application/json') => {
63
+ res.writeHead(status, { 'Content-Type': type, 'Content-Length': Buffer.byteLength(body) });
64
+ res.end(body);
65
+ };
66
+ if (!r) return send(404, JSON.stringify({ message: 'Not Found' }));
67
+ const origin = `http://127.0.0.1:${server.address().port}`;
68
+ const base = `${origin}/${slug}/releases/download/${encodeURIComponent(tagOf(r))}/`;
69
+ const sums = sumsOf(r);
70
+
71
+ if (url.pathname === `/repos/${slug}/releases/latest`) {
72
+ const assets = r.assets.map((a) => ({ name: a.name, size: sizeOf(a), browser_download_url: base + encodeURIComponent(a.name) }));
73
+ if (sums !== null) assets.push({ name: 'SHA256SUMS', size: Buffer.byteLength(sums), browser_download_url: `${base}SHA256SUMS` });
74
+ return send(200, JSON.stringify({
75
+ tag_name: tagOf(r),
76
+ html_url: `${origin}/${slug}/releases/tag/${encodeURIComponent(tagOf(r))}`,
77
+ draft: Boolean(r.draft),
78
+ prerelease: Boolean(r.prerelease),
79
+ assets,
80
+ }));
81
+ }
82
+ const prefix = `/${slug}/releases/download/${encodeURIComponent(tagOf(r))}/`;
83
+ if (url.pathname.startsWith(prefix)) {
84
+ const name = decodeURIComponent(url.pathname.slice(prefix.length));
85
+ if (name === 'SHA256SUMS' && sums !== null) return send(200, sums, 'text/plain');
86
+ const asset = r.assets.find((a) => a.name === name);
87
+ if (!asset) return send(404, 'Not Found', 'text/plain');
88
+ const body = bytesOf(asset);
89
+ res.writeHead(200, { 'Content-Type': 'application/octet-stream', 'Content-Length': body.length });
90
+ if (r.stall === name) {
91
+ res.write(body.subarray(0, Math.floor(body.length / 2))); // then nothing, until the client gives up
92
+ return undefined;
93
+ }
94
+ return res.end(body);
95
+ }
96
+ return send(404, JSON.stringify({ message: 'Not Found' }));
97
+ }
98
+ server.on('connection', (socket) => { open.add(socket); socket.on('close', () => open.delete(socket)); });
99
+ await new Promise((resolve, reject) => {
100
+ server.once('error', reject);
101
+ server.listen(port, '127.0.0.1', resolve); // this computer only, never another address
102
+ });
103
+ return {
104
+ origin: `http://127.0.0.1:${server.address().port}`,
105
+ requests,
106
+ setRelease(next) { current = next; },
107
+ close() {
108
+ for (const socket of open) socket.destroy();
109
+ return new Promise((resolve) => server.close(() => resolve()));
110
+ },
111
+ };
112
+ }
113
+
114
+ // ── from the command line ──
115
+ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
116
+ const arg = (name) => {
117
+ const i = process.argv.indexOf(`--${name}`);
118
+ return i > 0 ? process.argv[i + 1] : undefined;
119
+ };
120
+ const dir = arg('dir');
121
+ if (!dir) {
122
+ console.error('Usage: node scripts/test-release-server.mjs --dir DIR [--version X.Y.Z] [--port 3303]');
123
+ process.exit(2);
124
+ }
125
+ const names = fs.readdirSync(dir).filter((n) => n.endsWith('.dmg')).sort();
126
+ const version = arg('version') || names.map((n) => n.match(/^Six-Degrees-(\d+(?:\.\d+)+)-(?:arm64|x64)\.dmg$/)?.[1]).find(Boolean);
127
+ if (!names.length || !version) {
128
+ console.error(`No Six-Degrees-<version>-<chip>.dmg in ${dir} (or pass --version).`);
129
+ process.exit(2);
130
+ }
131
+ const pkg = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
132
+ const slug = String(pkg.repository?.url || '').match(/github\.com[/:]([^/]+\/[^/.#?]+)/)?.[1];
133
+ const served = await startTestReleaseServer({
134
+ slug,
135
+ port: Number(arg('port') || 0),
136
+ release: { version, assets: names.map((name) => ({ name, file: path.join(dir, name) })) },
137
+ });
138
+ console.log(`A pretend release ${version} of ${slug}: ${names.join(', ')}, SHA256SUMS`);
139
+ console.log(`SIX_DEGREES_TEST_RELEASES=${served.origin}`);
140
+ const stop = () => served.close().then(() => process.exit(0));
141
+ process.on('SIGINT', stop);
142
+ process.on('SIGTERM', stop);
143
+ }
@@ -9,7 +9,7 @@ const currentPort = parseInt(process.env.PORT, 10) || 3000
9
9
  const hostname = process.env.HOSTNAME || '0.0.0.0'
10
10
 
11
11
  let keepAliveTimeout = parseInt(process.env.KEEP_ALIVE_TIMEOUT, 10)
12
- const nextConfig = {"env":{},"webpack":null,"typescript":{"ignoreBuildErrors":false},"typedRoutes":false,"distDir":"./.next","cleanDistDir":true,"assetPrefix":"","cacheMaxMemorySize":52428800,"configOrigin":"next.config.mjs","useFileSystemPublicRoutes":true,"generateEtags":true,"pageExtensions":["tsx","ts","jsx","js"],"instrumentationClientInject":[],"poweredByHeader":true,"compress":true,"images":{"deviceSizes":[640,750,828,1080,1200,1920,2048,3840],"imageSizes":[32,48,64,96,128,256,384],"path":"/_next/image","loader":"default","loaderFile":"","domains":[],"disableStaticImages":false,"minimumCacheTTL":14400,"formats":["image/webp"],"maximumRedirects":3,"maximumResponseBody":50000000,"dangerouslyAllowLocalIP":false,"dangerouslyAllowSVG":false,"contentSecurityPolicy":"script-src 'none'; frame-src 'none'; sandbox;","contentDispositionType":"attachment","localPatterns":[{"pathname":"**","search":""}],"remotePatterns":[],"qualities":[75],"unoptimized":false,"customCacheHandler":false},"devIndicators":{"position":"bottom-left"},"onDemandEntries":{"maxInactiveAge":60000,"pagesBufferLength":5},"basePath":"","sassOptions":{},"trailingSlash":false,"i18n":null,"productionBrowserSourceMaps":false,"excludeDefaultMomentLocales":true,"reactProductionProfiling":false,"reactStrictMode":null,"reactMaxHeadersLength":6000,"httpAgentOptions":{"keepAlive":true},"logging":{"serverFunctions":true,"browserToTerminal":"warn"},"compiler":{},"expireTime":31536000,"staticPageGenerationTimeout":60,"output":"standalone","modularizeImports":{"@mui/icons-material":{"transform":"@mui/icons-material/{{member}}"},"lodash":{"transform":"lodash/{{member}}"}},"outputFileTracingRoot":"/home/runner/work/six-degrees/six-degrees","enablePrerenderSourceMaps":true,"cacheComponents":false,"cacheLife":{"default":{"stale":300,"revalidate":900,"expire":4294967294},"seconds":{"stale":30,"revalidate":1,"expire":60},"minutes":{"stale":300,"revalidate":60,"expire":3600},"hours":{"stale":300,"revalidate":3600,"expire":86400},"days":{"stale":300,"revalidate":86400,"expire":604800},"weeks":{"stale":300,"revalidate":604800,"expire":2592000},"max":{"stale":300,"revalidate":2592000,"expire":31536000}},"cacheHandlers":{},"experimental":{"appNewScrollHandler":true,"coldCacheBadge":false,"devValidationWorker":true,"useSkewCookie":false,"cssChunking":true,"multiZoneDraftMode":false,"appNavFailHandling":false,"prerenderEarlyExit":true,"serverMinification":true,"linkNoTouchStart":false,"caseSensitiveRoutes":false,"cachedNavigations":false,"dynamicOnHover":false,"useOffline":false,"varyParams":true,"optimisticRouting":true,"instrumentationClientRouterTransitionEvents":false,"prefetchInlining":{"maxSize":2048,"maxBundleSize":10240},"preloadEntriesOnStart":true,"clientRouterFilter":true,"clientRouterFilterRedirects":false,"fetchCacheKeyPrefix":"","proxyPrefetch":"flexible","optimisticClientCache":true,"manualClientBasePath":false,"cpus":3,"memoryBasedWorkersCount":false,"imgOptConcurrency":null,"imgOptOperationCache":null,"imgOptTimeoutInSeconds":7,"imgOptMaxInputPixels":268402689,"imgOptSequentialRead":null,"isrFlushToDisk":true,"workerThreads":false,"optimizeCss":false,"nextScriptWorkers":false,"scrollRestoration":false,"externalDir":false,"devMemoryThresholdRestart":true,"disableOptimizedLoading":false,"gzipSize":true,"craCompat":false,"esmExternals":true,"fullySpecified":false,"swcTraceProfiling":false,"forceSwcTransforms":false,"requestInsights":false,"largePageDataBytes":128000,"typedEnv":false,"parallelServerCompiles":false,"parallelServerBuildTraces":false,"ppr":false,"authInterrupts":false,"webpackMemoryOptimizations":false,"optimizeServerReact":true,"strictRouteTypes":false,"useTypeScriptCli":true,"removeUncaughtErrorAndRejectionListeners":false,"validateRSCRequestHeaders":true,"staleTimes":{"dynamic":0,"static":300},"reactDebugChannel":true,"serverComponentsHmrCache":true,"serverComponentsHmrCancellation":false,"staticGenerationMaxConcurrency":8,"staticGenerationMinPagesPerWorker":25,"transitionIndicator":false,"gestureTransition":false,"inlineCss":false,"useCache":false,"globalNotFound":false,"browserDebugInfoInTerminal":"warn","lockDistDir":true,"proxyClientMaxBodySize":10485760,"hideLogsAfterAbort":false,"mcpServer":true,"turbopackFileSystemCacheForDev":true,"turbopackFileSystemCacheForBuild":true,"turbopackInferModuleSideEffects":true,"turbopackPluginRuntimeStrategy":"childProcesses","turbopackMemoryEvictionMode":"auto","optimizePackageImports":["lucide-react","date-fns","lodash-es","ramda","antd","react-bootstrap","ahooks","@ant-design/icons","@headlessui/react","@headlessui-float/react","@heroicons/react/20/solid","@heroicons/react/24/solid","@heroicons/react/24/outline","@visx/visx","@tremor/react","rxjs","@mui/material","@mui/icons-material","recharts","react-use","effect","@effect/schema","@effect/platform","@effect/platform-node","@effect/platform-browser","@effect/platform-bun","@effect/sql","@effect/sql-mssql","@effect/sql-mysql2","@effect/sql-pg","@effect/sql-sqlite-node","@effect/sql-sqlite-bun","@effect/sql-sqlite-wasm","@effect/sql-sqlite-react-native","@effect/rpc","@effect/rpc-http","@effect/typeclass","@effect/experimental","@effect/opentelemetry","@material-ui/core","@material-ui/icons","@tabler/icons-react","mui-core","react-icons/ai","react-icons/bi","react-icons/bs","react-icons/cg","react-icons/ci","react-icons/di","react-icons/fa","react-icons/fa6","react-icons/fc","react-icons/fi","react-icons/gi","react-icons/go","react-icons/gr","react-icons/hi","react-icons/hi2","react-icons/im","react-icons/io","react-icons/io5","react-icons/lia","react-icons/lib","react-icons/lu","react-icons/md","react-icons/pi","react-icons/ri","react-icons/rx","react-icons/si","react-icons/sl","react-icons/tb","react-icons/tfi","react-icons/ti","react-icons/vsc","react-icons/wi"],"useCacheTimeout":54,"instantInsights":{"validationLevel":"warning"},"trustHostHeader":false,"isExperimentalCompile":false},"htmlLimitedBots":"[\\w-]+-Google|Google-[\\w-]+|Chrome-Lighthouse|Slurp|DuckDuckBot|baiduspider|yandex|sogou|bitlybot|tumblr|vkShare|quora link preview|redditbot|ia_archiver|Bingbot|BingPreview|applebot|facebookexternalhit|facebookcatalog|Twitterbot|LinkedInBot|Slackbot|Discordbot|WhatsApp|SkypeUriPreview|Yeti|googleweblight","bundlePagesRouterDependencies":false,"configFileName":"next.config.mjs","outputFileTracingExcludes":{"*":["dist/*.app/**","dist/*.dmg","dist/staging/**","dist/electron-stage/**","docs/**","tests/**",".git/**","*.log","scripts/dmg/**","desktop/**","node_modules/electron/**","node_modules/@electron/**"]},"serverExternalPackages":["node:sqlite"],"repoRoot":"/home/runner/work/six-degrees/six-degrees","turbopack":{"root":"/home/runner/work/six-degrees/six-degrees"},"distDirRoot":".next"}
12
+ const nextConfig = {"env":{},"webpack":null,"typescript":{"ignoreBuildErrors":false},"typedRoutes":false,"distDir":"./.next","cleanDistDir":true,"assetPrefix":"","cacheMaxMemorySize":52428800,"configOrigin":"next.config.mjs","useFileSystemPublicRoutes":true,"generateEtags":true,"pageExtensions":["tsx","ts","jsx","js"],"instrumentationClientInject":[],"poweredByHeader":true,"compress":true,"images":{"deviceSizes":[640,750,828,1080,1200,1920,2048,3840],"imageSizes":[32,48,64,96,128,256,384],"path":"/_next/image","loader":"default","loaderFile":"","domains":[],"disableStaticImages":false,"minimumCacheTTL":14400,"formats":["image/webp"],"maximumRedirects":3,"maximumResponseBody":50000000,"dangerouslyAllowLocalIP":false,"dangerouslyAllowSVG":false,"contentSecurityPolicy":"script-src 'none'; frame-src 'none'; sandbox;","contentDispositionType":"attachment","localPatterns":[{"pathname":"**","search":""}],"remotePatterns":[],"qualities":[75],"unoptimized":false,"customCacheHandler":false},"devIndicators":{"position":"bottom-left"},"onDemandEntries":{"maxInactiveAge":60000,"pagesBufferLength":5},"basePath":"","sassOptions":{},"trailingSlash":false,"i18n":null,"productionBrowserSourceMaps":false,"excludeDefaultMomentLocales":true,"reactProductionProfiling":false,"reactStrictMode":null,"reactMaxHeadersLength":6000,"httpAgentOptions":{"keepAlive":true},"logging":{"serverFunctions":true,"browserToTerminal":"warn"},"compiler":{},"expireTime":31536000,"staticPageGenerationTimeout":60,"output":"standalone","modularizeImports":{"@mui/icons-material":{"transform":"@mui/icons-material/{{member}}"},"lodash":{"transform":"lodash/{{member}}"}},"outputFileTracingRoot":"/home/runner/work/six-degrees/six-degrees","enablePrerenderSourceMaps":true,"cacheComponents":false,"cacheLife":{"default":{"stale":300,"revalidate":900,"expire":4294967294},"seconds":{"stale":30,"revalidate":1,"expire":60},"minutes":{"stale":300,"revalidate":60,"expire":3600},"hours":{"stale":300,"revalidate":3600,"expire":86400},"days":{"stale":300,"revalidate":86400,"expire":604800},"weeks":{"stale":300,"revalidate":604800,"expire":2592000},"max":{"stale":300,"revalidate":2592000,"expire":31536000}},"cacheHandlers":{},"experimental":{"appNewScrollHandler":true,"coldCacheBadge":false,"devValidationWorker":true,"useSkewCookie":false,"cssChunking":true,"multiZoneDraftMode":false,"appNavFailHandling":false,"prerenderEarlyExit":true,"serverMinification":true,"linkNoTouchStart":false,"caseSensitiveRoutes":false,"cachedNavigations":false,"dynamicOnHover":false,"useOffline":false,"varyParams":true,"optimisticRouting":true,"instrumentationClientRouterTransitionEvents":false,"prefetchInlining":{"maxSize":2048,"maxBundleSize":10240},"preloadEntriesOnStart":true,"clientRouterFilter":true,"clientRouterFilterRedirects":false,"fetchCacheKeyPrefix":"","proxyPrefetch":"flexible","optimisticClientCache":true,"manualClientBasePath":false,"cpus":3,"memoryBasedWorkersCount":false,"imgOptConcurrency":null,"imgOptOperationCache":null,"imgOptTimeoutInSeconds":7,"imgOptMaxInputPixels":268402689,"imgOptSequentialRead":null,"isrFlushToDisk":true,"workerThreads":false,"optimizeCss":false,"nextScriptWorkers":false,"scrollRestoration":false,"externalDir":false,"devMemoryThresholdRestart":true,"disableOptimizedLoading":false,"gzipSize":true,"craCompat":false,"esmExternals":true,"fullySpecified":false,"swcTraceProfiling":false,"forceSwcTransforms":false,"requestInsights":false,"largePageDataBytes":128000,"typedEnv":false,"parallelServerCompiles":false,"parallelServerBuildTraces":false,"ppr":false,"authInterrupts":false,"webpackMemoryOptimizations":false,"optimizeServerReact":true,"strictRouteTypes":false,"useTypeScriptCli":true,"removeUncaughtErrorAndRejectionListeners":false,"validateRSCRequestHeaders":true,"staleTimes":{"dynamic":0,"static":300},"reactDebugChannel":true,"serverComponentsHmrCache":true,"serverComponentsHmrCancellation":false,"staticGenerationMaxConcurrency":8,"staticGenerationMinPagesPerWorker":25,"transitionIndicator":false,"gestureTransition":false,"inlineCss":false,"useCache":false,"globalNotFound":false,"browserDebugInfoInTerminal":"warn","lockDistDir":true,"proxyClientMaxBodySize":10485760,"hideLogsAfterAbort":false,"mcpServer":true,"turbopackFileSystemCacheForDev":true,"turbopackFileSystemCacheForBuild":true,"turbopackInferModuleSideEffects":true,"turbopackPluginRuntimeStrategy":"childProcesses","turbopackMemoryEvictionMode":"auto","optimizePackageImports":["lucide-react","date-fns","lodash-es","ramda","antd","react-bootstrap","ahooks","@ant-design/icons","@headlessui/react","@headlessui-float/react","@heroicons/react/20/solid","@heroicons/react/24/solid","@heroicons/react/24/outline","@visx/visx","@tremor/react","rxjs","@mui/material","@mui/icons-material","recharts","react-use","effect","@effect/schema","@effect/platform","@effect/platform-node","@effect/platform-browser","@effect/platform-bun","@effect/sql","@effect/sql-mssql","@effect/sql-mysql2","@effect/sql-pg","@effect/sql-sqlite-node","@effect/sql-sqlite-bun","@effect/sql-sqlite-wasm","@effect/sql-sqlite-react-native","@effect/rpc","@effect/rpc-http","@effect/typeclass","@effect/experimental","@effect/opentelemetry","@material-ui/core","@material-ui/icons","@tabler/icons-react","mui-core","react-icons/ai","react-icons/bi","react-icons/bs","react-icons/cg","react-icons/ci","react-icons/di","react-icons/fa","react-icons/fa6","react-icons/fc","react-icons/fi","react-icons/gi","react-icons/go","react-icons/gr","react-icons/hi","react-icons/hi2","react-icons/im","react-icons/io","react-icons/io5","react-icons/lia","react-icons/lib","react-icons/lu","react-icons/md","react-icons/pi","react-icons/ri","react-icons/rx","react-icons/si","react-icons/sl","react-icons/tb","react-icons/tfi","react-icons/ti","react-icons/vsc","react-icons/wi"],"useCacheTimeout":54,"instantInsights":{"validationLevel":"warning"},"trustHostHeader":false,"isExperimentalCompile":false},"htmlLimitedBots":"[\\w-]+-Google|Google-[\\w-]+|Chrome-Lighthouse|Slurp|DuckDuckBot|baiduspider|yandex|sogou|bitlybot|tumblr|vkShare|quora link preview|redditbot|ia_archiver|Bingbot|BingPreview|applebot|facebookexternalhit|facebookcatalog|Twitterbot|LinkedInBot|Slackbot|Discordbot|WhatsApp|SkypeUriPreview|Yeti|googleweblight","bundlePagesRouterDependencies":false,"configFileName":"next.config.mjs","outputFileTracingExcludes":{"*":["dist/*.app/**","dist/*.dmg","dist/staging/**","dist/electron-stage/**","docs/**","tests/**",".git",".git/**","*.log","scripts/dmg/**","scripts/pin-python-packages.mjs","desktop/**","node_modules/electron/**","node_modules/@electron/**","site/**"]},"serverExternalPackages":["node:sqlite"],"repoRoot":"/home/runner/work/six-degrees/six-degrees","turbopack":{"root":"/home/runner/work/six-degrees/six-degrees"},"distDirRoot":".next"}
13
13
 
14
14
  process.env.__NEXT_PRIVATE_STANDALONE_CONFIG = JSON.stringify(nextConfig)
15
15