six-degrees 0.6.0 → 0.7.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 (557) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/app-path-routes-manifest.json +3 -0
  3. package/.next/standalone/.next/build-manifest.json +3 -3
  4. package/.next/standalone/.next/prerender-manifest.json +14 -14
  5. package/.next/standalone/.next/routes-manifest.json +18 -0
  6. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  7. package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  11. package/.next/standalone/.next/server/app/_not-found/page.js +1 -1
  12. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  13. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  14. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  15. package/.next/standalone/.next/server/app/_not-found.rsc +26 -18
  16. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +26 -18
  17. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +25 -16
  18. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  19. package/.next/standalone/.next/server/app/api/admin-delete/route.js +2 -2
  20. package/.next/standalone/.next/server/app/api/admin-delete/route.js.nft.json +1 -1
  21. package/.next/standalone/.next/server/app/api/admin-update/route.js +2 -2
  22. package/.next/standalone/.next/server/app/api/admin-update/route.js.nft.json +1 -1
  23. package/.next/standalone/.next/server/app/api/bridges/route.js +2 -2
  24. package/.next/standalone/.next/server/app/api/bridges/route.js.nft.json +1 -1
  25. package/.next/standalone/.next/server/app/api/bulk-import/route.js +2 -2
  26. package/.next/standalone/.next/server/app/api/bulk-import/route.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/api/company-scores/legacy/route.js +3 -3
  28. package/.next/standalone/.next/server/app/api/company-scores/legacy/route.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/api/company-scores/route.js +3 -3
  30. package/.next/standalone/.next/server/app/api/company-scores/route.js.nft.json +1 -1
  31. package/.next/standalone/.next/server/app/api/connections/route.js +2 -2
  32. package/.next/standalone/.next/server/app/api/connections/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/data/backup/route/app-paths-manifest.json +3 -0
  34. package/.next/standalone/.next/server/app/api/data/backup/route/build-manifest.json +12 -0
  35. package/.next/standalone/.next/server/app/api/data/backup/route/server-reference-manifest.json +4 -0
  36. package/.next/standalone/.next/server/app/api/data/backup/route.js +7 -0
  37. package/.next/standalone/.next/server/app/api/data/backup/route.js.map +5 -0
  38. package/.next/standalone/.next/server/app/api/data/backup/route.js.nft.json +1 -0
  39. package/.next/standalone/.next/server/app/api/data/backup/route_client-reference-manifest.js +3 -0
  40. package/.next/standalone/.next/server/app/api/data/csv/route/app-paths-manifest.json +3 -0
  41. package/.next/standalone/.next/server/app/api/data/csv/route/build-manifest.json +12 -0
  42. package/.next/standalone/.next/server/app/api/data/csv/route/server-reference-manifest.json +4 -0
  43. package/.next/standalone/.next/server/app/api/data/csv/route.js +8 -0
  44. package/.next/standalone/.next/server/app/api/data/csv/route.js.map +5 -0
  45. package/.next/standalone/.next/server/app/api/data/csv/route.js.nft.json +1 -0
  46. package/.next/standalone/.next/server/app/api/data/csv/route_client-reference-manifest.js +3 -0
  47. package/.next/standalone/.next/server/app/api/data/export/route.js +1 -1
  48. package/.next/standalone/.next/server/app/api/data/export/route.js.nft.json +1 -1
  49. package/.next/standalone/.next/server/app/api/data/import/route.js +1 -1
  50. package/.next/standalone/.next/server/app/api/data/import/route.js.nft.json +1 -1
  51. package/.next/standalone/.next/server/app/api/data/restart/route.js +1 -1
  52. package/.next/standalone/.next/server/app/api/data/restart/route.js.nft.json +1 -1
  53. package/.next/standalone/.next/server/app/api/data/restore/route/app-paths-manifest.json +3 -0
  54. package/.next/standalone/.next/server/app/api/data/restore/route/build-manifest.json +12 -0
  55. package/.next/standalone/.next/server/app/api/data/restore/route/server-reference-manifest.json +4 -0
  56. package/.next/standalone/.next/server/app/api/data/restore/route.js +7 -0
  57. package/.next/standalone/.next/server/app/api/data/restore/route.js.map +5 -0
  58. package/.next/standalone/.next/server/app/api/data/restore/route.js.nft.json +1 -0
  59. package/.next/standalone/.next/server/app/api/data/restore/route_client-reference-manifest.js +3 -0
  60. package/.next/standalone/.next/server/app/api/data/reveal/route.js +1 -1
  61. package/.next/standalone/.next/server/app/api/data/reveal/route.js.nft.json +1 -1
  62. package/.next/standalone/.next/server/app/api/data/route.js +1 -1
  63. package/.next/standalone/.next/server/app/api/data/route.js.nft.json +1 -1
  64. package/.next/standalone/.next/server/app/api/delete-cluster/route.js +2 -2
  65. package/.next/standalone/.next/server/app/api/delete-cluster/route.js.nft.json +1 -1
  66. package/.next/standalone/.next/server/app/api/ingest/route.js +2 -2
  67. package/.next/standalone/.next/server/app/api/ingest/route.js.nft.json +1 -1
  68. package/.next/standalone/.next/server/app/api/network/route.js +2 -2
  69. package/.next/standalone/.next/server/app/api/network/route.js.nft.json +1 -1
  70. package/.next/standalone/.next/server/app/api/notifications/route.js +2 -2
  71. package/.next/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  72. package/.next/standalone/.next/server/app/api/outreach/route.js +2 -2
  73. package/.next/standalone/.next/server/app/api/outreach/route.js.nft.json +1 -1
  74. package/.next/standalone/.next/server/app/api/queue/route.js +2 -2
  75. package/.next/standalone/.next/server/app/api/queue/route.js.nft.json +1 -1
  76. package/.next/standalone/.next/server/app/api/scraper/route.js +4 -2
  77. package/.next/standalone/.next/server/app/api/scraper/route.js.nft.json +1 -1
  78. package/.next/standalone/.next/server/app/api/settings/route.js +3 -3
  79. package/.next/standalone/.next/server/app/api/settings/route.js.nft.json +1 -1
  80. package/.next/standalone/.next/server/app/api/settings/sector-preview/route.js +3 -3
  81. package/.next/standalone/.next/server/app/api/settings/sector-preview/route.js.nft.json +1 -1
  82. package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route.js +3 -3
  83. package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route.js.nft.json +1 -1
  84. package/.next/standalone/.next/server/app/api/setup-profile/route.js +2 -2
  85. package/.next/standalone/.next/server/app/api/setup-profile/route.js.nft.json +1 -1
  86. package/.next/standalone/.next/server/app/api/social/crm/route.js +1 -1
  87. package/.next/standalone/.next/server/app/api/social/crm/route.js.nft.json +1 -1
  88. package/.next/standalone/.next/server/app/api/social/messages/route.js +1 -1
  89. package/.next/standalone/.next/server/app/api/social/messages/route.js.nft.json +1 -1
  90. package/.next/standalone/.next/server/app/api/social/route.js +2 -2
  91. package/.next/standalone/.next/server/app/api/social/route.js.nft.json +1 -1
  92. package/.next/standalone/.next/server/app/api/unlock/route.js +2 -2
  93. package/.next/standalone/.next/server/app/api/unlock/route.js.nft.json +1 -1
  94. package/.next/standalone/.next/server/app/api/update/route.js.nft.json +1 -1
  95. package/.next/standalone/.next/server/app/api/update-images/route.js +2 -2
  96. package/.next/standalone/.next/server/app/api/update-images/route.js.nft.json +1 -1
  97. package/.next/standalone/.next/server/app/api/users/route.js +2 -2
  98. package/.next/standalone/.next/server/app/api/users/route.js.nft.json +1 -1
  99. package/.next/standalone/.next/server/app/avatars/[file]/route.js +1 -1
  100. package/.next/standalone/.next/server/app/avatars/[file]/route.js.nft.json +1 -1
  101. package/.next/standalone/.next/server/app/import/page.js +1 -1
  102. package/.next/standalone/.next/server/app/import/page.js.nft.json +1 -1
  103. package/.next/standalone/.next/server/app/import/page_client-reference-manifest.js +1 -1
  104. package/.next/standalone/.next/server/app/import.html +1 -1
  105. package/.next/standalone/.next/server/app/import.rsc +32 -24
  106. package/.next/standalone/.next/server/app/import.segments/_full.segment.rsc +32 -24
  107. package/.next/standalone/.next/server/app/import.segments/_tree.segment.rsc +2 -2
  108. package/.next/standalone/.next/server/app/import.segments/import/__PAGE__.segment.rsc +23 -18
  109. package/.next/standalone/.next/server/app/index.html +1 -1
  110. package/.next/standalone/.next/server/app/index.rsc +29 -22
  111. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +20 -15
  112. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +29 -22
  113. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  114. package/.next/standalone/.next/server/app/launch/page.js +1 -1
  115. package/.next/standalone/.next/server/app/launch/page.js.nft.json +1 -1
  116. package/.next/standalone/.next/server/app/launch/page_client-reference-manifest.js +1 -1
  117. package/.next/standalone/.next/server/app/launch.html +2 -2
  118. package/.next/standalone/.next/server/app/launch.rsc +32 -24
  119. package/.next/standalone/.next/server/app/launch.segments/_full.segment.rsc +32 -24
  120. package/.next/standalone/.next/server/app/launch.segments/_tree.segment.rsc +2 -2
  121. package/.next/standalone/.next/server/app/launch.segments/launch/__PAGE__.segment.rsc +23 -18
  122. package/.next/standalone/.next/server/app/page.js +1 -1
  123. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  124. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  125. package/.next/standalone/.next/server/app/paths/page.js +1 -1
  126. package/.next/standalone/.next/server/app/paths/page.js.nft.json +1 -1
  127. package/.next/standalone/.next/server/app/paths/page_client-reference-manifest.js +1 -1
  128. package/.next/standalone/.next/server/app/paths.html +1 -1
  129. package/.next/standalone/.next/server/app/paths.rsc +32 -24
  130. package/.next/standalone/.next/server/app/paths.segments/_full.segment.rsc +32 -24
  131. package/.next/standalone/.next/server/app/paths.segments/_tree.segment.rsc +2 -2
  132. package/.next/standalone/.next/server/app/paths.segments/paths/__PAGE__.segment.rsc +21 -18
  133. package/.next/standalone/.next/server/app/profile/page.js +1 -1
  134. package/.next/standalone/.next/server/app/profile/page.js.nft.json +1 -1
  135. package/.next/standalone/.next/server/app/profile/page_client-reference-manifest.js +1 -1
  136. package/.next/standalone/.next/server/app/profile.html +1 -1
  137. package/.next/standalone/.next/server/app/profile.rsc +32 -24
  138. package/.next/standalone/.next/server/app/profile.segments/_full.segment.rsc +32 -24
  139. package/.next/standalone/.next/server/app/profile.segments/_tree.segment.rsc +2 -2
  140. package/.next/standalone/.next/server/app/profile.segments/profile/__PAGE__.segment.rsc +23 -18
  141. package/.next/standalone/.next/server/app/queue/page.js +1 -1
  142. package/.next/standalone/.next/server/app/queue/page.js.nft.json +1 -1
  143. package/.next/standalone/.next/server/app/queue/page_client-reference-manifest.js +1 -1
  144. package/.next/standalone/.next/server/app/queue.html +1 -1
  145. package/.next/standalone/.next/server/app/queue.rsc +32 -24
  146. package/.next/standalone/.next/server/app/queue.segments/_full.segment.rsc +32 -24
  147. package/.next/standalone/.next/server/app/queue.segments/_tree.segment.rsc +2 -2
  148. package/.next/standalone/.next/server/app/queue.segments/queue/__PAGE__.segment.rsc +20 -18
  149. package/.next/standalone/.next/server/app/scores/page.js +1 -1
  150. package/.next/standalone/.next/server/app/scores/page.js.nft.json +1 -1
  151. package/.next/standalone/.next/server/app/scores/page_client-reference-manifest.js +1 -1
  152. package/.next/standalone/.next/server/app/scores.html +1 -1
  153. package/.next/standalone/.next/server/app/scores.rsc +32 -24
  154. package/.next/standalone/.next/server/app/scores.segments/_full.segment.rsc +32 -24
  155. package/.next/standalone/.next/server/app/scores.segments/_tree.segment.rsc +2 -2
  156. package/.next/standalone/.next/server/app/scores.segments/scores/__PAGE__.segment.rsc +23 -18
  157. package/.next/standalone/.next/server/app/settings/page.js +1 -1
  158. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  159. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  160. package/.next/standalone/.next/server/app/settings.html +1 -1
  161. package/.next/standalone/.next/server/app/settings.rsc +32 -24
  162. package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +32 -24
  163. package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  164. package/.next/standalone/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +20 -18
  165. package/.next/standalone/.next/server/app/setup/page.js +1 -1
  166. package/.next/standalone/.next/server/app/setup/page.js.nft.json +1 -1
  167. package/.next/standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  168. package/.next/standalone/.next/server/app/setup.html +1 -1
  169. package/.next/standalone/.next/server/app/setup.rsc +32 -24
  170. package/.next/standalone/.next/server/app/setup.segments/_full.segment.rsc +32 -24
  171. package/.next/standalone/.next/server/app/setup.segments/_tree.segment.rsc +2 -2
  172. package/.next/standalone/.next/server/app/setup.segments/setup/__PAGE__.segment.rsc +21 -18
  173. package/.next/standalone/.next/server/app/social/page.js +1 -1
  174. package/.next/standalone/.next/server/app/social/page.js.nft.json +1 -1
  175. package/.next/standalone/.next/server/app/social/page_client-reference-manifest.js +1 -1
  176. package/.next/standalone/.next/server/app/social.html +1 -1
  177. package/.next/standalone/.next/server/app/social.rsc +32 -24
  178. package/.next/standalone/.next/server/app/social.segments/_full.segment.rsc +32 -24
  179. package/.next/standalone/.next/server/app/social.segments/_tree.segment.rsc +2 -2
  180. package/.next/standalone/.next/server/app/social.segments/social/__PAGE__.segment.rsc +23 -18
  181. package/.next/standalone/.next/server/app-paths-manifest.json +3 -0
  182. package/.next/standalone/.next/server/chunks/[root-of-the-server]__00la_y0._.js +1 -1
  183. package/.next/standalone/.next/server/chunks/[root-of-the-server]__05_3-22._.js +1 -1
  184. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__15wc9e-._.js → [root-of-the-server]__063s0x7._.js} +2 -2
  185. package/.next/standalone/.next/server/chunks/[root-of-the-server]__066rab1._.js +7 -0
  186. package/.next/standalone/.next/server/chunks/[root-of-the-server]__07t47cm._.js +1 -1
  187. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__13dx4sd._.js → [root-of-the-server]__0_4y8z7._.js} +3 -3
  188. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0cj5jqc._.js +1 -1
  189. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0e0fei3._.js +1 -1
  190. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0fooke1._.js +7 -0
  191. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ljnyho._.js +1 -1
  192. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o5-tcg._.js +1 -1
  193. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0qrnous._.js +1 -1
  194. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0t3v1a1._.js +1 -1
  195. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0t6y79e._.js +7 -0
  196. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0wmexuc._.js +1 -1
  197. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0x29muh._.js +7 -0
  198. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0xu34sz._.js +1 -1
  199. package/.next/standalone/.next/server/chunks/[root-of-the-server]__133knx1._.js +1 -1
  200. package/.next/standalone/.next/server/chunks/[root-of-the-server]__15-17hl._.js +1 -1
  201. package/.next/standalone/.next/server/chunks/[root-of-the-server]__19b7mig._.js +1 -1
  202. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1a_6x37._.js +8 -0
  203. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1an_8pa._.js +7 -0
  204. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1bbcwub._.js +1 -1
  205. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1ducpz2._.js +3 -0
  206. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1eris29._.js +1 -1
  207. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1gjo_cq._.js +1 -1
  208. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1qcm08_._.js +1 -1
  209. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tir0vc._.js +1 -1
  210. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1w25q0m._.js +1 -1
  211. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1xhyinn._.js +3 -0
  212. package/.next/standalone/.next/server/chunks/[root-of-the-server]__20rwfdw._.js +1 -1
  213. package/.next/standalone/.next/server/chunks/_02twlca._.js +1 -1
  214. package/.next/standalone/.next/server/chunks/_072jl0y._.js +1 -1
  215. package/.next/standalone/.next/server/chunks/_0lw8_i0._.js +1 -1
  216. package/.next/standalone/.next/server/chunks/_0sqqpzb._.js +3 -0
  217. package/.next/standalone/.next/server/chunks/_1_fynia._.js +7 -7
  218. package/.next/standalone/.next/server/chunks/_1fn30_l._.js +1 -1
  219. package/.next/standalone/.next/server/chunks/_1itd1o_._.js +1 -1
  220. package/.next/standalone/.next/server/chunks/_1iwkh8t._.js +1 -1
  221. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_data_backup_route_actions_0zoj04p.js +3 -0
  222. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_data_csv_route_actions_0l6m7lg.js +3 -0
  223. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_data_restore_route_actions_0-b05bv.js +3 -0
  224. package/.next/standalone/.next/server/chunks/lib_02iz_9q._.js +4 -0
  225. package/.next/standalone/.next/server/chunks/lib_05agvk3._.js +4 -0
  226. package/.next/standalone/.next/server/chunks/lib_0v84u66._.js +4 -0
  227. package/.next/standalone/.next/server/chunks/lib_db-client_0_dch35.js +174 -0
  228. package/.next/standalone/.next/server/chunks/lib_linkedin-export_12imqxg.js +1 -1
  229. package/.next/standalone/.next/server/chunks/lib_rpc_005abye.js +3 -0
  230. package/.next/standalone/.next/server/chunks/lib_rpc_0b17r6v.js +3 -0
  231. package/.next/standalone/.next/server/chunks/lib_rpc_0c9d9ti.js +7 -0
  232. package/.next/standalone/.next/server/chunks/lib_rpc_0doq0t-.js +3 -0
  233. package/.next/standalone/.next/server/chunks/lib_rpc_0fd2rlw.js +3 -0
  234. package/.next/standalone/.next/server/chunks/lib_rpc_0is4qoc.js +3 -0
  235. package/.next/standalone/.next/server/chunks/lib_rpc_0jnfp2e.js +3 -0
  236. package/.next/standalone/.next/server/chunks/lib_rpc_0nef_n7.js +3 -0
  237. package/.next/standalone/.next/server/chunks/lib_rpc_0plrzra.js +3 -0
  238. package/.next/standalone/.next/server/chunks/lib_rpc_13a4m0c.js +3 -0
  239. package/.next/standalone/.next/server/chunks/lib_rpc_1if17c-.js +3 -0
  240. package/.next/standalone/.next/server/chunks/lib_rpc_1j-zw7v.js +3 -0
  241. package/.next/standalone/.next/server/chunks/lib_rpc_1sngt-q.js +3 -0
  242. package/.next/standalone/.next/server/chunks/lib_rpc_1v2sgme.js +3 -0
  243. package/.next/standalone/.next/server/chunks/lib_rpc_1vh-tgx.js +3 -0
  244. package/.next/standalone/.next/server/chunks/lib_rpc_1zaubz7.js +3 -0
  245. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qd8pzm._.js +3 -0
  246. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0qdeuyo._.js → [root-of-the-server]__1-012q1._.js} +2 -2
  247. package/.next/standalone/.next/server/chunks/ssr/_01o3i5y._.js +7 -0
  248. package/.next/standalone/.next/server/chunks/ssr/_02sband._.js +7 -0
  249. package/.next/standalone/.next/server/chunks/ssr/_06rgzfh._.js +3 -0
  250. package/.next/standalone/.next/server/chunks/ssr/_07zb7lu._.js +3 -0
  251. package/.next/standalone/.next/server/chunks/ssr/_0_79idq._.js +3 -0
  252. package/.next/standalone/.next/server/chunks/ssr/_0gy11ar._.js +3 -0
  253. package/.next/standalone/.next/server/chunks/ssr/_0hb-dhy._.js +3 -0
  254. package/.next/standalone/.next/server/chunks/ssr/_14nxd3q._.js +3 -0
  255. package/.next/standalone/.next/server/chunks/ssr/_189g8lg._.js +3 -0
  256. package/.next/standalone/.next/server/chunks/ssr/_1f_n00p._.js +1 -1
  257. package/.next/standalone/.next/server/chunks/ssr/_1fm1ih_._.js +2 -2
  258. package/.next/standalone/.next/server/chunks/ssr/_1k2nmbw._.js +2 -2
  259. package/.next/standalone/.next/server/chunks/ssr/_1k9qfsk._.js +3 -0
  260. package/.next/standalone/.next/server/chunks/ssr/app_141w189._.js +1 -1
  261. package/.next/standalone/.next/server/chunks/ssr/app_components_0t908t-._.js +1 -1
  262. package/.next/standalone/.next/server/chunks/ssr/app_components_0uuff77._.js +25 -2
  263. package/.next/standalone/.next/server/chunks/ssr/app_components_1j3q-h5._.js +25 -2
  264. package/.next/standalone/.next/server/chunks/ssr/app_components_CompanyScores_1g0mytz.js +1 -1
  265. package/.next/standalone/.next/server/chunks/ssr/app_import_page_1fmu6_l.js +1 -1
  266. package/.next/standalone/.next/server/chunks/ssr/app_launch_page_19t0dyd.js +1 -1
  267. package/.next/standalone/.next/server/chunks/ssr/app_page_1fazqbf.js +25 -13
  268. package/.next/standalone/.next/server/chunks/ssr/app_paths_page_1ol90m7.js +2 -2
  269. package/.next/standalone/.next/server/chunks/ssr/app_profile_page_0u3-l5y.js +1 -1
  270. package/.next/standalone/.next/server/chunks/ssr/app_queue_page_0c6znd6.js +11 -11
  271. package/.next/standalone/.next/server/chunks/ssr/app_settings_page_1f8mjj_.js +1 -1
  272. package/.next/standalone/.next/server/chunks/ssr/app_setup_page_1-282zx.js +12 -13
  273. package/.next/standalone/.next/server/chunks/ssr/app_social_page_0h7zxda.js +1 -1
  274. package/.next/standalone/.next/server/chunks/ssr/{lib_1c4ieuw._.js → lib_csv_1ttiu_h.js} +2 -2
  275. package/.next/standalone/.next/server/chunks/ssr/lib_galaxy-lab_108_lu-.js +1 -1
  276. package/.next/standalone/.next/server/chunks/ssr/lib_separation_1305zsx.js +3 -0
  277. package/.next/standalone/.next/server/edge/chunks/[root-of-the-server]__0pco7a6._.js +1 -1
  278. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  279. package/.next/standalone/.next/server/middleware-manifest.json +5 -5
  280. package/.next/standalone/.next/server/pages/404.html +1 -1
  281. package/.next/standalone/.next/server/pages/500.html +1 -1
  282. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  283. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  284. package/.next/standalone/.next/static/chunks/03-_esd2x_yk6.js +1 -0
  285. package/.next/standalone/.next/static/chunks/03-demj2iy-0p.js +44 -0
  286. package/.next/standalone/.next/static/chunks/0_1r-3csplxnj.js +1 -0
  287. package/.next/standalone/.next/static/chunks/0an_b6zi1r_qq.js +5 -0
  288. package/.next/standalone/.next/static/chunks/0e3g0sil79cus.js +21 -0
  289. package/.next/standalone/.next/static/chunks/0kscsqnvfq3-r.js +1 -0
  290. package/.next/standalone/.next/static/chunks/0p3g5tfostsep.js +7 -0
  291. package/.next/standalone/.next/static/chunks/0zd0t7e60bn4z.js +1 -0
  292. package/.next/standalone/.next/static/chunks/163b-20vwa06i.js +34 -0
  293. package/.next/standalone/.next/static/chunks/{0ws7zy0w57ufi.js → 1eyajb9ntzlrl.js} +1 -1
  294. package/.next/standalone/.next/static/chunks/1pa-2v1ajvrko.js +1 -0
  295. package/.next/standalone/.next/static/chunks/1qbjermbf68xj.js +1 -0
  296. package/.next/standalone/.next/static/chunks/1z9lb-dbcg4aa.js +1 -0
  297. package/.next/standalone/.next/static/chunks/23_1z47al92nz.js +57 -0
  298. package/.next/standalone/.next/static/chunks/260bomtr214m8.js +34 -0
  299. package/.next/standalone/.next/static/chunks/2eo9pqq9gay3r.js +34 -0
  300. package/.next/standalone/.next/static/chunks/2xk2ra4piiuod.js +34 -0
  301. package/.next/standalone/.next/static/chunks/2z1-q0_vunbql.js +2 -0
  302. package/.next/standalone/.next/static/chunks/2zf4jmz9l3li2.js +1 -0
  303. package/.next/standalone/.next/static/chunks/34g73qhayuevu.js +1 -0
  304. package/.next/standalone/.next/static/chunks/3b0-14cxqsprt.js +1 -0
  305. package/.next/standalone/.next/static/chunks/3jarus1_38s2z.js +1 -0
  306. package/.next/standalone/.next/static/chunks/3ssu341hav_sq.css +3 -0
  307. package/.next/standalone/.next/static/chunks/3v1qd3sqhqn0d.js +1 -0
  308. package/.next/standalone/.next/static/chunks/43pg2sursiypz.js +1 -0
  309. package/.next/standalone/.next/static/chunks/44yn0tcd9dfk3.js +1 -0
  310. package/.next/standalone/CHANGELOG.md +143 -0
  311. package/.next/standalone/README.md +71 -49
  312. package/.next/standalone/SECURITY.md +17 -11
  313. package/.next/standalone/app/api/data/backup/route.js +25 -0
  314. package/.next/standalone/app/api/data/csv/route.js +74 -0
  315. package/.next/standalone/app/api/data/export/route.js +1 -1
  316. package/.next/standalone/app/api/data/restore/route.js +48 -0
  317. package/.next/standalone/app/api/data/reveal/route.js +19 -6
  318. package/.next/standalone/app/api/data/route.js +15 -4
  319. package/.next/standalone/app/api/ingest/route.js +1 -1
  320. package/.next/standalone/app/api/queue/route.js +1 -1
  321. package/.next/standalone/app/api/scraper/route.js +120 -32
  322. package/.next/standalone/app/api/social/crm/route.js +2 -1
  323. package/.next/standalone/app/components/AppHeader.js +20 -16
  324. package/.next/standalone/app/components/AppTabs.js +8 -8
  325. package/.next/standalone/app/components/AutoScanButton.js +2 -2
  326. package/.next/standalone/app/components/Avatar.js +1 -1
  327. package/.next/standalone/app/components/ChainView.js +184 -112
  328. package/.next/standalone/app/components/CirclePanel.js +16 -16
  329. package/.next/standalone/app/components/ClusterSpinner.js +64 -0
  330. package/.next/standalone/app/components/CompanyScores.js +20 -20
  331. package/.next/standalone/app/components/EmptyState.js +39 -18
  332. package/.next/standalone/app/components/FieldStep.js +15 -13
  333. package/.next/standalone/app/components/FilterPanel.js +17 -17
  334. package/.next/standalone/app/components/ForceGraph.js +49 -14
  335. package/.next/standalone/app/components/GalaxyLab.js +33 -33
  336. package/.next/standalone/app/components/GridView.js +18 -18
  337. package/.next/standalone/app/components/LegacyScoresCard.js +10 -11
  338. package/.next/standalone/app/components/LinkedInLimits.js +24 -19
  339. package/.next/standalone/app/components/LiquidGlass.js +99 -0
  340. package/.next/standalone/app/components/ListView.js +20 -20
  341. package/.next/standalone/app/components/MapControls.js +5 -5
  342. package/.next/standalone/app/components/MappingProgress.js +16 -16
  343. package/.next/standalone/app/components/NoteDetail.js +13 -13
  344. package/.next/standalone/app/components/OnboardingGate.js +4 -4
  345. package/.next/standalone/app/components/OutlinkQuest.js +25 -25
  346. package/.next/standalone/app/components/PathsAnalyzer.js +37 -36
  347. package/.next/standalone/app/components/PeopleMap.js +21 -21
  348. package/.next/standalone/app/components/RingsView.js +11 -11
  349. package/.next/standalone/app/components/ScanRadar.js +84 -39
  350. package/.next/standalone/app/components/ScanStatusBar.js +22 -18
  351. package/.next/standalone/app/components/ScanTrail.js +8 -5
  352. package/.next/standalone/app/components/SeparationView.js +168 -104
  353. package/.next/standalone/app/components/Sidebar.js +161 -161
  354. package/.next/standalone/app/components/StaleServerBanner.js +4 -4
  355. package/.next/standalone/app/components/TheirCircle.js +26 -26
  356. package/.next/standalone/app/components/ThemeDefs.js +42 -0
  357. package/.next/standalone/app/components/ThemeLoader.js +20 -1
  358. package/.next/standalone/app/components/UpdatePanel.js +22 -22
  359. package/.next/standalone/app/components/UserProvider.js +22 -15
  360. package/.next/standalone/app/components/settings/AppearanceSection.js +47 -24
  361. package/.next/standalone/app/components/settings/DataSection.js +276 -109
  362. package/.next/standalone/app/components/settings/NetworkHealthSection.js +13 -13
  363. package/.next/standalone/app/components/settings/SectorPicker.js +12 -12
  364. package/.next/standalone/app/components/settings/SectorSection.js +13 -14
  365. package/.next/standalone/app/components/settings/TierSection.js +9 -9
  366. package/.next/standalone/app/components/settings/TitleSection.js +11 -11
  367. package/.next/standalone/app/components/settings/UsageSection.js +313 -0
  368. package/.next/standalone/app/components/ui.js +8 -8
  369. package/.next/standalone/app/globals.css +159 -2
  370. package/.next/standalone/app/import/page.js +94 -38
  371. package/.next/standalone/app/launch/page.js +5 -5
  372. package/.next/standalone/app/layout.js +6 -2
  373. package/.next/standalone/app/page.js +53 -37
  374. package/.next/standalone/app/paths/page.js +63 -63
  375. package/.next/standalone/app/profile/page.js +58 -58
  376. package/.next/standalone/app/queue/page.js +54 -54
  377. package/.next/standalone/app/scores/page.js +2 -2
  378. package/.next/standalone/app/settings/page.js +16 -13
  379. package/.next/standalone/app/setup/page.js +221 -125
  380. package/.next/standalone/app/social/Crm.js +63 -62
  381. package/.next/standalone/app/social/SocialHub.js +22 -22
  382. package/.next/standalone/app/social/Thread.js +7 -7
  383. package/.next/standalone/app/social/page.js +2 -2
  384. package/.next/standalone/lib/backups.js +458 -0
  385. package/.next/standalone/lib/chain-layout.js +32 -0
  386. package/.next/standalone/lib/csv-store.js +131 -0
  387. package/.next/standalone/lib/csv.js +137 -39
  388. package/.next/standalone/lib/data-folder.js +19 -3
  389. package/.next/standalone/lib/data-import.js +5 -2
  390. package/.next/standalone/lib/db-client.js +78 -20
  391. package/.next/standalone/lib/demo.js +5 -4
  392. package/.next/standalone/lib/galaxy-export.js +9 -2
  393. package/.next/standalone/lib/galaxy-lab.js +19 -5
  394. package/.next/standalone/lib/galaxy-layouts.js +43 -0
  395. package/.next/standalone/lib/gate.js +10 -4
  396. package/.next/standalone/lib/ingest.js +1 -1
  397. package/.next/standalone/lib/linkedin-limits.js +212 -11
  398. package/.next/standalone/lib/liquid-glass.js +59 -0
  399. package/.next/standalone/lib/notifications.js +13 -0
  400. package/.next/standalone/lib/scan-pace.js +11 -0
  401. package/.next/standalone/lib/scan-progress.js +20 -0
  402. package/.next/standalone/lib/scan-risk.js +44 -0
  403. package/.next/standalone/lib/scanner-setup.js +97 -15
  404. package/.next/standalone/lib/scraper-client.js +17 -3
  405. package/.next/standalone/lib/search-risk.js +7 -3
  406. package/.next/standalone/lib/settings-client.js +5 -3
  407. package/.next/standalone/lib/settings.js +10 -0
  408. package/.next/standalone/lib/social-store.js +3 -2
  409. package/.next/standalone/lib/synced-setting.js +46 -0
  410. package/.next/standalone/lib/theme-store.js +42 -10
  411. package/.next/standalone/lib/themes.js +111 -21
  412. package/.next/standalone/lib/usage.js +195 -0
  413. package/.next/standalone/package-lock.json +2 -2
  414. package/.next/standalone/package.json +1 -1
  415. package/.next/standalone/scripts/scrape.py +26 -2
  416. package/.next/static/chunks/03-_esd2x_yk6.js +1 -0
  417. package/.next/static/chunks/03-demj2iy-0p.js +44 -0
  418. package/.next/static/chunks/0_1r-3csplxnj.js +1 -0
  419. package/.next/static/chunks/0an_b6zi1r_qq.js +5 -0
  420. package/.next/static/chunks/0e3g0sil79cus.js +21 -0
  421. package/.next/static/chunks/0kscsqnvfq3-r.js +1 -0
  422. package/.next/static/chunks/0p3g5tfostsep.js +7 -0
  423. package/.next/static/chunks/0zd0t7e60bn4z.js +1 -0
  424. package/.next/static/chunks/163b-20vwa06i.js +34 -0
  425. package/.next/static/chunks/{0ws7zy0w57ufi.js → 1eyajb9ntzlrl.js} +1 -1
  426. package/.next/static/chunks/1pa-2v1ajvrko.js +1 -0
  427. package/.next/static/chunks/1qbjermbf68xj.js +1 -0
  428. package/.next/static/chunks/1z9lb-dbcg4aa.js +1 -0
  429. package/.next/static/chunks/23_1z47al92nz.js +57 -0
  430. package/.next/static/chunks/260bomtr214m8.js +34 -0
  431. package/.next/static/chunks/2eo9pqq9gay3r.js +34 -0
  432. package/.next/static/chunks/2xk2ra4piiuod.js +34 -0
  433. package/.next/static/chunks/2z1-q0_vunbql.js +2 -0
  434. package/.next/static/chunks/2zf4jmz9l3li2.js +1 -0
  435. package/.next/static/chunks/34g73qhayuevu.js +1 -0
  436. package/.next/static/chunks/3b0-14cxqsprt.js +1 -0
  437. package/.next/static/chunks/3jarus1_38s2z.js +1 -0
  438. package/.next/static/chunks/3ssu341hav_sq.css +3 -0
  439. package/.next/static/chunks/3v1qd3sqhqn0d.js +1 -0
  440. package/.next/static/chunks/43pg2sursiypz.js +1 -0
  441. package/.next/static/chunks/44yn0tcd9dfk3.js +1 -0
  442. package/CHANGELOG.md +143 -0
  443. package/README.md +71 -49
  444. package/SECURITY.md +17 -11
  445. package/lib/backups.js +458 -0
  446. package/lib/chain-layout.js +32 -0
  447. package/lib/csv-store.js +131 -0
  448. package/lib/csv.js +137 -39
  449. package/lib/data-folder.js +19 -3
  450. package/lib/data-import.js +5 -2
  451. package/lib/db-client.js +78 -20
  452. package/lib/demo.js +5 -4
  453. package/lib/galaxy-export.js +9 -2
  454. package/lib/galaxy-lab.js +19 -5
  455. package/lib/galaxy-layouts.js +43 -0
  456. package/lib/gate.js +10 -4
  457. package/lib/ingest.js +1 -1
  458. package/lib/linkedin-limits.js +212 -11
  459. package/lib/liquid-glass.js +59 -0
  460. package/lib/notifications.js +13 -0
  461. package/lib/scan-pace.js +11 -0
  462. package/lib/scan-progress.js +20 -0
  463. package/lib/scan-risk.js +44 -0
  464. package/lib/scanner-setup.js +97 -15
  465. package/lib/scraper-client.js +17 -3
  466. package/lib/search-risk.js +7 -3
  467. package/lib/settings-client.js +5 -3
  468. package/lib/settings.js +10 -0
  469. package/lib/social-store.js +3 -2
  470. package/lib/synced-setting.js +46 -0
  471. package/lib/theme-store.js +42 -10
  472. package/lib/themes.js +111 -21
  473. package/lib/usage.js +195 -0
  474. package/package.json +1 -1
  475. package/scripts/scrape.py +26 -2
  476. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-rnla2._.js +0 -3
  477. package/.next/standalone/.next/server/chunks/[root-of-the-server]__14ou7j5._.js +0 -3
  478. package/.next/standalone/.next/server/chunks/[root-of-the-server]__16iu0hv._.js +0 -3
  479. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1kbl21f._.js +0 -3
  480. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1rphsdk._.js +0 -3
  481. package/.next/standalone/.next/server/chunks/lib_0851sk0._.js +0 -8
  482. package/.next/standalone/.next/server/chunks/lib_1q7p2ro._.js +0 -8
  483. package/.next/standalone/.next/server/chunks/lib_1r6l86l._.js +0 -8
  484. package/.next/standalone/.next/server/chunks/lib_db-client_15le7cw.js +0 -174
  485. package/.next/standalone/.next/server/chunks/lib_rpc_04s8mzr.js +0 -3
  486. package/.next/standalone/.next/server/chunks/lib_rpc_053z7as.js +0 -3
  487. package/.next/standalone/.next/server/chunks/lib_rpc_0b_-2te.js +0 -3
  488. package/.next/standalone/.next/server/chunks/lib_rpc_0dhbxdm.js +0 -3
  489. package/.next/standalone/.next/server/chunks/lib_rpc_0dxlk31.js +0 -3
  490. package/.next/standalone/.next/server/chunks/lib_rpc_0f8b-qc.js +0 -3
  491. package/.next/standalone/.next/server/chunks/lib_rpc_0pzie7_.js +0 -3
  492. package/.next/standalone/.next/server/chunks/lib_rpc_0r76fq_.js +0 -3
  493. package/.next/standalone/.next/server/chunks/lib_rpc_0ut7pjp.js +0 -3
  494. package/.next/standalone/.next/server/chunks/lib_rpc_15zyqaz.js +0 -3
  495. package/.next/standalone/.next/server/chunks/lib_rpc_18zpjtq.js +0 -3
  496. package/.next/standalone/.next/server/chunks/lib_rpc_1a8xkiz.js +0 -3
  497. package/.next/standalone/.next/server/chunks/lib_rpc_1e_55xo.js +0 -3
  498. package/.next/standalone/.next/server/chunks/lib_rpc_1oxfd1_.js +0 -3
  499. package/.next/standalone/.next/server/chunks/lib_rpc_1s0lod0.js +0 -3
  500. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__08oxiqh._.js +0 -3
  501. package/.next/standalone/.next/server/chunks/ssr/_03l7vge._.js +0 -3
  502. package/.next/standalone/.next/server/chunks/ssr/_08g31gm._.js +0 -3
  503. package/.next/standalone/.next/server/chunks/ssr/_0gk_ofx._.js +0 -3
  504. package/.next/standalone/.next/server/chunks/ssr/_0nwsf93._.js +0 -3
  505. package/.next/standalone/.next/server/chunks/ssr/_0vho1lb._.js +0 -3
  506. package/.next/standalone/.next/server/chunks/ssr/_1-658fq._.js +0 -3
  507. package/.next/standalone/.next/server/chunks/ssr/_1bc46kz._.js +0 -3
  508. package/.next/standalone/.next/server/chunks/ssr/_2105jz8._.js +0 -3
  509. package/.next/standalone/.next/server/chunks/ssr/app_components_settings_SectorPicker_1c-29qw.js +0 -3
  510. package/.next/standalone/.next/static/chunks/02pq76u7y4v9-.js +0 -22
  511. package/.next/standalone/.next/static/chunks/0cmq0exd4d0h9.js +0 -1
  512. package/.next/standalone/.next/static/chunks/0i02yxppzds1o.js +0 -1
  513. package/.next/standalone/.next/static/chunks/0m_p4k-q2qh--.js +0 -1
  514. package/.next/standalone/.next/static/chunks/0oyslj62jkubd.js +0 -1
  515. package/.next/standalone/.next/static/chunks/0vo-l8hxneba6.css +0 -3
  516. package/.next/standalone/.next/static/chunks/0yh8z2tbrc5i_.js +0 -1
  517. package/.next/standalone/.next/static/chunks/17-rhp8sg4m1b.js +0 -44
  518. package/.next/standalone/.next/static/chunks/172e10mi-tpo_.js +0 -34
  519. package/.next/standalone/.next/static/chunks/1ei5vbe2uw6fl.js +0 -1
  520. package/.next/standalone/.next/static/chunks/1f7_pl_wq7lh3.js +0 -11
  521. package/.next/standalone/.next/static/chunks/1jzbe7zdx9vbj.js +0 -2
  522. package/.next/standalone/.next/static/chunks/1m3017gp790pw.js +0 -45
  523. package/.next/standalone/.next/static/chunks/1t20e8u0st7ep.js +0 -1
  524. package/.next/standalone/.next/static/chunks/2-ch9rm27eril.js +0 -11
  525. package/.next/standalone/.next/static/chunks/2ldv-v3s9yaif.js +0 -1
  526. package/.next/standalone/.next/static/chunks/3bp284gjf7dyq.js +0 -1
  527. package/.next/standalone/.next/static/chunks/3k1f6clbn3gj3.js +0 -7
  528. package/.next/standalone/.next/static/chunks/3l4t8je6iguzc.js +0 -1
  529. package/.next/standalone/.next/static/chunks/3tplzuc9jk-tl.js +0 -1
  530. package/.next/standalone/.next/static/chunks/42m0bqr8pbovz.js +0 -11
  531. package/.next/static/chunks/02pq76u7y4v9-.js +0 -22
  532. package/.next/static/chunks/0cmq0exd4d0h9.js +0 -1
  533. package/.next/static/chunks/0i02yxppzds1o.js +0 -1
  534. package/.next/static/chunks/0m_p4k-q2qh--.js +0 -1
  535. package/.next/static/chunks/0oyslj62jkubd.js +0 -1
  536. package/.next/static/chunks/0vo-l8hxneba6.css +0 -3
  537. package/.next/static/chunks/0yh8z2tbrc5i_.js +0 -1
  538. package/.next/static/chunks/17-rhp8sg4m1b.js +0 -44
  539. package/.next/static/chunks/172e10mi-tpo_.js +0 -34
  540. package/.next/static/chunks/1ei5vbe2uw6fl.js +0 -1
  541. package/.next/static/chunks/1f7_pl_wq7lh3.js +0 -11
  542. package/.next/static/chunks/1jzbe7zdx9vbj.js +0 -2
  543. package/.next/static/chunks/1m3017gp790pw.js +0 -45
  544. package/.next/static/chunks/1t20e8u0st7ep.js +0 -1
  545. package/.next/static/chunks/2-ch9rm27eril.js +0 -11
  546. package/.next/static/chunks/2ldv-v3s9yaif.js +0 -1
  547. package/.next/static/chunks/3bp284gjf7dyq.js +0 -1
  548. package/.next/static/chunks/3k1f6clbn3gj3.js +0 -7
  549. package/.next/static/chunks/3l4t8je6iguzc.js +0 -1
  550. package/.next/static/chunks/3tplzuc9jk-tl.js +0 -1
  551. package/.next/static/chunks/42m0bqr8pbovz.js +0 -11
  552. /package/.next/standalone/.next/static/{dQUvvVSXqbkEFC3-CNcwT → ZVpLBK-2vpllv2HxOFs2d}/_buildManifest.js +0 -0
  553. /package/.next/standalone/.next/static/{dQUvvVSXqbkEFC3-CNcwT → ZVpLBK-2vpllv2HxOFs2d}/_clientMiddlewareManifest.js +0 -0
  554. /package/.next/standalone/.next/static/{dQUvvVSXqbkEFC3-CNcwT → ZVpLBK-2vpllv2HxOFs2d}/_ssgManifest.js +0 -0
  555. /package/.next/static/{dQUvvVSXqbkEFC3-CNcwT → ZVpLBK-2vpllv2HxOFs2d}/_buildManifest.js +0 -0
  556. /package/.next/static/{dQUvvVSXqbkEFC3-CNcwT → ZVpLBK-2vpllv2HxOFs2d}/_clientMiddlewareManifest.js +0 -0
  557. /package/.next/static/{dQUvvVSXqbkEFC3-CNcwT → ZVpLBK-2vpllv2HxOFs2d}/_ssgManifest.js +0 -0
package/lib/csv.js CHANGED
@@ -1,10 +1,12 @@
1
1
  import { scorePerson, curveCutoffs, curvedTier } from './scoring.js';
2
2
  import { toIsoDate } from './ingest.js';
3
- // LinkedIn Connections.csv import — parsed ENTIRELY in the browser.
3
+ // LinkedIn Connections.csv import, read in the browser.
4
4
  //
5
- // Nothing is uploaded and nothing is written to the database: an imported
6
- // network lives in sessionStorage and disappears when the tab closes. That
7
- // keeps other people's connection lists off this deployment's Supabase.
5
+ // The file itself never leaves the page. What the map needs about each person
6
+ // goes to this computer's own server, which keeps it in the data folder
7
+ // (csv-network.json, lib/csv-store.js) so it's still there after the window
8
+ // closes. It's never written into the database or mixed with a scanned
9
+ // network, and nothing goes anywhere else.
8
10
  //
9
11
  // The official export carries six usable columns — First Name, Last Name,
10
12
  // URL, Company, Position, Connected On. It has NO profile photos and no rich
@@ -55,7 +57,7 @@ export function scoreRecord(headline, company) {
55
57
  * a CSV import uses the app's defaults, never your settings (no company
56
58
  * scores, no sector), and the curve is the default, so the first look at any
57
59
  * export has a top. Scores → Tiers says so while one is open. Run on the
58
- * parse (the import page's counts) and on every load (the tab keeps only
60
+ * parse (the import page's counts) and on every load (the data folder keeps only
59
61
  * what the export says, packConnections), so the two always agree.
60
62
  */
61
63
  function gradeOnCurve(connections) {
@@ -154,20 +156,46 @@ export function parseConnectionsCsv(text) {
154
156
  return { connections, skipped, total: connections.length + skipped };
155
157
  }
156
158
 
157
- // ── Session-scoped storage (never the database) ────────────────────────────
158
- export function saveSessionNetwork({ degree1 = [], degree2 = [], source = 'csv' }) {
159
+ // ── Where an open network is kept ──────────────────────────────────────────
160
+ // A CSV import is kept in the data folder through /api/data/csv
161
+ // (lib/csv-store.js) until × beside "Your CSV" removes it. It used to live in
162
+ // sessionStorage and was gone when the window closed (the 1.0 list,
163
+ // 2026-10-03: CSV imports survive closing the window).
164
+ //
165
+ // The sample network stays in this window only. It's a look around, not
166
+ // anyone's network, so it never goes into the data folder, a copy or a backup.
167
+
168
+ const SAMPLE_KEY = 'six-degrees-sample-network';
169
+ const CSV_ROUTE = '/api/data/csv';
170
+
171
+ export function saveSampleNetwork({ degree1 = [], degree2 = [] }) {
159
172
  try {
160
- sessionStorage.setItem(STORAGE_KEY, JSON.stringify({ degree1, degree2, source, importedAt: Date.now() }));
173
+ sessionStorage.setItem(SAMPLE_KEY, JSON.stringify({ degree1, degree2, source: 'sample', importedAt: Date.now() }));
161
174
  return true;
162
175
  } catch {
163
- return false; // quota — very large networks
176
+ return false; // storage full or off
164
177
  }
165
178
  }
166
179
 
167
- // A scored row is ~540 characters and the tab holds ~5 million, which capped an
168
- // import near 9,000 connections. So the tab keeps only what the export says about
169
- // each person (~130 characters) and scores it again on load: an export at
170
- // LinkedIn's own maximum of 30,000 connections fits.
180
+ function sampleOpen() {
181
+ try { return !!sessionStorage.getItem(SAMPLE_KEY); } catch { return false; }
182
+ }
183
+
184
+ function sampleNetwork() {
185
+ try {
186
+ const parsed = JSON.parse(sessionStorage.getItem(SAMPLE_KEY));
187
+ return parsed ? { degree1: parsed.degree1 || [], degree2: parsed.degree2 || [], source: 'sample' } : null;
188
+ } catch { return null; }
189
+ }
190
+
191
+ function leaveSample() {
192
+ try { sessionStorage.removeItem(SAMPLE_KEY); } catch { /* storage off: nothing was kept */ }
193
+ }
194
+
195
+ // The file keeps only what the export says about each person (~130 characters)
196
+ // and the page scores it again on every load, so it always follows the scoring
197
+ // model and LinkedIn's maximum of 30,000 connections is one request of about
198
+ // 4 MB, well under the 10 MB Next reads before middleware (docs/brain/ENDPOINTS.md).
171
199
  export function packConnections(connections) {
172
200
  return connections.map((c) => [c.id, c.name, c.role, c.company, c.profile_url, c.connected_date]);
173
201
  }
@@ -177,41 +205,111 @@ export function unpackConnections(rows = []) {
177
205
  .sort((a, b) => b.power_score - a.power_score);
178
206
  }
179
207
 
180
- export function saveCsvNetwork(connections) {
181
- try {
182
- sessionStorage.setItem(STORAGE_KEY, JSON.stringify({
183
- packed: packConnections(connections), degree2: [], source: 'csv', importedAt: Date.now(),
184
- }));
185
- return true;
186
- } catch {
187
- return false; // quota
188
- }
208
+ /**
209
+ * Which network every page opens on: 'sample', 'csv' or 'own' (the database).
210
+ * The one rule, so the map, Paths, Settings and who "you" are can't disagree.
211
+ *
212
+ * The sample, opened in this window, is shown until it's left. A kept CSV
213
+ * import is shown only while the database has no 1st-degree people: once you
214
+ * have scanned your own connections, the scan is your network. When the CSV
215
+ * lived in the window, closing it handed the map back to the scan by itself;
216
+ * kept, it would otherwise hide every scan after it for good (the 1.0 review,
217
+ * 2026-10-03). The file itself stays on the disk, never deleted for you, and
218
+ * the import page says it's there and offers to remove it.
219
+ */
220
+ export function openNetworkSource({ sample = false, csvKept = false, scannedFirstDegree = 0 } = {}) {
221
+ if (sample) return 'sample';
222
+ if (csvKept && !(Number(scannedFirstDegree) > 0)) return 'csv';
223
+ return 'own';
189
224
  }
190
225
 
191
- export function hasCsvNetwork() {
192
- try { return !!sessionStorage.getItem(STORAGE_KEY); } catch { return false; }
226
+ // The kept import, and how many 1st-degree people the database has, asked for
227
+ // once per page: who "you" are, the map, and the Settings sections all ask,
228
+ // and it's the same answer. Forgotten whenever it changes, since a link to
229
+ // another page keeps this module loaded.
230
+ let kept = null;
231
+ function keptCsv() {
232
+ kept ??= fetch(CSV_ROUTE, { cache: 'no-store' })
233
+ .then(async (r) => {
234
+ const j = await r.json().catch(() => ({}));
235
+ if (!r.ok) return { csv: null, scanned: 0, problem: `Your kept CSV import couldn't be loaded (${j.error || `the app answered ${r.status}`}).` };
236
+ return { csv: j.csv || null, scanned: Number(j.scanned) || 0, problem: j.problem || null };
237
+ })
238
+ // No answer at all: the app isn't running, and nothing else on the page will load either.
239
+ .catch(() => ({ csv: null, scanned: 0, problem: null }));
240
+ return kept;
193
241
  }
194
242
 
195
- /** 'csv' or 'sample' when this tab holds one, else null — without rescoring it as loadCsvNetwork() does. */
196
- export function csvNetworkSource() {
197
- try {
198
- const raw = sessionStorage.getItem(STORAGE_KEY);
199
- return raw ? JSON.parse(raw).source || 'csv' : null;
200
- } catch { return null; }
243
+ async function opening() {
244
+ const k = await keptCsv();
245
+ return { ...k, source: openNetworkSource({ sample: sampleOpen(), csvKept: Boolean(k.csv), scannedFirstDegree: k.scanned }) };
201
246
  }
202
247
 
203
- export function loadCsvNetwork() {
248
+ /** The network open instead of the database: the sample or a kept CSV import (openNetworkSource), else null. */
249
+ export async function loadCsvNetwork() {
250
+ const { source, csv } = await opening();
251
+ if (source === 'sample') return sampleNetwork();
252
+ if (source !== 'csv') return null;
253
+ return { degree1: unpackConnections(csv.connections), degree2: [], source: 'csv', importedAt: csv.importedAt };
254
+ }
255
+
256
+ /** 'sample' or 'csv' when one is open instead of the database, else null, without scoring it as loadCsvNetwork() does. */
257
+ export async function csvNetworkSource() {
258
+ const { source } = await opening();
259
+ return source === 'own' ? null : source;
260
+ }
261
+
262
+ /**
263
+ * A CSV import kept here but not shown, because the database has your scanned
264
+ * connections: `{ people, importedAt }`, else null. The import page says so
265
+ * and offers to remove it.
266
+ */
267
+ export async function keptBehindScan() {
268
+ const { source, csv } = await opening();
269
+ return csv && source === 'own' ? { people: csv.people, importedAt: csv.importedAt } : null;
270
+ }
271
+
272
+ /** What × and the import page's Remove ask before a kept import is deleted. */
273
+ export const REMOVE_CSV_QUESTION = 'Remove this CSV import from this computer? Your Connections.csv file isn’t touched, so you can import it again.';
274
+
275
+ /** Why a kept import is there but can't be opened, or null. Said on the welcome screen, never passed off as no import (TRAPS §7). */
276
+ export async function keptCsvProblem() {
277
+ return (await keptCsv()).problem;
278
+ }
279
+
280
+ /** Keep an import, in place of any kept before. Resolves { ok: true, people } or { ok: false, error }, the error fit to show. */
281
+ export async function saveCsvNetwork(connections) {
282
+ kept = null;
204
283
  try {
205
- const raw = sessionStorage.getItem(STORAGE_KEY);
206
- if (!raw) return null;
207
- const parsed = JSON.parse(raw);
208
- const degree1 = parsed.packed ? unpackConnections(parsed.packed) : parsed.degree1 || [];
209
- return { degree1, degree2: parsed.degree2 || [], source: parsed.source || 'csv' };
210
- } catch { return null; }
284
+ const r = await fetch(CSV_ROUTE, {
285
+ method: 'POST',
286
+ headers: { 'Content-Type': 'application/json' },
287
+ body: JSON.stringify({ connections: packConnections(connections) }),
288
+ });
289
+ const j = await r.json().catch(() => ({}));
290
+ if (!r.ok) return { ok: false, error: j.error || `The import couldn't be kept (the app answered ${r.status}).` };
291
+ // The import is what the map opens on now, not a sample still open in this window.
292
+ leaveSample();
293
+ return { ok: true, people: j.people };
294
+ } catch {
295
+ return { ok: false, error: "The import couldn't be kept: Six Degrees didn't answer. Check that it's still running, then try again." };
296
+ }
211
297
  }
212
298
 
213
- export function clearCsvNetwork() {
214
- try { sessionStorage.removeItem(STORAGE_KEY); } catch {}
299
+ /** Close what's open: the sample is forgotten by this window; a CSV import is removed from the data folder. Resolves { ok } or { ok: false, error }. */
300
+ export async function closeCsvNetwork(source) {
301
+ kept = null;
302
+ if (source === 'sample') {
303
+ leaveSample();
304
+ return { ok: true };
305
+ }
306
+ try {
307
+ const r = await fetch(CSV_ROUTE, { method: 'DELETE' });
308
+ const j = await r.json().catch(() => ({}));
309
+ return r.ok ? { ok: true } : { ok: false, error: j.error || `The import couldn't be removed (the app answered ${r.status}).` };
310
+ } catch {
311
+ return { ok: false, error: "The import couldn't be removed: Six Degrees didn't answer. Check that it's still running, then try again." };
312
+ }
215
313
  }
216
314
 
217
315
  export const CSV_USER = { id: 'csv-local-user', name: 'You' };
@@ -17,18 +17,29 @@ import { AVATAR_FILE } from './photos.js';
17
17
  // import accepts exactly those names.
18
18
  export { AVATAR_FILE };
19
19
 
20
+ /**
21
+ * A LinkedIn Connections.csv import, kept so it is still there after the
22
+ * window closes (lib/csv-store.js). Kept beside the database, never merged
23
+ * into it: a CSV row has no bridge, no user and no id the scanner made, and
24
+ * folding it in by profile link is how duplicates and lost rows happened
25
+ * before (TRAPS §19, §25, §36).
26
+ */
27
+ export const CSV_NETWORK_FILE = 'csv-network.json';
28
+
20
29
  /**
21
30
  * The scanner's notes about the network itself: without them, people whose
22
31
  * circles are hidden or half-read would be retried from scratch on the new
23
32
  * computer (TRAPS §15, §34). They describe one network's people, so an import
24
33
  * replaces them along with the network: this computer's notes would say which
25
34
  * pages of the old network's people were read, and the scanner would skip
26
- * those pages for the new one.
35
+ * those pages for the new one. A kept CSV import is a network of its own, so
36
+ * it travels and is replaced the same way (what was here goes to backups/).
27
37
  */
28
38
  export const NETWORK_FILES = [
29
39
  'bridge-progress.json',
30
40
  'bridge-skips.json',
31
41
  'bridge-unclear.json',
42
+ CSV_NETWORK_FILE,
32
43
  ];
33
44
 
34
45
  /**
@@ -209,10 +220,15 @@ export function folderOpener(platform = process.platform) {
209
220
  /**
210
221
  * Open the data folder in Finder (or the Linux file browser). The path is the
211
222
  * data folder itself, never anything from a request. Resolves { ok, message }.
223
+ * `select` is a file in `dir` (a backup, lib/backups.js), its whole path, that
224
+ * Finder shows selected (`open -R`); a Linux file browser just opens the
225
+ * folder. The caller builds the path: one built here makes Next's build trace
226
+ * the whole project into the app (TRAPS §27).
212
227
  */
213
- export function revealFolder(dir, { platform = process.platform, spawnImpl = spawn, waitMs = 3000 } = {}) {
228
+ export function revealFolder(dir, { platform = process.platform, spawnImpl = spawn, waitMs = 3000, select = null } = {}) {
214
229
  const cmd = folderOpener(platform);
215
230
  if (!cmd) return Promise.resolve({ ok: false, message: 'There is no file browser to open it with on this computer.' });
231
+ const args = select && cmd === 'open' ? ['-R', select] : [dir];
216
232
  return new Promise((resolve) => {
217
233
  let timer = null;
218
234
  let settled = false;
@@ -224,7 +240,7 @@ export function revealFolder(dir, { platform = process.platform, spawnImpl = spa
224
240
  };
225
241
  let child;
226
242
  try {
227
- child = spawnImpl(cmd, [dir], { stdio: 'ignore', detached: true });
243
+ child = spawnImpl(cmd, args, { stdio: 'ignore', detached: true });
228
244
  } catch (err) {
229
245
  done({ ok: false, message: `It couldn't be opened (${err.message}).` });
230
246
  return;
@@ -270,7 +270,7 @@ export function validateImport(file, { applySchema, schemaSql, appVersion, retir
270
270
  }
271
271
  const present = new Set(objects.filter((o) => o.type === 'table').map((o) => o.name));
272
272
  if (!present.has('sd_export_manifest') || !present.has('sd_export_files')) {
273
- throw new ImportError("This is a database but not a Six Degrees export, so nothing was imported. Make one on the other computer with Settings → Your data → Save a copy.");
273
+ throw new ImportError("This is a database but not a Six Degrees export, so nothing was imported. Make one on the other computer with Export backup file… in Settings → Your data.");
274
274
  }
275
275
  for (const [t, { required, allowed }] of ref.tables) {
276
276
  if (!present.has(t)) {
@@ -380,7 +380,7 @@ export function validateImport(file, { applySchema, schemaSql, appVersion, retir
380
380
  * @returns what the Settings page shows while the import waits.
381
381
  */
382
382
  export function stageImport(upload, {
383
- dir, applySchema, schemaSql, appVersion, replacedPeople = 0, now = new Date(), retired = RETIRED,
383
+ dir, applySchema, schemaSql, appVersion, replacedPeople = 0, now = new Date(), retired = RETIRED, restoredFrom = null,
384
384
  }) {
385
385
  const info = validateImport(upload, { applySchema, schemaSql, appVersion, retired });
386
386
  const work = path.dirname(upload);
@@ -436,6 +436,8 @@ export function stageImport(upload, {
436
436
  people: info.people,
437
437
  photos: info.photos,
438
438
  replacedPeople,
439
+ // A backup from backups/ being put back (lib/backups.js restoreBackup), by its name.
440
+ ...(restoredFrom ? { restoredFrom } : {}),
439
441
  };
440
442
  writeFileSync(path.join(work, 'READY'), `${JSON.stringify(ready, null, 2)}\n`);
441
443
  // On the disk before import-pending/ appears: the next start moves this
@@ -822,6 +824,7 @@ export function applyPendingImport({ dir, dbFile, now = new Date(), copyDatabase
822
824
  people: ready.people ?? null,
823
825
  photos: ready.photos ?? null,
824
826
  replacedPeople: ready.replacedPeople ?? null,
827
+ restoredFrom: ready.restoredFrom ?? null,
825
828
  keptDatabase: exists(keptDb) ? shown(keptDb) : null,
826
829
  keptFiles: exists(keptFiles) ? shown(keptFiles) : null,
827
830
  // Set when the database that was here couldn't be read, so it was kept
package/lib/db-client.js CHANGED
@@ -1,10 +1,15 @@
1
1
  import { DatabaseSync } from 'node:sqlite';
2
- import { mkdirSync, existsSync, readFileSync, writeFileSync, readdirSync, statSync, rmSync } from 'node:fs';
2
+ import { mkdirSync, existsSync, readFileSync, writeFileSync, renameSync, rmSync } from 'node:fs';
3
3
  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
7
  import { applyPendingImport, recordImport } from './data-import.js';
8
+ import { compareVersions } from './release.js';
9
+ import { durable } from './durable.js';
10
+ import {
11
+ makeBackup, rotateBackups, dailyBackupIfDue, noteBackupFailure, AUTO_BACKUP_PREFIX,
12
+ } from './backups.js';
8
13
 
9
14
  // The database lives OUTSIDE the app directory so an installed copy never
10
15
  // writes into its own package. Override with SIX_DEGREES_HOME.
@@ -78,7 +83,7 @@ export function getDb() {
78
83
  // Before this version changes anything: if a different version opened this
79
84
  // data last, keep a copy of it as it was. See backupOnNewVersion().
80
85
  try {
81
- const saved = backupOnNewVersion(_db, { dir: path.dirname(file), version: APP_VERSION });
86
+ const saved = backupOnNewVersion(_db, { dir: path.dirname(file), dataDir: dataDir(), version: APP_VERSION });
82
87
  if (saved) console.log(`Six Degrees ${APP_VERSION}: backed up your data first → ${saved}`);
83
88
  } catch (err) {
84
89
  console.error(`Six Degrees: could not back up your data before ${APP_VERSION} (will try again next start): ${err.message}`);
@@ -90,11 +95,39 @@ export function getDb() {
90
95
 
91
96
  if (imported) {
92
97
  try { recordImport(_db, imported); } catch { /* only the Settings page's note is lost */ }
98
+ // The copy that import kept of what was here joins the rotation (lib/backups.js KEEP).
99
+ try { rotateBackups(backupsFolder()); } catch { /* tried again after the next backup */ }
100
+ }
101
+
102
+ // Once a day: checked here, as the server opens the data, and after each
103
+ // scan (app/api/scraper/route.js). No timer: nothing runs that nobody started.
104
+ try {
105
+ const daily = backUpDailyIfDue(_db);
106
+ if (daily) console.log(`Six Degrees: made today's backup → ${daily.file}`);
107
+ } catch (err) {
108
+ console.error(`Six Degrees: could not make today's backup (will try again after the next scan or start): ${err.message}`);
93
109
  }
94
110
 
95
111
  return _db;
96
112
  }
97
113
 
114
+ /** backups/, beside the database (where an import keeps its copy too). */
115
+ export function backupsFolder() {
116
+ return path.join(path.dirname(dbFile()), 'backups');
117
+ }
118
+
119
+ /** What lib/backups.js needs to make, check and restore this copy's backups. */
120
+ export function backupOptions(db = getDb()) {
121
+ return {
122
+ db, dataDir: dataDir(), backupsDir: backupsFolder(), appVersion: APP_VERSION, schemaSql: SCHEMA_SQL, applySchema,
123
+ };
124
+ }
125
+
126
+ /** The daily backup, if one is due (lib/backups.js dailyBackupIfDue). @returns it, or null. */
127
+ export function backUpDailyIfDue(db = getDb(), now = new Date()) {
128
+ return dailyBackupIfDue({ ...backupOptions(db), now });
129
+ }
130
+
98
131
  /**
99
132
  * Every table, index and column this version uses, made if missing. An import
100
133
  * is rebuilt into exactly this (lib/data-import.js), so only rows travel.
@@ -120,16 +153,24 @@ function addMissingColumns(db) {
120
153
 
121
154
  // ── a copy of the data before each new version touches it ──────────────────
122
155
  // The first time a version opens a database that a different version (or one
123
- // from before this existed) used last, it copies the database, as it was, into
124
- // backups/ — before the schema step or anything else writes to it. VACUUM INTO
125
- // makes one consistent file even mid-WAL. The newest five automatic copies are
126
- // kept; anything else in backups/ (copies made by hand) is never touched. The
127
- // marker is written only after the copy succeeds, so a failed copy (a full
128
- // disk) is retried at the next start.
156
+ // from before this existed) used last, it backs it up, as it was, into
157
+ // backups/: before the schema step or anything else writes to it. The backup
158
+ // is the whole network, photos and files too (lib/backups.js makeBackup), so
159
+ // Settings → Your data → Restore can put it back. The newest three are kept;
160
+ // anything someone put in backups/ themselves is never touched. The marker is
161
+ // written only after a copy succeeds, so a failed one (a full disk) is retried
162
+ // at the next start.
163
+ //
164
+ // If that backup can't be made or fails its check (a database holding
165
+ // something an export never does, say), the database alone is still copied,
166
+ // as every version before this did: a copy that only a person can put back is
167
+ // better than none before an update. The page says the backup didn't work.
129
168
 
130
- export const AUTO_BACKUP_PREFIX = 'auto-before-';
169
+ export { AUTO_BACKUP_PREFIX };
131
170
 
132
- export function backupOnNewVersion(db, { dir, version, keep = 5, now = new Date() }) {
171
+ export function backupOnNewVersion(db, {
172
+ dir, version, dataDir: filesDir = dir, now = new Date(), schemaSql = SCHEMA_SQL, applySchema: schemaStep = applySchema,
173
+ }) {
133
174
  const marker = path.join(dir, 'app-version');
134
175
  let last = null;
135
176
  try { last = readFileSync(marker, 'utf8').trim(); } catch { /* first run, or older than this */ }
@@ -141,16 +182,33 @@ export function backupOnNewVersion(db, { dir, version, keep = 5, now = new Date(
141
182
  ).get().n > 0;
142
183
  if (hasData) {
143
184
  const folder = path.join(dir, 'backups');
144
- mkdirSync(folder, { recursive: true });
145
- const stamp = now.toISOString().replace(/[:.]/g, '-');
146
- saved = path.join(folder, `${AUTO_BACKUP_PREFIX}${version}-${stamp}.sqlite`);
147
- db.exec(`VACUUM INTO '${saved.replace(/'/g, "''")}'`);
148
-
149
- const autos = readdirSync(folder)
150
- .filter((f) => f.startsWith(AUTO_BACKUP_PREFIX) && f.endsWith('.sqlite'))
151
- .map((f) => ({ f, at: statSync(path.join(folder, f)).mtimeMs }))
152
- .sort((a, b) => b.at - a.at || b.f.localeCompare(a.f));
153
- for (const { f } of autos.slice(keep)) rmSync(path.join(folder, f), { force: true });
185
+ // The data is the last version's, not this one's: say so in the copy, so
186
+ // that version can restore it if this one has to be put back. (Data a
187
+ // newer version wrote, or with no marker, is labelled with this version.)
188
+ const known = /^v?\d+\.\d+\.\d+/.test(last || '') && compareVersions(last, version) <= 0;
189
+ try {
190
+ saved = makeBackup('before-update', {
191
+ db, dataDir: filesDir, backupsDir: folder, appVersion: version, dataVersion: known ? last : version,
192
+ schemaSql, applySchema: schemaStep, now,
193
+ }).file;
194
+ } catch (err) {
195
+ noteBackupFailure('before-update', err, now);
196
+ mkdirSync(folder, { recursive: true });
197
+ saved = path.join(folder, `${AUTO_BACKUP_PREFIX}${version}-${now.toISOString().replace(/[:.]/g, '-')}.sqlite`);
198
+ // Through the open handle, and synced before it takes its name (the
199
+ // vacuumCopy pattern, lib/data-import.js).
200
+ const partial = path.join(folder, `.${path.basename(saved)}.partial`);
201
+ try {
202
+ db.exec(`VACUUM INTO '${partial.replace(/'/g, "''")}'`);
203
+ durable.syncFile(partial);
204
+ renameSync(partial, saved);
205
+ durable.syncFolder(folder);
206
+ } catch (copyErr) {
207
+ rmSync(partial, { force: true });
208
+ throw copyErr;
209
+ }
210
+ try { rotateBackups(folder, { now }); } catch { /* tried again after the next one */ }
211
+ }
154
212
  }
155
213
  writeFileSync(marker, `${version}\n`);
156
214
  return saved;
package/lib/demo.js CHANGED
@@ -4,7 +4,7 @@
4
4
  // It exists so a first run has something to show, so screenshots contain no real
5
5
  // people, and so the Bridges view can be explored before you have data of your own.
6
6
 
7
- import { saveSessionNetwork } from './csv';
7
+ import { saveSampleNetwork } from './csv';
8
8
 
9
9
  export const IS_DEMO = process.env.NEXT_PUBLIC_DEMO_MODE === 'true';
10
10
 
@@ -19,16 +19,17 @@ let _cache = null;
19
19
  export async function loadDemoNetwork() {
20
20
  if (_cache) return _cache;
21
21
  const res = await fetch('/demo-data.json', { cache: 'no-cache' });
22
- if (!res.ok) throw new Error('demo-data.json not found — run: node scripts/gen-synthetic.mjs');
22
+ if (!res.ok) throw new Error('demo-data.json not found. Run: node scripts/gen-synthetic.mjs');
23
23
  const data = await res.json();
24
24
  _cache = { degree1: data.degree1 || [], degree2: data.degree2 || [] };
25
25
  return _cache;
26
26
  }
27
27
 
28
28
  // Loads the sample into this browser tab and reports how big it is. Nothing is
29
- // written to the database, so it disappears when the tab closes.
29
+ // written to the database or the data folder, so it disappears when the tab
30
+ // closes (a CSV import is kept; the sample isn't anyone's: lib/csv.js).
30
31
  export async function loadSampleIntoSession() {
31
32
  const net = await loadDemoNetwork();
32
- saveSessionNetwork({ degree1: net.degree1, degree2: net.degree2, source: 'sample' });
33
+ saveSampleNetwork({ degree1: net.degree1, degree2: net.degree2 });
33
34
  return { degree1: net.degree1.length, degree2: net.degree2.length };
34
35
  }
@@ -8,6 +8,10 @@ import { MAP_LOOK } from './themes.js';
8
8
 
9
9
  let current = null; // { svg, stamp: () => text or null, legend: () => [[label, colour]] }
10
10
 
11
+ // The small credit in the corner of every picture and video. A shared picture is
12
+ // how someone finds the app, so it names where to get it.
13
+ export const CREDIT = 'sixdegreesapp.com';
14
+
11
15
  export function registerGalaxy(entry) {
12
16
  current = entry;
13
17
  return () => { if (current === entry) current = null; };
@@ -31,7 +35,10 @@ function svgImage(svg) {
31
35
  // The page's font, which the <svg> inherits on screen but not in a file.
32
36
  clone.setAttribute('font-family', getComputedStyle(svg).fontFamily || 'system-ui, sans-serif');
33
37
  const style = document.createElementNS('http://www.w3.org/2000/svg', 'style');
34
- style.textContent = EXPORT_CSS;
38
+ // The theme's colours (lib/themes.js), which the map reads from the page and a file can't.
39
+ const root = document.documentElement;
40
+ const vars = [...root.style].filter((k) => k.startsWith('--sd-')).map((k) => `${k}:${root.style.getPropertyValue(k)}`).join(';');
41
+ style.textContent = `svg{${vars}}${EXPORT_CSS}`;
35
42
  clone.insertBefore(style, clone.firstChild);
36
43
  const xml = new XMLSerializer().serializeToString(clone);
37
44
  const img = new Image();
@@ -76,7 +83,7 @@ async function drawFrame(ctx, w, h) {
76
83
  }
77
84
  ctx.textAlign = 'right';
78
85
  ctx.fillStyle = 'rgba(255,255,255,0.35)';
79
- ctx.fillText('Six Degrees', cw - 16, 24);
86
+ ctx.fillText(CREDIT, cw - 16, 24);
80
87
  ctx.restore();
81
88
  }
82
89
 
package/lib/galaxy-lab.js CHANGED
@@ -9,6 +9,9 @@
9
9
 
10
10
  import { keyFor, score } from './separation.js';
11
11
  import { warmthOf } from './linkedin-export.js';
12
+ import { LAYOUT_KEYS, cleanLayouts } from './galaxy-layouts.js';
13
+ import { saveSettings } from './settings-client.js';
14
+ import { reconcileSaved } from './synced-setting.js';
12
15
 
13
16
  const KEY = 'six-degrees-galaxy-lab';
14
17
  const LAYOUTS_KEY = 'six-degrees-galaxy-layouts';
@@ -34,9 +37,6 @@ export const LAB_DEFAULTS = Object.freeze({
34
37
  v: 2, // which defaults a saved setting was made under (2: the branch starts off)
35
38
  });
36
39
 
37
- /** The slider settings a saved layout keeps (not whether the lab is on, nor the names switch). */
38
- const LAYOUT_KEYS = ['gravity', 'rings', 'push', 'pull', 'distance', 'orbit', 'dotSize', 'lines', 'sizeBy', 'names', 'branch', 'colourBy', 'speed', 'loop'];
39
-
40
40
  /** The forces a layout sets, and the ones the Galaxy re-settles for when they change. */
41
41
  export const FORCE_KEYS = ['gravity', 'rings', 'push', 'pull', 'distance'];
42
42
 
@@ -105,7 +105,10 @@ export function watchLab(f) {
105
105
  /** What the Galaxy should use: the sliders while the lab is on, today's layout otherwise (with the names switch). */
106
106
  export const effectiveLab = (lab) => (lab.on ? lab : lab.labels === LAB_DEFAULTS.labels ? LAB_DEFAULTS : { ...LAB_DEFAULTS, labels: lab.labels });
107
107
 
108
- // Saved layouts: your own named slider settings, in this browser.
108
+ // Saved layouts: your own named slider settings (lib/galaxy-layouts.js), saved
109
+ // with the network's settings so they travel with a backup or a restore, and
110
+ // copied in this browser to show before the server has answered
111
+ // (lib/synced-setting.js, app/components/ThemeLoader.js).
109
112
  let layoutsCache = null;
110
113
  const layoutListeners = new Set();
111
114
  export function layoutsNow() {
@@ -115,10 +118,21 @@ export function layoutsNow() {
115
118
  }
116
119
  return layoutsCache;
117
120
  }
118
- function writeLayouts(list) {
121
+ function writeLayouts(list, { save = true } = {}) {
119
122
  layoutsCache = list;
120
123
  try { localStorage.setItem(LAYOUTS_KEY, JSON.stringify(list)); } catch {}
121
124
  layoutListeners.forEach((f) => f());
125
+ if (save) saveSettings({ galaxyLayouts: list });
126
+ }
127
+ /** The layouts saved with the network (GET /api/settings 'galaxyLayouts'), once the page has them. */
128
+ export function adoptSavedLayouts(saved) {
129
+ const cached = cleanLayouts(layoutsNow());
130
+ const { use, save, changed } = reconcileSaved({
131
+ saved: saved == null ? null : cleanLayouts(saved), cached,
132
+ isDefault: (list) => list.length === 0, same: (a, b) => JSON.stringify(a) === JSON.stringify(b),
133
+ });
134
+ if (save) saveSettings({ galaxyLayouts: cached });
135
+ if (changed) writeLayouts(use, { save: false });
122
136
  }
123
137
  export function watchLayouts(f) { layoutListeners.add(f); return () => layoutListeners.delete(f); }
124
138
  export function saveLayout(name) {
@@ -0,0 +1,43 @@
1
+ // The Galaxy's saved layouts (Physics → Save layout): your own named slider
2
+ // settings. Kept with the network's settings (lib/settings.js), so they travel
3
+ // with a backup, an export and a restore; the browser keeps a copy to read
4
+ // before the page has asked the server (lib/galaxy-lab.js). No imports: the
5
+ // server checks a saved list with the same rules the browser keeps it by.
6
+
7
+ /** The slider settings a saved layout keeps (not whether the lab is on, nor the names switch). */
8
+ export const LAYOUT_KEYS = ['gravity', 'rings', 'push', 'pull', 'distance', 'orbit', 'dotSize', 'lines', 'sizeBy', 'names', 'branch', 'colourBy', 'speed', 'loop'];
9
+
10
+ /** More than anyone names by hand, and a bound on what a stray request can store. */
11
+ export const MAX_LAYOUTS = 50;
12
+
13
+ const fine = (v) => (typeof v === 'boolean')
14
+ || (typeof v === 'number' && Number.isFinite(v) && Math.abs(v) <= 1000)
15
+ || (typeof v === 'string' && /^[a-z]{1,20}$/i.test(v));
16
+
17
+ /**
18
+ * A saved list made safe: each layout a name (1 to 40 characters) and only the
19
+ * slider settings a layout keeps, each a plain number, word or switch. A name
20
+ * saved twice keeps its last settings, where the first one stood.
21
+ */
22
+ export function cleanLayouts(raw) {
23
+ if (!Array.isArray(raw)) return [];
24
+ const byName = new Map();
25
+ for (const item of raw) {
26
+ const name = typeof item?.name === 'string' ? item.name.trim().slice(0, 40) : '';
27
+ if (!name || !item.settings || typeof item.settings !== 'object' || Array.isArray(item.settings)) continue;
28
+ const settings = {};
29
+ for (const k of LAYOUT_KEYS) if (Object.hasOwn(item.settings, k) && fine(item.settings[k])) settings[k] = item.settings[k];
30
+ byName.set(name, { name, settings });
31
+ }
32
+ return [...byName.values()].slice(0, MAX_LAYOUTS);
33
+ }
34
+
35
+ /** In lib/settings.js: null until a list is saved on this network, so a browser's own list is saved once. */
36
+ export const GALAXY_LAYOUTS_SETTING = {
37
+ default: null,
38
+ parse(value) {
39
+ if (value === null) return null;
40
+ if (!Array.isArray(value)) throw new Error('Saved layouts must be a list.');
41
+ return cleanLayouts(value);
42
+ },
43
+ };
package/lib/gate.js CHANGED
@@ -8,10 +8,13 @@
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
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.
11
+ // The six under /api/data are Settings → Your data: export hands over the
12
+ // whole network in one file, import replaces it, restart stops the server,
13
+ // reveal starts a process, backup writes the whole network to a file, and
14
+ // restore replaces it with a backup. Reading /api/data itself (sizes, the
15
+ // folder's path, the backups) changes nothing and stays open, like every other read.
16
+ // /api/data/csv keeps, replaces, hands over and removes a CSV import kept in
17
+ // the data folder.
15
18
  export const DESTRUCTIVE_ROUTES = [
16
19
  '/api/admin-delete',
17
20
  '/api/admin-update',
@@ -23,6 +26,9 @@ export const DESTRUCTIVE_ROUTES = [
23
26
  '/api/data/import',
24
27
  '/api/data/restart',
25
28
  '/api/data/reveal',
29
+ '/api/data/backup',
30
+ '/api/data/restore',
31
+ '/api/data/csv',
26
32
  ];
27
33
 
28
34
  const LOOPBACK = new Set(['127.0.0.1', '::1', 'localhost']);