@singi-labs/sifa-sdk 0.19.44 → 0.19.45

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 (575) hide show
  1. package/dist/atproto/index.js +9 -3
  2. package/dist/atproto/index.js.map +1 -1
  3. package/dist/badge/index.js +5 -141
  4. package/dist/badge/index.js.map +1 -1
  5. package/dist/cards/adult-content.js +14 -0
  6. package/dist/cards/adult-content.js.map +1 -0
  7. package/dist/cards/app-url-patterns.js +369 -0
  8. package/dist/cards/app-url-patterns.js.map +1 -0
  9. package/dist/cards/index.js +24 -0
  10. package/dist/cards/index.js.map +1 -0
  11. package/dist/cards/resolve-card-url.js +198 -0
  12. package/dist/cards/resolve-card-url.js.map +1 -0
  13. package/dist/cards/visibility.js +45 -0
  14. package/dist/cards/visibility.js.map +1 -0
  15. package/dist/flags/index.js +4 -3
  16. package/dist/flags/index.js.map +1 -1
  17. package/dist/format/collapse-contributors.js +19 -0
  18. package/dist/format/collapse-contributors.js.map +1 -0
  19. package/dist/format/company-name.js +61 -0
  20. package/dist/format/company-name.js.map +1 -0
  21. package/dist/format/compare-text.js +9 -0
  22. package/dist/format/compare-text.js.map +1 -0
  23. package/dist/format/doi.js +8 -0
  24. package/dist/format/doi.js.map +1 -0
  25. package/dist/format/embed.js +131 -0
  26. package/dist/format/embed.js.map +1 -0
  27. package/dist/format/endorsement-context.js +35 -0
  28. package/dist/format/endorsement-context.js.map +1 -0
  29. package/dist/format/format-display-url.js +42 -0
  30. package/dist/format/format-display-url.js.map +1 -0
  31. package/dist/format/format-duration.js +13 -0
  32. package/dist/format/format-duration.js.map +1 -0
  33. package/dist/format/format-time.js +30 -0
  34. package/dist/format/format-time.js.map +1 -0
  35. package/dist/format/handle-utils.js +19 -0
  36. package/dist/format/handle-utils.js.map +1 -0
  37. package/dist/format/index.js +100 -0
  38. package/dist/format/index.js.map +1 -0
  39. package/dist/format/location-segments.js +29 -0
  40. package/dist/format/location-segments.js.map +1 -0
  41. package/dist/format/location-utils.js +42 -0
  42. package/dist/format/location-utils.js.map +1 -0
  43. package/dist/format/meta-description.js +35 -0
  44. package/dist/format/meta-description.js.map +1 -0
  45. package/dist/format/normalize-company-key.js +12 -0
  46. package/dist/format/normalize-company-key.js.map +1 -0
  47. package/dist/format/normalize-legal-form.js +71 -0
  48. package/dist/format/normalize-legal-form.js.map +1 -0
  49. package/dist/format/pds-utils.js +73 -0
  50. package/dist/format/pds-utils.js.map +1 -0
  51. package/dist/format/sort-by-date.js +39 -0
  52. package/dist/format/sort-by-date.js.map +1 -0
  53. package/dist/format/summarize-deliveries.js +37 -0
  54. package/dist/format/summarize-deliveries.js.map +1 -0
  55. package/dist/format/talk-slug.js +21 -0
  56. package/dist/format/talk-slug.js.map +1 -0
  57. package/dist/format/text-sanitize.js +29 -0
  58. package/dist/format/text-sanitize.js.map +1 -0
  59. package/dist/format/text-truncate.js +14 -0
  60. package/dist/format/text-truncate.js.map +1 -0
  61. package/dist/format/time-utils.js +19 -0
  62. package/dist/format/time-utils.js.map +1 -0
  63. package/dist/format/timeline.js +29 -0
  64. package/dist/format/timeline.js.map +1 -0
  65. package/dist/format/wcag-contrast.js +34 -0
  66. package/dist/format/wcag-contrast.js.map +1 -0
  67. package/dist/import/index.js +22 -0
  68. package/dist/import/index.js.map +1 -0
  69. package/dist/import/presentation-csv.js +116 -0
  70. package/dist/import/presentation-csv.js.map +1 -0
  71. package/dist/index.cjs +1 -1
  72. package/dist/index.js +675 -7086
  73. package/dist/index.js.map +1 -1
  74. package/dist/jev/index.js +18 -5
  75. package/dist/jev/index.js.map +1 -1
  76. package/dist/jsonld/index.js +30 -987
  77. package/dist/jsonld/index.js.map +1 -1
  78. package/dist/jsonld/profile.js +222 -0
  79. package/dist/jsonld/profile.js.map +1 -0
  80. package/dist/jsonld/terms.js +496 -0
  81. package/dist/jsonld/terms.js.map +1 -0
  82. package/dist/jsonld/url.js +17 -0
  83. package/dist/jsonld/url.js.map +1 -0
  84. package/dist/jsonld/works-graph.js +41 -0
  85. package/dist/jsonld/works-graph.js.map +1 -0
  86. package/dist/jsonld/works.js +178 -0
  87. package/dist/jsonld/works.js.map +1 -0
  88. package/dist/logic/actor-identity.js +8 -0
  89. package/dist/logic/actor-identity.js.map +1 -0
  90. package/dist/logic/agent-ref.js +24 -0
  91. package/dist/logic/agent-ref.js.map +1 -0
  92. package/dist/logic/company-page-indexable.js +23 -0
  93. package/dist/logic/company-page-indexable.js.map +1 -0
  94. package/dist/logic/date-lookahead.js +31 -0
  95. package/dist/logic/date-lookahead.js.map +1 -0
  96. package/dist/logic/domain-detect.js +43 -0
  97. package/dist/logic/domain-detect.js.map +1 -0
  98. package/dist/logic/entity-disambiguation.js +24 -0
  99. package/dist/logic/entity-disambiguation.js.map +1 -0
  100. package/dist/logic/entity-ref-anchor.js +33 -0
  101. package/dist/logic/entity-ref-anchor.js.map +1 -0
  102. package/dist/logic/index.js +91 -0
  103. package/dist/logic/index.js.map +1 -0
  104. package/dist/logic/org-floor.js +66 -0
  105. package/dist/logic/org-floor.js.map +1 -0
  106. package/dist/logic/primary-item.js +9 -0
  107. package/dist/logic/primary-item.js.map +1 -0
  108. package/dist/logic/primary-position.js +16 -0
  109. package/dist/logic/primary-position.js.map +1 -0
  110. package/dist/logic/profile-completeness.js +21 -0
  111. package/dist/logic/profile-completeness.js.map +1 -0
  112. package/dist/logic/profile-dimensions.js +50 -0
  113. package/dist/logic/profile-dimensions.js.map +1 -0
  114. package/dist/logic/profile-summary.js +23 -0
  115. package/dist/logic/profile-summary.js.map +1 -0
  116. package/dist/logic/pseudo-employer.js +34 -0
  117. package/dist/logic/pseudo-employer.js.map +1 -0
  118. package/dist/logic/recent-activity.js +10 -0
  119. package/dist/logic/recent-activity.js.map +1 -0
  120. package/dist/logic/self-app.js +10 -0
  121. package/dist/logic/self-app.js.map +1 -0
  122. package/dist/{org-BCrvebcf.d.ts → org-CxTEjjrm.d.ts} +1 -1
  123. package/dist/{org-settings-C73Sin5E.d.ts → org-settings-BShvhyFp.d.ts} +1 -1
  124. package/dist/profile/headline-role-dedupe.js +12 -0
  125. package/dist/profile/headline-role-dedupe.js.map +1 -0
  126. package/dist/profile/highlights.js +200 -0
  127. package/dist/profile/highlights.js.map +1 -0
  128. package/dist/profile/index.js +66 -0
  129. package/dist/profile/index.js.map +1 -0
  130. package/dist/profile/involvement-grouping.js +31 -0
  131. package/dist/profile/involvement-grouping.js.map +1 -0
  132. package/dist/profile/language-sort.js +24 -0
  133. package/dist/profile/language-sort.js.map +1 -0
  134. package/dist/profile/publication-versions.js +98 -0
  135. package/dist/profile/publication-versions.js.map +1 -0
  136. package/dist/profile/range-sort.js +14 -0
  137. package/dist/profile/range-sort.js.map +1 -0
  138. package/dist/profile/section-model.js +122 -0
  139. package/dist/profile/section-model.js.map +1 -0
  140. package/dist/profile/section-sorts.js +33 -0
  141. package/dist/profile/section-sorts.js.map +1 -0
  142. package/dist/publishers/index.js +16 -0
  143. package/dist/publishers/index.js.map +1 -0
  144. package/dist/publishers/registry.js +87 -0
  145. package/dist/publishers/registry.js.map +1 -0
  146. package/dist/publishing/index.js +46 -184
  147. package/dist/publishing/index.js.map +1 -1
  148. package/dist/publishing/linkblog.js +42 -0
  149. package/dist/publishing/linkblog.js.map +1 -0
  150. package/dist/publishing/registry.js +32 -0
  151. package/dist/publishing/registry.js.map +1 -0
  152. package/dist/publishing/schemas.js +74 -0
  153. package/dist/publishing/schemas.js.map +1 -0
  154. package/dist/publishing/types.js +32 -0
  155. package/dist/publishing/types.js.map +1 -0
  156. package/dist/query/client.js +119 -0
  157. package/dist/query/client.js.map +1 -0
  158. package/dist/query/config.js +23 -0
  159. package/dist/query/config.js.map +1 -0
  160. package/dist/query/fetchers/accounts.js +29 -0
  161. package/dist/query/fetchers/accounts.js.map +1 -0
  162. package/dist/query/fetchers/activity-items-hide.js +31 -0
  163. package/dist/query/fetchers/activity-items-hide.js.map +1 -0
  164. package/dist/query/fetchers/activity.js +61 -0
  165. package/dist/query/fetchers/activity.js.map +1 -0
  166. package/dist/query/fetchers/actor.js +30 -0
  167. package/dist/query/fetchers/actor.js.map +1 -0
  168. package/dist/query/fetchers/admin-feature-allowlists.js +41 -0
  169. package/dist/query/fetchers/admin-feature-allowlists.js.map +1 -0
  170. package/dist/query/fetchers/admin-review-queues.js +30 -0
  171. package/dist/query/fetchers/admin-review-queues.js.map +1 -0
  172. package/dist/query/fetchers/apps.js +31 -0
  173. package/dist/query/fetchers/apps.js.map +1 -0
  174. package/dist/query/fetchers/bsky-preferences.js +55 -0
  175. package/dist/query/fetchers/bsky-preferences.js.map +1 -0
  176. package/dist/query/fetchers/confirmations.js +55 -0
  177. package/dist/query/fetchers/confirmations.js.map +1 -0
  178. package/dist/query/fetchers/destructive.js +34 -0
  179. package/dist/query/fetchers/destructive.js.map +1 -0
  180. package/dist/query/fetchers/discovery.js +67 -0
  181. package/dist/query/fetchers/discovery.js.map +1 -0
  182. package/dist/query/fetchers/education.js +23 -0
  183. package/dist/query/fetchers/education.js.map +1 -0
  184. package/dist/query/fetchers/endorsement-inbox.js +36 -0
  185. package/dist/query/fetchers/endorsement-inbox.js.map +1 -0
  186. package/dist/query/fetchers/endorsement.js +23 -0
  187. package/dist/query/fetchers/endorsement.js.map +1 -0
  188. package/dist/query/fetchers/endorsements.js +20 -0
  189. package/dist/query/fetchers/endorsements.js.map +1 -0
  190. package/dist/query/fetchers/entities.js +65 -0
  191. package/dist/query/fetchers/entities.js.map +1 -0
  192. package/dist/query/fetchers/external-accounts.js +74 -0
  193. package/dist/query/fetchers/external-accounts.js.map +1 -0
  194. package/dist/query/fetchers/follow-extras.js +38 -0
  195. package/dist/query/fetchers/follow-extras.js.map +1 -0
  196. package/dist/query/fetchers/follow.js +105 -0
  197. package/dist/query/fetchers/follow.js.map +1 -0
  198. package/dist/query/fetchers/get-profile-view.js +21 -0
  199. package/dist/query/fetchers/get-profile-view.js.map +1 -0
  200. package/dist/query/fetchers/github-prs.js +45 -0
  201. package/dist/query/fetchers/github-prs.js.map +1 -0
  202. package/dist/query/fetchers/inbox.js +29 -0
  203. package/dist/query/fetchers/inbox.js.map +1 -0
  204. package/dist/query/fetchers/index.d.ts +2 -2
  205. package/dist/query/fetchers/index.js +360 -2045
  206. package/dist/query/fetchers/index.js.map +1 -1
  207. package/dist/query/fetchers/investments.js +18 -0
  208. package/dist/query/fetchers/investments.js.map +1 -0
  209. package/dist/query/fetchers/keytrace-claims.js +23 -0
  210. package/dist/query/fetchers/keytrace-claims.js.map +1 -0
  211. package/dist/query/fetchers/marque-domains.js +23 -0
  212. package/dist/query/fetchers/marque-domains.js.map +1 -0
  213. package/dist/query/fetchers/network-map.js +36 -0
  214. package/dist/query/fetchers/network-map.js.map +1 -0
  215. package/dist/query/fetchers/org.js +48 -0
  216. package/dist/query/fetchers/org.js.map +1 -0
  217. package/dist/query/fetchers/positions.js +77 -0
  218. package/dist/query/fetchers/positions.js.map +1 -0
  219. package/dist/query/fetchers/profile-completeness.js +45 -0
  220. package/dist/query/fetchers/profile-completeness.js.map +1 -0
  221. package/dist/query/fetchers/profile-items-hide.js +36 -0
  222. package/dist/query/fetchers/profile-items-hide.js.map +1 -0
  223. package/dist/query/fetchers/profile-locations.js +23 -0
  224. package/dist/query/fetchers/profile-locations.js.map +1 -0
  225. package/dist/query/fetchers/profile-mutations.js +82 -0
  226. package/dist/query/fetchers/profile-mutations.js.map +1 -0
  227. package/dist/query/fetchers/profile-summary.js +14 -0
  228. package/dist/query/fetchers/profile-summary.js.map +1 -0
  229. package/dist/query/fetchers/profile.js +31 -0
  230. package/dist/query/fetchers/profile.js.map +1 -0
  231. package/dist/query/fetchers/publications.js +66 -0
  232. package/dist/query/fetchers/publications.js.map +1 -0
  233. package/dist/query/fetchers/quoted-posts.js +40 -0
  234. package/dist/query/fetchers/quoted-posts.js.map +1 -0
  235. package/dist/query/fetchers/reactions.js +85 -0
  236. package/dist/query/fetchers/reactions.js.map +1 -0
  237. package/dist/query/fetchers/received-endorsements.js +20 -0
  238. package/dist/query/fetchers/received-endorsements.js.map +1 -0
  239. package/dist/query/fetchers/reciprocity.js +23 -0
  240. package/dist/query/fetchers/reciprocity.js.map +1 -0
  241. package/dist/query/fetchers/records.js +27 -0
  242. package/dist/query/fetchers/records.js.map +1 -0
  243. package/dist/query/fetchers/repo-inventory.js +23 -0
  244. package/dist/query/fetchers/repo-inventory.js.map +1 -0
  245. package/dist/query/fetchers/roadmap.js +73 -0
  246. package/dist/query/fetchers/roadmap.js.map +1 -0
  247. package/dist/query/fetchers/rpg.js +36 -0
  248. package/dist/query/fetchers/rpg.js.map +1 -0
  249. package/dist/query/fetchers/search.js +78 -0
  250. package/dist/query/fetchers/search.js.map +1 -0
  251. package/dist/query/fetchers/section-primary.js +23 -0
  252. package/dist/query/fetchers/section-primary.js.map +1 -0
  253. package/dist/query/fetchers/skills.js +30 -0
  254. package/dist/query/fetchers/skills.js.map +1 -0
  255. package/dist/query/fetchers/speakers.js +42 -0
  256. package/dist/query/fetchers/speakers.js.map +1 -0
  257. package/dist/query/fetchers/stats.js +16 -0
  258. package/dist/query/fetchers/stats.js.map +1 -0
  259. package/dist/query/fetchers/stream.js +30 -0
  260. package/dist/query/fetchers/stream.js.map +1 -0
  261. package/dist/query/fetchers/unlinked-positions.js +30 -0
  262. package/dist/query/fetchers/unlinked-positions.js.map +1 -0
  263. package/dist/query/hooks/index.d.ts +3 -3
  264. package/dist/query/hooks/index.js +283 -3555
  265. package/dist/query/hooks/index.js.map +1 -1
  266. package/dist/query/hooks/use-accounts.js +19 -0
  267. package/dist/query/hooks/use-accounts.js.map +1 -0
  268. package/dist/query/hooks/use-activity-items-hide.js +52 -0
  269. package/dist/query/hooks/use-activity-items-hide.js.map +1 -0
  270. package/dist/query/hooks/use-activity.js +44 -0
  271. package/dist/query/hooks/use-activity.js.map +1 -0
  272. package/dist/query/hooks/use-admin-review-queues.js +21 -0
  273. package/dist/query/hooks/use-admin-review-queues.js.map +1 -0
  274. package/dist/query/hooks/use-apps.js +31 -0
  275. package/dist/query/hooks/use-apps.js.map +1 -0
  276. package/dist/query/hooks/use-bsky-content-prefs.js +45 -0
  277. package/dist/query/hooks/use-bsky-content-prefs.js.map +1 -0
  278. package/dist/query/hooks/use-confirmations.js +121 -0
  279. package/dist/query/hooks/use-confirmations.js.map +1 -0
  280. package/dist/query/hooks/use-create-position.js +28 -0
  281. package/dist/query/hooks/use-create-position.js.map +1 -0
  282. package/dist/query/hooks/use-destructive.js +63 -0
  283. package/dist/query/hooks/use-destructive.js.map +1 -0
  284. package/dist/query/hooks/use-discovery.js +53 -0
  285. package/dist/query/hooks/use-discovery.js.map +1 -0
  286. package/dist/query/hooks/use-education-mutations.js +67 -0
  287. package/dist/query/hooks/use-education-mutations.js.map +1 -0
  288. package/dist/query/hooks/use-endorsement-inbox.js +80 -0
  289. package/dist/query/hooks/use-endorsement-inbox.js.map +1 -0
  290. package/dist/query/hooks/use-endorsement-mutations.js +33 -0
  291. package/dist/query/hooks/use-endorsement-mutations.js.map +1 -0
  292. package/dist/query/hooks/use-endorsement.js +20 -0
  293. package/dist/query/hooks/use-endorsement.js.map +1 -0
  294. package/dist/query/hooks/use-entity-search.js +73 -0
  295. package/dist/query/hooks/use-entity-search.js.map +1 -0
  296. package/dist/query/hooks/use-external-accounts.js +143 -0
  297. package/dist/query/hooks/use-external-accounts.js.map +1 -0
  298. package/dist/query/hooks/use-feature-allowlist.js +104 -0
  299. package/dist/query/hooks/use-feature-allowlist.js.map +1 -0
  300. package/dist/query/hooks/use-follow-extras.js +38 -0
  301. package/dist/query/hooks/use-follow-extras.js.map +1 -0
  302. package/dist/query/hooks/use-follow.js +113 -0
  303. package/dist/query/hooks/use-follow.js.map +1 -0
  304. package/dist/query/hooks/use-get-profile-view.js +20 -0
  305. package/dist/query/hooks/use-get-profile-view.js.map +1 -0
  306. package/dist/query/hooks/use-github-prs.js +21 -0
  307. package/dist/query/hooks/use-github-prs.js.map +1 -0
  308. package/dist/query/hooks/use-inbox.js +19 -0
  309. package/dist/query/hooks/use-inbox.js.map +1 -0
  310. package/dist/query/hooks/use-investment-mutations.js +65 -0
  311. package/dist/query/hooks/use-investment-mutations.js.map +1 -0
  312. package/dist/query/hooks/use-keytrace-claims.js +50 -0
  313. package/dist/query/hooks/use-keytrace-claims.js.map +1 -0
  314. package/dist/query/hooks/use-location-mutations.js +71 -0
  315. package/dist/query/hooks/use-location-mutations.js.map +1 -0
  316. package/dist/query/hooks/use-marque-domains.js +50 -0
  317. package/dist/query/hooks/use-marque-domains.js.map +1 -0
  318. package/dist/query/hooks/use-org-claim.js +29 -0
  319. package/dist/query/hooks/use-org-claim.js.map +1 -0
  320. package/dist/query/hooks/use-org-domains.js +28 -0
  321. package/dist/query/hooks/use-org-domains.js.map +1 -0
  322. package/dist/query/hooks/use-org-notification-emails.js +28 -0
  323. package/dist/query/hooks/use-org-notification-emails.js.map +1 -0
  324. package/dist/query/hooks/use-org-profile-settings.js +29 -0
  325. package/dist/query/hooks/use-org-profile-settings.js.map +1 -0
  326. package/dist/query/hooks/use-org-profile.js +21 -0
  327. package/dist/query/hooks/use-org-profile.js.map +1 -0
  328. package/dist/query/hooks/use-position-mutations.js +128 -0
  329. package/dist/query/hooks/use-position-mutations.js.map +1 -0
  330. package/dist/query/hooks/use-profile-completeness.js +21 -0
  331. package/dist/query/hooks/use-profile-completeness.js.map +1 -0
  332. package/dist/query/hooks/use-profile-items-hide.js +53 -0
  333. package/dist/query/hooks/use-profile-items-hide.js.map +1 -0
  334. package/dist/query/hooks/use-profile-mutations.js +143 -0
  335. package/dist/query/hooks/use-profile-mutations.js.map +1 -0
  336. package/dist/query/hooks/use-profile.js +30 -0
  337. package/dist/query/hooks/use-profile.js.map +1 -0
  338. package/dist/query/hooks/use-publication-mutations.js +90 -0
  339. package/dist/query/hooks/use-publication-mutations.js.map +1 -0
  340. package/dist/query/hooks/use-reaction-mutations.js +48 -0
  341. package/dist/query/hooks/use-reaction-mutations.js.map +1 -0
  342. package/dist/query/hooks/use-reactions.js +32 -0
  343. package/dist/query/hooks/use-reactions.js.map +1 -0
  344. package/dist/query/hooks/use-received-endorsements.js +23 -0
  345. package/dist/query/hooks/use-received-endorsements.js.map +1 -0
  346. package/dist/query/hooks/use-reciprocity.js +19 -0
  347. package/dist/query/hooks/use-reciprocity.js.map +1 -0
  348. package/dist/query/hooks/use-record-mutations.js +67 -0
  349. package/dist/query/hooks/use-record-mutations.js.map +1 -0
  350. package/dist/query/hooks/use-repo-inventory.js +43 -0
  351. package/dist/query/hooks/use-repo-inventory.js.map +1 -0
  352. package/dist/query/hooks/use-roadmap-mutations.js +51 -0
  353. package/dist/query/hooks/use-roadmap-mutations.js.map +1 -0
  354. package/dist/query/hooks/use-roadmap.js +31 -0
  355. package/dist/query/hooks/use-roadmap.js.map +1 -0
  356. package/dist/query/hooks/use-search.js +63 -0
  357. package/dist/query/hooks/use-search.js.map +1 -0
  358. package/dist/query/hooks/use-section-primary-mutations.js +53 -0
  359. package/dist/query/hooks/use-section-primary-mutations.js.map +1 -0
  360. package/dist/query/hooks/use-skill-mutations.js +91 -0
  361. package/dist/query/hooks/use-skill-mutations.js.map +1 -0
  362. package/dist/query/hooks/use-stats.js +19 -0
  363. package/dist/query/hooks/use-stats.js.map +1 -0
  364. package/dist/query/hooks/use-stream.js +20 -0
  365. package/dist/query/hooks/use-stream.js.map +1 -0
  366. package/dist/query/hooks/use-unlinked-positions.js +45 -0
  367. package/dist/query/hooks/use-unlinked-positions.js.map +1 -0
  368. package/dist/query/index.d.ts +4 -4
  369. package/dist/query/index.js +685 -3744
  370. package/dist/query/index.js.map +1 -1
  371. package/dist/query/keys.js +138 -0
  372. package/dist/query/keys.js.map +1 -0
  373. package/dist/repo/groups.js +81 -0
  374. package/dist/repo/groups.js.map +1 -0
  375. package/dist/repo/index.js +9 -0
  376. package/dist/repo/index.js.map +1 -0
  377. package/dist/repo/labels.js +121 -0
  378. package/dist/repo/labels.js.map +1 -0
  379. package/dist/repo/types.js +1 -0
  380. package/dist/repo/types.js.map +1 -0
  381. package/dist/resume/index.js +5 -179
  382. package/dist/resume/index.js.map +1 -1
  383. package/dist/resume/json-resume.js +153 -0
  384. package/dist/resume/json-resume.js.map +1 -0
  385. package/dist/roadmap/index.js +101 -0
  386. package/dist/roadmap/index.js.map +1 -0
  387. package/dist/rpg/catalog.js +101 -0
  388. package/dist/rpg/catalog.js.map +1 -0
  389. package/dist/rpg/index.js +13 -116
  390. package/dist/rpg/index.js.map +1 -1
  391. package/dist/rpg/unlocks.js +21 -0
  392. package/dist/rpg/unlocks.js.map +1 -0
  393. package/dist/schemas/blob-ref.js +12 -0
  394. package/dist/schemas/blob-ref.js.map +1 -0
  395. package/dist/schemas/confirmation.js +23 -0
  396. package/dist/schemas/confirmation.js.map +1 -0
  397. package/dist/schemas/endorsement-confirmation.js +18 -0
  398. package/dist/schemas/endorsement-confirmation.js.map +1 -0
  399. package/dist/schemas/endorsement.js +19 -0
  400. package/dist/schemas/endorsement.js.map +1 -0
  401. package/dist/schemas/entity.js +74 -0
  402. package/dist/schemas/entity.js.map +1 -0
  403. package/dist/schemas/feed.js +73 -0
  404. package/dist/schemas/feed.js.map +1 -0
  405. package/dist/schemas/follow-profile.js +32 -0
  406. package/dist/schemas/follow-profile.js.map +1 -0
  407. package/dist/schemas/graph-follow.js +19 -0
  408. package/dist/schemas/graph-follow.js.map +1 -0
  409. package/dist/schemas/index.js +164 -629
  410. package/dist/schemas/index.js.map +1 -1
  411. package/dist/schemas/org-employment-attestation.js +28 -0
  412. package/dist/schemas/org-employment-attestation.js.map +1 -0
  413. package/dist/schemas/org-profile.js +64 -0
  414. package/dist/schemas/org-profile.js.map +1 -0
  415. package/dist/schemas/profile-certification.js +28 -0
  416. package/dist/schemas/profile-certification.js.map +1 -0
  417. package/dist/schemas/profile-course.js +32 -0
  418. package/dist/schemas/profile-course.js.map +1 -0
  419. package/dist/schemas/profile-education.js +33 -0
  420. package/dist/schemas/profile-education.js.map +1 -0
  421. package/dist/schemas/profile-external-account.js +15 -0
  422. package/dist/schemas/profile-external-account.js.map +1 -0
  423. package/dist/schemas/profile-honor.js +18 -0
  424. package/dist/schemas/profile-honor.js.map +1 -0
  425. package/dist/schemas/profile-investment.js +48 -0
  426. package/dist/schemas/profile-investment.js.map +1 -0
  427. package/dist/schemas/profile-involvement.js +47 -0
  428. package/dist/schemas/profile-involvement.js.map +1 -0
  429. package/dist/schemas/profile-language.js +12 -0
  430. package/dist/schemas/profile-language.js.map +1 -0
  431. package/dist/schemas/profile-position.js +42 -0
  432. package/dist/schemas/profile-position.js.map +1 -0
  433. package/dist/schemas/profile-presentation-delivery.js +38 -0
  434. package/dist/schemas/profile-presentation-delivery.js.map +1 -0
  435. package/dist/schemas/profile-presentation.js +36 -0
  436. package/dist/schemas/profile-presentation.js.map +1 -0
  437. package/dist/schemas/profile-project.js +48 -0
  438. package/dist/schemas/profile-project.js.map +1 -0
  439. package/dist/schemas/profile-publication.js +30 -0
  440. package/dist/schemas/profile-publication.js.map +1 -0
  441. package/dist/schemas/profile-self.js +22 -0
  442. package/dist/schemas/profile-self.js.map +1 -0
  443. package/dist/schemas/profile-skill.js +13 -0
  444. package/dist/schemas/profile-skill.js.map +1 -0
  445. package/dist/schemas/profile-volunteering.js +20 -0
  446. package/dist/schemas/profile-volunteering.js.map +1 -0
  447. package/dist/schemas/shared.js +56 -0
  448. package/dist/schemas/shared.js.map +1 -0
  449. package/dist/schemas/write/certification.js +16 -0
  450. package/dist/schemas/write/certification.js.map +1 -0
  451. package/dist/schemas/write/course.js +36 -0
  452. package/dist/schemas/write/course.js.map +1 -0
  453. package/dist/schemas/write/education.js +18 -0
  454. package/dist/schemas/write/education.js.map +1 -0
  455. package/dist/schemas/write/external-account.js +13 -0
  456. package/dist/schemas/write/external-account.js.map +1 -0
  457. package/dist/schemas/write/honor.js +14 -0
  458. package/dist/schemas/write/honor.js.map +1 -0
  459. package/dist/schemas/write/index.d.ts +1 -1
  460. package/dist/schemas/write/index.js +89 -593
  461. package/dist/schemas/write/index.js.map +1 -1
  462. package/dist/schemas/write/investment.js +32 -0
  463. package/dist/schemas/write/investment.js.map +1 -0
  464. package/dist/schemas/write/involvement.js +34 -0
  465. package/dist/schemas/write/involvement.js.map +1 -0
  466. package/dist/schemas/write/language.js +10 -0
  467. package/dist/schemas/write/language.js.map +1 -0
  468. package/dist/schemas/write/org-claim.js +56 -0
  469. package/dist/schemas/write/org-claim.js.map +1 -0
  470. package/dist/schemas/write/org-employment-attestation.js +19 -0
  471. package/dist/schemas/write/org-employment-attestation.js.map +1 -0
  472. package/dist/schemas/write/org-profile.js +54 -0
  473. package/dist/schemas/write/org-profile.js.map +1 -0
  474. package/dist/schemas/write/org-settings.js +81 -0
  475. package/dist/schemas/write/org-settings.js.map +1 -0
  476. package/dist/schemas/write/position.js +24 -0
  477. package/dist/schemas/write/position.js.map +1 -0
  478. package/dist/schemas/write/presentation-delivery.js +34 -0
  479. package/dist/schemas/write/presentation-delivery.js.map +1 -0
  480. package/dist/schemas/write/presentation.js +23 -0
  481. package/dist/schemas/write/presentation.js.map +1 -0
  482. package/dist/schemas/write/profile-location.js +21 -0
  483. package/dist/schemas/write/profile-location.js.map +1 -0
  484. package/dist/schemas/write/profile-self.js +27 -0
  485. package/dist/schemas/write/profile-self.js.map +1 -0
  486. package/dist/schemas/write/project.js +27 -0
  487. package/dist/schemas/write/project.js.map +1 -0
  488. package/dist/schemas/write/publication.js +41 -0
  489. package/dist/schemas/write/publication.js.map +1 -0
  490. package/dist/schemas/write/shared.js +96 -0
  491. package/dist/schemas/write/shared.js.map +1 -0
  492. package/dist/schemas/write/skill.js +18 -0
  493. package/dist/schemas/write/skill.js.map +1 -0
  494. package/dist/schemas/write/volunteering.js +16 -0
  495. package/dist/schemas/write/volunteering.js.map +1 -0
  496. package/dist/stream/activity-item.js +1 -0
  497. package/dist/stream/activity-item.js.map +1 -0
  498. package/dist/stream/index.js +49 -0
  499. package/dist/stream/index.js.map +1 -0
  500. package/dist/stream/stream-card-vm-schema.js +265 -0
  501. package/dist/stream/stream-card-vm-schema.js.map +1 -0
  502. package/dist/stream/stream-card-vm.js +1 -0
  503. package/dist/stream/stream-card-vm.js.map +1 -0
  504. package/dist/stream/to-stream-card-vm.js +793 -0
  505. package/dist/stream/to-stream-card-vm.js.map +1 -0
  506. package/dist/stream/two-tier.js +30 -0
  507. package/dist/stream/two-tier.js.map +1 -0
  508. package/dist/stream/verbs.js +120 -0
  509. package/dist/stream/verbs.js.map +1 -0
  510. package/dist/taxonomy/activity-tiers.js +956 -0
  511. package/dist/taxonomy/activity-tiers.js.map +1 -0
  512. package/dist/taxonomy/app-categories.js +42 -0
  513. package/dist/taxonomy/app-categories.js.map +1 -0
  514. package/dist/taxonomy/app-category-map.js +166 -0
  515. package/dist/taxonomy/app-category-map.js.map +1 -0
  516. package/dist/taxonomy/artifact-link-kind.js +29 -0
  517. package/dist/taxonomy/artifact-link-kind.js.map +1 -0
  518. package/dist/taxonomy/calendar-event.js +28 -0
  519. package/dist/taxonomy/calendar-event.js.map +1 -0
  520. package/dist/taxonomy/continents.js +215 -0
  521. package/dist/taxonomy/continents.js.map +1 -0
  522. package/dist/taxonomy/countries.js +203 -0
  523. package/dist/taxonomy/countries.js.map +1 -0
  524. package/dist/taxonomy/course-role.js +37 -0
  525. package/dist/taxonomy/course-role.js.map +1 -0
  526. package/dist/taxonomy/employment-type.js +82 -0
  527. package/dist/taxonomy/employment-type.js.map +1 -0
  528. package/dist/taxonomy/eqf-level.js +59 -0
  529. package/dist/taxonomy/eqf-level.js.map +1 -0
  530. package/dist/taxonomy/index.js +263 -0
  531. package/dist/taxonomy/index.js.map +1 -0
  532. package/dist/taxonomy/industry-taxonomy.js +281 -0
  533. package/dist/taxonomy/industry-taxonomy.js.map +1 -0
  534. package/dist/taxonomy/investment.js +54 -0
  535. package/dist/taxonomy/investment.js.map +1 -0
  536. package/dist/taxonomy/involvement-kind.js +30 -0
  537. package/dist/taxonomy/involvement-kind.js.map +1 -0
  538. package/dist/taxonomy/open-to.js +128 -0
  539. package/dist/taxonomy/open-to.js.map +1 -0
  540. package/dist/taxonomy/platforms.js +56 -0
  541. package/dist/taxonomy/platforms.js.map +1 -0
  542. package/dist/taxonomy/presentation-link-type.js +22 -0
  543. package/dist/taxonomy/presentation-link-type.js.map +1 -0
  544. package/dist/taxonomy/presentation-role.js +21 -0
  545. package/dist/taxonomy/presentation-role.js.map +1 -0
  546. package/dist/taxonomy/publication-type.js +54 -0
  547. package/dist/taxonomy/publication-type.js.map +1 -0
  548. package/dist/taxonomy/relational.js +25 -0
  549. package/dist/taxonomy/relational.js.map +1 -0
  550. package/dist/taxonomy/search-filter-defs.js +66 -0
  551. package/dist/taxonomy/search-filter-defs.js.map +1 -0
  552. package/dist/taxonomy/skill-categories.js +28 -0
  553. package/dist/taxonomy/skill-categories.js.map +1 -0
  554. package/dist/taxonomy/skill-display-groups.js +12 -0
  555. package/dist/taxonomy/skill-display-groups.js.map +1 -0
  556. package/dist/taxonomy/skill-grouping.js +90 -0
  557. package/dist/taxonomy/skill-grouping.js.map +1 -0
  558. package/dist/taxonomy/verification-providers.js +69 -0
  559. package/dist/taxonomy/verification-providers.js.map +1 -0
  560. package/dist/taxonomy/workplace-type.js +38 -0
  561. package/dist/taxonomy/workplace-type.js.map +1 -0
  562. package/dist/tokens/colors.js +11 -0
  563. package/dist/tokens/colors.js.map +1 -0
  564. package/dist/tokens/icons.js +15 -0
  565. package/dist/tokens/icons.js.map +1 -0
  566. package/dist/tokens/index.js +10 -39
  567. package/dist/tokens/index.js.map +1 -1
  568. package/dist/tokens/typography.js +23 -0
  569. package/dist/tokens/typography.js.map +1 -0
  570. package/dist/types/index.js +10 -0
  571. package/dist/types/index.js.map +1 -0
  572. package/dist/types/profile-view.js +1 -0
  573. package/dist/types/profile-view.js.map +1 -0
  574. package/dist/{use-org-notification-emails-0hjt-dEw.d.ts → use-org-notification-emails-CWWi1D8t.d.ts} +2 -2
  575. package/package.json +4 -3
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/logic/org-floor.ts"],"sourcesContent":["import type { OrgProfileRecord } from '../schemas/org-profile.js';\nimport { looksLikeDomain } from './domain-detect.js';\nimport type { AccountFacetMode } from '../types/index.js';\n\n/**\n * Handle hosts that are shared PDS / signup domains, never a single\n * organization's registrable identity. A handle under one of these (e.g.\n * `acme.bsky.social`) is a subdomain of someone else's domain, so it can never\n * satisfy the org rendering floor. Lowercased, leading-dot form for a suffix\n * match. Kept in sync with the PDS provider list in `format/pds-utils.ts`.\n */\nconst KNOWN_HANDLE_HOST_SUFFIXES = [\n '.bsky.social',\n '.blacksky.app',\n '.eurosky.social',\n '.northsky.social',\n] as const;\n\n/**\n * Approximate \"is this handle a custom registrable domain?\" check for the org\n * rendering floor.\n *\n * IMPORTANT -- this is deliberately NARROW, not authoritative. The SDK has no\n * public-suffix-list (PSL) dependency, so it cannot compute the true eTLD+1\n * apex: it cannot tell `acme.co.uk` (an apex) from `careers.acme.co.uk` (a\n * subdomain that routes to the subsidiary case), nor reject every PSL-private\n * host. It performs only the rejects it CAN make correctly with no PSL:\n * - DIDs (never a domain handle),\n * - bare / no-dot handles and non-TLD-shaped hosts (via `looksLikeDomain`),\n * - known shared PDS / handle hosts (`*.bsky.social` and friends).\n *\n * The AUTHORITATIVE apex + eTLD+1 + confusable check is server-side in\n * sifa-api's `bind-domain-guard` (`isBindableHandleDomain`, backed by `tldts`);\n * `computeOrgFloorVerdict` there is the source of truth and is surfaced to\n * clients on the profile resolve (read it via `useOrgProfile`). Recompute here\n * only when a client-side floor gate is needed without a round-trip.\n *\n * TODO(#160): promote a shared PSL-backed eTLD+1 helper into the SDK (or vendor\n * a compact public-suffix table) and tighten this to a true apex check.\n *\n * Pure: no network, no I/O.\n */\nexport function isRegistrableDomainHandle(handle: string): boolean {\n const h = handle.trim().toLowerCase();\n if (!h) return false;\n if (h.startsWith('did:')) return false;\n // Requires >=1 dot and a TLD-shaped final label; rejects bare usernames.\n if (!looksLikeDomain(h)) return false;\n for (const suffix of KNOWN_HANDLE_HOST_SUFFIXES) {\n if (h.endsWith(suffix)) return false;\n }\n return true;\n}\n\n/**\n * The org rendering floor (#160) -- the \"any AppView can evaluate\" promise made\n * executable. An account renders as an organization when BOTH:\n * 1. It has an `id.sifa.org.profile` record (record present), AND\n * 2. Its handle is a custom registrable domain\n * ({@link isRegistrableDomainHandle}).\n *\n * Only the PRESENCE of the record matters here (declaration marks the account\n * as presenting-as-org; trust is layered separately). The handle check is LIVE:\n * evaluate it on every render so a lost domain drops org rendering\n * automatically.\n *\n * Pure: no network, no I/O. Pass the fetched record (or null) and the current\n * handle.\n */\nexport function qualifiesAsOrg(\n record: OrgProfileRecord | null | undefined,\n handle: string,\n): boolean {\n if (record === null || record === undefined) return false;\n return isRegistrableDomainHandle(handle);\n}\n\n/**\n * The person-facet fields that decide whether an account has a CV worth\n * keeping. A structural subset of `Profile`, so callers can pass a profile\n * straight in. Every list is optional; `positions`, `education`, and `skills`\n * are always present on a real profile but typed optional here so partial\n * fixtures and trimmed payloads work.\n */\nexport interface PersonalFacetContent {\n headline?: string | null;\n about?: string | null;\n positions?: unknown[];\n education?: unknown[];\n skills?: unknown[];\n certifications?: unknown[];\n projects?: unknown[];\n publications?: unknown[];\n volunteering?: unknown[];\n involvement?: unknown[];\n honors?: unknown[];\n courses?: unknown[];\n}\n\n/**\n * Does this account have a personal profile worth keeping visible?\n *\n * Decides the claim-flow default when an account claims its domain as an\n * organization. An empty account claiming a domain is a plain company signup:\n * present solely as an org, ask nothing. An account that already carries a CV\n * is the sole-trader case: keeping both facets is the default, and hiding the\n * person facet must be a deliberate choice rather than a silent side effect of\n * claiming.\n *\n * True when any profile section holds at least one record, or when the headline\n * or about text is non-blank. Whitespace-only text does not count.\n *\n * Pure: no network, no I/O.\n */\nexport function hasPersonalProfileContent(profile: PersonalFacetContent): boolean {\n const filled = (text: string | null | undefined): boolean =>\n typeof text === 'string' && text.trim() !== '';\n if (filled(profile.headline) || filled(profile.about)) return true;\n const sections = [\n profile.positions,\n profile.education,\n profile.skills,\n profile.certifications,\n profile.projects,\n profile.publications,\n profile.volunteering,\n profile.involvement,\n profile.honors,\n profile.courses,\n ];\n return sections.some((section) => Array.isArray(section) && section.length > 0);\n}\n\n/**\n * Should this account's personal profile render at `/p/`, given the org verdict?\n *\n * The single routing predicate behind the freelancer dual-identity case: a\n * claimed org that set `personalProfileVisible` renders both facets, so `/p/`\n * serves the person instead of redirecting to `/c/`. Every other account keeps\n * the exclusive behaviour -- an org (claimed or merely recognized) redirects\n * `/p/` to `/c/`, and a plain person renders at `/p/` as always.\n *\n * Pure: no network, no I/O.\n */\nexport function rendersPersonalProfile(\n org: { isOrg: boolean; recognized: boolean; personalProfileVisible?: boolean } | null | undefined,\n renderPreference?: 'person' | 'company' | null,\n): boolean {\n // An explicit 'person' choice is the owner's own override and wins outright:\n // it is what makes \"Person\" mean person, even for an account holding an org\n // record (which stays in the PDS, inert).\n if (renderPreference === 'person') return true;\n if (org === null || org === undefined) return true;\n if (!org.isOrg && !org.recognized) return true;\n // Gated on isOrg, not merely on the flag: `personalProfileVisible` lives in\n // the `id.sifa.org.profile` record, so an account that is only RECOGNIZED\n // (known company domain, never claimed) has no record to carry it and keeps\n // the redirect. Claiming is the way in.\n return org.isOrg && org.personalProfileVisible === true;\n}\n\n/**\n * Should this account's company page render at `/c/`?\n *\n * The mirror of {@link rendersPersonalProfile}. True for any company account --\n * claimed or merely recognized -- EXCEPT when the owner explicitly chose to\n * present as a person, which suppresses the company page rather than deleting\n * anything. The org record stays in the PDS and the choice is reversible.\n *\n * Pure: no network, no I/O.\n */\nexport function rendersCompanyProfile(\n org: { isOrg: boolean; recognized: boolean } | null | undefined,\n renderPreference?: 'person' | 'company' | null,\n): boolean {\n if (renderPreference === 'person') return false;\n if (org === null || org === undefined) return false;\n return org.isOrg || org.recognized;\n}\n\n/**\n * Resolve the three-way \"are you a person, a company, or both?\" answer that the\n * account settings switch presents, from the two places the answer is stored.\n *\n * Precedence, highest first:\n * 1. `renderPreference === 'person'` -- the owner's explicit local override.\n * Wins even over a claimed org record, which stays inert in the PDS.\n * 2. A claimed org that declared `personalProfileVisible` -- `'both'`, the\n * sole trader. Read from the PDS record, so it is portable.\n * 3. Any other company account (claimed, recognized, or `renderPreference`\n * `'company'`) -- `'company'`.\n * 4. Everything else -- `'person'`.\n *\n * Pure: no network, no I/O.\n */\nexport function resolveAccountFacetMode(profile: {\n org?: { isOrg: boolean; recognized: boolean; personalProfileVisible?: boolean } | null;\n renderPreference?: 'person' | 'company' | null;\n}): AccountFacetMode {\n if (profile.renderPreference === 'person') return 'person';\n const org = profile.org;\n if (org?.isOrg && org.personalProfileVisible === true) return 'both';\n if (org?.isOrg || org?.recognized || profile.renderPreference === 'company') return 'company';\n return 'person';\n}\n"],"mappings":";AACA,SAAS,uBAAuB;AAUhC,IAAM,6BAA6B;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AA0BO,SAAS,0BAA0B,QAAyB;AACjE,QAAM,IAAI,OAAO,KAAK,EAAE,YAAY;AACpC,MAAI,CAAC,EAAG,QAAO;AACf,MAAI,EAAE,WAAW,MAAM,EAAG,QAAO;AAEjC,MAAI,CAAC,gBAAgB,CAAC,EAAG,QAAO;AAChC,aAAW,UAAU,4BAA4B;AAC/C,QAAI,EAAE,SAAS,MAAM,EAAG,QAAO;AAAA,EACjC;AACA,SAAO;AACT;AAiBO,SAAS,eACd,QACA,QACS;AACT,MAAI,WAAW,QAAQ,WAAW,OAAW,QAAO;AACpD,SAAO,0BAA0B,MAAM;AACzC;AAuCO,SAAS,0BAA0B,SAAwC;AAChF,QAAM,SAAS,CAAC,SACd,OAAO,SAAS,YAAY,KAAK,KAAK,MAAM;AAC9C,MAAI,OAAO,QAAQ,QAAQ,KAAK,OAAO,QAAQ,KAAK,EAAG,QAAO;AAC9D,QAAM,WAAW;AAAA,IACf,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AACA,SAAO,SAAS,KAAK,CAAC,YAAY,MAAM,QAAQ,OAAO,KAAK,QAAQ,SAAS,CAAC;AAChF;AAaO,SAAS,uBACd,KACA,kBACS;AAIT,MAAI,qBAAqB,SAAU,QAAO;AAC1C,MAAI,QAAQ,QAAQ,QAAQ,OAAW,QAAO;AAC9C,MAAI,CAAC,IAAI,SAAS,CAAC,IAAI,WAAY,QAAO;AAK1C,SAAO,IAAI,SAAS,IAAI,2BAA2B;AACrD;AAYO,SAAS,sBACd,KACA,kBACS;AACT,MAAI,qBAAqB,SAAU,QAAO;AAC1C,MAAI,QAAQ,QAAQ,QAAQ,OAAW,QAAO;AAC9C,SAAO,IAAI,SAAS,IAAI;AAC1B;AAiBO,SAAS,wBAAwB,SAGnB;AACnB,MAAI,QAAQ,qBAAqB,SAAU,QAAO;AAClD,QAAM,MAAM,QAAQ;AACpB,MAAI,KAAK,SAAS,IAAI,2BAA2B,KAAM,QAAO;AAC9D,MAAI,KAAK,SAAS,KAAK,cAAc,QAAQ,qBAAqB,UAAW,QAAO;AACpF,SAAO;AACT;","names":[]}
@@ -0,0 +1,9 @@
1
+ // src/logic/primary-item.ts
2
+ function pickPrimaryFlagged(items, isEligible = () => true) {
3
+ if (!items || items.length === 0) return void 0;
4
+ return items.find((item) => item.primary === true && !item.hidden && isEligible(item));
5
+ }
6
+ export {
7
+ pickPrimaryFlagged
8
+ };
9
+ //# sourceMappingURL=primary-item.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/logic/primary-item.ts"],"sourcesContent":["/**\n * Shared \"which item did the user flag as primary?\" rule for the profile\n * sections that feed the Highlights block (education, publication, presentation,\n * involvement, project).\n *\n * Returns the item explicitly flagged `primary`, excluding hidden items and any\n * the eligibility predicate rejects, or `undefined` when none qualifies. Callers\n * layer their own automatic fallback behind it, e.g.\n * `pickPrimaryFlagged(items) ?? mostRecent(items)`, so an explicit choice\n * overrides the default pick without changing the default itself.\n *\n * Hidden wins over primary, matching {@link pickPrimaryPosition}: an item flagged\n * both hidden and primary is excluded rather than surfaced.\n *\n * Eligibility differs by section and is passed in by the caller: education and\n * publication allow any item; involvement and project allow only ongoing items\n * (no end date); presentation allows a reusable talk. Standalone one-off sessions\n * are never candidates because only `presentation` records carry the flag.\n */\nexport interface PrimaryFlagCandidate {\n primary?: boolean;\n hidden?: boolean;\n}\n\nexport function pickPrimaryFlagged<T extends PrimaryFlagCandidate>(\n items: readonly T[] | undefined,\n isEligible: (item: T) => boolean = () => true,\n): T | undefined {\n if (!items || items.length === 0) return undefined;\n return items.find((item) => item.primary === true && !item.hidden && isEligible(item));\n}\n"],"mappings":";AAwBO,SAAS,mBACd,OACA,aAAmC,MAAM,MAC1B;AACf,MAAI,CAAC,SAAS,MAAM,WAAW,EAAG,QAAO;AACzC,SAAO,MAAM,KAAK,CAAC,SAAS,KAAK,YAAY,QAAQ,CAAC,KAAK,UAAU,WAAW,IAAI,CAAC;AACvF;","names":[]}
@@ -0,0 +1,16 @@
1
+ // src/logic/primary-position.ts
2
+ import { isUpcomingStart } from "./date-lookahead.js";
3
+ function pickPrimaryPosition(positions, now = /* @__PURE__ */ new Date()) {
4
+ if (!positions || positions.length === 0) return void 0;
5
+ const active = positions.filter(
6
+ (p) => !p.endedAt && !p.hidden && !isUpcomingStart(p.startedAt, now)
7
+ );
8
+ if (active.length === 0) return void 0;
9
+ const flagged = active.find((p) => p.primary === true);
10
+ if (flagged) return flagged;
11
+ return [...active].sort((a, b) => (b.startedAt ?? "").localeCompare(a.startedAt ?? ""))[0];
12
+ }
13
+ export {
14
+ pickPrimaryPosition
15
+ };
16
+ //# sourceMappingURL=primary-position.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/logic/primary-position.ts"],"sourcesContent":["/**\n * Canonical rule for picking the \"primary\" position to surface on profile cards,\n * social previews, and structured-data outputs.\n *\n * Without this shared helper, different surfaces (visible hero, OG image,\n * <meta description>, JSON-LD) re-derived \"primary\" inconsistently and diverged\n * for users with multiple concurrent roles.\n *\n * A `hidden` position is one the user chose not to show on their public profile.\n * It is never surfaced as the featured role — hidden wins over `primary`, so a\n * role flagged both hidden and primary is excluded rather than featured.\n *\n * A position whose start month has not arrived yet (an accepted offer) is not\n * active: it is nobody's current role until it starts (sifa-workspace#581).\n *\n * Rules (in order):\n * 1. A position the user explicitly flagged `primary` AND that is still active AND not hidden.\n * 2. Otherwise, the active, non-hidden position with the most recent `startedAt`.\n * 3. Otherwise, undefined (no eligible position).\n */\n\nimport { isUpcomingStart } from './date-lookahead.js';\n\nexport interface PrimaryPositionCandidate {\n startedAt?: string;\n endedAt?: string;\n primary?: boolean;\n hidden?: boolean;\n}\n\nexport function pickPrimaryPosition<T extends PrimaryPositionCandidate>(\n positions: readonly T[] | undefined,\n now: Date = new Date(),\n): T | undefined {\n if (!positions || positions.length === 0) return undefined;\n\n const active = positions.filter(\n (p) => !p.endedAt && !p.hidden && !isUpcomingStart(p.startedAt, now),\n );\n if (active.length === 0) return undefined;\n\n const flagged = active.find((p) => p.primary === true);\n if (flagged) return flagged;\n\n return [...active].sort((a, b) => (b.startedAt ?? '').localeCompare(a.startedAt ?? ''))[0];\n}\n"],"mappings":";AAqBA,SAAS,uBAAuB;AASzB,SAAS,oBACd,WACA,MAAY,oBAAI,KAAK,GACN;AACf,MAAI,CAAC,aAAa,UAAU,WAAW,EAAG,QAAO;AAEjD,QAAM,SAAS,UAAU;AAAA,IACvB,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,EAAE,UAAU,CAAC,gBAAgB,EAAE,WAAW,GAAG;AAAA,EACrE;AACA,MAAI,OAAO,WAAW,EAAG,QAAO;AAEhC,QAAM,UAAU,OAAO,KAAK,CAAC,MAAM,EAAE,YAAY,IAAI;AACrD,MAAI,QAAS,QAAO;AAEpB,SAAO,CAAC,GAAG,MAAM,EAAE,KAAK,CAAC,GAAG,OAAO,EAAE,aAAa,IAAI,cAAc,EAAE,aAAa,EAAE,CAAC,EAAE,CAAC;AAC3F;","names":[]}
@@ -0,0 +1,21 @@
1
+ // src/logic/profile-completeness.ts
2
+ var COMPLETENESS_MAX_SCORE = 6;
3
+ function completenessScore(c) {
4
+ let filled = 0;
5
+ if (c.hasHeadline) filled++;
6
+ if (c.hasAbout) filled++;
7
+ if (c.positionCount > 0) filled++;
8
+ if (c.educationCount > 0) filled++;
9
+ if (c.skillCount > 0) filled++;
10
+ if (c.certificationCount > 0) filled++;
11
+ return filled;
12
+ }
13
+ function completenessPercent(c) {
14
+ return Math.round(completenessScore(c) / COMPLETENESS_MAX_SCORE * 100);
15
+ }
16
+ export {
17
+ COMPLETENESS_MAX_SCORE,
18
+ completenessPercent,
19
+ completenessScore
20
+ };
21
+ //# sourceMappingURL=profile-completeness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/logic/profile-completeness.ts"],"sourcesContent":["/**\n * Profile completeness scoring.\n *\n * Six binary signals, equal weight: headline filled, about filled, has at least\n * one position, education, skill, certification.\n *\n * Score: integer 0..6. Percent: round(score / 6 * 100) -- discrete values\n * {0, 17, 33, 50, 67, 83, 100}.\n *\n * Canonical source of truth for completeness scoring across Sifa clients.\n * sifa-api computes the underlying `ProfileCompletion` shape inline (in SQL\n * for admin-stats queries, in route handlers elsewhere) but defers scoring\n * to this module.\n */\n\nexport const COMPLETENESS_MAX_SCORE = 6;\n\nexport interface ProfileCompletion {\n hasHeadline: boolean;\n hasAbout: boolean;\n positionCount: number;\n educationCount: number;\n skillCount: number;\n certificationCount: number;\n}\n\n/** Pure-TS computation of the 0..6 score. */\nexport function completenessScore(c: ProfileCompletion): number {\n let filled = 0;\n if (c.hasHeadline) filled++;\n if (c.hasAbout) filled++;\n if (c.positionCount > 0) filled++;\n if (c.educationCount > 0) filled++;\n if (c.skillCount > 0) filled++;\n if (c.certificationCount > 0) filled++;\n return filled;\n}\n\n/** Completion as a rounded 0..100 integer percent (matches API serialisation). */\nexport function completenessPercent(c: ProfileCompletion): number {\n return Math.round((completenessScore(c) / COMPLETENESS_MAX_SCORE) * 100);\n}\n"],"mappings":";AAeO,IAAM,yBAAyB;AAY/B,SAAS,kBAAkB,GAA8B;AAC9D,MAAI,SAAS;AACb,MAAI,EAAE,YAAa;AACnB,MAAI,EAAE,SAAU;AAChB,MAAI,EAAE,gBAAgB,EAAG;AACzB,MAAI,EAAE,iBAAiB,EAAG;AAC1B,MAAI,EAAE,aAAa,EAAG;AACtB,MAAI,EAAE,qBAAqB,EAAG;AAC9B,SAAO;AACT;AAGO,SAAS,oBAAoB,GAA8B;AAChE,SAAO,KAAK,MAAO,kBAAkB,CAAC,IAAI,yBAA0B,GAAG;AACzE;","names":[]}
@@ -0,0 +1,50 @@
1
+ // src/logic/profile-dimensions.ts
2
+ var DIMENSIONS_MAX_SCORE = 6;
3
+ var MIN_SKILLS = 3;
4
+ function nonEmptyString(value) {
5
+ return typeof value === "string" && value.trim().length > 0;
6
+ }
7
+ function dimensionsFromInputs(inputs) {
8
+ return {
9
+ avatar: inputs.hasAvatar,
10
+ headline: inputs.hasHeadline,
11
+ about: inputs.hasAbout,
12
+ currentPosition: inputs.currentPositionCount > 0,
13
+ skills: inputs.skillCount >= MIN_SKILLS,
14
+ education: inputs.educationCount > 0
15
+ };
16
+ }
17
+ function profileToDimensionInputs(profile) {
18
+ const positions = profile.positions ?? [];
19
+ const skills = profile.skills ?? [];
20
+ const education = profile.education ?? [];
21
+ const currentPositionCount = positions.filter((p) => !p.endedAt).length;
22
+ return {
23
+ hasAvatar: nonEmptyString(profile.avatar),
24
+ hasHeadline: nonEmptyString(profile.headline),
25
+ hasAbout: nonEmptyString(profile.about),
26
+ currentPositionCount,
27
+ skillCount: skills.length,
28
+ educationCount: education.length
29
+ };
30
+ }
31
+ function getFilledDimensionsMap(profile) {
32
+ return dimensionsFromInputs(profileToDimensionInputs(profile));
33
+ }
34
+ function countFilledDimensions(input) {
35
+ const map = "hasAvatar" in input ? dimensionsFromInputs(input) : getFilledDimensionsMap(input);
36
+ let n = 0;
37
+ for (const v of Object.values(map)) {
38
+ if (v) n++;
39
+ }
40
+ return n;
41
+ }
42
+ export {
43
+ DIMENSIONS_MAX_SCORE,
44
+ MIN_SKILLS,
45
+ countFilledDimensions,
46
+ dimensionsFromInputs,
47
+ getFilledDimensionsMap,
48
+ profileToDimensionInputs
49
+ };
50
+ //# sourceMappingURL=profile-dimensions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/logic/profile-dimensions.ts"],"sourcesContent":["/**\n * Profile dimensions -- the routing signal used by the Sifa homepage to choose\n * between the V3 \"still building\" and V4 \"established\" variants.\n *\n * Six binary signals, equal weight: avatar, headline, about, current position,\n * minimum-skill threshold, and at least one education entry. The routing\n * threshold itself (e.g. \">= 4 of 6 -> ESTABLISHED\") lives in the consumer\n * because different surfaces may want to cut at different bars over time.\n *\n * Canonical source of truth for dimension scoring across Sifa clients.\n * sifa-api computes the underlying `ProfileDimensionInputs` shape inline\n * (in SQL on the session route) but defers scoring to this module so the\n * frontend and backend cannot drift on what \"filled\" means.\n *\n * See `docs/plans/2026-04-27-homepage-variants.md` (sifa-web) for the\n * history of why these six were chosen over the original eight.\n */\n\nimport type { Profile, ProfilePosition } from '../types/index.js';\n\nexport const DIMENSIONS_MAX_SCORE = 6;\nexport const MIN_SKILLS = 3;\n\nexport type DimensionKey =\n 'avatar' | 'headline' | 'about' | 'currentPosition' | 'skills' | 'education';\n\nexport type DimensionMap = Record<DimensionKey, boolean>;\n\n/**\n * Minimal raw inputs needed to compute the dimension map. sifa-api can\n * populate this from a single composite SQL query without loading the full\n * Profile shape, which is the whole point of putting this in the SDK.\n */\nexport interface ProfileDimensionInputs {\n hasAvatar: boolean;\n hasHeadline: boolean;\n hasAbout: boolean;\n currentPositionCount: number;\n skillCount: number;\n educationCount: number;\n}\n\nfunction nonEmptyString(value: string | undefined | null): boolean {\n return typeof value === 'string' && value.trim().length > 0;\n}\n\n/**\n * Pure-TS computation of the 6-key boolean map from minimal inputs.\n * Use this when you already have aggregate counts (e.g. server-side).\n */\nexport function dimensionsFromInputs(inputs: ProfileDimensionInputs): DimensionMap {\n return {\n avatar: inputs.hasAvatar,\n headline: inputs.hasHeadline,\n about: inputs.hasAbout,\n currentPosition: inputs.currentPositionCount > 0,\n skills: inputs.skillCount >= MIN_SKILLS,\n education: inputs.educationCount > 0,\n };\n}\n\n/**\n * Derives `ProfileDimensionInputs` from a full Profile. Use this on the\n * client when you already have the profile loaded.\n */\nexport function profileToDimensionInputs(profile: Profile): ProfileDimensionInputs {\n const positions = profile.positions ?? [];\n const skills = profile.skills ?? [];\n const education = profile.education ?? [];\n const currentPositionCount = positions.filter((p: ProfilePosition) => !p.endedAt).length;\n return {\n hasAvatar: nonEmptyString(profile.avatar),\n hasHeadline: nonEmptyString(profile.headline),\n hasAbout: nonEmptyString(profile.about),\n currentPositionCount,\n skillCount: skills.length,\n educationCount: education.length,\n };\n}\n\n/** Convenience: full Profile -> dimension boolean map. */\nexport function getFilledDimensionsMap(profile: Profile): DimensionMap {\n return dimensionsFromInputs(profileToDimensionInputs(profile));\n}\n\n/** Count of filled dimensions, 0..6. Accepts either raw inputs or a full Profile. */\nexport function countFilledDimensions(input: ProfileDimensionInputs | Profile): number {\n const map = 'hasAvatar' in input ? dimensionsFromInputs(input) : getFilledDimensionsMap(input);\n let n = 0;\n for (const v of Object.values(map)) {\n if (v) n++;\n }\n return n;\n}\n"],"mappings":";AAoBO,IAAM,uBAAuB;AAC7B,IAAM,aAAa;AAqB1B,SAAS,eAAe,OAA2C;AACjE,SAAO,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,SAAS;AAC5D;AAMO,SAAS,qBAAqB,QAA8C;AACjF,SAAO;AAAA,IACL,QAAQ,OAAO;AAAA,IACf,UAAU,OAAO;AAAA,IACjB,OAAO,OAAO;AAAA,IACd,iBAAiB,OAAO,uBAAuB;AAAA,IAC/C,QAAQ,OAAO,cAAc;AAAA,IAC7B,WAAW,OAAO,iBAAiB;AAAA,EACrC;AACF;AAMO,SAAS,yBAAyB,SAA0C;AACjF,QAAM,YAAY,QAAQ,aAAa,CAAC;AACxC,QAAM,SAAS,QAAQ,UAAU,CAAC;AAClC,QAAM,YAAY,QAAQ,aAAa,CAAC;AACxC,QAAM,uBAAuB,UAAU,OAAO,CAAC,MAAuB,CAAC,EAAE,OAAO,EAAE;AAClF,SAAO;AAAA,IACL,WAAW,eAAe,QAAQ,MAAM;AAAA,IACxC,aAAa,eAAe,QAAQ,QAAQ;AAAA,IAC5C,UAAU,eAAe,QAAQ,KAAK;AAAA,IACtC;AAAA,IACA,YAAY,OAAO;AAAA,IACnB,gBAAgB,UAAU;AAAA,EAC5B;AACF;AAGO,SAAS,uBAAuB,SAAgC;AACrE,SAAO,qBAAqB,yBAAyB,OAAO,CAAC;AAC/D;AAGO,SAAS,sBAAsB,OAAiD;AACrF,QAAM,MAAM,eAAe,QAAQ,qBAAqB,KAAK,IAAI,uBAAuB,KAAK;AAC7F,MAAI,IAAI;AACR,aAAW,KAAK,OAAO,OAAO,GAAG,GAAG;AAClC,QAAI,EAAG;AAAA,EACT;AACA,SAAO;AACT;","names":[]}
@@ -0,0 +1,23 @@
1
+ // src/logic/profile-summary.ts
2
+ import { pickPrimaryPosition } from "./primary-position.js";
3
+ var DEFAULT_MAX_SKILLS = 5;
4
+ function summarizeProfileView(view, options = {}) {
5
+ const limit = Math.max(0, options.maxSkills ?? DEFAULT_MAX_SKILLS);
6
+ const primary = pickPrimaryPosition(view.positions);
7
+ return {
8
+ did: view.did,
9
+ handle: view.handle,
10
+ displayName: view.displayName,
11
+ avatar: view.avatar,
12
+ pronouns: view.pronouns,
13
+ headline: view.headline,
14
+ currentTitle: primary?.title,
15
+ currentCompany: primary ? primary.entityName ?? primary.company : void 0,
16
+ topSkills: (view.skills ?? []).slice(0, limit).map((s) => s.name),
17
+ claimed: view.claimed ?? false
18
+ };
19
+ }
20
+ export {
21
+ summarizeProfileView
22
+ };
23
+ //# sourceMappingURL=profile-summary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/logic/profile-summary.ts"],"sourcesContent":["/**\n * Compact professional summary derived from a {@link ProfileView}.\n *\n * The full `id.sifa.getProfileView` payload carries every section (all\n * positions, education, skills, publications, talks, and more). Third-party\n * surfaces that only want to show \"who is this, what do they do now\" — a badge,\n * a maintainer card, a byline — need a much smaller shape. This is that shape,\n * derived with the same canonical rules the Sifa web app uses (see\n * {@link pickPrimaryPosition}) so an embedded summary matches the profile page.\n *\n * Pure and I/O-free: pass it a view you already fetched. For the fetch +\n * summarize convenience, use `fetchProfileSummary` from `@singi-labs/sifa-sdk`.\n */\n\nimport type { ProfileView } from '../types/profile-view.js';\nimport { pickPrimaryPosition } from './primary-position.js';\n\nexport interface ProfileSummary {\n did: string;\n handle: string;\n displayName?: string;\n avatar?: string;\n pronouns?: string;\n headline?: string;\n /** Title of the current primary role, if the person has an active position. */\n currentTitle?: string;\n /** Current employer: the resolved entity name when linked, else the free-text company. */\n currentCompany?: string;\n /** Skill names in the order the AppView returns them, capped by `maxSkills`. */\n topSkills: string[];\n /** Whether the profile is claimed by its owner (vs an unclaimed placeholder). */\n claimed: boolean;\n}\n\nexport interface SummarizeProfileViewOptions {\n /** Maximum number of skills to include in `topSkills`. Default 5; negatives clamp to 0. */\n maxSkills?: number;\n}\n\nconst DEFAULT_MAX_SKILLS = 5;\n\nexport function summarizeProfileView(\n view: ProfileView,\n options: SummarizeProfileViewOptions = {},\n): ProfileSummary {\n const limit = Math.max(0, options.maxSkills ?? DEFAULT_MAX_SKILLS);\n const primary = pickPrimaryPosition(view.positions);\n\n return {\n did: view.did,\n handle: view.handle,\n displayName: view.displayName,\n avatar: view.avatar,\n pronouns: view.pronouns,\n headline: view.headline,\n currentTitle: primary?.title,\n currentCompany: primary ? (primary.entityName ?? primary.company) : undefined,\n topSkills: (view.skills ?? []).slice(0, limit).map((s) => s.name),\n claimed: view.claimed ?? false,\n };\n}\n"],"mappings":";AAeA,SAAS,2BAA2B;AAwBpC,IAAM,qBAAqB;AAEpB,SAAS,qBACd,MACA,UAAuC,CAAC,GACxB;AAChB,QAAM,QAAQ,KAAK,IAAI,GAAG,QAAQ,aAAa,kBAAkB;AACjE,QAAM,UAAU,oBAAoB,KAAK,SAAS;AAElD,SAAO;AAAA,IACL,KAAK,KAAK;AAAA,IACV,QAAQ,KAAK;AAAA,IACb,aAAa,KAAK;AAAA,IAClB,QAAQ,KAAK;AAAA,IACb,UAAU,KAAK;AAAA,IACf,UAAU,KAAK;AAAA,IACf,cAAc,SAAS;AAAA,IACvB,gBAAgB,UAAW,QAAQ,cAAc,QAAQ,UAAW;AAAA,IACpE,YAAY,KAAK,UAAU,CAAC,GAAG,MAAM,GAAG,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA,IAChE,SAAS,KAAK,WAAW;AAAA,EAC3B;AACF;","names":[]}
@@ -0,0 +1,34 @@
1
+ // src/logic/pseudo-employer.ts
2
+ var PSEUDO_EMPLOYERS = /* @__PURE__ */ new Set([
3
+ "self",
4
+ "self employed",
5
+ "selfemployed",
6
+ "self employment",
7
+ "self employed freelance",
8
+ "freelance",
9
+ "freelancer",
10
+ "freelancing",
11
+ "freelance work",
12
+ "independent",
13
+ "independent contractor",
14
+ "independent consultant",
15
+ "independent professional",
16
+ "sole proprietor",
17
+ "sole proprietorship",
18
+ "sole trader",
19
+ "own business",
20
+ "my own business",
21
+ "self employed consultant"
22
+ ]);
23
+ function normalizePseudo(value) {
24
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim().replace(/\s+/g, " ");
25
+ }
26
+ function isPseudoEmployer(company) {
27
+ const normalized = normalizePseudo(company);
28
+ if (!normalized) return false;
29
+ return PSEUDO_EMPLOYERS.has(normalized);
30
+ }
31
+ export {
32
+ isPseudoEmployer
33
+ };
34
+ //# sourceMappingURL=pseudo-employer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/logic/pseudo-employer.ts"],"sourcesContent":["/**\n * Pseudo-employer detection (#159). Self-employment strings (\"self-employed\",\n * \"freelance\", \"independent\", ...) are NOT organizations: the position editor\n * routes them to the company-optional / \"Independent\" path instead of the entity\n * typeahead, and they are excluded from entity matching everywhere.\n *\n * Matching is on the WHOLE normalized string, not substrings, so real employers\n * that merely contain one of these words (\"Independent School District 191\")\n * are not misclassified. Mirrors the sifa-api matcher.\n */\nconst PSEUDO_EMPLOYERS = new Set<string>([\n 'self',\n 'self employed',\n 'selfemployed',\n 'self employment',\n 'self employed freelance',\n 'freelance',\n 'freelancer',\n 'freelancing',\n 'freelance work',\n 'independent',\n 'independent contractor',\n 'independent consultant',\n 'independent professional',\n 'sole proprietor',\n 'sole proprietorship',\n 'sole trader',\n 'own business',\n 'my own business',\n 'self employed consultant',\n]);\n\nfunction normalizePseudo(value: string): string {\n return value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, ' ')\n .trim()\n .replace(/\\s+/g, ' ');\n}\n\n/** True when the whole employer string is a self-employment / pseudo employer. */\nexport function isPseudoEmployer(company: string): boolean {\n const normalized = normalizePseudo(company);\n if (!normalized) return false;\n return PSEUDO_EMPLOYERS.has(normalized);\n}\n"],"mappings":";AAUA,IAAM,mBAAmB,oBAAI,IAAY;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,SAAS,gBAAgB,OAAuB;AAC9C,SAAO,MACJ,YAAY,EACZ,QAAQ,eAAe,GAAG,EAC1B,KAAK,EACL,QAAQ,QAAQ,GAAG;AACxB;AAGO,SAAS,iBAAiB,SAA0B;AACzD,QAAM,aAAa,gBAAgB,OAAO;AAC1C,MAAI,CAAC,WAAY,QAAO;AACxB,SAAO,iBAAiB,IAAI,UAAU;AACxC;","names":[]}
@@ -0,0 +1,10 @@
1
+ // src/logic/recent-activity.ts
2
+ var DAY_MS = 864e5;
3
+ function countRecentActivity(days, windowDays, now = /* @__PURE__ */ new Date()) {
4
+ const cutoff = new Date(now.getTime() - windowDays * DAY_MS).toISOString().slice(0, 10);
5
+ return days.reduce((sum, day) => day.date >= cutoff ? sum + day.total : sum, 0);
6
+ }
7
+ export {
8
+ countRecentActivity
9
+ };
10
+ //# sourceMappingURL=recent-activity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/logic/recent-activity.ts"],"sourcesContent":["/**\n * Minimal per-day activity count. Structurally satisfied by the SDK's\n * `HeatmapDay` (query subpath), but defined here so this predicate stays\n * importable from the main entrypoint without pulling the query layer -- same\n * structural-type discipline as {@link isCompanyPageIndexable}.\n */\nexport interface DailyActivityCount {\n /** Calendar day in `YYYY-MM-DD` (UTC). Compared lexicographically. */\n date: string;\n /** Number of activities recorded on that day. */\n total: number;\n}\n\nconst DAY_MS = 86_400_000;\n\n/**\n * How many activities fall within the last `windowDays` calendar days.\n *\n * Sums {@link DailyActivityCount.total | total} for every day whose\n * {@link DailyActivityCount.date | date} is on or after the cutoff, where the\n * cutoff is `now` minus `windowDays` days (UTC calendar date). `YYYY-MM-DD`\n * strings sort chronologically, so the comparison is a plain string compare --\n * no per-day `Date` parsing, no timezone drift.\n *\n * The day exactly `windowDays` ago is INCLUDED (inclusive lower bound).\n *\n * Pure: no network, no I/O. `now` is injectable for deterministic tests and\n * defaults to the current time.\n */\nexport function countRecentActivity(\n days: readonly DailyActivityCount[],\n windowDays: number,\n now: Date = new Date(),\n): number {\n const cutoff = new Date(now.getTime() - windowDays * DAY_MS).toISOString().slice(0, 10);\n return days.reduce((sum, day) => (day.date >= cutoff ? sum + day.total : sum), 0);\n}\n"],"mappings":";AAaA,IAAM,SAAS;AAgBR,SAAS,oBACd,MACA,YACA,MAAY,oBAAI,KAAK,GACb;AACR,QAAM,SAAS,IAAI,KAAK,IAAI,QAAQ,IAAI,aAAa,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AACtF,SAAO,KAAK,OAAO,CAAC,KAAK,QAAS,IAAI,QAAQ,SAAS,MAAM,IAAI,QAAQ,KAAM,CAAC;AAClF;","names":[]}
@@ -0,0 +1,10 @@
1
+ // src/logic/self-app.ts
2
+ var SELF_APP_ID = "sifa";
3
+ function excludeSelfApp(apps) {
4
+ return apps.filter((app) => app.id !== SELF_APP_ID);
5
+ }
6
+ export {
7
+ SELF_APP_ID,
8
+ excludeSelfApp
9
+ };
10
+ //# sourceMappingURL=self-app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/logic/self-app.ts"],"sourcesContent":["/**\n * The AppView injects a synthetic entry with this id into a claimed profile's\n * `activeApps` so external consumers of the public profile data (e.g.\n * atproto.nl/members reading `sifa.id/api/embed/<did>/data`) can show Sifa as\n * one of the platforms a person is on. Sifa's own `id.sifa.*` collections are\n * excluded from stream activity, so Sifa is otherwise absent from the list.\n *\n * First-party surfaces (sifa-web, sifa-app) hide it with {@link excludeSelfApp}:\n * on a Sifa page, \"you're on Sifa\" is redundant.\n */\nexport const SELF_APP_ID = 'sifa';\n\n/**\n * Minimal structural shape of an app-in-list entry: just an `id`. Structurally\n * satisfied by the SDK's `ActiveApp`, but defined narrowly so this predicate\n * stays importable from the main entrypoint without pulling extra layers --\n * same discipline as {@link countRecentActivity}.\n */\nexport interface AppWithId {\n id: string;\n}\n\n/**\n * Drop the synthetic Sifa self-app ({@link SELF_APP_ID}) from an `activeApps`\n * list. Apply this in first-party UI that renders the list on a Sifa surface,\n * where showing Sifa is stating the obvious. Do NOT apply it in the data layer\n * that feeds external consumers (the public embed endpoint / SDK fetcher) --\n * those must keep the entry.\n *\n * Pure: returns a new array, preserves order, leaves inputs without a Sifa\n * entry unchanged.\n */\nexport function excludeSelfApp<T extends AppWithId>(apps: readonly T[]): T[] {\n return apps.filter((app) => app.id !== SELF_APP_ID);\n}\n"],"mappings":";AAUO,IAAM,cAAc;AAsBpB,SAAS,eAAoC,MAAyB;AAC3E,SAAO,KAAK,OAAO,CAAC,QAAQ,IAAI,OAAO,WAAW;AACpD;","names":[]}
@@ -2,7 +2,7 @@ import { W as WriteResult, K as SifaApiConfig, M as ApiFetchOptions, N as Create
2
2
  import { p as ExternalAccount } from './index-D9GC0u__.js';
3
3
  import { s as FollowProfileItem, p as FeatureAllowlistEntry, r as FeatureFlag } from './follow-profile-D1l6pulB.js';
4
4
  import { z } from 'zod';
5
- import { d as OrgNotificationEmailRequestInput, b as OrgDomainChallengeRequestInput, O as OrgClaimRequestInput, a as OrgProfileUpdateRequestInput, c as OrgDomainVerifyRequestInput } from './org-settings-C73Sin5E.js';
5
+ import { f as OrgNotificationEmailRequestInput, b as OrgDomainChallengeRequestInput, O as OrgClaimRequestInput, h as OrgProfileUpdateRequestInput, d as OrgDomainVerifyRequestInput } from './org-settings-BShvhyFp.js';
6
6
 
7
7
  /** Industry/domain entry on the profile self record. */
8
8
  interface ProfileIndustryInput {
@@ -115,4 +115,4 @@ declare const OrgNotificationEmailRequestSchema: z.ZodObject<{
115
115
  }, z.core.$strip>;
116
116
  type OrgNotificationEmailRequestInput = z.infer<typeof OrgNotificationEmailRequestSchema>;
117
117
 
118
- export { type OrgClaimRequestInput as O, type OrgProfileUpdateRequestInput as a, type OrgDomainChallengeRequestInput as b, type OrgDomainVerifyRequestInput as c, type OrgNotificationEmailRequestInput as d, OrgClaimRequestSchema as e, OrgDomainChallengeRequestSchema as f, OrgDomainVerifyRequestSchema as g, OrgNotificationEmailRequestSchema as h, OrgProfileUpdateRequestSchema as i };
118
+ export { type OrgClaimRequestInput as O, OrgClaimRequestSchema as a, type OrgDomainChallengeRequestInput as b, OrgDomainChallengeRequestSchema as c, type OrgDomainVerifyRequestInput as d, OrgDomainVerifyRequestSchema as e, type OrgNotificationEmailRequestInput as f, OrgNotificationEmailRequestSchema as g, type OrgProfileUpdateRequestInput as h, OrgProfileUpdateRequestSchema as i };
@@ -0,0 +1,12 @@
1
+ // src/profile/headline-role-dedupe.ts
2
+ function normalize(value) {
3
+ return value.trim().replace(/\s+/g, " ").replace(/[.,;:·|]+$/, "").trim().toLowerCase();
4
+ }
5
+ function isRoleLineRedundant(headline, roleLine) {
6
+ if (!headline || !roleLine) return false;
7
+ return normalize(headline) === normalize(roleLine);
8
+ }
9
+ export {
10
+ isRoleLineRedundant
11
+ };
12
+ //# sourceMappingURL=headline-role-dedupe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/profile/headline-role-dedupe.ts"],"sourcesContent":["/**\n * Profiles carry both a freeform `headline` and a derived current-role line\n * (\"{title} at {company}\"). Most people write a headline distinct from their\n * job title, so both add information. But LinkedIn imports default the headline\n * to \"Title at Company\" -- which collides with the derived role line and renders\n * the same text twice (a doubled tagline).\n *\n * This predicate lets each surface (profile header, meta description, embed card)\n * suppress the role line when it merely repeats the headline. Callers compose the\n * role line themselves -- including any localized connector word (\"at\") -- and ask\n * whether it duplicates the headline. Comparison is case-, whitespace-, and\n * trailing-punctuation-insensitive; a headline that adds anything beyond the role\n * line is not treated as redundant.\n */\nfunction normalize(value: string): string {\n return value\n .trim()\n .replace(/\\s+/g, ' ')\n .replace(/[.,;:·|]+$/, '')\n .trim()\n .toLowerCase();\n}\n\nexport function isRoleLineRedundant(\n headline: string | null | undefined,\n roleLine: string | null | undefined,\n): boolean {\n if (!headline || !roleLine) return false;\n return normalize(headline) === normalize(roleLine);\n}\n"],"mappings":";AAcA,SAAS,UAAU,OAAuB;AACxC,SAAO,MACJ,KAAK,EACL,QAAQ,QAAQ,GAAG,EACnB,QAAQ,cAAc,EAAE,EACxB,KAAK,EACL,YAAY;AACjB;AAEO,SAAS,oBACd,UACA,UACS;AACT,MAAI,CAAC,YAAY,CAAC,SAAU,QAAO;AACnC,SAAO,UAAU,QAAQ,MAAM,UAAU,QAAQ;AACnD;","names":[]}
@@ -0,0 +1,200 @@
1
+ // src/profile/highlights.ts
2
+ import { pickPrimaryPosition } from "../logic/primary-position.js";
3
+ import { pickPrimaryFlagged } from "../logic/primary-item.js";
4
+ import { getInvolvementKindHeading } from "../taxonomy/involvement-kind.js";
5
+ import {
6
+ sortByDateDesc,
7
+ lexiconDateExtractor,
8
+ dateRangeExtractor,
9
+ singleDateExtractor
10
+ } from "../format/sort-by-date.js";
11
+ import { formatTimelineDate } from "../format/index.js";
12
+ import { getPresentationRoleLabel } from "../taxonomy/presentation-role.js";
13
+ import { getCalendarEventModeLabel } from "../taxonomy/calendar-event.js";
14
+ function coPeopleLabel(people, self) {
15
+ const names = (people ?? []).filter((p) => p.did !== self.did && (!p.handle || p.handle !== self.handle)).map((p) => p.displayName ?? p.name ?? p.handle).filter((n) => Boolean(n && n.trim()));
16
+ return names.length ? `with ${names.join(", ")}` : void 0;
17
+ }
18
+ var EN_DASH = "\u2013";
19
+ function visible(items) {
20
+ return (items ?? []).filter((i) => !i.hidden);
21
+ }
22
+ function metaLine(parts) {
23
+ const kept = parts.filter((p) => Boolean(p && p.trim()));
24
+ return kept.length ? kept.join(" \xB7 ") : void 0;
25
+ }
26
+ function formatSpanDate(start, end) {
27
+ if (start && !end) return `Since ${formatTimelineDate(start)}`;
28
+ if (!start && !end) return void 0;
29
+ if (!start) return formatTimelineDate(end);
30
+ const a = formatTimelineDate(start);
31
+ const b = formatTimelineDate(end);
32
+ return a === b ? a : `${a} ${EN_DASH} ${b}`;
33
+ }
34
+ function formatSingleDate(date) {
35
+ return date ? formatTimelineDate(date) : void 0;
36
+ }
37
+ var EVENT_DAY_FMT = new Intl.DateTimeFormat("en", {
38
+ year: "numeric",
39
+ month: "short",
40
+ day: "numeric",
41
+ timeZone: "UTC"
42
+ });
43
+ function formatEventDate(date) {
44
+ if (!date) return void 0;
45
+ if (date.length === 10) {
46
+ const d = /* @__PURE__ */ new Date(`${date}T00:00:00Z`);
47
+ if (!Number.isNaN(d.getTime())) return EVENT_DAY_FMT.format(d);
48
+ }
49
+ return formatTimelineDate(date);
50
+ }
51
+ function pickTalk(presentations, deliveries, today, self) {
52
+ const talkRkeys = new Set(presentations.map((p) => p.rkey));
53
+ const occasions = [];
54
+ for (const p of presentations) {
55
+ const dated = visible(p.deliveries).filter((d) => d.date);
56
+ if (!dated.length) continue;
57
+ const date = dated.reduce((max, d) => d.date && d.date > max ? d.date : max, "");
58
+ const delivery2 = dated.find((d) => d.date === date);
59
+ if (!delivery2) continue;
60
+ occasions.push({
61
+ date,
62
+ title: p.title,
63
+ delivery: delivery2,
64
+ imageUrl: p.coverImageUrl ?? void 0,
65
+ primary: p.primary === true
66
+ });
67
+ }
68
+ for (const d of deliveries) {
69
+ const standalone = !d.presentationRkey || !talkRkeys.has(d.presentationRkey);
70
+ if (!standalone || !d.date || d.hidden) continue;
71
+ occasions.push({ date: d.date, title: d.title || d.eventName || "Talk", delivery: d });
72
+ }
73
+ if (!occasions.length) return void 0;
74
+ const primaryOccasions = occasions.filter((o) => o.primary);
75
+ const pool = primaryOccasions.length ? primaryOccasions : occasions;
76
+ const best = pool.reduce((a, b) => b.date > a.date ? b : a);
77
+ const { delivery } = best;
78
+ return {
79
+ section: "talk",
80
+ href: "#presentations",
81
+ title: best.title,
82
+ meta: metaLine([
83
+ delivery.eventName,
84
+ delivery.location,
85
+ getPresentationRoleLabel(delivery.role ?? void 0),
86
+ getCalendarEventModeLabel(delivery.mode ?? void 0),
87
+ coPeopleLabel(delivery.coSpeakers, self)
88
+ ]),
89
+ dateStr: formatEventDate(best.date),
90
+ imageUrl: best.imageUrl,
91
+ status: best.date > today ? "upcoming" : "recent"
92
+ };
93
+ }
94
+ function buildProfileHighlights(profile, { today }) {
95
+ const row1 = [];
96
+ const row2 = [];
97
+ const self = { did: profile.did, handle: profile.handle };
98
+ const talk = pickTalk(
99
+ visible(profile.presentations),
100
+ visible(profile.presentationDeliveries),
101
+ today,
102
+ self
103
+ );
104
+ if (talk) row1.push(talk);
105
+ const visiblePublications = visible(profile.publications);
106
+ const publication = pickPrimaryFlagged(visiblePublications) ?? sortByDateDesc(visiblePublications, singleDateExtractor)[0];
107
+ if (publication) {
108
+ const venue = publication.publicationName ?? publication.publisher;
109
+ const coAuthors = coPeopleLabel(publication.contributors, self);
110
+ const venueIsSelf = venue === profile.handle || !!profile.displayName && venue === profile.displayName && !coAuthors;
111
+ row1.push({
112
+ section: "publication",
113
+ href: "#publications",
114
+ title: publication.title,
115
+ meta: metaLine([coAuthors, venue && !venueIsSelf ? venue : void 0, publication.subtitle]),
116
+ dateStr: formatSingleDate(publication.date),
117
+ imageUrl: publication.image ?? void 0,
118
+ status: "recent"
119
+ });
120
+ }
121
+ const positions = visible(profile.positions);
122
+ const career = pickPrimaryPosition(positions) ?? sortByDateDesc(positions, lexiconDateExtractor)[0];
123
+ if (career) {
124
+ row2.push({
125
+ section: "career",
126
+ href: "#career",
127
+ title: career.title,
128
+ meta: metaLine([career.entityName ?? career.company]),
129
+ dateStr: formatSpanDate(career.startedAt, career.endedAt),
130
+ status: career.endedAt ? "recent" : "current"
131
+ });
132
+ }
133
+ const visibleEducation = visible(profile.education);
134
+ const education = pickPrimaryFlagged(visibleEducation) ?? sortByDateDesc(visibleEducation, lexiconDateExtractor)[0];
135
+ if (education) {
136
+ const institution = education.entityName ?? education.institution;
137
+ const title = education.degree || education.fieldOfStudy || institution || "Education";
138
+ row2.push({
139
+ section: "education",
140
+ href: "#education",
141
+ title,
142
+ meta: metaLine([title === institution ? void 0 : institution]),
143
+ dateStr: formatSpanDate(education.startedAt, education.endedAt),
144
+ status: education.endedAt ? "recent" : "current"
145
+ });
146
+ } else {
147
+ const course = visible(profile.courses)[0];
148
+ if (course) {
149
+ row2.push({
150
+ section: "education",
151
+ href: "#courses",
152
+ title: course.name,
153
+ meta: metaLine([course.entityName ?? course.institution]),
154
+ dateStr: formatSingleDate(course.completedAt),
155
+ status: "recent"
156
+ });
157
+ }
158
+ }
159
+ const ongoingProjects = visible(profile.projects).filter((p) => !p.endDate);
160
+ const project = pickPrimaryFlagged(ongoingProjects) ?? sortByDateDesc(ongoingProjects, dateRangeExtractor)[0];
161
+ if (project) {
162
+ row2.push({
163
+ section: "project",
164
+ href: "#projects",
165
+ title: project.name,
166
+ meta: coPeopleLabel(project.members, self),
167
+ dateStr: formatSpanDate(project.startDate, project.endDate),
168
+ status: "current"
169
+ });
170
+ }
171
+ const ongoingInvolvement = visible(profile.involvement).filter((i) => !i.endedAt);
172
+ const involvement = pickPrimaryFlagged(ongoingInvolvement) ?? sortByDateDesc(ongoingInvolvement, lexiconDateExtractor)[0];
173
+ if (involvement) {
174
+ row2.push({
175
+ section: "involvement",
176
+ href: "#involvement",
177
+ // No role: the kind's readable heading ("Volunteering"), never the raw
178
+ // lexicon token (`id.sifa.defs#involvementCharity`).
179
+ title: involvement.role || getInvolvementKindHeading(involvement.kind),
180
+ meta: metaLine([
181
+ involvement.entityName ?? involvement.upstream,
182
+ coPeopleLabel(involvement.collaborators, self)
183
+ ]),
184
+ dateStr: formatSpanDate(involvement.startedAt, involvement.endedAt),
185
+ status: "current"
186
+ });
187
+ }
188
+ return { row1, row2 };
189
+ }
190
+ function shouldRenderHighlights(rows) {
191
+ return rows.row1.length + rows.row2.length >= 2;
192
+ }
193
+ export {
194
+ buildProfileHighlights,
195
+ formatEventDate,
196
+ formatSingleDate,
197
+ formatSpanDate,
198
+ shouldRenderHighlights
199
+ };
200
+ //# sourceMappingURL=highlights.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/profile/highlights.ts"],"sourcesContent":["/**\n * Selection + formatting logic for the profile \"Highlights\" block: one tile per\n * section showing the ongoing record (or the most-recent one, per section\n * policy). Pure functions (no components, no rendering), so every surface can\n * share the same selection and labeling and never drift:\n *\n * - sifa-web renders these tiles as a React/Tailwind card grid on `/p/{handle}`.\n * - sifa-page-renderer renders them as academicpages HTML below the personal\n * site's About section (page.sifa.id/{handle}).\n *\n * Both consume `buildProfileHighlights`; only the presentation differs. Selection\n * reuses the SDK's shared date primitives so date-field naming differences and\n * the \"ongoing sorts first\" rule stay in one place; only the block-specific\n * display strings (\"Since …\", day-level event dates) live here.\n */\nimport { pickPrimaryPosition } from '../logic/primary-position.js';\nimport { pickPrimaryFlagged } from '../logic/primary-item.js';\nimport { getInvolvementKindHeading } from '../taxonomy/involvement-kind.js';\nimport {\n sortByDateDesc,\n lexiconDateExtractor,\n dateRangeExtractor,\n singleDateExtractor,\n} from '../format/sort-by-date.js';\nimport { formatTimelineDate } from '../format/index.js';\nimport { getPresentationRoleLabel } from '../taxonomy/presentation-role.js';\nimport { getCalendarEventModeLabel } from '../taxonomy/calendar-event.js';\nimport type { Profile, ProfilePresentation, ProfilePresentationDelivery } from '../types/index.js';\n\n/** The slice of a profile the Highlights block reads. Both `Profile` and a\n * page's anonymous `Omit<Profile,'isOwnProfile'>` view satisfy it. `did` and\n * `handle` identify the owner so co-author/co-speaker/member lists can exclude\n * them (the owner is always implied; only other people are worth naming). */\nexport type ProfileHighlightsInput = Pick<\n Profile,\n | 'did'\n | 'handle'\n | 'displayName'\n | 'presentations'\n | 'presentationDeliveries'\n | 'publications'\n | 'positions'\n | 'education'\n | 'courses'\n | 'projects'\n | 'involvement'\n>;\n\n/** A named person on a record: a publication contributor or an ActorCard\n * (co-speaker, project member, involvement collaborator). */\ninterface PersonRef {\n did?: string;\n handle?: string;\n displayName?: string;\n name?: string;\n}\n\n/**\n * \"with Jane Doe, Alex Roe\" for the OTHER people on a record, or undefined when\n * the owner is the only one. The owner is always implied on their own profile,\n * so naming them adds nothing; co-people are the differentiator.\n */\nfunction coPeopleLabel(\n people: readonly PersonRef[] | undefined,\n self: { did: string; handle: string },\n): string | undefined {\n const names = (people ?? [])\n .filter((p) => p.did !== self.did && (!p.handle || p.handle !== self.handle))\n .map((p) => p.displayName ?? p.name ?? p.handle)\n .filter((n): n is string => Boolean(n && n.trim()));\n return names.length ? `with ${names.join(', ')}` : undefined;\n}\n\nexport type ProfileHighlightSection =\n 'talk' | 'publication' | 'career' | 'education' | 'project' | 'involvement';\n\n/** Blue pill: upcoming/current. Grey pill: recent (most recent, ended). */\nexport type ProfileHighlightStatus = 'upcoming' | 'current' | 'recent';\n\nexport interface ProfileHighlightTile {\n section: ProfileHighlightSection;\n /** Same-page anchor for the section this tile summarizes. */\n href: string;\n title: string;\n meta?: string;\n dateStr?: string;\n imageUrl?: string;\n status: ProfileHighlightStatus;\n}\n\nconst EN_DASH = '–';\n\n/** Drop items the owner has hidden - they must never surface as \"current\". */\nfunction visible<T extends { hidden?: boolean }>(items: readonly T[] | undefined): T[] {\n return (items ?? []).filter((i) => !i.hidden);\n}\n\n/** Join the truthy parts of a meta line with the profile \"·\" separator. */\nfunction metaLine(parts: (string | undefined | null)[]): string | undefined {\n const kept = parts.filter((p): p is string => Boolean(p && p.trim()));\n return kept.length ? kept.join(' · ') : undefined;\n}\n\n/**\n * A start/end span. Ongoing (a start with no end) reads \"Since Jan 2014\"; a\n * closed span uses an en dash; equal ends collapse to one month.\n */\nexport function formatSpanDate(start?: string, end?: string): string | undefined {\n if (start && !end) return `Since ${formatTimelineDate(start)}`;\n if (!start && !end) return undefined;\n if (!start) return formatTimelineDate(end as string);\n const a = formatTimelineDate(start);\n const b = formatTimelineDate(end as string);\n return a === b ? a : `${a} ${EN_DASH} ${b}`;\n}\n\n/** A single month/year date (publications, completed courses). */\nexport function formatSingleDate(date?: string): string | undefined {\n return date ? formatTimelineDate(date) : undefined;\n}\n\nconst EVENT_DAY_FMT = new Intl.DateTimeFormat('en', {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n timeZone: 'UTC',\n});\n\n/**\n * A talk delivery date. Day-level strings (\"2026-10-12\") render as\n * \"Oct 12, 2026\"; month- or year-only strings fall back to the month formatter.\n * UTC-fixed so server and client agree.\n */\nexport function formatEventDate(date?: string): string | undefined {\n if (!date) return undefined;\n if (date.length === 10) {\n const d = new Date(`${date}T00:00:00Z`);\n if (!Number.isNaN(d.getTime())) return EVENT_DAY_FMT.format(d);\n }\n return formatTimelineDate(date);\n}\n\ninterface TalkOccasion {\n date: string;\n title: string;\n delivery: ProfilePresentationDelivery;\n imageUrl?: string;\n /** True when this occasion belongs to a talk the user flagged primary. */\n primary?: boolean;\n}\n\n/**\n * The single most recent (or soonest upcoming) speaking occasion, drawn from\n * both reusable talks (with their nested deliveries) and one-off sessions that\n * are not linked to a talk. Returns undefined when nothing is dated.\n */\nfunction pickTalk(\n presentations: ProfilePresentation[],\n deliveries: ProfilePresentationDelivery[],\n today: string,\n self: { did: string; handle: string },\n): ProfileHighlightTile | undefined {\n const talkRkeys = new Set(presentations.map((p) => p.rkey));\n const occasions: TalkOccasion[] = [];\n\n for (const p of presentations) {\n const dated = visible(p.deliveries).filter((d) => d.date);\n if (!dated.length) continue;\n const date = dated.reduce((max, d) => (d.date && d.date > max ? d.date : max), '');\n const delivery = dated.find((d) => d.date === date);\n if (!delivery) continue;\n occasions.push({\n date,\n title: p.title,\n delivery,\n imageUrl: p.coverImageUrl ?? undefined,\n primary: p.primary === true,\n });\n }\n for (const d of deliveries) {\n const standalone = !d.presentationRkey || !talkRkeys.has(d.presentationRkey);\n if (!standalone || !d.date || d.hidden) continue;\n occasions.push({ date: d.date, title: d.title || d.eventName || 'Talk', delivery: d });\n }\n\n if (!occasions.length) return undefined;\n // A flagged primary talk wins over the automatic latest/soonest pick. Standalone\n // sessions never carry the flag, so the pool is empty unless a reusable talk is\n // primary, in which case its most recent or soonest occasion is used.\n const primaryOccasions = occasions.filter((o) => o.primary);\n const pool = primaryOccasions.length ? primaryOccasions : occasions;\n const best = pool.reduce((a, b) => (b.date > a.date ? b : a));\n const { delivery } = best;\n\n return {\n section: 'talk',\n href: '#presentations',\n title: best.title,\n meta: metaLine([\n delivery.eventName,\n delivery.location,\n getPresentationRoleLabel(delivery.role ?? undefined),\n getCalendarEventModeLabel(delivery.mode ?? undefined),\n coPeopleLabel(delivery.coSpeakers, self),\n ]),\n dateStr: formatEventDate(best.date),\n imageUrl: best.imageUrl,\n status: best.date > today ? 'upcoming' : 'recent',\n };\n}\n\nexport interface BuildProfileHighlightsOptions {\n /** Injectable \"today\" (YYYY-MM-DD) so upcoming/latest is deterministic in tests. */\n today: string;\n}\n\n/**\n * Compute the tiles for the Highlights block. Row 1 = Talk + Publication (media);\n * row 2 = Career, Education, Project, Involvement (compact). Per-section policy:\n * Career + Education always show their most-recent record; Project + Involvement\n * appear only when something is ongoing; Talk/Publication always show the latest.\n * Hidden records are excluded everywhere. Both rows are returned in a fixed order\n * so every surface renders the same sequence.\n */\nexport function buildProfileHighlights(\n profile: ProfileHighlightsInput,\n { today }: BuildProfileHighlightsOptions,\n): { row1: ProfileHighlightTile[]; row2: ProfileHighlightTile[] } {\n const row1: ProfileHighlightTile[] = [];\n const row2: ProfileHighlightTile[] = [];\n const self = { did: profile.did, handle: profile.handle };\n\n // --- Row 1: Talk ---\n const talk = pickTalk(\n visible(profile.presentations),\n visible(profile.presentationDeliveries),\n today,\n self,\n );\n if (talk) row1.push(talk);\n\n // --- Row 1: Publication (any item may be the user-flagged primary) ---\n const visiblePublications = visible(profile.publications);\n const publication =\n pickPrimaryFlagged(visiblePublications) ??\n sortByDateDesc(visiblePublications, singleDateExtractor)[0];\n if (publication) {\n // Drop the venue when it is the owner themself: self-published articles carry\n // the owner's own handle or display name as publisher/publicationName, which\n // reads as \"me\". The handle match is safe (handles are unique). The display\n // name is freeform and could collide with a real journal (e.g. \"Nature\"), so\n // only drop it on a solo byline (no co-authors); a real journal piece almost\n // always has co-authors or a non-matching venue. Co-authors go first.\n const venue = publication.publicationName ?? publication.publisher;\n const coAuthors = coPeopleLabel(publication.contributors, self);\n const venueIsSelf =\n venue === profile.handle ||\n (!!profile.displayName && venue === profile.displayName && !coAuthors);\n row1.push({\n section: 'publication',\n href: '#publications',\n title: publication.title,\n meta: metaLine([coAuthors, venue && !venueIsSelf ? venue : undefined, publication.subtitle]),\n dateStr: formatSingleDate(publication.date),\n imageUrl: publication.image ?? undefined,\n status: 'recent',\n });\n }\n\n // --- Row 2: Career (always shown) ---\n const positions = visible(profile.positions);\n const career =\n pickPrimaryPosition(positions) ?? sortByDateDesc(positions, lexiconDateExtractor)[0];\n if (career) {\n row2.push({\n section: 'career',\n href: '#career',\n title: career.title,\n meta: metaLine([career.entityName ?? career.company]),\n dateStr: formatSpanDate(career.startedAt, career.endedAt),\n status: career.endedAt ? 'recent' : 'current',\n });\n }\n\n // --- Row 2: Education (always shown; any item may be primary; falls back to a Course) ---\n const visibleEducation = visible(profile.education);\n const education =\n pickPrimaryFlagged(visibleEducation) ??\n sortByDateDesc(visibleEducation, lexiconDateExtractor)[0];\n if (education) {\n const institution = education.entityName ?? education.institution;\n const title = education.degree || education.fieldOfStudy || institution || 'Education';\n row2.push({\n section: 'education',\n href: '#education',\n title,\n meta: metaLine([title === institution ? undefined : institution]),\n dateStr: formatSpanDate(education.startedAt, education.endedAt),\n status: education.endedAt ? 'recent' : 'current',\n });\n } else {\n const course = visible(profile.courses)[0];\n if (course) {\n row2.push({\n section: 'education',\n href: '#courses',\n title: course.name,\n meta: metaLine([course.entityName ?? course.institution]),\n dateStr: formatSingleDate(course.completedAt),\n status: 'recent',\n });\n }\n }\n\n // --- Row 2: Project (only when ongoing; an ongoing project may be primary) ---\n const ongoingProjects = visible(profile.projects).filter((p) => !p.endDate);\n const project =\n pickPrimaryFlagged(ongoingProjects) ?? sortByDateDesc(ongoingProjects, dateRangeExtractor)[0];\n if (project) {\n row2.push({\n section: 'project',\n href: '#projects',\n title: project.name,\n meta: coPeopleLabel(project.members, self),\n dateStr: formatSpanDate(project.startDate, project.endDate),\n status: 'current',\n });\n }\n\n // --- Row 2: Involvement (only when ongoing; an ongoing involvement may be primary) ---\n const ongoingInvolvement = visible(profile.involvement).filter((i) => !i.endedAt);\n const involvement =\n pickPrimaryFlagged(ongoingInvolvement) ??\n sortByDateDesc(ongoingInvolvement, lexiconDateExtractor)[0];\n if (involvement) {\n row2.push({\n section: 'involvement',\n href: '#involvement',\n // No role: the kind's readable heading (\"Volunteering\"), never the raw\n // lexicon token (`id.sifa.defs#involvementCharity`).\n title: involvement.role || getInvolvementKindHeading(involvement.kind),\n meta: metaLine([\n involvement.entityName ?? involvement.upstream,\n coPeopleLabel(involvement.collaborators, self),\n ]),\n dateStr: formatSpanDate(involvement.startedAt, involvement.endedAt),\n status: 'current',\n });\n }\n\n return { row1, row2 };\n}\n\n/**\n * Whether the Highlights block is worth rendering at all. A single tile just\n * duplicates the one section right below it, so the block is omitted under two\n * tiles. Shared by every surface (profile page + personal site) so the collapse\n * threshold can never diverge between them.\n */\nexport function shouldRenderHighlights(rows: {\n row1: ProfileHighlightTile[];\n row2: ProfileHighlightTile[];\n}): boolean {\n return rows.row1.length + rows.row2.length >= 2;\n}\n"],"mappings":";AAeA,SAAS,2BAA2B;AACpC,SAAS,0BAA0B;AACnC,SAAS,iCAAiC;AAC1C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,0BAA0B;AACnC,SAAS,gCAAgC;AACzC,SAAS,iCAAiC;AAoC1C,SAAS,cACP,QACA,MACoB;AACpB,QAAM,SAAS,UAAU,CAAC,GACvB,OAAO,CAAC,MAAM,EAAE,QAAQ,KAAK,QAAQ,CAAC,EAAE,UAAU,EAAE,WAAW,KAAK,OAAO,EAC3E,IAAI,CAAC,MAAM,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,EAC9C,OAAO,CAAC,MAAmB,QAAQ,KAAK,EAAE,KAAK,CAAC,CAAC;AACpD,SAAO,MAAM,SAAS,QAAQ,MAAM,KAAK,IAAI,CAAC,KAAK;AACrD;AAmBA,IAAM,UAAU;AAGhB,SAAS,QAAwC,OAAsC;AACrF,UAAQ,SAAS,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM;AAC9C;AAGA,SAAS,SAAS,OAA0D;AAC1E,QAAM,OAAO,MAAM,OAAO,CAAC,MAAmB,QAAQ,KAAK,EAAE,KAAK,CAAC,CAAC;AACpE,SAAO,KAAK,SAAS,KAAK,KAAK,QAAK,IAAI;AAC1C;AAMO,SAAS,eAAe,OAAgB,KAAkC;AAC/E,MAAI,SAAS,CAAC,IAAK,QAAO,SAAS,mBAAmB,KAAK,CAAC;AAC5D,MAAI,CAAC,SAAS,CAAC,IAAK,QAAO;AAC3B,MAAI,CAAC,MAAO,QAAO,mBAAmB,GAAa;AACnD,QAAM,IAAI,mBAAmB,KAAK;AAClC,QAAM,IAAI,mBAAmB,GAAa;AAC1C,SAAO,MAAM,IAAI,IAAI,GAAG,CAAC,IAAI,OAAO,IAAI,CAAC;AAC3C;AAGO,SAAS,iBAAiB,MAAmC;AAClE,SAAO,OAAO,mBAAmB,IAAI,IAAI;AAC3C;AAEA,IAAM,gBAAgB,IAAI,KAAK,eAAe,MAAM;AAAA,EAClD,MAAM;AAAA,EACN,OAAO;AAAA,EACP,KAAK;AAAA,EACL,UAAU;AACZ,CAAC;AAOM,SAAS,gBAAgB,MAAmC;AACjE,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI,KAAK,WAAW,IAAI;AACtB,UAAM,IAAI,oBAAI,KAAK,GAAG,IAAI,YAAY;AACtC,QAAI,CAAC,OAAO,MAAM,EAAE,QAAQ,CAAC,EAAG,QAAO,cAAc,OAAO,CAAC;AAAA,EAC/D;AACA,SAAO,mBAAmB,IAAI;AAChC;AAgBA,SAAS,SACP,eACA,YACA,OACA,MACkC;AAClC,QAAM,YAAY,IAAI,IAAI,cAAc,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;AAC1D,QAAM,YAA4B,CAAC;AAEnC,aAAW,KAAK,eAAe;AAC7B,UAAM,QAAQ,QAAQ,EAAE,UAAU,EAAE,OAAO,CAAC,MAAM,EAAE,IAAI;AACxD,QAAI,CAAC,MAAM,OAAQ;AACnB,UAAM,OAAO,MAAM,OAAO,CAAC,KAAK,MAAO,EAAE,QAAQ,EAAE,OAAO,MAAM,EAAE,OAAO,KAAM,EAAE;AACjF,UAAMA,YAAW,MAAM,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI;AAClD,QAAI,CAACA,UAAU;AACf,cAAU,KAAK;AAAA,MACb;AAAA,MACA,OAAO,EAAE;AAAA,MACT,UAAAA;AAAA,MACA,UAAU,EAAE,iBAAiB;AAAA,MAC7B,SAAS,EAAE,YAAY;AAAA,IACzB,CAAC;AAAA,EACH;AACA,aAAW,KAAK,YAAY;AAC1B,UAAM,aAAa,CAAC,EAAE,oBAAoB,CAAC,UAAU,IAAI,EAAE,gBAAgB;AAC3E,QAAI,CAAC,cAAc,CAAC,EAAE,QAAQ,EAAE,OAAQ;AACxC,cAAU,KAAK,EAAE,MAAM,EAAE,MAAM,OAAO,EAAE,SAAS,EAAE,aAAa,QAAQ,UAAU,EAAE,CAAC;AAAA,EACvF;AAEA,MAAI,CAAC,UAAU,OAAQ,QAAO;AAI9B,QAAM,mBAAmB,UAAU,OAAO,CAAC,MAAM,EAAE,OAAO;AAC1D,QAAM,OAAO,iBAAiB,SAAS,mBAAmB;AAC1D,QAAM,OAAO,KAAK,OAAO,CAAC,GAAG,MAAO,EAAE,OAAO,EAAE,OAAO,IAAI,CAAE;AAC5D,QAAM,EAAE,SAAS,IAAI;AAErB,SAAO;AAAA,IACL,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OAAO,KAAK;AAAA,IACZ,MAAM,SAAS;AAAA,MACb,SAAS;AAAA,MACT,SAAS;AAAA,MACT,yBAAyB,SAAS,QAAQ,MAAS;AAAA,MACnD,0BAA0B,SAAS,QAAQ,MAAS;AAAA,MACpD,cAAc,SAAS,YAAY,IAAI;AAAA,IACzC,CAAC;AAAA,IACD,SAAS,gBAAgB,KAAK,IAAI;AAAA,IAClC,UAAU,KAAK;AAAA,IACf,QAAQ,KAAK,OAAO,QAAQ,aAAa;AAAA,EAC3C;AACF;AAeO,SAAS,uBACd,SACA,EAAE,MAAM,GACwD;AAChE,QAAM,OAA+B,CAAC;AACtC,QAAM,OAA+B,CAAC;AACtC,QAAM,OAAO,EAAE,KAAK,QAAQ,KAAK,QAAQ,QAAQ,OAAO;AAGxD,QAAM,OAAO;AAAA,IACX,QAAQ,QAAQ,aAAa;AAAA,IAC7B,QAAQ,QAAQ,sBAAsB;AAAA,IACtC;AAAA,IACA;AAAA,EACF;AACA,MAAI,KAAM,MAAK,KAAK,IAAI;AAGxB,QAAM,sBAAsB,QAAQ,QAAQ,YAAY;AACxD,QAAM,cACJ,mBAAmB,mBAAmB,KACtC,eAAe,qBAAqB,mBAAmB,EAAE,CAAC;AAC5D,MAAI,aAAa;AAOf,UAAM,QAAQ,YAAY,mBAAmB,YAAY;AACzD,UAAM,YAAY,cAAc,YAAY,cAAc,IAAI;AAC9D,UAAM,cACJ,UAAU,QAAQ,UACjB,CAAC,CAAC,QAAQ,eAAe,UAAU,QAAQ,eAAe,CAAC;AAC9D,SAAK,KAAK;AAAA,MACR,SAAS;AAAA,MACT,MAAM;AAAA,MACN,OAAO,YAAY;AAAA,MACnB,MAAM,SAAS,CAAC,WAAW,SAAS,CAAC,cAAc,QAAQ,QAAW,YAAY,QAAQ,CAAC;AAAA,MAC3F,SAAS,iBAAiB,YAAY,IAAI;AAAA,MAC1C,UAAU,YAAY,SAAS;AAAA,MAC/B,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAGA,QAAM,YAAY,QAAQ,QAAQ,SAAS;AAC3C,QAAM,SACJ,oBAAoB,SAAS,KAAK,eAAe,WAAW,oBAAoB,EAAE,CAAC;AACrF,MAAI,QAAQ;AACV,SAAK,KAAK;AAAA,MACR,SAAS;AAAA,MACT,MAAM;AAAA,MACN,OAAO,OAAO;AAAA,MACd,MAAM,SAAS,CAAC,OAAO,cAAc,OAAO,OAAO,CAAC;AAAA,MACpD,SAAS,eAAe,OAAO,WAAW,OAAO,OAAO;AAAA,MACxD,QAAQ,OAAO,UAAU,WAAW;AAAA,IACtC,CAAC;AAAA,EACH;AAGA,QAAM,mBAAmB,QAAQ,QAAQ,SAAS;AAClD,QAAM,YACJ,mBAAmB,gBAAgB,KACnC,eAAe,kBAAkB,oBAAoB,EAAE,CAAC;AAC1D,MAAI,WAAW;AACb,UAAM,cAAc,UAAU,cAAc,UAAU;AACtD,UAAM,QAAQ,UAAU,UAAU,UAAU,gBAAgB,eAAe;AAC3E,SAAK,KAAK;AAAA,MACR,SAAS;AAAA,MACT,MAAM;AAAA,MACN;AAAA,MACA,MAAM,SAAS,CAAC,UAAU,cAAc,SAAY,WAAW,CAAC;AAAA,MAChE,SAAS,eAAe,UAAU,WAAW,UAAU,OAAO;AAAA,MAC9D,QAAQ,UAAU,UAAU,WAAW;AAAA,IACzC,CAAC;AAAA,EACH,OAAO;AACL,UAAM,SAAS,QAAQ,QAAQ,OAAO,EAAE,CAAC;AACzC,QAAI,QAAQ;AACV,WAAK,KAAK;AAAA,QACR,SAAS;AAAA,QACT,MAAM;AAAA,QACN,OAAO,OAAO;AAAA,QACd,MAAM,SAAS,CAAC,OAAO,cAAc,OAAO,WAAW,CAAC;AAAA,QACxD,SAAS,iBAAiB,OAAO,WAAW;AAAA,QAC5C,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAAA,EACF;AAGA,QAAM,kBAAkB,QAAQ,QAAQ,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO;AAC1E,QAAM,UACJ,mBAAmB,eAAe,KAAK,eAAe,iBAAiB,kBAAkB,EAAE,CAAC;AAC9F,MAAI,SAAS;AACX,SAAK,KAAK;AAAA,MACR,SAAS;AAAA,MACT,MAAM;AAAA,MACN,OAAO,QAAQ;AAAA,MACf,MAAM,cAAc,QAAQ,SAAS,IAAI;AAAA,MACzC,SAAS,eAAe,QAAQ,WAAW,QAAQ,OAAO;AAAA,MAC1D,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAGA,QAAM,qBAAqB,QAAQ,QAAQ,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO;AAChF,QAAM,cACJ,mBAAmB,kBAAkB,KACrC,eAAe,oBAAoB,oBAAoB,EAAE,CAAC;AAC5D,MAAI,aAAa;AACf,SAAK,KAAK;AAAA,MACR,SAAS;AAAA,MACT,MAAM;AAAA;AAAA;AAAA,MAGN,OAAO,YAAY,QAAQ,0BAA0B,YAAY,IAAI;AAAA,MACrE,MAAM,SAAS;AAAA,QACb,YAAY,cAAc,YAAY;AAAA,QACtC,cAAc,YAAY,eAAe,IAAI;AAAA,MAC/C,CAAC;AAAA,MACD,SAAS,eAAe,YAAY,WAAW,YAAY,OAAO;AAAA,MAClE,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAEA,SAAO,EAAE,MAAM,KAAK;AACtB;AAQO,SAAS,uBAAuB,MAG3B;AACV,SAAO,KAAK,KAAK,SAAS,KAAK,KAAK,UAAU;AAChD;","names":["delivery"]}
@@ -0,0 +1,66 @@
1
+ // src/profile/index.ts
2
+ import {
3
+ ALL_SECTIONS,
4
+ SECTION_GROUPS,
5
+ SECTION_LABELS,
6
+ filterHidden,
7
+ getVisibleSectionIds,
8
+ isSectionPopulated,
9
+ visibleItems
10
+ } from "./section-model.js";
11
+ import {
12
+ hoistPrimary,
13
+ sortCertifications,
14
+ sortEducation,
15
+ sortHonors,
16
+ sortLanguages,
17
+ sortPositions,
18
+ sortProjects,
19
+ sortPublications
20
+ } from "./section-sorts.js";
21
+ import { sortByActiveDateRange } from "./range-sort.js";
22
+ import { sortLanguagesByProficiency } from "./language-sort.js";
23
+ import { isRoleLineRedundant } from "./headline-role-dedupe.js";
24
+ import {
25
+ INVOLVEMENT_HEADING_ORDER,
26
+ groupInvolvementByHeading
27
+ } from "./involvement-grouping.js";
28
+ import {
29
+ groupPublicationVersions
30
+ } from "./publication-versions.js";
31
+ import {
32
+ buildProfileHighlights,
33
+ shouldRenderHighlights,
34
+ formatSpanDate,
35
+ formatSingleDate,
36
+ formatEventDate
37
+ } from "./highlights.js";
38
+ export {
39
+ ALL_SECTIONS,
40
+ INVOLVEMENT_HEADING_ORDER,
41
+ SECTION_GROUPS,
42
+ SECTION_LABELS,
43
+ buildProfileHighlights,
44
+ filterHidden,
45
+ formatEventDate,
46
+ formatSingleDate,
47
+ formatSpanDate,
48
+ getVisibleSectionIds,
49
+ groupInvolvementByHeading,
50
+ groupPublicationVersions,
51
+ hoistPrimary,
52
+ isRoleLineRedundant,
53
+ isSectionPopulated,
54
+ shouldRenderHighlights,
55
+ sortByActiveDateRange,
56
+ sortCertifications,
57
+ sortEducation,
58
+ sortHonors,
59
+ sortLanguages,
60
+ sortLanguagesByProficiency,
61
+ sortPositions,
62
+ sortProjects,
63
+ sortPublications,
64
+ visibleItems
65
+ };
66
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/profile/index.ts"],"sourcesContent":["export {\n ALL_SECTIONS,\n SECTION_GROUPS,\n SECTION_LABELS,\n filterHidden,\n getVisibleSectionIds,\n isSectionPopulated,\n visibleItems,\n type SectionGroupId,\n type SectionId,\n} from './section-model.js';\n\nexport {\n hoistPrimary,\n sortCertifications,\n sortEducation,\n sortHonors,\n sortLanguages,\n sortPositions,\n sortProjects,\n sortPublications,\n} from './section-sorts.js';\n\nexport { sortByActiveDateRange } from './range-sort.js';\nexport { sortLanguagesByProficiency } from './language-sort.js';\nexport { isRoleLineRedundant } from './headline-role-dedupe.js';\n\nexport {\n INVOLVEMENT_HEADING_ORDER,\n groupInvolvementByHeading,\n type InvolvementGroup,\n} from './involvement-grouping.js';\n\nexport {\n groupPublicationVersions,\n type GroupPublicationVersionsOptions,\n type PublicationVersionGroup,\n type VersionablePublication,\n} from './publication-versions.js';\n\nexport {\n buildProfileHighlights,\n shouldRenderHighlights,\n formatSpanDate,\n formatSingleDate,\n formatEventDate,\n type ProfileHighlightsInput,\n type ProfileHighlightSection,\n type ProfileHighlightStatus,\n type ProfileHighlightTile,\n type BuildProfileHighlightsOptions,\n} from './highlights.js';\n"],"mappings":";AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAEP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,6BAA6B;AACtC,SAAS,kCAAkC;AAC3C,SAAS,2BAA2B;AAEpC;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AAEP;AAAA,EACE;AAAA,OAIK;AAEP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAMK;","names":[]}