@singi-labs/sifa-sdk 0.19.44 → 0.19.46

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 (582) hide show
  1. package/dist/atproto/index.js +9 -3
  2. package/dist/atproto/index.js.map +1 -1
  3. package/dist/badge/index.cjs.map +1 -1
  4. package/dist/badge/index.js +5 -141
  5. package/dist/badge/index.js.map +1 -1
  6. package/dist/cards/adult-content.js +14 -0
  7. package/dist/cards/adult-content.js.map +1 -0
  8. package/dist/cards/app-url-patterns.js +369 -0
  9. package/dist/cards/app-url-patterns.js.map +1 -0
  10. package/dist/cards/index.js +24 -0
  11. package/dist/cards/index.js.map +1 -0
  12. package/dist/cards/resolve-card-url.js +198 -0
  13. package/dist/cards/resolve-card-url.js.map +1 -0
  14. package/dist/cards/visibility.js +45 -0
  15. package/dist/cards/visibility.js.map +1 -0
  16. package/dist/flags/index.js +4 -3
  17. package/dist/flags/index.js.map +1 -1
  18. package/dist/format/collapse-contributors.js +19 -0
  19. package/dist/format/collapse-contributors.js.map +1 -0
  20. package/dist/format/company-name.js +61 -0
  21. package/dist/format/company-name.js.map +1 -0
  22. package/dist/format/compare-text.js +9 -0
  23. package/dist/format/compare-text.js.map +1 -0
  24. package/dist/format/doi.js +8 -0
  25. package/dist/format/doi.js.map +1 -0
  26. package/dist/format/embed.js +131 -0
  27. package/dist/format/embed.js.map +1 -0
  28. package/dist/format/endorsement-context.js +35 -0
  29. package/dist/format/endorsement-context.js.map +1 -0
  30. package/dist/format/format-display-url.js +42 -0
  31. package/dist/format/format-display-url.js.map +1 -0
  32. package/dist/format/format-duration.js +13 -0
  33. package/dist/format/format-duration.js.map +1 -0
  34. package/dist/format/format-time.js +30 -0
  35. package/dist/format/format-time.js.map +1 -0
  36. package/dist/format/handle-utils.js +19 -0
  37. package/dist/format/handle-utils.js.map +1 -0
  38. package/dist/format/index.js +106 -0
  39. package/dist/format/index.js.map +1 -0
  40. package/dist/format/location-segments.js +29 -0
  41. package/dist/format/location-segments.js.map +1 -0
  42. package/dist/format/location-utils.js +42 -0
  43. package/dist/format/location-utils.js.map +1 -0
  44. package/dist/format/meta-description.js +35 -0
  45. package/dist/format/meta-description.js.map +1 -0
  46. package/dist/format/normalize-company-key.js +12 -0
  47. package/dist/format/normalize-company-key.js.map +1 -0
  48. package/dist/format/normalize-legal-form.js +71 -0
  49. package/dist/format/normalize-legal-form.js.map +1 -0
  50. package/dist/format/pds-utils.js +73 -0
  51. package/dist/format/pds-utils.js.map +1 -0
  52. package/dist/format/sort-by-date.js +39 -0
  53. package/dist/format/sort-by-date.js.map +1 -0
  54. package/dist/format/summarize-deliveries.js +37 -0
  55. package/dist/format/summarize-deliveries.js.map +1 -0
  56. package/dist/format/talk-slug.js +21 -0
  57. package/dist/format/talk-slug.js.map +1 -0
  58. package/dist/format/text-sanitize.js +29 -0
  59. package/dist/format/text-sanitize.js.map +1 -0
  60. package/dist/format/text-truncate.js +14 -0
  61. package/dist/format/text-truncate.js.map +1 -0
  62. package/dist/format/time-utils.js +19 -0
  63. package/dist/format/time-utils.js.map +1 -0
  64. package/dist/format/timeline.js +33 -0
  65. package/dist/format/timeline.js.map +1 -0
  66. package/dist/format/wcag-contrast.js +34 -0
  67. package/dist/format/wcag-contrast.js.map +1 -0
  68. package/dist/import/index.js +22 -0
  69. package/dist/import/index.js.map +1 -0
  70. package/dist/import/presentation-csv.js +116 -0
  71. package/dist/import/presentation-csv.js.map +1 -0
  72. package/dist/index.cjs +7 -1
  73. package/dist/index.cjs.map +1 -1
  74. package/dist/index.d.cts +8 -1
  75. package/dist/index.d.ts +8 -1
  76. package/dist/index.js +677 -7086
  77. package/dist/index.js.map +1 -1
  78. package/dist/jev/index.js +18 -5
  79. package/dist/jev/index.js.map +1 -1
  80. package/dist/jsonld/index.cjs.map +1 -1
  81. package/dist/jsonld/index.js +30 -987
  82. package/dist/jsonld/index.js.map +1 -1
  83. package/dist/jsonld/profile.js +222 -0
  84. package/dist/jsonld/profile.js.map +1 -0
  85. package/dist/jsonld/terms.js +496 -0
  86. package/dist/jsonld/terms.js.map +1 -0
  87. package/dist/jsonld/url.js +17 -0
  88. package/dist/jsonld/url.js.map +1 -0
  89. package/dist/jsonld/works-graph.js +41 -0
  90. package/dist/jsonld/works-graph.js.map +1 -0
  91. package/dist/jsonld/works.js +178 -0
  92. package/dist/jsonld/works.js.map +1 -0
  93. package/dist/logic/actor-identity.js +8 -0
  94. package/dist/logic/actor-identity.js.map +1 -0
  95. package/dist/logic/agent-ref.js +24 -0
  96. package/dist/logic/agent-ref.js.map +1 -0
  97. package/dist/logic/company-page-indexable.js +23 -0
  98. package/dist/logic/company-page-indexable.js.map +1 -0
  99. package/dist/logic/date-lookahead.js +33 -0
  100. package/dist/logic/date-lookahead.js.map +1 -0
  101. package/dist/logic/domain-detect.js +43 -0
  102. package/dist/logic/domain-detect.js.map +1 -0
  103. package/dist/logic/entity-disambiguation.js +24 -0
  104. package/dist/logic/entity-disambiguation.js.map +1 -0
  105. package/dist/logic/entity-ref-anchor.js +33 -0
  106. package/dist/logic/entity-ref-anchor.js.map +1 -0
  107. package/dist/logic/index.js +91 -0
  108. package/dist/logic/index.js.map +1 -0
  109. package/dist/logic/org-floor.js +66 -0
  110. package/dist/logic/org-floor.js.map +1 -0
  111. package/dist/logic/primary-item.js +9 -0
  112. package/dist/logic/primary-item.js.map +1 -0
  113. package/dist/logic/primary-position.js +16 -0
  114. package/dist/logic/primary-position.js.map +1 -0
  115. package/dist/logic/profile-completeness.js +21 -0
  116. package/dist/logic/profile-completeness.js.map +1 -0
  117. package/dist/logic/profile-dimensions.js +50 -0
  118. package/dist/logic/profile-dimensions.js.map +1 -0
  119. package/dist/logic/profile-summary.js +23 -0
  120. package/dist/logic/profile-summary.js.map +1 -0
  121. package/dist/logic/pseudo-employer.js +34 -0
  122. package/dist/logic/pseudo-employer.js.map +1 -0
  123. package/dist/logic/recent-activity.js +10 -0
  124. package/dist/logic/recent-activity.js.map +1 -0
  125. package/dist/logic/self-app.js +10 -0
  126. package/dist/logic/self-app.js.map +1 -0
  127. package/dist/{org-BCrvebcf.d.ts → org-CxTEjjrm.d.ts} +1 -1
  128. package/dist/{org-settings-C73Sin5E.d.ts → org-settings-BShvhyFp.d.ts} +1 -1
  129. package/dist/profile/headline-role-dedupe.js +12 -0
  130. package/dist/profile/headline-role-dedupe.js.map +1 -0
  131. package/dist/profile/highlights.js +200 -0
  132. package/dist/profile/highlights.js.map +1 -0
  133. package/dist/profile/index.js +66 -0
  134. package/dist/profile/index.js.map +1 -0
  135. package/dist/profile/involvement-grouping.js +31 -0
  136. package/dist/profile/involvement-grouping.js.map +1 -0
  137. package/dist/profile/language-sort.js +24 -0
  138. package/dist/profile/language-sort.js.map +1 -0
  139. package/dist/profile/publication-versions.js +98 -0
  140. package/dist/profile/publication-versions.js.map +1 -0
  141. package/dist/profile/range-sort.js +14 -0
  142. package/dist/profile/range-sort.js.map +1 -0
  143. package/dist/profile/section-model.js +122 -0
  144. package/dist/profile/section-model.js.map +1 -0
  145. package/dist/profile/section-sorts.js +33 -0
  146. package/dist/profile/section-sorts.js.map +1 -0
  147. package/dist/publishers/index.js +16 -0
  148. package/dist/publishers/index.js.map +1 -0
  149. package/dist/publishers/registry.js +87 -0
  150. package/dist/publishers/registry.js.map +1 -0
  151. package/dist/publishing/index.js +46 -184
  152. package/dist/publishing/index.js.map +1 -1
  153. package/dist/publishing/linkblog.js +42 -0
  154. package/dist/publishing/linkblog.js.map +1 -0
  155. package/dist/publishing/registry.js +32 -0
  156. package/dist/publishing/registry.js.map +1 -0
  157. package/dist/publishing/schemas.js +74 -0
  158. package/dist/publishing/schemas.js.map +1 -0
  159. package/dist/publishing/types.js +32 -0
  160. package/dist/publishing/types.js.map +1 -0
  161. package/dist/query/client.js +119 -0
  162. package/dist/query/client.js.map +1 -0
  163. package/dist/query/config.js +23 -0
  164. package/dist/query/config.js.map +1 -0
  165. package/dist/query/fetchers/accounts.js +29 -0
  166. package/dist/query/fetchers/accounts.js.map +1 -0
  167. package/dist/query/fetchers/activity-items-hide.js +31 -0
  168. package/dist/query/fetchers/activity-items-hide.js.map +1 -0
  169. package/dist/query/fetchers/activity.js +61 -0
  170. package/dist/query/fetchers/activity.js.map +1 -0
  171. package/dist/query/fetchers/actor.js +30 -0
  172. package/dist/query/fetchers/actor.js.map +1 -0
  173. package/dist/query/fetchers/admin-feature-allowlists.js +41 -0
  174. package/dist/query/fetchers/admin-feature-allowlists.js.map +1 -0
  175. package/dist/query/fetchers/admin-review-queues.js +30 -0
  176. package/dist/query/fetchers/admin-review-queues.js.map +1 -0
  177. package/dist/query/fetchers/apps.js +31 -0
  178. package/dist/query/fetchers/apps.js.map +1 -0
  179. package/dist/query/fetchers/bsky-preferences.js +55 -0
  180. package/dist/query/fetchers/bsky-preferences.js.map +1 -0
  181. package/dist/query/fetchers/confirmations.js +55 -0
  182. package/dist/query/fetchers/confirmations.js.map +1 -0
  183. package/dist/query/fetchers/destructive.js +34 -0
  184. package/dist/query/fetchers/destructive.js.map +1 -0
  185. package/dist/query/fetchers/discovery.js +67 -0
  186. package/dist/query/fetchers/discovery.js.map +1 -0
  187. package/dist/query/fetchers/education.js +23 -0
  188. package/dist/query/fetchers/education.js.map +1 -0
  189. package/dist/query/fetchers/endorsement-inbox.js +36 -0
  190. package/dist/query/fetchers/endorsement-inbox.js.map +1 -0
  191. package/dist/query/fetchers/endorsement.js +23 -0
  192. package/dist/query/fetchers/endorsement.js.map +1 -0
  193. package/dist/query/fetchers/endorsements.js +20 -0
  194. package/dist/query/fetchers/endorsements.js.map +1 -0
  195. package/dist/query/fetchers/entities.js +65 -0
  196. package/dist/query/fetchers/entities.js.map +1 -0
  197. package/dist/query/fetchers/external-accounts.js +74 -0
  198. package/dist/query/fetchers/external-accounts.js.map +1 -0
  199. package/dist/query/fetchers/follow-extras.js +38 -0
  200. package/dist/query/fetchers/follow-extras.js.map +1 -0
  201. package/dist/query/fetchers/follow.js +105 -0
  202. package/dist/query/fetchers/follow.js.map +1 -0
  203. package/dist/query/fetchers/get-profile-view.js +21 -0
  204. package/dist/query/fetchers/get-profile-view.js.map +1 -0
  205. package/dist/query/fetchers/github-prs.js +45 -0
  206. package/dist/query/fetchers/github-prs.js.map +1 -0
  207. package/dist/query/fetchers/inbox.js +29 -0
  208. package/dist/query/fetchers/inbox.js.map +1 -0
  209. package/dist/query/fetchers/index.cjs.map +1 -1
  210. package/dist/query/fetchers/index.d.ts +2 -2
  211. package/dist/query/fetchers/index.js +360 -2045
  212. package/dist/query/fetchers/index.js.map +1 -1
  213. package/dist/query/fetchers/investments.js +18 -0
  214. package/dist/query/fetchers/investments.js.map +1 -0
  215. package/dist/query/fetchers/keytrace-claims.js +23 -0
  216. package/dist/query/fetchers/keytrace-claims.js.map +1 -0
  217. package/dist/query/fetchers/marque-domains.js +23 -0
  218. package/dist/query/fetchers/marque-domains.js.map +1 -0
  219. package/dist/query/fetchers/network-map.js +36 -0
  220. package/dist/query/fetchers/network-map.js.map +1 -0
  221. package/dist/query/fetchers/org.js +48 -0
  222. package/dist/query/fetchers/org.js.map +1 -0
  223. package/dist/query/fetchers/positions.js +77 -0
  224. package/dist/query/fetchers/positions.js.map +1 -0
  225. package/dist/query/fetchers/profile-completeness.js +45 -0
  226. package/dist/query/fetchers/profile-completeness.js.map +1 -0
  227. package/dist/query/fetchers/profile-items-hide.js +36 -0
  228. package/dist/query/fetchers/profile-items-hide.js.map +1 -0
  229. package/dist/query/fetchers/profile-locations.js +23 -0
  230. package/dist/query/fetchers/profile-locations.js.map +1 -0
  231. package/dist/query/fetchers/profile-mutations.js +82 -0
  232. package/dist/query/fetchers/profile-mutations.js.map +1 -0
  233. package/dist/query/fetchers/profile-summary.js +14 -0
  234. package/dist/query/fetchers/profile-summary.js.map +1 -0
  235. package/dist/query/fetchers/profile.js +31 -0
  236. package/dist/query/fetchers/profile.js.map +1 -0
  237. package/dist/query/fetchers/publications.js +66 -0
  238. package/dist/query/fetchers/publications.js.map +1 -0
  239. package/dist/query/fetchers/quoted-posts.js +40 -0
  240. package/dist/query/fetchers/quoted-posts.js.map +1 -0
  241. package/dist/query/fetchers/reactions.js +85 -0
  242. package/dist/query/fetchers/reactions.js.map +1 -0
  243. package/dist/query/fetchers/received-endorsements.js +20 -0
  244. package/dist/query/fetchers/received-endorsements.js.map +1 -0
  245. package/dist/query/fetchers/reciprocity.js +23 -0
  246. package/dist/query/fetchers/reciprocity.js.map +1 -0
  247. package/dist/query/fetchers/records.js +27 -0
  248. package/dist/query/fetchers/records.js.map +1 -0
  249. package/dist/query/fetchers/repo-inventory.js +23 -0
  250. package/dist/query/fetchers/repo-inventory.js.map +1 -0
  251. package/dist/query/fetchers/roadmap.js +73 -0
  252. package/dist/query/fetchers/roadmap.js.map +1 -0
  253. package/dist/query/fetchers/rpg.js +36 -0
  254. package/dist/query/fetchers/rpg.js.map +1 -0
  255. package/dist/query/fetchers/search.js +78 -0
  256. package/dist/query/fetchers/search.js.map +1 -0
  257. package/dist/query/fetchers/section-primary.js +23 -0
  258. package/dist/query/fetchers/section-primary.js.map +1 -0
  259. package/dist/query/fetchers/skills.js +30 -0
  260. package/dist/query/fetchers/skills.js.map +1 -0
  261. package/dist/query/fetchers/speakers.js +42 -0
  262. package/dist/query/fetchers/speakers.js.map +1 -0
  263. package/dist/query/fetchers/stats.js +16 -0
  264. package/dist/query/fetchers/stats.js.map +1 -0
  265. package/dist/query/fetchers/stream.js +30 -0
  266. package/dist/query/fetchers/stream.js.map +1 -0
  267. package/dist/query/fetchers/unlinked-positions.js +30 -0
  268. package/dist/query/fetchers/unlinked-positions.js.map +1 -0
  269. package/dist/query/hooks/index.d.ts +3 -3
  270. package/dist/query/hooks/index.js +283 -3555
  271. package/dist/query/hooks/index.js.map +1 -1
  272. package/dist/query/hooks/use-accounts.js +19 -0
  273. package/dist/query/hooks/use-accounts.js.map +1 -0
  274. package/dist/query/hooks/use-activity-items-hide.js +52 -0
  275. package/dist/query/hooks/use-activity-items-hide.js.map +1 -0
  276. package/dist/query/hooks/use-activity.js +44 -0
  277. package/dist/query/hooks/use-activity.js.map +1 -0
  278. package/dist/query/hooks/use-admin-review-queues.js +21 -0
  279. package/dist/query/hooks/use-admin-review-queues.js.map +1 -0
  280. package/dist/query/hooks/use-apps.js +31 -0
  281. package/dist/query/hooks/use-apps.js.map +1 -0
  282. package/dist/query/hooks/use-bsky-content-prefs.js +45 -0
  283. package/dist/query/hooks/use-bsky-content-prefs.js.map +1 -0
  284. package/dist/query/hooks/use-confirmations.js +121 -0
  285. package/dist/query/hooks/use-confirmations.js.map +1 -0
  286. package/dist/query/hooks/use-create-position.js +28 -0
  287. package/dist/query/hooks/use-create-position.js.map +1 -0
  288. package/dist/query/hooks/use-destructive.js +63 -0
  289. package/dist/query/hooks/use-destructive.js.map +1 -0
  290. package/dist/query/hooks/use-discovery.js +53 -0
  291. package/dist/query/hooks/use-discovery.js.map +1 -0
  292. package/dist/query/hooks/use-education-mutations.js +67 -0
  293. package/dist/query/hooks/use-education-mutations.js.map +1 -0
  294. package/dist/query/hooks/use-endorsement-inbox.js +80 -0
  295. package/dist/query/hooks/use-endorsement-inbox.js.map +1 -0
  296. package/dist/query/hooks/use-endorsement-mutations.js +33 -0
  297. package/dist/query/hooks/use-endorsement-mutations.js.map +1 -0
  298. package/dist/query/hooks/use-endorsement.js +20 -0
  299. package/dist/query/hooks/use-endorsement.js.map +1 -0
  300. package/dist/query/hooks/use-entity-search.js +73 -0
  301. package/dist/query/hooks/use-entity-search.js.map +1 -0
  302. package/dist/query/hooks/use-external-accounts.js +143 -0
  303. package/dist/query/hooks/use-external-accounts.js.map +1 -0
  304. package/dist/query/hooks/use-feature-allowlist.js +104 -0
  305. package/dist/query/hooks/use-feature-allowlist.js.map +1 -0
  306. package/dist/query/hooks/use-follow-extras.js +38 -0
  307. package/dist/query/hooks/use-follow-extras.js.map +1 -0
  308. package/dist/query/hooks/use-follow.js +113 -0
  309. package/dist/query/hooks/use-follow.js.map +1 -0
  310. package/dist/query/hooks/use-get-profile-view.js +20 -0
  311. package/dist/query/hooks/use-get-profile-view.js.map +1 -0
  312. package/dist/query/hooks/use-github-prs.js +21 -0
  313. package/dist/query/hooks/use-github-prs.js.map +1 -0
  314. package/dist/query/hooks/use-inbox.js +19 -0
  315. package/dist/query/hooks/use-inbox.js.map +1 -0
  316. package/dist/query/hooks/use-investment-mutations.js +65 -0
  317. package/dist/query/hooks/use-investment-mutations.js.map +1 -0
  318. package/dist/query/hooks/use-keytrace-claims.js +50 -0
  319. package/dist/query/hooks/use-keytrace-claims.js.map +1 -0
  320. package/dist/query/hooks/use-location-mutations.js +71 -0
  321. package/dist/query/hooks/use-location-mutations.js.map +1 -0
  322. package/dist/query/hooks/use-marque-domains.js +50 -0
  323. package/dist/query/hooks/use-marque-domains.js.map +1 -0
  324. package/dist/query/hooks/use-org-claim.js +29 -0
  325. package/dist/query/hooks/use-org-claim.js.map +1 -0
  326. package/dist/query/hooks/use-org-domains.js +28 -0
  327. package/dist/query/hooks/use-org-domains.js.map +1 -0
  328. package/dist/query/hooks/use-org-notification-emails.js +28 -0
  329. package/dist/query/hooks/use-org-notification-emails.js.map +1 -0
  330. package/dist/query/hooks/use-org-profile-settings.js +29 -0
  331. package/dist/query/hooks/use-org-profile-settings.js.map +1 -0
  332. package/dist/query/hooks/use-org-profile.js +21 -0
  333. package/dist/query/hooks/use-org-profile.js.map +1 -0
  334. package/dist/query/hooks/use-position-mutations.js +128 -0
  335. package/dist/query/hooks/use-position-mutations.js.map +1 -0
  336. package/dist/query/hooks/use-profile-completeness.js +21 -0
  337. package/dist/query/hooks/use-profile-completeness.js.map +1 -0
  338. package/dist/query/hooks/use-profile-items-hide.js +53 -0
  339. package/dist/query/hooks/use-profile-items-hide.js.map +1 -0
  340. package/dist/query/hooks/use-profile-mutations.js +143 -0
  341. package/dist/query/hooks/use-profile-mutations.js.map +1 -0
  342. package/dist/query/hooks/use-profile.js +30 -0
  343. package/dist/query/hooks/use-profile.js.map +1 -0
  344. package/dist/query/hooks/use-publication-mutations.js +90 -0
  345. package/dist/query/hooks/use-publication-mutations.js.map +1 -0
  346. package/dist/query/hooks/use-reaction-mutations.js +48 -0
  347. package/dist/query/hooks/use-reaction-mutations.js.map +1 -0
  348. package/dist/query/hooks/use-reactions.js +32 -0
  349. package/dist/query/hooks/use-reactions.js.map +1 -0
  350. package/dist/query/hooks/use-received-endorsements.js +23 -0
  351. package/dist/query/hooks/use-received-endorsements.js.map +1 -0
  352. package/dist/query/hooks/use-reciprocity.js +19 -0
  353. package/dist/query/hooks/use-reciprocity.js.map +1 -0
  354. package/dist/query/hooks/use-record-mutations.js +67 -0
  355. package/dist/query/hooks/use-record-mutations.js.map +1 -0
  356. package/dist/query/hooks/use-repo-inventory.js +43 -0
  357. package/dist/query/hooks/use-repo-inventory.js.map +1 -0
  358. package/dist/query/hooks/use-roadmap-mutations.js +51 -0
  359. package/dist/query/hooks/use-roadmap-mutations.js.map +1 -0
  360. package/dist/query/hooks/use-roadmap.js +31 -0
  361. package/dist/query/hooks/use-roadmap.js.map +1 -0
  362. package/dist/query/hooks/use-search.js +63 -0
  363. package/dist/query/hooks/use-search.js.map +1 -0
  364. package/dist/query/hooks/use-section-primary-mutations.js +53 -0
  365. package/dist/query/hooks/use-section-primary-mutations.js.map +1 -0
  366. package/dist/query/hooks/use-skill-mutations.js +91 -0
  367. package/dist/query/hooks/use-skill-mutations.js.map +1 -0
  368. package/dist/query/hooks/use-stats.js +19 -0
  369. package/dist/query/hooks/use-stats.js.map +1 -0
  370. package/dist/query/hooks/use-stream.js +20 -0
  371. package/dist/query/hooks/use-stream.js.map +1 -0
  372. package/dist/query/hooks/use-unlinked-positions.js +45 -0
  373. package/dist/query/hooks/use-unlinked-positions.js.map +1 -0
  374. package/dist/query/index.cjs.map +1 -1
  375. package/dist/query/index.d.ts +4 -4
  376. package/dist/query/index.js +685 -3744
  377. package/dist/query/index.js.map +1 -1
  378. package/dist/query/keys.js +138 -0
  379. package/dist/query/keys.js.map +1 -0
  380. package/dist/repo/groups.js +81 -0
  381. package/dist/repo/groups.js.map +1 -0
  382. package/dist/repo/index.js +9 -0
  383. package/dist/repo/index.js.map +1 -0
  384. package/dist/repo/labels.js +121 -0
  385. package/dist/repo/labels.js.map +1 -0
  386. package/dist/repo/types.js +1 -0
  387. package/dist/repo/types.js.map +1 -0
  388. package/dist/resume/index.js +5 -179
  389. package/dist/resume/index.js.map +1 -1
  390. package/dist/resume/json-resume.js +153 -0
  391. package/dist/resume/json-resume.js.map +1 -0
  392. package/dist/roadmap/index.js +101 -0
  393. package/dist/roadmap/index.js.map +1 -0
  394. package/dist/rpg/catalog.js +101 -0
  395. package/dist/rpg/catalog.js.map +1 -0
  396. package/dist/rpg/index.js +13 -116
  397. package/dist/rpg/index.js.map +1 -1
  398. package/dist/rpg/unlocks.js +21 -0
  399. package/dist/rpg/unlocks.js.map +1 -0
  400. package/dist/schemas/blob-ref.js +12 -0
  401. package/dist/schemas/blob-ref.js.map +1 -0
  402. package/dist/schemas/confirmation.js +23 -0
  403. package/dist/schemas/confirmation.js.map +1 -0
  404. package/dist/schemas/endorsement-confirmation.js +18 -0
  405. package/dist/schemas/endorsement-confirmation.js.map +1 -0
  406. package/dist/schemas/endorsement.js +19 -0
  407. package/dist/schemas/endorsement.js.map +1 -0
  408. package/dist/schemas/entity.js +74 -0
  409. package/dist/schemas/entity.js.map +1 -0
  410. package/dist/schemas/feed.js +73 -0
  411. package/dist/schemas/feed.js.map +1 -0
  412. package/dist/schemas/follow-profile.js +32 -0
  413. package/dist/schemas/follow-profile.js.map +1 -0
  414. package/dist/schemas/graph-follow.js +19 -0
  415. package/dist/schemas/graph-follow.js.map +1 -0
  416. package/dist/schemas/index.js +164 -629
  417. package/dist/schemas/index.js.map +1 -1
  418. package/dist/schemas/org-employment-attestation.js +28 -0
  419. package/dist/schemas/org-employment-attestation.js.map +1 -0
  420. package/dist/schemas/org-profile.js +64 -0
  421. package/dist/schemas/org-profile.js.map +1 -0
  422. package/dist/schemas/profile-certification.js +28 -0
  423. package/dist/schemas/profile-certification.js.map +1 -0
  424. package/dist/schemas/profile-course.js +32 -0
  425. package/dist/schemas/profile-course.js.map +1 -0
  426. package/dist/schemas/profile-education.js +33 -0
  427. package/dist/schemas/profile-education.js.map +1 -0
  428. package/dist/schemas/profile-external-account.js +15 -0
  429. package/dist/schemas/profile-external-account.js.map +1 -0
  430. package/dist/schemas/profile-honor.js +18 -0
  431. package/dist/schemas/profile-honor.js.map +1 -0
  432. package/dist/schemas/profile-investment.js +48 -0
  433. package/dist/schemas/profile-investment.js.map +1 -0
  434. package/dist/schemas/profile-involvement.js +47 -0
  435. package/dist/schemas/profile-involvement.js.map +1 -0
  436. package/dist/schemas/profile-language.js +12 -0
  437. package/dist/schemas/profile-language.js.map +1 -0
  438. package/dist/schemas/profile-position.js +42 -0
  439. package/dist/schemas/profile-position.js.map +1 -0
  440. package/dist/schemas/profile-presentation-delivery.js +38 -0
  441. package/dist/schemas/profile-presentation-delivery.js.map +1 -0
  442. package/dist/schemas/profile-presentation.js +36 -0
  443. package/dist/schemas/profile-presentation.js.map +1 -0
  444. package/dist/schemas/profile-project.js +48 -0
  445. package/dist/schemas/profile-project.js.map +1 -0
  446. package/dist/schemas/profile-publication.js +30 -0
  447. package/dist/schemas/profile-publication.js.map +1 -0
  448. package/dist/schemas/profile-self.js +22 -0
  449. package/dist/schemas/profile-self.js.map +1 -0
  450. package/dist/schemas/profile-skill.js +13 -0
  451. package/dist/schemas/profile-skill.js.map +1 -0
  452. package/dist/schemas/profile-volunteering.js +20 -0
  453. package/dist/schemas/profile-volunteering.js.map +1 -0
  454. package/dist/schemas/shared.js +56 -0
  455. package/dist/schemas/shared.js.map +1 -0
  456. package/dist/schemas/write/certification.js +16 -0
  457. package/dist/schemas/write/certification.js.map +1 -0
  458. package/dist/schemas/write/course.js +36 -0
  459. package/dist/schemas/write/course.js.map +1 -0
  460. package/dist/schemas/write/education.js +18 -0
  461. package/dist/schemas/write/education.js.map +1 -0
  462. package/dist/schemas/write/external-account.js +13 -0
  463. package/dist/schemas/write/external-account.js.map +1 -0
  464. package/dist/schemas/write/honor.js +14 -0
  465. package/dist/schemas/write/honor.js.map +1 -0
  466. package/dist/schemas/write/index.d.ts +1 -1
  467. package/dist/schemas/write/index.js +89 -593
  468. package/dist/schemas/write/index.js.map +1 -1
  469. package/dist/schemas/write/investment.js +32 -0
  470. package/dist/schemas/write/investment.js.map +1 -0
  471. package/dist/schemas/write/involvement.js +34 -0
  472. package/dist/schemas/write/involvement.js.map +1 -0
  473. package/dist/schemas/write/language.js +10 -0
  474. package/dist/schemas/write/language.js.map +1 -0
  475. package/dist/schemas/write/org-claim.js +56 -0
  476. package/dist/schemas/write/org-claim.js.map +1 -0
  477. package/dist/schemas/write/org-employment-attestation.js +19 -0
  478. package/dist/schemas/write/org-employment-attestation.js.map +1 -0
  479. package/dist/schemas/write/org-profile.js +54 -0
  480. package/dist/schemas/write/org-profile.js.map +1 -0
  481. package/dist/schemas/write/org-settings.js +81 -0
  482. package/dist/schemas/write/org-settings.js.map +1 -0
  483. package/dist/schemas/write/position.js +24 -0
  484. package/dist/schemas/write/position.js.map +1 -0
  485. package/dist/schemas/write/presentation-delivery.js +34 -0
  486. package/dist/schemas/write/presentation-delivery.js.map +1 -0
  487. package/dist/schemas/write/presentation.js +23 -0
  488. package/dist/schemas/write/presentation.js.map +1 -0
  489. package/dist/schemas/write/profile-location.js +21 -0
  490. package/dist/schemas/write/profile-location.js.map +1 -0
  491. package/dist/schemas/write/profile-self.js +27 -0
  492. package/dist/schemas/write/profile-self.js.map +1 -0
  493. package/dist/schemas/write/project.js +27 -0
  494. package/dist/schemas/write/project.js.map +1 -0
  495. package/dist/schemas/write/publication.js +41 -0
  496. package/dist/schemas/write/publication.js.map +1 -0
  497. package/dist/schemas/write/shared.js +96 -0
  498. package/dist/schemas/write/shared.js.map +1 -0
  499. package/dist/schemas/write/skill.js +18 -0
  500. package/dist/schemas/write/skill.js.map +1 -0
  501. package/dist/schemas/write/volunteering.js +16 -0
  502. package/dist/schemas/write/volunteering.js.map +1 -0
  503. package/dist/stream/activity-item.js +1 -0
  504. package/dist/stream/activity-item.js.map +1 -0
  505. package/dist/stream/index.js +49 -0
  506. package/dist/stream/index.js.map +1 -0
  507. package/dist/stream/stream-card-vm-schema.js +265 -0
  508. package/dist/stream/stream-card-vm-schema.js.map +1 -0
  509. package/dist/stream/stream-card-vm.js +1 -0
  510. package/dist/stream/stream-card-vm.js.map +1 -0
  511. package/dist/stream/to-stream-card-vm.js +793 -0
  512. package/dist/stream/to-stream-card-vm.js.map +1 -0
  513. package/dist/stream/two-tier.js +30 -0
  514. package/dist/stream/two-tier.js.map +1 -0
  515. package/dist/stream/verbs.js +120 -0
  516. package/dist/stream/verbs.js.map +1 -0
  517. package/dist/taxonomy/activity-tiers.js +956 -0
  518. package/dist/taxonomy/activity-tiers.js.map +1 -0
  519. package/dist/taxonomy/app-categories.js +42 -0
  520. package/dist/taxonomy/app-categories.js.map +1 -0
  521. package/dist/taxonomy/app-category-map.js +166 -0
  522. package/dist/taxonomy/app-category-map.js.map +1 -0
  523. package/dist/taxonomy/artifact-link-kind.js +29 -0
  524. package/dist/taxonomy/artifact-link-kind.js.map +1 -0
  525. package/dist/taxonomy/calendar-event.js +28 -0
  526. package/dist/taxonomy/calendar-event.js.map +1 -0
  527. package/dist/taxonomy/continents.js +215 -0
  528. package/dist/taxonomy/continents.js.map +1 -0
  529. package/dist/taxonomy/countries.js +203 -0
  530. package/dist/taxonomy/countries.js.map +1 -0
  531. package/dist/taxonomy/course-role.js +37 -0
  532. package/dist/taxonomy/course-role.js.map +1 -0
  533. package/dist/taxonomy/employment-type.js +82 -0
  534. package/dist/taxonomy/employment-type.js.map +1 -0
  535. package/dist/taxonomy/eqf-level.js +59 -0
  536. package/dist/taxonomy/eqf-level.js.map +1 -0
  537. package/dist/taxonomy/index.js +263 -0
  538. package/dist/taxonomy/index.js.map +1 -0
  539. package/dist/taxonomy/industry-taxonomy.js +281 -0
  540. package/dist/taxonomy/industry-taxonomy.js.map +1 -0
  541. package/dist/taxonomy/investment.js +54 -0
  542. package/dist/taxonomy/investment.js.map +1 -0
  543. package/dist/taxonomy/involvement-kind.js +30 -0
  544. package/dist/taxonomy/involvement-kind.js.map +1 -0
  545. package/dist/taxonomy/open-to.js +128 -0
  546. package/dist/taxonomy/open-to.js.map +1 -0
  547. package/dist/taxonomy/platforms.js +56 -0
  548. package/dist/taxonomy/platforms.js.map +1 -0
  549. package/dist/taxonomy/presentation-link-type.js +22 -0
  550. package/dist/taxonomy/presentation-link-type.js.map +1 -0
  551. package/dist/taxonomy/presentation-role.js +21 -0
  552. package/dist/taxonomy/presentation-role.js.map +1 -0
  553. package/dist/taxonomy/publication-type.js +54 -0
  554. package/dist/taxonomy/publication-type.js.map +1 -0
  555. package/dist/taxonomy/relational.js +25 -0
  556. package/dist/taxonomy/relational.js.map +1 -0
  557. package/dist/taxonomy/search-filter-defs.js +66 -0
  558. package/dist/taxonomy/search-filter-defs.js.map +1 -0
  559. package/dist/taxonomy/skill-categories.js +28 -0
  560. package/dist/taxonomy/skill-categories.js.map +1 -0
  561. package/dist/taxonomy/skill-display-groups.js +12 -0
  562. package/dist/taxonomy/skill-display-groups.js.map +1 -0
  563. package/dist/taxonomy/skill-grouping.js +90 -0
  564. package/dist/taxonomy/skill-grouping.js.map +1 -0
  565. package/dist/taxonomy/verification-providers.js +69 -0
  566. package/dist/taxonomy/verification-providers.js.map +1 -0
  567. package/dist/taxonomy/workplace-type.js +38 -0
  568. package/dist/taxonomy/workplace-type.js.map +1 -0
  569. package/dist/tokens/colors.js +11 -0
  570. package/dist/tokens/colors.js.map +1 -0
  571. package/dist/tokens/icons.js +15 -0
  572. package/dist/tokens/icons.js.map +1 -0
  573. package/dist/tokens/index.js +10 -39
  574. package/dist/tokens/index.js.map +1 -1
  575. package/dist/tokens/typography.js +23 -0
  576. package/dist/tokens/typography.js.map +1 -0
  577. package/dist/types/index.js +10 -0
  578. package/dist/types/index.js.map +1 -0
  579. package/dist/types/profile-view.js +1 -0
  580. package/dist/types/profile-view.js.map +1 -0
  581. package/dist/{use-org-notification-emails-0hjt-dEw.d.ts → use-org-notification-emails-CWWi1D8t.d.ts} +2 -2
  582. package/package.json +4 -3
@@ -35,7 +35,13 @@ async function createEndorsementConfirmation(agent, input) {
35
35
  });
36
36
  return { uri: data.uri, cid: data.cid };
37
37
  }
38
-
39
- export { createEndorsementConfirmation, followUser, likeRecord, repostRecord, unfollowUser, unlikeRecord, unrepostRecord };
40
- //# sourceMappingURL=index.js.map
38
+ export {
39
+ createEndorsementConfirmation,
40
+ followUser,
41
+ likeRecord,
42
+ repostRecord,
43
+ unfollowUser,
44
+ unlikeRecord,
45
+ unrepostRecord
46
+ };
41
47
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/atproto/index.ts"],"names":[],"mappings":";AAuCO,SAAS,UAAA,CAAW,OAA0B,OAAA,EAAwC;AAC3F,EAAA,OAAO,KAAA,CAAM,IAAA,CAAK,OAAA,CAAQ,GAAA,EAAK,QAAQ,GAAG,CAAA;AAC5C;AAGO,SAAS,YAAA,CAAa,OAA0B,OAAA,EAAgC;AACrF,EAAA,OAAO,KAAA,CAAM,WAAW,OAAO,CAAA;AACjC;AAMO,SAAS,YAAA,CAAa,OAA0B,OAAA,EAAwC;AAC7F,EAAA,OAAO,KAAA,CAAM,MAAA,CAAO,OAAA,CAAQ,GAAA,EAAK,QAAQ,GAAG,CAAA;AAC9C;AAGO,SAAS,cAAA,CAAe,OAA0B,SAAA,EAAkC;AACzF,EAAA,OAAO,KAAA,CAAM,aAAa,SAAS,CAAA;AACrC;AAMO,SAAS,UAAA,CAAW,OAA0B,UAAA,EAAwC;AAC3F,EAAA,OAAO,KAAA,CAAM,OAAO,UAAU,CAAA;AAChC;AAGO,SAAS,YAAA,CAAa,OAA0B,SAAA,EAAkC;AACvF,EAAA,OAAO,KAAA,CAAM,aAAa,SAAS,CAAA;AACrC;AAGA,IAAM,mCAAA,GAAsC,kCAAA;AAiC5C,eAAsB,6BAAA,CACpB,OACA,KAAA,EACoB;AACpB,EAAA,IAAI,CAAC,MAAM,GAAA,EAAK;AACd,IAAA,MAAM,IAAI,MAAM,0EAA0E,CAAA;AAAA,EAC5F;AACA,EAAA,MAAM,MAAA,GAAkC;AAAA,IACtC,KAAA,EAAO,mCAAA;AAAA,IACP,aAAa,KAAA,CAAM,WAAA;AAAA,IACnB,GAAI,MAAM,KAAA,GAAQ,EAAE,OAAO,KAAA,CAAM,KAAA,KAAU,EAAC;AAAA,IAC5C,SAAA,EAAA,iBAAW,IAAI,IAAA,EAAK,EAAE,WAAA;AAAY,GACpC;AACA,EAAA,MAAM,EAAE,MAAK,GAAI,MAAM,MAAM,GAAA,CAAI,OAAA,CAAQ,KAAK,YAAA,CAAa;AAAA,IACzD,MAAM,KAAA,CAAM,GAAA;AAAA,IACZ,UAAA,EAAY,mCAAA;AAAA,IACZ;AAAA,GACD,CAAA;AACD,EAAA,OAAO,EAAE,GAAA,EAAK,IAAA,CAAK,GAAA,EAAK,GAAA,EAAK,KAAK,GAAA,EAAI;AACxC","file":"index.js","sourcesContent":["/**\n * Direct-to-PDS write helpers for AT Protocol interactions (like, repost, follow,\n * endorsement confirmation).\n *\n * The native app performs OAuth on-device and holds its own authenticated agent,\n * then writes records directly to the user's PDS. These helpers are the shared,\n * typed service layer for those writes: the app passes its agent in, and the SDK\n * never constructs one itself. Reads stay on the sifa-api HTTP client (the rest\n * of this package); this subpath is the write side.\n *\n * Kept dependency-free: the agent is described structurally (below) rather than\n * imported from `@atproto/api`, so consuming this subpath adds no dependency.\n * The app's real `Agent` satisfies the structural type.\n */\n\n/** A strong reference to a record: its AT-URI and content id. */\nexport interface StrongRef {\n uri: string;\n cid: string;\n}\n\n/**\n * The subset of an authenticated AT Protocol agent (e.g. `@atproto/api`'s\n * `Agent`) that these write helpers use. Declared structurally so the SDK needs\n * no `@atproto/api` dependency.\n */\nexport interface AtprotoWriteAgent {\n like(uri: string, cid: string): Promise<StrongRef>;\n deleteLike(likeUri: string): Promise<void>;\n repost(uri: string, cid: string): Promise<StrongRef>;\n deleteRepost(repostUri: string): Promise<void>;\n follow(subjectDid: string): Promise<StrongRef>;\n deleteFollow(followUri: string): Promise<void>;\n}\n\n/**\n * Like a record. `subject` is the post being liked; the returned ref is the\n * like record itself, which {@link unlikeRecord} needs to undo it.\n */\nexport function likeRecord(agent: AtprotoWriteAgent, subject: StrongRef): Promise<StrongRef> {\n return agent.like(subject.uri, subject.cid);\n}\n\n/** Remove a like, given the like record's AT-URI (from {@link likeRecord}). */\nexport function unlikeRecord(agent: AtprotoWriteAgent, likeUri: string): Promise<void> {\n return agent.deleteLike(likeUri);\n}\n\n/**\n * Repost a record. `subject` is the post being reposted; the returned ref is the\n * repost record itself, which {@link unrepostRecord} needs to undo it.\n */\nexport function repostRecord(agent: AtprotoWriteAgent, subject: StrongRef): Promise<StrongRef> {\n return agent.repost(subject.uri, subject.cid);\n}\n\n/** Remove a repost, given the repost record's AT-URI (from {@link repostRecord}). */\nexport function unrepostRecord(agent: AtprotoWriteAgent, repostUri: string): Promise<void> {\n return agent.deleteRepost(repostUri);\n}\n\n/**\n * Follow an actor by DID. The returned ref is the follow record itself, which\n * {@link unfollowUser} needs to undo it.\n */\nexport function followUser(agent: AtprotoWriteAgent, subjectDid: string): Promise<StrongRef> {\n return agent.follow(subjectDid);\n}\n\n/** Unfollow, given the follow record's AT-URI (from {@link followUser}). */\nexport function unfollowUser(agent: AtprotoWriteAgent, followUri: string): Promise<void> {\n return agent.deleteFollow(followUri);\n}\n\n/** Collection NSID (and record `$type`) for an endorsement confirmation. */\nconst ENDORSEMENT_CONFIRMATION_COLLECTION = 'id.sifa.endorsement.confirmation';\n\n/**\n * The subset of an authenticated agent used to write a record to an arbitrary\n * collection. Convenience methods like {@link AtprotoWriteAgent.like} do not\n * cover custom `id.sifa.*` collections, so those go through `createRecord`.\n * Declared structurally so the SDK needs no `@atproto/api` dependency; the app's\n * real `Agent` satisfies it. `did` is the authenticated repo the record lands in.\n */\nexport interface AtprotoRecordWriteAgent {\n did?: string;\n com: {\n atproto: {\n repo: {\n createRecord(input: {\n repo: string;\n collection: string;\n record: Record<string, unknown>;\n }): Promise<{ data: { uri: string; cid: string } }>;\n };\n };\n };\n}\n\n/**\n * Confirm an endorsement by writing an `id.sifa.endorsement.confirmation` record\n * to the endorsee's own repository. `endorsement` is a strong ref to the\n * endorser's endorsement record. `skill` links the skill this endorsement\n * applies to: pass it when the endorsement proposed a skill the endorsee then\n * created, or when a proposed name matched a skill the endorsee already had (the\n * endorser cannot write to the endorsee's repo, so the skill record is theirs to\n * create and reference). The returned ref is the confirmation record itself.\n */\nexport async function createEndorsementConfirmation(\n agent: AtprotoRecordWriteAgent,\n input: { endorsement: StrongRef; skill?: StrongRef },\n): Promise<StrongRef> {\n if (!agent.did) {\n throw new Error('createEndorsementConfirmation requires an authenticated agent with a did');\n }\n const record: Record<string, unknown> = {\n $type: ENDORSEMENT_CONFIRMATION_COLLECTION,\n endorsement: input.endorsement,\n ...(input.skill ? { skill: input.skill } : {}),\n createdAt: new Date().toISOString(),\n };\n const { data } = await agent.com.atproto.repo.createRecord({\n repo: agent.did,\n collection: ENDORSEMENT_CONFIRMATION_COLLECTION,\n record,\n });\n return { uri: data.uri, cid: data.cid };\n}\n"]}
1
+ {"version":3,"sources":["../../src/atproto/index.ts"],"sourcesContent":["/**\n * Direct-to-PDS write helpers for AT Protocol interactions (like, repost, follow,\n * endorsement confirmation).\n *\n * The native app performs OAuth on-device and holds its own authenticated agent,\n * then writes records directly to the user's PDS. These helpers are the shared,\n * typed service layer for those writes: the app passes its agent in, and the SDK\n * never constructs one itself. Reads stay on the sifa-api HTTP client (the rest\n * of this package); this subpath is the write side.\n *\n * Kept dependency-free: the agent is described structurally (below) rather than\n * imported from `@atproto/api`, so consuming this subpath adds no dependency.\n * The app's real `Agent` satisfies the structural type.\n */\n\n/** A strong reference to a record: its AT-URI and content id. */\nexport interface StrongRef {\n uri: string;\n cid: string;\n}\n\n/**\n * The subset of an authenticated AT Protocol agent (e.g. `@atproto/api`'s\n * `Agent`) that these write helpers use. Declared structurally so the SDK needs\n * no `@atproto/api` dependency.\n */\nexport interface AtprotoWriteAgent {\n like(uri: string, cid: string): Promise<StrongRef>;\n deleteLike(likeUri: string): Promise<void>;\n repost(uri: string, cid: string): Promise<StrongRef>;\n deleteRepost(repostUri: string): Promise<void>;\n follow(subjectDid: string): Promise<StrongRef>;\n deleteFollow(followUri: string): Promise<void>;\n}\n\n/**\n * Like a record. `subject` is the post being liked; the returned ref is the\n * like record itself, which {@link unlikeRecord} needs to undo it.\n */\nexport function likeRecord(agent: AtprotoWriteAgent, subject: StrongRef): Promise<StrongRef> {\n return agent.like(subject.uri, subject.cid);\n}\n\n/** Remove a like, given the like record's AT-URI (from {@link likeRecord}). */\nexport function unlikeRecord(agent: AtprotoWriteAgent, likeUri: string): Promise<void> {\n return agent.deleteLike(likeUri);\n}\n\n/**\n * Repost a record. `subject` is the post being reposted; the returned ref is the\n * repost record itself, which {@link unrepostRecord} needs to undo it.\n */\nexport function repostRecord(agent: AtprotoWriteAgent, subject: StrongRef): Promise<StrongRef> {\n return agent.repost(subject.uri, subject.cid);\n}\n\n/** Remove a repost, given the repost record's AT-URI (from {@link repostRecord}). */\nexport function unrepostRecord(agent: AtprotoWriteAgent, repostUri: string): Promise<void> {\n return agent.deleteRepost(repostUri);\n}\n\n/**\n * Follow an actor by DID. The returned ref is the follow record itself, which\n * {@link unfollowUser} needs to undo it.\n */\nexport function followUser(agent: AtprotoWriteAgent, subjectDid: string): Promise<StrongRef> {\n return agent.follow(subjectDid);\n}\n\n/** Unfollow, given the follow record's AT-URI (from {@link followUser}). */\nexport function unfollowUser(agent: AtprotoWriteAgent, followUri: string): Promise<void> {\n return agent.deleteFollow(followUri);\n}\n\n/** Collection NSID (and record `$type`) for an endorsement confirmation. */\nconst ENDORSEMENT_CONFIRMATION_COLLECTION = 'id.sifa.endorsement.confirmation';\n\n/**\n * The subset of an authenticated agent used to write a record to an arbitrary\n * collection. Convenience methods like {@link AtprotoWriteAgent.like} do not\n * cover custom `id.sifa.*` collections, so those go through `createRecord`.\n * Declared structurally so the SDK needs no `@atproto/api` dependency; the app's\n * real `Agent` satisfies it. `did` is the authenticated repo the record lands in.\n */\nexport interface AtprotoRecordWriteAgent {\n did?: string;\n com: {\n atproto: {\n repo: {\n createRecord(input: {\n repo: string;\n collection: string;\n record: Record<string, unknown>;\n }): Promise<{ data: { uri: string; cid: string } }>;\n };\n };\n };\n}\n\n/**\n * Confirm an endorsement by writing an `id.sifa.endorsement.confirmation` record\n * to the endorsee's own repository. `endorsement` is a strong ref to the\n * endorser's endorsement record. `skill` links the skill this endorsement\n * applies to: pass it when the endorsement proposed a skill the endorsee then\n * created, or when a proposed name matched a skill the endorsee already had (the\n * endorser cannot write to the endorsee's repo, so the skill record is theirs to\n * create and reference). The returned ref is the confirmation record itself.\n */\nexport async function createEndorsementConfirmation(\n agent: AtprotoRecordWriteAgent,\n input: { endorsement: StrongRef; skill?: StrongRef },\n): Promise<StrongRef> {\n if (!agent.did) {\n throw new Error('createEndorsementConfirmation requires an authenticated agent with a did');\n }\n const record: Record<string, unknown> = {\n $type: ENDORSEMENT_CONFIRMATION_COLLECTION,\n endorsement: input.endorsement,\n ...(input.skill ? { skill: input.skill } : {}),\n createdAt: new Date().toISOString(),\n };\n const { data } = await agent.com.atproto.repo.createRecord({\n repo: agent.did,\n collection: ENDORSEMENT_CONFIRMATION_COLLECTION,\n record,\n });\n return { uri: data.uri, cid: data.cid };\n}\n"],"mappings":";AAuCO,SAAS,WAAW,OAA0B,SAAwC;AAC3F,SAAO,MAAM,KAAK,QAAQ,KAAK,QAAQ,GAAG;AAC5C;AAGO,SAAS,aAAa,OAA0B,SAAgC;AACrF,SAAO,MAAM,WAAW,OAAO;AACjC;AAMO,SAAS,aAAa,OAA0B,SAAwC;AAC7F,SAAO,MAAM,OAAO,QAAQ,KAAK,QAAQ,GAAG;AAC9C;AAGO,SAAS,eAAe,OAA0B,WAAkC;AACzF,SAAO,MAAM,aAAa,SAAS;AACrC;AAMO,SAAS,WAAW,OAA0B,YAAwC;AAC3F,SAAO,MAAM,OAAO,UAAU;AAChC;AAGO,SAAS,aAAa,OAA0B,WAAkC;AACvF,SAAO,MAAM,aAAa,SAAS;AACrC;AAGA,IAAM,sCAAsC;AAiC5C,eAAsB,8BACpB,OACA,OACoB;AACpB,MAAI,CAAC,MAAM,KAAK;AACd,UAAM,IAAI,MAAM,0EAA0E;AAAA,EAC5F;AACA,QAAM,SAAkC;AAAA,IACtC,OAAO;AAAA,IACP,aAAa,MAAM;AAAA,IACnB,GAAI,MAAM,QAAQ,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;AAAA,IAC5C,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,EACpC;AACA,QAAM,EAAE,KAAK,IAAI,MAAM,MAAM,IAAI,QAAQ,KAAK,aAAa;AAAA,IACzD,MAAM,MAAM;AAAA,IACZ,YAAY;AAAA,IACZ;AAAA,EACF,CAAC;AACD,SAAO,EAAE,KAAK,KAAK,KAAK,KAAK,KAAK,IAAI;AACxC;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/logic/date-lookahead.ts","../../src/logic/primary-position.ts","../../src/logic/profile-summary.ts","../../src/query/client.ts","../../src/query/fetchers/get-profile-view.ts","../../src/query/fetchers/profile-summary.ts","../../src/badge/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * How far ahead a profile date may lie, per kind of date (sifa-workspace#581).\n *\n * A date that records something which already happened (a credential being\n * issued, an award, the end of a finished role) cannot be in the future. Some\n * dates are naturally a little ahead: an accepted job or a forthcoming paper,\n * a booked talk, an expected graduation. A credential's expiry has no cap.\n *\n * One table, so the editors on every client apply the same limits.\n */\nexport const DATE_LOOKAHEAD_MONTHS = {\n credentialIssue: 0,\n award: 0,\n course: 0,\n involvementStart: 0,\n investmentStart: 0,\n /** The end date of any finished (not ongoing) range. */\n completedRangeEnd: 0,\n careerStart: 6,\n projectStart: 6,\n publication: 6,\n talk: 12,\n educationEnd: 96,\n} as const;\n\nexport type DateLookaheadKind = keyof typeof DATE_LOOKAHEAD_MONTHS;\n\n/**\n * Is a partial date (`YYYY`, `YYYY-MM`, or `YYYY-MM-DD`) more than `months`\n * months after the current month? Compared at year-month resolution. A bare\n * year counts from its first month, so the current year always passes. A\n * malformed or empty value is never \"beyond\" (other validation handles it).\n */\nexport function isBeyondLookahead(value: string, months: number, now: Date = new Date()): boolean {\n const match = /^(\\d{4})(?:-(\\d{2}))?/.exec(value);\n if (!match) return false;\n const index = Number(match[1]) * 12 + (match[2] ? Number(match[2]) : 1);\n const limit = now.getFullYear() * 12 + now.getMonth() + 1 + months;\n return index > limit;\n}\n\n/**\n * Has a start date not arrived yet? A role that starts next month is shown as\n * upcoming, and is not anyone's current role until its start month.\n */\nexport function isUpcomingStart(startedAt: string | undefined, now: Date = new Date()): boolean {\n return !!startedAt && isBeyondLookahead(startedAt, 0, now);\n}\n","/**\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","/**\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","/**\n * Foundation HTTP client for talking to the Sifa AppView.\n *\n * Stateless. Consumers supply a {@link SifaApiConfig} per call (the React\n * hooks read it from context; non-React consumers pass it explicitly). No\n * singletons, no module-level state.\n */\n\n/** Configuration passed to every fetcher. */\nexport interface SifaApiConfig {\n /** Base URL of the sifa-api AppView, e.g. `https://api.sifa.id`. No trailing slash. */\n baseUrl: string;\n /**\n * Optional fetch implementation. Defaults to {@link globalThis.fetch}.\n * Lets Next.js consumers pass their cache-enhanced fetch; node/Expo\n * consumers can leave this unset.\n */\n fetch?: typeof fetch;\n /**\n * Optional per-request auth-token minter for auth-required reads. The NATIVE\n * app supplies this (it has no sifa-api session cookie): given a method NSID\n * (`lxm`), it mints a short-lived AT Protocol service-auth JWT on the user's\n * PDS (see the SDK's `/atproto` `mintServiceAuthToken`) and returns it; the\n * fetcher attaches it as `Authorization: Bearer`. Web omits this and relies on\n * its session cookie (`credentials: 'include'`). Return `null` to send no\n * token. See `sifa-api` `createIdentityMiddleware`.\n */\n getAuthToken?: (lxm: string) => Promise<string | null>;\n}\n\n/** Options accepted by {@link apiFetch}. */\nexport interface ApiFetchOptions {\n method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\n /** Request body. Serialized to JSON automatically. */\n body?: unknown;\n /** AbortSignal. Defaults to `AbortSignal.timeout(timeoutMs)` if `timeoutMs` is set. */\n signal?: AbortSignal;\n /** Per-call timeout in milliseconds. Default: 10_000. Ignored if `signal` is provided. */\n timeoutMs?: number;\n /** Retry on HTTP 429 up to 3 times with the server's `Retry-After` delay (capped at 3s). */\n retryOn429?: boolean;\n /** Additional headers. `Content-Type: application/json` is set automatically when `body` is present. */\n headers?: Record<string, string>;\n credentials?: RequestCredentials;\n cache?: RequestCache;\n /**\n * Next.js-specific cache hints. Ignored on non-Next runtimes. Passed\n * through transparently as part of {@link RequestInit}.\n */\n next?: { revalidate?: number | false; tags?: string[] };\n}\n\n/** Error thrown by {@link apiFetch} on non-2xx responses. */\nexport class ApiError extends Error {\n readonly status: number;\n readonly body: unknown;\n\n constructor(message: string, status: number, body?: unknown) {\n super(message);\n this.name = 'ApiError';\n this.status = status;\n this.body = body;\n }\n}\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst MAX_RATE_LIMIT_RETRIES = 3;\nconst RATE_LIMIT_RETRY_CAP_SECONDS = 3;\n\n/**\n * Encode a handle or DID for safe interpolation into a request path,\n * idempotently.\n *\n * Plain {@link encodeURIComponent} double-encodes an already-encoded\n * identifier: Next.js hands route params to RSC pages percent-encoded on a\n * hard navigation (`did%3Aplc%3A...`) but decoded on client-side navigation\n * (`did:plc:...`). Encoding the former again turns `%` into `%25`, so the\n * AppView reads `did%3A...` as a literal handle, finds no match, and 404s —\n * which is why DID profile links broke on direct visits but worked when\n * clicked within the app.\n *\n * AT identifiers (handles are domain names, DIDs are `did:method:...`) never\n * contain a literal `%`, so decoding a decoded value is a no-op. A malformed\n * percent sequence (lone `%`) makes {@link decodeURIComponent} throw; in that\n * case we keep the original value so behaviour matches the prior bare\n * `encodeURIComponent`.\n */\nexport function encodeIdentifier(value: string): string {\n let decoded = value;\n try {\n decoded = decodeURIComponent(value);\n } catch {\n // Malformed percent-encoding — fall back to the raw value.\n }\n return encodeURIComponent(decoded);\n}\n\n/**\n * Generic fetcher used by all SDK query and mutation functions.\n *\n * Returns parsed JSON typed as `T`. Throws {@link ApiError} on non-2xx\n * responses. Use {@link apiFetchOrNull} when 404 should resolve to `null`\n * instead.\n */\nexport async function apiFetch<T = unknown>(\n config: SifaApiConfig,\n path: string,\n options: ApiFetchOptions = {},\n): Promise<T> {\n const fetchFn = config.fetch ?? globalThis.fetch;\n const url = `${config.baseUrl}${path}`;\n const maxRetries = options.retryOn429 ? MAX_RATE_LIMIT_RETRIES : 0;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const signal = options.signal ?? AbortSignal.timeout(options.timeoutMs ?? DEFAULT_TIMEOUT_MS);\n\n const headers: Record<string, string> = { ...(options.headers ?? {}) };\n let body: BodyInit | undefined;\n if (options.body !== undefined) {\n headers['Content-Type'] ??= 'application/json';\n body = JSON.stringify(options.body);\n }\n\n // `next` is a Next.js extension to RequestInit; cast through.\n const init = {\n method: options.method ?? 'GET',\n headers,\n body,\n signal,\n credentials: options.credentials,\n cache: options.cache,\n ...(options.next ? { next: options.next } : {}),\n } as RequestInit;\n\n const res = await fetchFn(url, init);\n\n if (res.status === 429 && attempt < maxRetries) {\n const retryAfterRaw = res.headers.get('retry-after');\n const retryAfter = retryAfterRaw ? Number.parseInt(retryAfterRaw, 10) : 2;\n const waitSeconds = Math.min(\n Number.isFinite(retryAfter) ? retryAfter : 2,\n RATE_LIMIT_RETRY_CAP_SECONDS,\n );\n await new Promise((r) => setTimeout(r, waitSeconds * 1000));\n continue;\n }\n\n if (!res.ok) {\n let errBody: unknown;\n try {\n errBody = (await res.json()) as unknown;\n } catch {\n try {\n errBody = await res.text();\n } catch {\n errBody = undefined;\n }\n }\n throw new ApiError(`Sifa API ${res.status} on ${path}`, res.status, errBody);\n }\n\n // 204 and 205 are defined to carry no body, so res.json() throws on them.\n // apiWrite treats that throw as a failed write, which turned every\n // successful no-content response (roadmap unvote, remove reaction,\n // dismiss endorsement) into a reported error.\n if (res.status === 204 || res.status === 205) {\n return undefined as T;\n }\n\n return (await res.json()) as T;\n }\n\n throw new ApiError(`Sifa API exhausted retries on ${path}`, 429);\n}\n\n/**\n * Variant of {@link apiFetch} that resolves to `null` on HTTP 404 instead\n * of throwing. Useful for \"fetch by handle\" reads where missing is\n * expected (e.g. unknown profile).\n */\nexport async function apiFetchOrNull<T>(\n config: SifaApiConfig,\n path: string,\n options: ApiFetchOptions = {},\n): Promise<T | null> {\n try {\n return await apiFetch<T>(config, path, options);\n } catch (e) {\n if (e instanceof ApiError && e.status === 404) return null;\n throw e;\n }\n}\n\n/**\n * Result returned by record-write mutations (create / update / delete).\n *\n * Never throws -- writes against the user's PDS can fail in many ways\n * (network, PDS unreachable, rate limit) and the UI needs structured\n * results to render appropriate messages.\n */\nexport interface WriteResult {\n success: boolean;\n error?: string;\n /**\n * PDS hostname returned by sifa-api when a write failed at the user's\n * Personal Data Server (issue #167). Lets the UI render\n * \"Your data server (eurosky.social) isn't responding\" instead of a\n * generic \"Request failed (500)\".\n */\n pdsHost?: string;\n}\n\n/** Result returned by create mutations. Includes the newly created `rkey`. */\nexport interface CreateResult extends WriteResult {\n rkey?: string;\n}\n\ninterface ApiErrorBody {\n message?: string;\n pdsHost?: string;\n}\n\nfunction extractWriteError(data: unknown, status: number): { error: string; pdsHost?: string } {\n const body = (data ?? {}) as ApiErrorBody;\n return {\n error: body.message ?? `Request failed (${status})`,\n ...(body.pdsHost ? { pdsHost: body.pdsHost } : {}),\n };\n}\n\n/**\n * Write mutation against the Sifa AppView. Wraps {@link apiFetch} with the\n * never-throws contract used by all sifa-web mutations: returns a\n * structured {@link WriteResult} on both success and failure, and\n * preserves the `pdsHost` field when the AppView reports a PDS-side\n * failure (issue #167).\n *\n * On success: returns `{ success: true, ...payload }` where `payload` is\n * whatever the server returned in JSON (or `{}` for 204).\n *\n * On failure: returns `{ success: false, error, pdsHost? }`. Never throws.\n *\n * Use {@link apiWriteCreate} when you specifically need the `rkey` from a\n * create response folded into the result shape.\n */\nexport async function apiWrite<TExtra extends object = Record<never, never>>(\n config: SifaApiConfig,\n path: string,\n method: 'POST' | 'PUT' | 'DELETE' | 'PATCH',\n options: Omit<ApiFetchOptions, 'method'> = {},\n): Promise<WriteResult & TExtra> {\n try {\n const data = await apiFetch<TExtra>(config, path, {\n method,\n credentials: 'include',\n ...options,\n });\n return { success: true, ...(data ?? ({} as TExtra)) };\n } catch (e) {\n if (e instanceof ApiError) {\n return { success: false, ...extractWriteError(e.body, e.status) } as WriteResult & TExtra;\n }\n return { success: false, error: 'Network error' } as WriteResult & TExtra;\n }\n}\n\n/**\n * Write mutation that expects the server to return a record key (`rkey`)\n * in its response body. Wraps {@link apiWrite} and folds the `rkey` into\n * the result shape so consumers get `{ success, rkey?, error?, pdsHost? }`.\n *\n * If the server returns additional fields (e.g. `feedUrl` from external\n * account creation), pass them via the `TExtra` generic to keep them\n * typed.\n */\nexport function apiWriteCreate<TExtra extends object = Record<never, never>>(\n config: SifaApiConfig,\n path: string,\n body: unknown,\n options: Omit<ApiFetchOptions, 'method' | 'body'> = {},\n): Promise<CreateResult & TExtra> {\n return apiWrite<{ rkey?: string } & TExtra>(config, path, 'POST', {\n body,\n ...options,\n });\n}\n","import type { ProfileView } from '../../types/profile-view.js';\nimport {\n ApiError,\n apiFetch,\n encodeIdentifier,\n type ApiFetchOptions,\n type SifaApiConfig,\n} from '../client.js';\n\n/**\n * Read the aggregated public profile view via the `id.sifa.getProfileView`\n * XRPC query. This is the AppView-only join (positions, education, skills,\n * endorsements, and more) exposed as a standard lexicon method, distinct from\n * the internal `/api/profile/*` REST surface.\n *\n * Returns `null` when the AppView has no profile for the actor\n * (`ProfileNotFound`). Throws {@link ApiError} on other non-2xx responses.\n *\n * Server-callable (Next.js RSC) and client-callable (Expo, browser).\n */\nexport async function fetchGetProfileView(\n config: SifaApiConfig,\n actor: string,\n options: ApiFetchOptions = {},\n): Promise<ProfileView | null> {\n const path = `/xrpc/id.sifa.getProfileView?actor=${encodeIdentifier(actor)}`;\n try {\n return await apiFetch<ProfileView>(config, path, { retryOn429: true, ...options });\n } catch (e) {\n if (\n e instanceof ApiError &&\n e.status === 400 &&\n typeof e.body === 'object' &&\n e.body !== null &&\n (e.body as { error?: unknown }).error === 'ProfileNotFound'\n ) {\n return null;\n }\n throw e;\n }\n}\n","import {\n summarizeProfileView,\n type ProfileSummary,\n type SummarizeProfileViewOptions,\n} from '../../logic/profile-summary.js';\nimport { fetchGetProfileView } from './get-profile-view.js';\nimport type { ApiFetchOptions, SifaApiConfig } from '../client.js';\n\n/**\n * Fetch the aggregated public profile via `id.sifa.getProfileView` and reduce it\n * to a compact {@link ProfileSummary} (identity, headline, current role/employer,\n * top skills). Convenience over `fetchGetProfileView` + `summarizeProfileView`,\n * for third-party surfaces that only want the headline facts.\n *\n * Returns `null` when the AppView has no profile for the actor. Server-callable\n * (Next.js RSC) and client-callable (Expo, browser).\n */\nexport async function fetchProfileSummary(\n config: SifaApiConfig,\n actor: string,\n options: SummarizeProfileViewOptions & ApiFetchOptions = {},\n): Promise<ProfileSummary | null> {\n const { maxSkills, ...fetchOptions } = options;\n const view = await fetchGetProfileView(config, actor, fetchOptions);\n return view ? summarizeProfileView(view, { maxSkills }) : null;\n}\n","/**\n * `<sifa-badge>` — a framework-free custom element that renders a person's\n * professional headline (name, current role and employer, top skills) from\n * their Sifa profile. Drop it into any page:\n *\n * ```html\n * <script type=\"module\" src=\"https://esm.sh/@singi-labs/sifa-sdk/badge\"></script>\n * <sifa-badge handle=\"gui.do\"></sifa-badge>\n * ```\n *\n * Or, in a bundler: `import '@singi-labs/sifa-sdk/badge'` (importing registers\n * the element). Attributes: `handle` or `did` (required), `api` (AppView base,\n * default `https://api.sifa.id`), `max-skills` (default 5).\n *\n * Reads the public `id.sifa.getProfileView` query, so it needs no auth or key.\n * Cross-origin browser use depends on that endpoint's open CORS.\n */\n\nimport type { ProfileSummary } from '../logic/profile-summary.js';\nimport { fetchProfileSummary } from '../query/fetchers/profile-summary.js';\n\nconst TAG = 'sifa-badge';\nconst DEFAULT_API_BASE = 'https://api.sifa.id';\nconst PROFILE_BASE = 'https://sifa.id/p/';\n\n// Reference HTMLElement through a guarded base so importing this module never\n// throws in a non-DOM (SSR / Node) context. The real element is only ever\n// registered and instantiated in a browser, where defineSifaBadge runs.\nconst BaseElement: typeof HTMLElement =\n typeof HTMLElement !== 'undefined' ? HTMLElement : (class {} as unknown as typeof HTMLElement);\n\nconst STYLES = `\n :host { display: inline-block; max-width: 22rem; }\n a.card {\n display: flex; gap: 0.75rem; align-items: center;\n padding: 0.75rem 0.9rem; text-decoration: none; color: inherit;\n border: 1px solid rgba(0,0,0,0.12); border-radius: 0.75rem;\n background: canvas; font: 400 0.9rem/1.4 system-ui, sans-serif;\n }\n a.card:hover { border-color: rgba(0,0,0,0.28); }\n a.card:focus-visible { outline: 2px solid #2563eb; outline-offset: 2px; }\n .avatar { width: 2.75rem; height: 2.75rem; border-radius: 50%; object-fit: cover; flex: none; }\n .body { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }\n .name { font-weight: 600; }\n .role { color: color-mix(in srgb, canvastext 65%, canvas); }\n .skills { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-top: 0.25rem; }\n .chip {\n font-size: 0.72rem; padding: 0.1rem 0.45rem; border-radius: 999px;\n background: color-mix(in srgb, canvastext 8%, canvas);\n }\n .msg { margin: 0; padding: 0.5rem 0.6rem; color: color-mix(in srgb, canvastext 60%, canvas);\n font: 400 0.85rem/1.4 system-ui, sans-serif; }\n`;\n\nexport class SifaBadgeElement extends BaseElement {\n static readonly observedAttributes = ['handle', 'did', 'api', 'max-skills'];\n\n private readonly shadow: ShadowRoot;\n private renderToken = 0;\n private scheduled = false;\n\n constructor() {\n super();\n this.shadow = this.attachShadow({ mode: 'open' });\n }\n\n connectedCallback(): void {\n this.schedule();\n }\n\n attributeChangedCallback(): void {\n this.schedule();\n }\n\n /** Coalesce a burst of synchronous attribute writes (and the initial connect) into one load. */\n private schedule(): void {\n if (this.scheduled) return;\n this.scheduled = true;\n queueMicrotask(() => {\n this.scheduled = false;\n if (this.isConnected) void this.load();\n });\n }\n\n private async load(): Promise<void> {\n const token = ++this.renderToken;\n const actor = this.getAttribute('did') || this.getAttribute('handle');\n if (!actor) {\n this.paint(this.message('Set a handle or did attribute on <sifa-badge>.'));\n return;\n }\n\n const baseUrl = this.getAttribute('api') || DEFAULT_API_BASE;\n const rawMax = this.getAttribute('max-skills');\n const maxSkills = rawMax != null ? Number(rawMax) : undefined;\n const options = maxSkills != null && Number.isFinite(maxSkills) ? { maxSkills } : {};\n\n this.paint(this.message('Loading…'));\n\n let summary: ProfileSummary | null;\n try {\n summary = await fetchProfileSummary({ baseUrl }, actor, options);\n } catch {\n if (token === this.renderToken) this.paint(this.message('Could not load this profile.'));\n return;\n }\n\n if (token !== this.renderToken) return; // a newer load superseded this one\n this.paint(summary ? this.card(summary) : this.message(`No Sifa profile for ${actor}.`));\n }\n\n private paint(body: Node): void {\n const style = document.createElement('style');\n style.textContent = STYLES;\n this.shadow.replaceChildren(style, body);\n }\n\n private message(text: string): HTMLElement {\n const p = document.createElement('p');\n p.className = 'msg';\n p.textContent = text;\n return p;\n }\n\n private card(summary: ProfileSummary): HTMLElement {\n const name = summary.displayName || summary.handle;\n const link = document.createElement('a');\n link.className = 'card';\n link.href = PROFILE_BASE + encodeURIComponent(summary.handle);\n link.target = '_blank';\n link.rel = 'noopener';\n link.setAttribute('aria-label', `${name} on Sifa`);\n\n if (summary.avatar) {\n const img = document.createElement('img');\n img.className = 'avatar';\n img.src = summary.avatar;\n img.alt = '';\n img.loading = 'lazy';\n link.append(img);\n }\n\n const body = document.createElement('span');\n body.className = 'body';\n\n const nameEl = document.createElement('span');\n nameEl.className = 'name';\n nameEl.textContent = name;\n body.append(nameEl);\n\n const role =\n [summary.currentTitle, summary.currentCompany].filter(Boolean).join(' · ') ||\n summary.headline;\n if (role) {\n const roleEl = document.createElement('span');\n roleEl.className = 'role';\n roleEl.textContent = role;\n body.append(roleEl);\n }\n\n if (summary.topSkills.length > 0) {\n const skills = document.createElement('span');\n skills.className = 'skills';\n for (const skill of summary.topSkills) {\n const chip = document.createElement('span');\n chip.className = 'chip';\n chip.textContent = skill;\n skills.append(chip);\n }\n body.append(skills);\n }\n\n link.append(body);\n return link;\n }\n}\n\n/** Register the element (idempotent). Called automatically when this module is imported. */\nexport function defineSifaBadge(tag: string = TAG): void {\n if (typeof customElements !== 'undefined' && !customElements.get(tag)) {\n customElements.define(tag, SifaBadgeElement);\n }\n}\n\ndefineSifaBadge();\n"]}
1
+ {"version":3,"sources":["../../src/logic/date-lookahead.ts","../../src/logic/primary-position.ts","../../src/logic/profile-summary.ts","../../src/query/client.ts","../../src/query/fetchers/get-profile-view.ts","../../src/query/fetchers/profile-summary.ts","../../src/badge/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * How far ahead a profile date may lie, per kind of date (sifa-workspace#581).\n *\n * A date that records something which already happened (a credential being\n * issued, an award, the end of a finished role) cannot be in the future. Some\n * dates are naturally a little ahead: an accepted job or a forthcoming paper,\n * a booked talk, an expected graduation. A credential's expiry has no cap.\n *\n * One table, so the editors on every client apply the same limits.\n */\nexport const DATE_LOOKAHEAD_MONTHS = {\n credentialIssue: 0,\n award: 0,\n course: 0,\n involvementStart: 0,\n investmentStart: 0,\n /** The end date of any finished (not ongoing) range. */\n completedRangeEnd: 0,\n careerStart: 6,\n projectStart: 6,\n publication: 6,\n talk: 12,\n /** Admitted to a programme that starts next year. */\n educationStart: 12,\n educationEnd: 96,\n} as const;\n\nexport type DateLookaheadKind = keyof typeof DATE_LOOKAHEAD_MONTHS;\n\n/**\n * Is a partial date (`YYYY`, `YYYY-MM`, or `YYYY-MM-DD`) more than `months`\n * months after the current month? Compared at year-month resolution. A bare\n * year counts from its first month, so the current year always passes. A\n * malformed or empty value is never \"beyond\" (other validation handles it).\n */\nexport function isBeyondLookahead(value: string, months: number, now: Date = new Date()): boolean {\n const match = /^(\\d{4})(?:-(\\d{2}))?/.exec(value);\n if (!match) return false;\n const index = Number(match[1]) * 12 + (match[2] ? Number(match[2]) : 1);\n const limit = now.getFullYear() * 12 + now.getMonth() + 1 + months;\n return index > limit;\n}\n\n/**\n * Has a start date not arrived yet? A role that starts next month is shown as\n * upcoming, and is not anyone's current role until its start month.\n */\nexport function isUpcomingStart(startedAt: string | undefined, now: Date = new Date()): boolean {\n return !!startedAt && isBeyondLookahead(startedAt, 0, now);\n}\n","/**\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","/**\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","/**\n * Foundation HTTP client for talking to the Sifa AppView.\n *\n * Stateless. Consumers supply a {@link SifaApiConfig} per call (the React\n * hooks read it from context; non-React consumers pass it explicitly). No\n * singletons, no module-level state.\n */\n\n/** Configuration passed to every fetcher. */\nexport interface SifaApiConfig {\n /** Base URL of the sifa-api AppView, e.g. `https://api.sifa.id`. No trailing slash. */\n baseUrl: string;\n /**\n * Optional fetch implementation. Defaults to {@link globalThis.fetch}.\n * Lets Next.js consumers pass their cache-enhanced fetch; node/Expo\n * consumers can leave this unset.\n */\n fetch?: typeof fetch;\n /**\n * Optional per-request auth-token minter for auth-required reads. The NATIVE\n * app supplies this (it has no sifa-api session cookie): given a method NSID\n * (`lxm`), it mints a short-lived AT Protocol service-auth JWT on the user's\n * PDS (see the SDK's `/atproto` `mintServiceAuthToken`) and returns it; the\n * fetcher attaches it as `Authorization: Bearer`. Web omits this and relies on\n * its session cookie (`credentials: 'include'`). Return `null` to send no\n * token. See `sifa-api` `createIdentityMiddleware`.\n */\n getAuthToken?: (lxm: string) => Promise<string | null>;\n}\n\n/** Options accepted by {@link apiFetch}. */\nexport interface ApiFetchOptions {\n method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\n /** Request body. Serialized to JSON automatically. */\n body?: unknown;\n /** AbortSignal. Defaults to `AbortSignal.timeout(timeoutMs)` if `timeoutMs` is set. */\n signal?: AbortSignal;\n /** Per-call timeout in milliseconds. Default: 10_000. Ignored if `signal` is provided. */\n timeoutMs?: number;\n /** Retry on HTTP 429 up to 3 times with the server's `Retry-After` delay (capped at 3s). */\n retryOn429?: boolean;\n /** Additional headers. `Content-Type: application/json` is set automatically when `body` is present. */\n headers?: Record<string, string>;\n credentials?: RequestCredentials;\n cache?: RequestCache;\n /**\n * Next.js-specific cache hints. Ignored on non-Next runtimes. Passed\n * through transparently as part of {@link RequestInit}.\n */\n next?: { revalidate?: number | false; tags?: string[] };\n}\n\n/** Error thrown by {@link apiFetch} on non-2xx responses. */\nexport class ApiError extends Error {\n readonly status: number;\n readonly body: unknown;\n\n constructor(message: string, status: number, body?: unknown) {\n super(message);\n this.name = 'ApiError';\n this.status = status;\n this.body = body;\n }\n}\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst MAX_RATE_LIMIT_RETRIES = 3;\nconst RATE_LIMIT_RETRY_CAP_SECONDS = 3;\n\n/**\n * Encode a handle or DID for safe interpolation into a request path,\n * idempotently.\n *\n * Plain {@link encodeURIComponent} double-encodes an already-encoded\n * identifier: Next.js hands route params to RSC pages percent-encoded on a\n * hard navigation (`did%3Aplc%3A...`) but decoded on client-side navigation\n * (`did:plc:...`). Encoding the former again turns `%` into `%25`, so the\n * AppView reads `did%3A...` as a literal handle, finds no match, and 404s —\n * which is why DID profile links broke on direct visits but worked when\n * clicked within the app.\n *\n * AT identifiers (handles are domain names, DIDs are `did:method:...`) never\n * contain a literal `%`, so decoding a decoded value is a no-op. A malformed\n * percent sequence (lone `%`) makes {@link decodeURIComponent} throw; in that\n * case we keep the original value so behaviour matches the prior bare\n * `encodeURIComponent`.\n */\nexport function encodeIdentifier(value: string): string {\n let decoded = value;\n try {\n decoded = decodeURIComponent(value);\n } catch {\n // Malformed percent-encoding — fall back to the raw value.\n }\n return encodeURIComponent(decoded);\n}\n\n/**\n * Generic fetcher used by all SDK query and mutation functions.\n *\n * Returns parsed JSON typed as `T`. Throws {@link ApiError} on non-2xx\n * responses. Use {@link apiFetchOrNull} when 404 should resolve to `null`\n * instead.\n */\nexport async function apiFetch<T = unknown>(\n config: SifaApiConfig,\n path: string,\n options: ApiFetchOptions = {},\n): Promise<T> {\n const fetchFn = config.fetch ?? globalThis.fetch;\n const url = `${config.baseUrl}${path}`;\n const maxRetries = options.retryOn429 ? MAX_RATE_LIMIT_RETRIES : 0;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const signal = options.signal ?? AbortSignal.timeout(options.timeoutMs ?? DEFAULT_TIMEOUT_MS);\n\n const headers: Record<string, string> = { ...(options.headers ?? {}) };\n let body: BodyInit | undefined;\n if (options.body !== undefined) {\n headers['Content-Type'] ??= 'application/json';\n body = JSON.stringify(options.body);\n }\n\n // `next` is a Next.js extension to RequestInit; cast through.\n const init = {\n method: options.method ?? 'GET',\n headers,\n body,\n signal,\n credentials: options.credentials,\n cache: options.cache,\n ...(options.next ? { next: options.next } : {}),\n } as RequestInit;\n\n const res = await fetchFn(url, init);\n\n if (res.status === 429 && attempt < maxRetries) {\n const retryAfterRaw = res.headers.get('retry-after');\n const retryAfter = retryAfterRaw ? Number.parseInt(retryAfterRaw, 10) : 2;\n const waitSeconds = Math.min(\n Number.isFinite(retryAfter) ? retryAfter : 2,\n RATE_LIMIT_RETRY_CAP_SECONDS,\n );\n await new Promise((r) => setTimeout(r, waitSeconds * 1000));\n continue;\n }\n\n if (!res.ok) {\n let errBody: unknown;\n try {\n errBody = (await res.json()) as unknown;\n } catch {\n try {\n errBody = await res.text();\n } catch {\n errBody = undefined;\n }\n }\n throw new ApiError(`Sifa API ${res.status} on ${path}`, res.status, errBody);\n }\n\n // 204 and 205 are defined to carry no body, so res.json() throws on them.\n // apiWrite treats that throw as a failed write, which turned every\n // successful no-content response (roadmap unvote, remove reaction,\n // dismiss endorsement) into a reported error.\n if (res.status === 204 || res.status === 205) {\n return undefined as T;\n }\n\n return (await res.json()) as T;\n }\n\n throw new ApiError(`Sifa API exhausted retries on ${path}`, 429);\n}\n\n/**\n * Variant of {@link apiFetch} that resolves to `null` on HTTP 404 instead\n * of throwing. Useful for \"fetch by handle\" reads where missing is\n * expected (e.g. unknown profile).\n */\nexport async function apiFetchOrNull<T>(\n config: SifaApiConfig,\n path: string,\n options: ApiFetchOptions = {},\n): Promise<T | null> {\n try {\n return await apiFetch<T>(config, path, options);\n } catch (e) {\n if (e instanceof ApiError && e.status === 404) return null;\n throw e;\n }\n}\n\n/**\n * Result returned by record-write mutations (create / update / delete).\n *\n * Never throws -- writes against the user's PDS can fail in many ways\n * (network, PDS unreachable, rate limit) and the UI needs structured\n * results to render appropriate messages.\n */\nexport interface WriteResult {\n success: boolean;\n error?: string;\n /**\n * PDS hostname returned by sifa-api when a write failed at the user's\n * Personal Data Server (issue #167). Lets the UI render\n * \"Your data server (eurosky.social) isn't responding\" instead of a\n * generic \"Request failed (500)\".\n */\n pdsHost?: string;\n}\n\n/** Result returned by create mutations. Includes the newly created `rkey`. */\nexport interface CreateResult extends WriteResult {\n rkey?: string;\n}\n\ninterface ApiErrorBody {\n message?: string;\n pdsHost?: string;\n}\n\nfunction extractWriteError(data: unknown, status: number): { error: string; pdsHost?: string } {\n const body = (data ?? {}) as ApiErrorBody;\n return {\n error: body.message ?? `Request failed (${status})`,\n ...(body.pdsHost ? { pdsHost: body.pdsHost } : {}),\n };\n}\n\n/**\n * Write mutation against the Sifa AppView. Wraps {@link apiFetch} with the\n * never-throws contract used by all sifa-web mutations: returns a\n * structured {@link WriteResult} on both success and failure, and\n * preserves the `pdsHost` field when the AppView reports a PDS-side\n * failure (issue #167).\n *\n * On success: returns `{ success: true, ...payload }` where `payload` is\n * whatever the server returned in JSON (or `{}` for 204).\n *\n * On failure: returns `{ success: false, error, pdsHost? }`. Never throws.\n *\n * Use {@link apiWriteCreate} when you specifically need the `rkey` from a\n * create response folded into the result shape.\n */\nexport async function apiWrite<TExtra extends object = Record<never, never>>(\n config: SifaApiConfig,\n path: string,\n method: 'POST' | 'PUT' | 'DELETE' | 'PATCH',\n options: Omit<ApiFetchOptions, 'method'> = {},\n): Promise<WriteResult & TExtra> {\n try {\n const data = await apiFetch<TExtra>(config, path, {\n method,\n credentials: 'include',\n ...options,\n });\n return { success: true, ...(data ?? ({} as TExtra)) };\n } catch (e) {\n if (e instanceof ApiError) {\n return { success: false, ...extractWriteError(e.body, e.status) } as WriteResult & TExtra;\n }\n return { success: false, error: 'Network error' } as WriteResult & TExtra;\n }\n}\n\n/**\n * Write mutation that expects the server to return a record key (`rkey`)\n * in its response body. Wraps {@link apiWrite} and folds the `rkey` into\n * the result shape so consumers get `{ success, rkey?, error?, pdsHost? }`.\n *\n * If the server returns additional fields (e.g. `feedUrl` from external\n * account creation), pass them via the `TExtra` generic to keep them\n * typed.\n */\nexport function apiWriteCreate<TExtra extends object = Record<never, never>>(\n config: SifaApiConfig,\n path: string,\n body: unknown,\n options: Omit<ApiFetchOptions, 'method' | 'body'> = {},\n): Promise<CreateResult & TExtra> {\n return apiWrite<{ rkey?: string } & TExtra>(config, path, 'POST', {\n body,\n ...options,\n });\n}\n","import type { ProfileView } from '../../types/profile-view.js';\nimport {\n ApiError,\n apiFetch,\n encodeIdentifier,\n type ApiFetchOptions,\n type SifaApiConfig,\n} from '../client.js';\n\n/**\n * Read the aggregated public profile view via the `id.sifa.getProfileView`\n * XRPC query. This is the AppView-only join (positions, education, skills,\n * endorsements, and more) exposed as a standard lexicon method, distinct from\n * the internal `/api/profile/*` REST surface.\n *\n * Returns `null` when the AppView has no profile for the actor\n * (`ProfileNotFound`). Throws {@link ApiError} on other non-2xx responses.\n *\n * Server-callable (Next.js RSC) and client-callable (Expo, browser).\n */\nexport async function fetchGetProfileView(\n config: SifaApiConfig,\n actor: string,\n options: ApiFetchOptions = {},\n): Promise<ProfileView | null> {\n const path = `/xrpc/id.sifa.getProfileView?actor=${encodeIdentifier(actor)}`;\n try {\n return await apiFetch<ProfileView>(config, path, { retryOn429: true, ...options });\n } catch (e) {\n if (\n e instanceof ApiError &&\n e.status === 400 &&\n typeof e.body === 'object' &&\n e.body !== null &&\n (e.body as { error?: unknown }).error === 'ProfileNotFound'\n ) {\n return null;\n }\n throw e;\n }\n}\n","import {\n summarizeProfileView,\n type ProfileSummary,\n type SummarizeProfileViewOptions,\n} from '../../logic/profile-summary.js';\nimport { fetchGetProfileView } from './get-profile-view.js';\nimport type { ApiFetchOptions, SifaApiConfig } from '../client.js';\n\n/**\n * Fetch the aggregated public profile via `id.sifa.getProfileView` and reduce it\n * to a compact {@link ProfileSummary} (identity, headline, current role/employer,\n * top skills). Convenience over `fetchGetProfileView` + `summarizeProfileView`,\n * for third-party surfaces that only want the headline facts.\n *\n * Returns `null` when the AppView has no profile for the actor. Server-callable\n * (Next.js RSC) and client-callable (Expo, browser).\n */\nexport async function fetchProfileSummary(\n config: SifaApiConfig,\n actor: string,\n options: SummarizeProfileViewOptions & ApiFetchOptions = {},\n): Promise<ProfileSummary | null> {\n const { maxSkills, ...fetchOptions } = options;\n const view = await fetchGetProfileView(config, actor, fetchOptions);\n return view ? summarizeProfileView(view, { maxSkills }) : null;\n}\n","/**\n * `<sifa-badge>` — a framework-free custom element that renders a person's\n * professional headline (name, current role and employer, top skills) from\n * their Sifa profile. Drop it into any page:\n *\n * ```html\n * <script type=\"module\" src=\"https://esm.sh/@singi-labs/sifa-sdk/badge\"></script>\n * <sifa-badge handle=\"gui.do\"></sifa-badge>\n * ```\n *\n * Or, in a bundler: `import '@singi-labs/sifa-sdk/badge'` (importing registers\n * the element). Attributes: `handle` or `did` (required), `api` (AppView base,\n * default `https://api.sifa.id`), `max-skills` (default 5).\n *\n * Reads the public `id.sifa.getProfileView` query, so it needs no auth or key.\n * Cross-origin browser use depends on that endpoint's open CORS.\n */\n\nimport type { ProfileSummary } from '../logic/profile-summary.js';\nimport { fetchProfileSummary } from '../query/fetchers/profile-summary.js';\n\nconst TAG = 'sifa-badge';\nconst DEFAULT_API_BASE = 'https://api.sifa.id';\nconst PROFILE_BASE = 'https://sifa.id/p/';\n\n// Reference HTMLElement through a guarded base so importing this module never\n// throws in a non-DOM (SSR / Node) context. The real element is only ever\n// registered and instantiated in a browser, where defineSifaBadge runs.\nconst BaseElement: typeof HTMLElement =\n typeof HTMLElement !== 'undefined' ? HTMLElement : (class {} as unknown as typeof HTMLElement);\n\nconst STYLES = `\n :host { display: inline-block; max-width: 22rem; }\n a.card {\n display: flex; gap: 0.75rem; align-items: center;\n padding: 0.75rem 0.9rem; text-decoration: none; color: inherit;\n border: 1px solid rgba(0,0,0,0.12); border-radius: 0.75rem;\n background: canvas; font: 400 0.9rem/1.4 system-ui, sans-serif;\n }\n a.card:hover { border-color: rgba(0,0,0,0.28); }\n a.card:focus-visible { outline: 2px solid #2563eb; outline-offset: 2px; }\n .avatar { width: 2.75rem; height: 2.75rem; border-radius: 50%; object-fit: cover; flex: none; }\n .body { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }\n .name { font-weight: 600; }\n .role { color: color-mix(in srgb, canvastext 65%, canvas); }\n .skills { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-top: 0.25rem; }\n .chip {\n font-size: 0.72rem; padding: 0.1rem 0.45rem; border-radius: 999px;\n background: color-mix(in srgb, canvastext 8%, canvas);\n }\n .msg { margin: 0; padding: 0.5rem 0.6rem; color: color-mix(in srgb, canvastext 60%, canvas);\n font: 400 0.85rem/1.4 system-ui, sans-serif; }\n`;\n\nexport class SifaBadgeElement extends BaseElement {\n static readonly observedAttributes = ['handle', 'did', 'api', 'max-skills'];\n\n private readonly shadow: ShadowRoot;\n private renderToken = 0;\n private scheduled = false;\n\n constructor() {\n super();\n this.shadow = this.attachShadow({ mode: 'open' });\n }\n\n connectedCallback(): void {\n this.schedule();\n }\n\n attributeChangedCallback(): void {\n this.schedule();\n }\n\n /** Coalesce a burst of synchronous attribute writes (and the initial connect) into one load. */\n private schedule(): void {\n if (this.scheduled) return;\n this.scheduled = true;\n queueMicrotask(() => {\n this.scheduled = false;\n if (this.isConnected) void this.load();\n });\n }\n\n private async load(): Promise<void> {\n const token = ++this.renderToken;\n const actor = this.getAttribute('did') || this.getAttribute('handle');\n if (!actor) {\n this.paint(this.message('Set a handle or did attribute on <sifa-badge>.'));\n return;\n }\n\n const baseUrl = this.getAttribute('api') || DEFAULT_API_BASE;\n const rawMax = this.getAttribute('max-skills');\n const maxSkills = rawMax != null ? Number(rawMax) : undefined;\n const options = maxSkills != null && Number.isFinite(maxSkills) ? { maxSkills } : {};\n\n this.paint(this.message('Loading…'));\n\n let summary: ProfileSummary | null;\n try {\n summary = await fetchProfileSummary({ baseUrl }, actor, options);\n } catch {\n if (token === this.renderToken) this.paint(this.message('Could not load this profile.'));\n return;\n }\n\n if (token !== this.renderToken) return; // a newer load superseded this one\n this.paint(summary ? this.card(summary) : this.message(`No Sifa profile for ${actor}.`));\n }\n\n private paint(body: Node): void {\n const style = document.createElement('style');\n style.textContent = STYLES;\n this.shadow.replaceChildren(style, body);\n }\n\n private message(text: string): HTMLElement {\n const p = document.createElement('p');\n p.className = 'msg';\n p.textContent = text;\n return p;\n }\n\n private card(summary: ProfileSummary): HTMLElement {\n const name = summary.displayName || summary.handle;\n const link = document.createElement('a');\n link.className = 'card';\n link.href = PROFILE_BASE + encodeURIComponent(summary.handle);\n link.target = '_blank';\n link.rel = 'noopener';\n link.setAttribute('aria-label', `${name} on Sifa`);\n\n if (summary.avatar) {\n const img = document.createElement('img');\n img.className = 'avatar';\n img.src = summary.avatar;\n img.alt = '';\n img.loading = 'lazy';\n link.append(img);\n }\n\n const body = document.createElement('span');\n body.className = 'body';\n\n const nameEl = document.createElement('span');\n nameEl.className = 'name';\n nameEl.textContent = name;\n body.append(nameEl);\n\n const role =\n [summary.currentTitle, summary.currentCompany].filter(Boolean).join(' · ') ||\n summary.headline;\n if (role) {\n const roleEl = document.createElement('span');\n roleEl.className = 'role';\n roleEl.textContent = role;\n body.append(roleEl);\n }\n\n if (summary.topSkills.length > 0) {\n const skills = document.createElement('span');\n skills.className = 'skills';\n for (const skill of summary.topSkills) {\n const chip = document.createElement('span');\n chip.className = 'chip';\n chip.textContent = skill;\n skills.append(chip);\n }\n body.append(skills);\n }\n\n link.append(body);\n return link;\n }\n}\n\n/** Register the element (idempotent). Called automatically when this module is imported. */\nexport function defineSifaBadge(tag: string = TAG): void {\n if (typeof customElements !== 'undefined' && !customElements.get(tag)) {\n customElements.define(tag, SifaBadgeElement);\n }\n}\n\ndefineSifaBadge();\n"]}
@@ -1,142 +1,5 @@
1
- // src/logic/date-lookahead.ts
2
- function isBeyondLookahead(value, months, now = /* @__PURE__ */ new Date()) {
3
- const match = /^(\d{4})(?:-(\d{2}))?/.exec(value);
4
- if (!match) return false;
5
- const index = Number(match[1]) * 12 + (match[2] ? Number(match[2]) : 1);
6
- const limit = now.getFullYear() * 12 + now.getMonth() + 1 + months;
7
- return index > limit;
8
- }
9
- function isUpcomingStart(startedAt, now = /* @__PURE__ */ new Date()) {
10
- return !!startedAt && isBeyondLookahead(startedAt, 0, now);
11
- }
12
-
13
- // src/logic/primary-position.ts
14
- function pickPrimaryPosition(positions, now = /* @__PURE__ */ new Date()) {
15
- if (!positions || positions.length === 0) return void 0;
16
- const active = positions.filter(
17
- (p) => !p.endedAt && !p.hidden && !isUpcomingStart(p.startedAt, now)
18
- );
19
- if (active.length === 0) return void 0;
20
- const flagged = active.find((p) => p.primary === true);
21
- if (flagged) return flagged;
22
- return [...active].sort((a, b) => (b.startedAt ?? "").localeCompare(a.startedAt ?? ""))[0];
23
- }
24
-
25
- // src/logic/profile-summary.ts
26
- var DEFAULT_MAX_SKILLS = 5;
27
- function summarizeProfileView(view, options = {}) {
28
- const limit = Math.max(0, options.maxSkills ?? DEFAULT_MAX_SKILLS);
29
- const primary = pickPrimaryPosition(view.positions);
30
- return {
31
- did: view.did,
32
- handle: view.handle,
33
- displayName: view.displayName,
34
- avatar: view.avatar,
35
- pronouns: view.pronouns,
36
- headline: view.headline,
37
- currentTitle: primary?.title,
38
- currentCompany: primary ? primary.entityName ?? primary.company : void 0,
39
- topSkills: (view.skills ?? []).slice(0, limit).map((s) => s.name),
40
- claimed: view.claimed ?? false
41
- };
42
- }
43
-
44
- // src/query/client.ts
45
- var ApiError = class extends Error {
46
- status;
47
- body;
48
- constructor(message, status, body) {
49
- super(message);
50
- this.name = "ApiError";
51
- this.status = status;
52
- this.body = body;
53
- }
54
- };
55
- var DEFAULT_TIMEOUT_MS = 1e4;
56
- var MAX_RATE_LIMIT_RETRIES = 3;
57
- var RATE_LIMIT_RETRY_CAP_SECONDS = 3;
58
- function encodeIdentifier(value) {
59
- let decoded = value;
60
- try {
61
- decoded = decodeURIComponent(value);
62
- } catch {
63
- }
64
- return encodeURIComponent(decoded);
65
- }
66
- async function apiFetch(config, path, options = {}) {
67
- const fetchFn = config.fetch ?? globalThis.fetch;
68
- const url = `${config.baseUrl}${path}`;
69
- const maxRetries = options.retryOn429 ? MAX_RATE_LIMIT_RETRIES : 0;
70
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
71
- const signal = options.signal ?? AbortSignal.timeout(options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
72
- const headers = { ...options.headers ?? {} };
73
- let body;
74
- if (options.body !== void 0) {
75
- headers["Content-Type"] ??= "application/json";
76
- body = JSON.stringify(options.body);
77
- }
78
- const init = {
79
- method: options.method ?? "GET",
80
- headers,
81
- body,
82
- signal,
83
- credentials: options.credentials,
84
- cache: options.cache,
85
- ...options.next ? { next: options.next } : {}
86
- };
87
- const res = await fetchFn(url, init);
88
- if (res.status === 429 && attempt < maxRetries) {
89
- const retryAfterRaw = res.headers.get("retry-after");
90
- const retryAfter = retryAfterRaw ? Number.parseInt(retryAfterRaw, 10) : 2;
91
- const waitSeconds = Math.min(
92
- Number.isFinite(retryAfter) ? retryAfter : 2,
93
- RATE_LIMIT_RETRY_CAP_SECONDS
94
- );
95
- await new Promise((r) => setTimeout(r, waitSeconds * 1e3));
96
- continue;
97
- }
98
- if (!res.ok) {
99
- let errBody;
100
- try {
101
- errBody = await res.json();
102
- } catch {
103
- try {
104
- errBody = await res.text();
105
- } catch {
106
- errBody = void 0;
107
- }
108
- }
109
- throw new ApiError(`Sifa API ${res.status} on ${path}`, res.status, errBody);
110
- }
111
- if (res.status === 204 || res.status === 205) {
112
- return void 0;
113
- }
114
- return await res.json();
115
- }
116
- throw new ApiError(`Sifa API exhausted retries on ${path}`, 429);
117
- }
118
-
119
- // src/query/fetchers/get-profile-view.ts
120
- async function fetchGetProfileView(config, actor, options = {}) {
121
- const path = `/xrpc/id.sifa.getProfileView?actor=${encodeIdentifier(actor)}`;
122
- try {
123
- return await apiFetch(config, path, { retryOn429: true, ...options });
124
- } catch (e) {
125
- if (e instanceof ApiError && e.status === 400 && typeof e.body === "object" && e.body !== null && e.body.error === "ProfileNotFound") {
126
- return null;
127
- }
128
- throw e;
129
- }
130
- }
131
-
132
- // src/query/fetchers/profile-summary.ts
133
- async function fetchProfileSummary(config, actor, options = {}) {
134
- const { maxSkills, ...fetchOptions } = options;
135
- const view = await fetchGetProfileView(config, actor, fetchOptions);
136
- return view ? summarizeProfileView(view, { maxSkills }) : null;
137
- }
138
-
139
1
  // src/badge/index.ts
2
+ import { fetchProfileSummary } from "../query/fetchers/profile-summary.js";
140
3
  var TAG = "sifa-badge";
141
4
  var DEFAULT_API_BASE = "https://api.sifa.id";
142
5
  var PROFILE_BASE = "https://sifa.id/p/";
@@ -271,7 +134,8 @@ function defineSifaBadge(tag = TAG) {
271
134
  }
272
135
  }
273
136
  defineSifaBadge();
274
-
275
- export { SifaBadgeElement, defineSifaBadge };
276
- //# sourceMappingURL=index.js.map
137
+ export {
138
+ SifaBadgeElement,
139
+ defineSifaBadge
140
+ };
277
141
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/logic/date-lookahead.ts","../../src/logic/primary-position.ts","../../src/logic/profile-summary.ts","../../src/query/client.ts","../../src/query/fetchers/get-profile-view.ts","../../src/query/fetchers/profile-summary.ts","../../src/badge/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * How far ahead a profile date may lie, per kind of date (sifa-workspace#581).\n *\n * A date that records something which already happened (a credential being\n * issued, an award, the end of a finished role) cannot be in the future. Some\n * dates are naturally a little ahead: an accepted job or a forthcoming paper,\n * a booked talk, an expected graduation. A credential's expiry has no cap.\n *\n * One table, so the editors on every client apply the same limits.\n */\nexport const DATE_LOOKAHEAD_MONTHS = {\n credentialIssue: 0,\n award: 0,\n course: 0,\n involvementStart: 0,\n investmentStart: 0,\n /** The end date of any finished (not ongoing) range. */\n completedRangeEnd: 0,\n careerStart: 6,\n projectStart: 6,\n publication: 6,\n talk: 12,\n educationEnd: 96,\n} as const;\n\nexport type DateLookaheadKind = keyof typeof DATE_LOOKAHEAD_MONTHS;\n\n/**\n * Is a partial date (`YYYY`, `YYYY-MM`, or `YYYY-MM-DD`) more than `months`\n * months after the current month? Compared at year-month resolution. A bare\n * year counts from its first month, so the current year always passes. A\n * malformed or empty value is never \"beyond\" (other validation handles it).\n */\nexport function isBeyondLookahead(value: string, months: number, now: Date = new Date()): boolean {\n const match = /^(\\d{4})(?:-(\\d{2}))?/.exec(value);\n if (!match) return false;\n const index = Number(match[1]) * 12 + (match[2] ? Number(match[2]) : 1);\n const limit = now.getFullYear() * 12 + now.getMonth() + 1 + months;\n return index > limit;\n}\n\n/**\n * Has a start date not arrived yet? A role that starts next month is shown as\n * upcoming, and is not anyone's current role until its start month.\n */\nexport function isUpcomingStart(startedAt: string | undefined, now: Date = new Date()): boolean {\n return !!startedAt && isBeyondLookahead(startedAt, 0, now);\n}\n","/**\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","/**\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","/**\n * Foundation HTTP client for talking to the Sifa AppView.\n *\n * Stateless. Consumers supply a {@link SifaApiConfig} per call (the React\n * hooks read it from context; non-React consumers pass it explicitly). No\n * singletons, no module-level state.\n */\n\n/** Configuration passed to every fetcher. */\nexport interface SifaApiConfig {\n /** Base URL of the sifa-api AppView, e.g. `https://api.sifa.id`. No trailing slash. */\n baseUrl: string;\n /**\n * Optional fetch implementation. Defaults to {@link globalThis.fetch}.\n * Lets Next.js consumers pass their cache-enhanced fetch; node/Expo\n * consumers can leave this unset.\n */\n fetch?: typeof fetch;\n /**\n * Optional per-request auth-token minter for auth-required reads. The NATIVE\n * app supplies this (it has no sifa-api session cookie): given a method NSID\n * (`lxm`), it mints a short-lived AT Protocol service-auth JWT on the user's\n * PDS (see the SDK's `/atproto` `mintServiceAuthToken`) and returns it; the\n * fetcher attaches it as `Authorization: Bearer`. Web omits this and relies on\n * its session cookie (`credentials: 'include'`). Return `null` to send no\n * token. See `sifa-api` `createIdentityMiddleware`.\n */\n getAuthToken?: (lxm: string) => Promise<string | null>;\n}\n\n/** Options accepted by {@link apiFetch}. */\nexport interface ApiFetchOptions {\n method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\n /** Request body. Serialized to JSON automatically. */\n body?: unknown;\n /** AbortSignal. Defaults to `AbortSignal.timeout(timeoutMs)` if `timeoutMs` is set. */\n signal?: AbortSignal;\n /** Per-call timeout in milliseconds. Default: 10_000. Ignored if `signal` is provided. */\n timeoutMs?: number;\n /** Retry on HTTP 429 up to 3 times with the server's `Retry-After` delay (capped at 3s). */\n retryOn429?: boolean;\n /** Additional headers. `Content-Type: application/json` is set automatically when `body` is present. */\n headers?: Record<string, string>;\n credentials?: RequestCredentials;\n cache?: RequestCache;\n /**\n * Next.js-specific cache hints. Ignored on non-Next runtimes. Passed\n * through transparently as part of {@link RequestInit}.\n */\n next?: { revalidate?: number | false; tags?: string[] };\n}\n\n/** Error thrown by {@link apiFetch} on non-2xx responses. */\nexport class ApiError extends Error {\n readonly status: number;\n readonly body: unknown;\n\n constructor(message: string, status: number, body?: unknown) {\n super(message);\n this.name = 'ApiError';\n this.status = status;\n this.body = body;\n }\n}\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst MAX_RATE_LIMIT_RETRIES = 3;\nconst RATE_LIMIT_RETRY_CAP_SECONDS = 3;\n\n/**\n * Encode a handle or DID for safe interpolation into a request path,\n * idempotently.\n *\n * Plain {@link encodeURIComponent} double-encodes an already-encoded\n * identifier: Next.js hands route params to RSC pages percent-encoded on a\n * hard navigation (`did%3Aplc%3A...`) but decoded on client-side navigation\n * (`did:plc:...`). Encoding the former again turns `%` into `%25`, so the\n * AppView reads `did%3A...` as a literal handle, finds no match, and 404s —\n * which is why DID profile links broke on direct visits but worked when\n * clicked within the app.\n *\n * AT identifiers (handles are domain names, DIDs are `did:method:...`) never\n * contain a literal `%`, so decoding a decoded value is a no-op. A malformed\n * percent sequence (lone `%`) makes {@link decodeURIComponent} throw; in that\n * case we keep the original value so behaviour matches the prior bare\n * `encodeURIComponent`.\n */\nexport function encodeIdentifier(value: string): string {\n let decoded = value;\n try {\n decoded = decodeURIComponent(value);\n } catch {\n // Malformed percent-encoding — fall back to the raw value.\n }\n return encodeURIComponent(decoded);\n}\n\n/**\n * Generic fetcher used by all SDK query and mutation functions.\n *\n * Returns parsed JSON typed as `T`. Throws {@link ApiError} on non-2xx\n * responses. Use {@link apiFetchOrNull} when 404 should resolve to `null`\n * instead.\n */\nexport async function apiFetch<T = unknown>(\n config: SifaApiConfig,\n path: string,\n options: ApiFetchOptions = {},\n): Promise<T> {\n const fetchFn = config.fetch ?? globalThis.fetch;\n const url = `${config.baseUrl}${path}`;\n const maxRetries = options.retryOn429 ? MAX_RATE_LIMIT_RETRIES : 0;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const signal = options.signal ?? AbortSignal.timeout(options.timeoutMs ?? DEFAULT_TIMEOUT_MS);\n\n const headers: Record<string, string> = { ...(options.headers ?? {}) };\n let body: BodyInit | undefined;\n if (options.body !== undefined) {\n headers['Content-Type'] ??= 'application/json';\n body = JSON.stringify(options.body);\n }\n\n // `next` is a Next.js extension to RequestInit; cast through.\n const init = {\n method: options.method ?? 'GET',\n headers,\n body,\n signal,\n credentials: options.credentials,\n cache: options.cache,\n ...(options.next ? { next: options.next } : {}),\n } as RequestInit;\n\n const res = await fetchFn(url, init);\n\n if (res.status === 429 && attempt < maxRetries) {\n const retryAfterRaw = res.headers.get('retry-after');\n const retryAfter = retryAfterRaw ? Number.parseInt(retryAfterRaw, 10) : 2;\n const waitSeconds = Math.min(\n Number.isFinite(retryAfter) ? retryAfter : 2,\n RATE_LIMIT_RETRY_CAP_SECONDS,\n );\n await new Promise((r) => setTimeout(r, waitSeconds * 1000));\n continue;\n }\n\n if (!res.ok) {\n let errBody: unknown;\n try {\n errBody = (await res.json()) as unknown;\n } catch {\n try {\n errBody = await res.text();\n } catch {\n errBody = undefined;\n }\n }\n throw new ApiError(`Sifa API ${res.status} on ${path}`, res.status, errBody);\n }\n\n // 204 and 205 are defined to carry no body, so res.json() throws on them.\n // apiWrite treats that throw as a failed write, which turned every\n // successful no-content response (roadmap unvote, remove reaction,\n // dismiss endorsement) into a reported error.\n if (res.status === 204 || res.status === 205) {\n return undefined as T;\n }\n\n return (await res.json()) as T;\n }\n\n throw new ApiError(`Sifa API exhausted retries on ${path}`, 429);\n}\n\n/**\n * Variant of {@link apiFetch} that resolves to `null` on HTTP 404 instead\n * of throwing. Useful for \"fetch by handle\" reads where missing is\n * expected (e.g. unknown profile).\n */\nexport async function apiFetchOrNull<T>(\n config: SifaApiConfig,\n path: string,\n options: ApiFetchOptions = {},\n): Promise<T | null> {\n try {\n return await apiFetch<T>(config, path, options);\n } catch (e) {\n if (e instanceof ApiError && e.status === 404) return null;\n throw e;\n }\n}\n\n/**\n * Result returned by record-write mutations (create / update / delete).\n *\n * Never throws -- writes against the user's PDS can fail in many ways\n * (network, PDS unreachable, rate limit) and the UI needs structured\n * results to render appropriate messages.\n */\nexport interface WriteResult {\n success: boolean;\n error?: string;\n /**\n * PDS hostname returned by sifa-api when a write failed at the user's\n * Personal Data Server (issue #167). Lets the UI render\n * \"Your data server (eurosky.social) isn't responding\" instead of a\n * generic \"Request failed (500)\".\n */\n pdsHost?: string;\n}\n\n/** Result returned by create mutations. Includes the newly created `rkey`. */\nexport interface CreateResult extends WriteResult {\n rkey?: string;\n}\n\ninterface ApiErrorBody {\n message?: string;\n pdsHost?: string;\n}\n\nfunction extractWriteError(data: unknown, status: number): { error: string; pdsHost?: string } {\n const body = (data ?? {}) as ApiErrorBody;\n return {\n error: body.message ?? `Request failed (${status})`,\n ...(body.pdsHost ? { pdsHost: body.pdsHost } : {}),\n };\n}\n\n/**\n * Write mutation against the Sifa AppView. Wraps {@link apiFetch} with the\n * never-throws contract used by all sifa-web mutations: returns a\n * structured {@link WriteResult} on both success and failure, and\n * preserves the `pdsHost` field when the AppView reports a PDS-side\n * failure (issue #167).\n *\n * On success: returns `{ success: true, ...payload }` where `payload` is\n * whatever the server returned in JSON (or `{}` for 204).\n *\n * On failure: returns `{ success: false, error, pdsHost? }`. Never throws.\n *\n * Use {@link apiWriteCreate} when you specifically need the `rkey` from a\n * create response folded into the result shape.\n */\nexport async function apiWrite<TExtra extends object = Record<never, never>>(\n config: SifaApiConfig,\n path: string,\n method: 'POST' | 'PUT' | 'DELETE' | 'PATCH',\n options: Omit<ApiFetchOptions, 'method'> = {},\n): Promise<WriteResult & TExtra> {\n try {\n const data = await apiFetch<TExtra>(config, path, {\n method,\n credentials: 'include',\n ...options,\n });\n return { success: true, ...(data ?? ({} as TExtra)) };\n } catch (e) {\n if (e instanceof ApiError) {\n return { success: false, ...extractWriteError(e.body, e.status) } as WriteResult & TExtra;\n }\n return { success: false, error: 'Network error' } as WriteResult & TExtra;\n }\n}\n\n/**\n * Write mutation that expects the server to return a record key (`rkey`)\n * in its response body. Wraps {@link apiWrite} and folds the `rkey` into\n * the result shape so consumers get `{ success, rkey?, error?, pdsHost? }`.\n *\n * If the server returns additional fields (e.g. `feedUrl` from external\n * account creation), pass them via the `TExtra` generic to keep them\n * typed.\n */\nexport function apiWriteCreate<TExtra extends object = Record<never, never>>(\n config: SifaApiConfig,\n path: string,\n body: unknown,\n options: Omit<ApiFetchOptions, 'method' | 'body'> = {},\n): Promise<CreateResult & TExtra> {\n return apiWrite<{ rkey?: string } & TExtra>(config, path, 'POST', {\n body,\n ...options,\n });\n}\n","import type { ProfileView } from '../../types/profile-view.js';\nimport {\n ApiError,\n apiFetch,\n encodeIdentifier,\n type ApiFetchOptions,\n type SifaApiConfig,\n} from '../client.js';\n\n/**\n * Read the aggregated public profile view via the `id.sifa.getProfileView`\n * XRPC query. This is the AppView-only join (positions, education, skills,\n * endorsements, and more) exposed as a standard lexicon method, distinct from\n * the internal `/api/profile/*` REST surface.\n *\n * Returns `null` when the AppView has no profile for the actor\n * (`ProfileNotFound`). Throws {@link ApiError} on other non-2xx responses.\n *\n * Server-callable (Next.js RSC) and client-callable (Expo, browser).\n */\nexport async function fetchGetProfileView(\n config: SifaApiConfig,\n actor: string,\n options: ApiFetchOptions = {},\n): Promise<ProfileView | null> {\n const path = `/xrpc/id.sifa.getProfileView?actor=${encodeIdentifier(actor)}`;\n try {\n return await apiFetch<ProfileView>(config, path, { retryOn429: true, ...options });\n } catch (e) {\n if (\n e instanceof ApiError &&\n e.status === 400 &&\n typeof e.body === 'object' &&\n e.body !== null &&\n (e.body as { error?: unknown }).error === 'ProfileNotFound'\n ) {\n return null;\n }\n throw e;\n }\n}\n","import {\n summarizeProfileView,\n type ProfileSummary,\n type SummarizeProfileViewOptions,\n} from '../../logic/profile-summary.js';\nimport { fetchGetProfileView } from './get-profile-view.js';\nimport type { ApiFetchOptions, SifaApiConfig } from '../client.js';\n\n/**\n * Fetch the aggregated public profile via `id.sifa.getProfileView` and reduce it\n * to a compact {@link ProfileSummary} (identity, headline, current role/employer,\n * top skills). Convenience over `fetchGetProfileView` + `summarizeProfileView`,\n * for third-party surfaces that only want the headline facts.\n *\n * Returns `null` when the AppView has no profile for the actor. Server-callable\n * (Next.js RSC) and client-callable (Expo, browser).\n */\nexport async function fetchProfileSummary(\n config: SifaApiConfig,\n actor: string,\n options: SummarizeProfileViewOptions & ApiFetchOptions = {},\n): Promise<ProfileSummary | null> {\n const { maxSkills, ...fetchOptions } = options;\n const view = await fetchGetProfileView(config, actor, fetchOptions);\n return view ? summarizeProfileView(view, { maxSkills }) : null;\n}\n","/**\n * `<sifa-badge>` — a framework-free custom element that renders a person's\n * professional headline (name, current role and employer, top skills) from\n * their Sifa profile. Drop it into any page:\n *\n * ```html\n * <script type=\"module\" src=\"https://esm.sh/@singi-labs/sifa-sdk/badge\"></script>\n * <sifa-badge handle=\"gui.do\"></sifa-badge>\n * ```\n *\n * Or, in a bundler: `import '@singi-labs/sifa-sdk/badge'` (importing registers\n * the element). Attributes: `handle` or `did` (required), `api` (AppView base,\n * default `https://api.sifa.id`), `max-skills` (default 5).\n *\n * Reads the public `id.sifa.getProfileView` query, so it needs no auth or key.\n * Cross-origin browser use depends on that endpoint's open CORS.\n */\n\nimport type { ProfileSummary } from '../logic/profile-summary.js';\nimport { fetchProfileSummary } from '../query/fetchers/profile-summary.js';\n\nconst TAG = 'sifa-badge';\nconst DEFAULT_API_BASE = 'https://api.sifa.id';\nconst PROFILE_BASE = 'https://sifa.id/p/';\n\n// Reference HTMLElement through a guarded base so importing this module never\n// throws in a non-DOM (SSR / Node) context. The real element is only ever\n// registered and instantiated in a browser, where defineSifaBadge runs.\nconst BaseElement: typeof HTMLElement =\n typeof HTMLElement !== 'undefined' ? HTMLElement : (class {} as unknown as typeof HTMLElement);\n\nconst STYLES = `\n :host { display: inline-block; max-width: 22rem; }\n a.card {\n display: flex; gap: 0.75rem; align-items: center;\n padding: 0.75rem 0.9rem; text-decoration: none; color: inherit;\n border: 1px solid rgba(0,0,0,0.12); border-radius: 0.75rem;\n background: canvas; font: 400 0.9rem/1.4 system-ui, sans-serif;\n }\n a.card:hover { border-color: rgba(0,0,0,0.28); }\n a.card:focus-visible { outline: 2px solid #2563eb; outline-offset: 2px; }\n .avatar { width: 2.75rem; height: 2.75rem; border-radius: 50%; object-fit: cover; flex: none; }\n .body { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }\n .name { font-weight: 600; }\n .role { color: color-mix(in srgb, canvastext 65%, canvas); }\n .skills { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-top: 0.25rem; }\n .chip {\n font-size: 0.72rem; padding: 0.1rem 0.45rem; border-radius: 999px;\n background: color-mix(in srgb, canvastext 8%, canvas);\n }\n .msg { margin: 0; padding: 0.5rem 0.6rem; color: color-mix(in srgb, canvastext 60%, canvas);\n font: 400 0.85rem/1.4 system-ui, sans-serif; }\n`;\n\nexport class SifaBadgeElement extends BaseElement {\n static readonly observedAttributes = ['handle', 'did', 'api', 'max-skills'];\n\n private readonly shadow: ShadowRoot;\n private renderToken = 0;\n private scheduled = false;\n\n constructor() {\n super();\n this.shadow = this.attachShadow({ mode: 'open' });\n }\n\n connectedCallback(): void {\n this.schedule();\n }\n\n attributeChangedCallback(): void {\n this.schedule();\n }\n\n /** Coalesce a burst of synchronous attribute writes (and the initial connect) into one load. */\n private schedule(): void {\n if (this.scheduled) return;\n this.scheduled = true;\n queueMicrotask(() => {\n this.scheduled = false;\n if (this.isConnected) void this.load();\n });\n }\n\n private async load(): Promise<void> {\n const token = ++this.renderToken;\n const actor = this.getAttribute('did') || this.getAttribute('handle');\n if (!actor) {\n this.paint(this.message('Set a handle or did attribute on <sifa-badge>.'));\n return;\n }\n\n const baseUrl = this.getAttribute('api') || DEFAULT_API_BASE;\n const rawMax = this.getAttribute('max-skills');\n const maxSkills = rawMax != null ? Number(rawMax) : undefined;\n const options = maxSkills != null && Number.isFinite(maxSkills) ? { maxSkills } : {};\n\n this.paint(this.message('Loading…'));\n\n let summary: ProfileSummary | null;\n try {\n summary = await fetchProfileSummary({ baseUrl }, actor, options);\n } catch {\n if (token === this.renderToken) this.paint(this.message('Could not load this profile.'));\n return;\n }\n\n if (token !== this.renderToken) return; // a newer load superseded this one\n this.paint(summary ? this.card(summary) : this.message(`No Sifa profile for ${actor}.`));\n }\n\n private paint(body: Node): void {\n const style = document.createElement('style');\n style.textContent = STYLES;\n this.shadow.replaceChildren(style, body);\n }\n\n private message(text: string): HTMLElement {\n const p = document.createElement('p');\n p.className = 'msg';\n p.textContent = text;\n return p;\n }\n\n private card(summary: ProfileSummary): HTMLElement {\n const name = summary.displayName || summary.handle;\n const link = document.createElement('a');\n link.className = 'card';\n link.href = PROFILE_BASE + encodeURIComponent(summary.handle);\n link.target = '_blank';\n link.rel = 'noopener';\n link.setAttribute('aria-label', `${name} on Sifa`);\n\n if (summary.avatar) {\n const img = document.createElement('img');\n img.className = 'avatar';\n img.src = summary.avatar;\n img.alt = '';\n img.loading = 'lazy';\n link.append(img);\n }\n\n const body = document.createElement('span');\n body.className = 'body';\n\n const nameEl = document.createElement('span');\n nameEl.className = 'name';\n nameEl.textContent = name;\n body.append(nameEl);\n\n const role =\n [summary.currentTitle, summary.currentCompany].filter(Boolean).join(' · ') ||\n summary.headline;\n if (role) {\n const roleEl = document.createElement('span');\n roleEl.className = 'role';\n roleEl.textContent = role;\n body.append(roleEl);\n }\n\n if (summary.topSkills.length > 0) {\n const skills = document.createElement('span');\n skills.className = 'skills';\n for (const skill of summary.topSkills) {\n const chip = document.createElement('span');\n chip.className = 'chip';\n chip.textContent = skill;\n skills.append(chip);\n }\n body.append(skills);\n }\n\n link.append(body);\n return link;\n }\n}\n\n/** Register the element (idempotent). Called automatically when this module is imported. */\nexport function defineSifaBadge(tag: string = TAG): void {\n if (typeof customElements !== 'undefined' && !customElements.get(tag)) {\n customElements.define(tag, SifaBadgeElement);\n }\n}\n\ndefineSifaBadge();\n"]}
1
+ {"version":3,"sources":["../../src/badge/index.ts"],"sourcesContent":["/**\n * `<sifa-badge>` — a framework-free custom element that renders a person's\n * professional headline (name, current role and employer, top skills) from\n * their Sifa profile. Drop it into any page:\n *\n * ```html\n * <script type=\"module\" src=\"https://esm.sh/@singi-labs/sifa-sdk/badge\"></script>\n * <sifa-badge handle=\"gui.do\"></sifa-badge>\n * ```\n *\n * Or, in a bundler: `import '@singi-labs/sifa-sdk/badge'` (importing registers\n * the element). Attributes: `handle` or `did` (required), `api` (AppView base,\n * default `https://api.sifa.id`), `max-skills` (default 5).\n *\n * Reads the public `id.sifa.getProfileView` query, so it needs no auth or key.\n * Cross-origin browser use depends on that endpoint's open CORS.\n */\n\nimport type { ProfileSummary } from '../logic/profile-summary.js';\nimport { fetchProfileSummary } from '../query/fetchers/profile-summary.js';\n\nconst TAG = 'sifa-badge';\nconst DEFAULT_API_BASE = 'https://api.sifa.id';\nconst PROFILE_BASE = 'https://sifa.id/p/';\n\n// Reference HTMLElement through a guarded base so importing this module never\n// throws in a non-DOM (SSR / Node) context. The real element is only ever\n// registered and instantiated in a browser, where defineSifaBadge runs.\nconst BaseElement: typeof HTMLElement =\n typeof HTMLElement !== 'undefined' ? HTMLElement : (class {} as unknown as typeof HTMLElement);\n\nconst STYLES = `\n :host { display: inline-block; max-width: 22rem; }\n a.card {\n display: flex; gap: 0.75rem; align-items: center;\n padding: 0.75rem 0.9rem; text-decoration: none; color: inherit;\n border: 1px solid rgba(0,0,0,0.12); border-radius: 0.75rem;\n background: canvas; font: 400 0.9rem/1.4 system-ui, sans-serif;\n }\n a.card:hover { border-color: rgba(0,0,0,0.28); }\n a.card:focus-visible { outline: 2px solid #2563eb; outline-offset: 2px; }\n .avatar { width: 2.75rem; height: 2.75rem; border-radius: 50%; object-fit: cover; flex: none; }\n .body { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }\n .name { font-weight: 600; }\n .role { color: color-mix(in srgb, canvastext 65%, canvas); }\n .skills { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-top: 0.25rem; }\n .chip {\n font-size: 0.72rem; padding: 0.1rem 0.45rem; border-radius: 999px;\n background: color-mix(in srgb, canvastext 8%, canvas);\n }\n .msg { margin: 0; padding: 0.5rem 0.6rem; color: color-mix(in srgb, canvastext 60%, canvas);\n font: 400 0.85rem/1.4 system-ui, sans-serif; }\n`;\n\nexport class SifaBadgeElement extends BaseElement {\n static readonly observedAttributes = ['handle', 'did', 'api', 'max-skills'];\n\n private readonly shadow: ShadowRoot;\n private renderToken = 0;\n private scheduled = false;\n\n constructor() {\n super();\n this.shadow = this.attachShadow({ mode: 'open' });\n }\n\n connectedCallback(): void {\n this.schedule();\n }\n\n attributeChangedCallback(): void {\n this.schedule();\n }\n\n /** Coalesce a burst of synchronous attribute writes (and the initial connect) into one load. */\n private schedule(): void {\n if (this.scheduled) return;\n this.scheduled = true;\n queueMicrotask(() => {\n this.scheduled = false;\n if (this.isConnected) void this.load();\n });\n }\n\n private async load(): Promise<void> {\n const token = ++this.renderToken;\n const actor = this.getAttribute('did') || this.getAttribute('handle');\n if (!actor) {\n this.paint(this.message('Set a handle or did attribute on <sifa-badge>.'));\n return;\n }\n\n const baseUrl = this.getAttribute('api') || DEFAULT_API_BASE;\n const rawMax = this.getAttribute('max-skills');\n const maxSkills = rawMax != null ? Number(rawMax) : undefined;\n const options = maxSkills != null && Number.isFinite(maxSkills) ? { maxSkills } : {};\n\n this.paint(this.message('Loading…'));\n\n let summary: ProfileSummary | null;\n try {\n summary = await fetchProfileSummary({ baseUrl }, actor, options);\n } catch {\n if (token === this.renderToken) this.paint(this.message('Could not load this profile.'));\n return;\n }\n\n if (token !== this.renderToken) return; // a newer load superseded this one\n this.paint(summary ? this.card(summary) : this.message(`No Sifa profile for ${actor}.`));\n }\n\n private paint(body: Node): void {\n const style = document.createElement('style');\n style.textContent = STYLES;\n this.shadow.replaceChildren(style, body);\n }\n\n private message(text: string): HTMLElement {\n const p = document.createElement('p');\n p.className = 'msg';\n p.textContent = text;\n return p;\n }\n\n private card(summary: ProfileSummary): HTMLElement {\n const name = summary.displayName || summary.handle;\n const link = document.createElement('a');\n link.className = 'card';\n link.href = PROFILE_BASE + encodeURIComponent(summary.handle);\n link.target = '_blank';\n link.rel = 'noopener';\n link.setAttribute('aria-label', `${name} on Sifa`);\n\n if (summary.avatar) {\n const img = document.createElement('img');\n img.className = 'avatar';\n img.src = summary.avatar;\n img.alt = '';\n img.loading = 'lazy';\n link.append(img);\n }\n\n const body = document.createElement('span');\n body.className = 'body';\n\n const nameEl = document.createElement('span');\n nameEl.className = 'name';\n nameEl.textContent = name;\n body.append(nameEl);\n\n const role =\n [summary.currentTitle, summary.currentCompany].filter(Boolean).join(' · ') ||\n summary.headline;\n if (role) {\n const roleEl = document.createElement('span');\n roleEl.className = 'role';\n roleEl.textContent = role;\n body.append(roleEl);\n }\n\n if (summary.topSkills.length > 0) {\n const skills = document.createElement('span');\n skills.className = 'skills';\n for (const skill of summary.topSkills) {\n const chip = document.createElement('span');\n chip.className = 'chip';\n chip.textContent = skill;\n skills.append(chip);\n }\n body.append(skills);\n }\n\n link.append(body);\n return link;\n }\n}\n\n/** Register the element (idempotent). Called automatically when this module is imported. */\nexport function defineSifaBadge(tag: string = TAG): void {\n if (typeof customElements !== 'undefined' && !customElements.get(tag)) {\n customElements.define(tag, SifaBadgeElement);\n }\n}\n\ndefineSifaBadge();\n"],"mappings":"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","names":[]}
@@ -0,0 +1,14 @@
1
+ // src/cards/adult-content.ts
2
+ var ADULT_CONTENT_LABELS = ["porn", "sexual", "nudity", "graphic-media"];
3
+ function hasAdultContent(item) {
4
+ const labels = item.labels;
5
+ if (!labels || labels.length === 0) return false;
6
+ return labels.some(
7
+ (label) => !label.neg && ADULT_CONTENT_LABELS.includes(label.val)
8
+ );
9
+ }
10
+ export {
11
+ ADULT_CONTENT_LABELS,
12
+ hasAdultContent
13
+ };
14
+ //# sourceMappingURL=adult-content.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/cards/adult-content.ts"],"sourcesContent":["/**\n * Adult-content gating for Bluesky activity items.\n *\n * Bluesky's official moderation labeler attaches content labels to\n * `app.bsky.feed.defs#postView.labels`. The four sex-and-violence labels\n * below are the ones we hide by default on Sifa: media is replaced with an\n * `<AdultContentWarning>` placeholder card in sifa-web. Authenticated\n * viewers can reveal per-card; anonymous viewers cannot.\n *\n * The list mirrors Bluesky's own globalLabelStrings; if the official set\n * grows (e.g. a new `extreme-violence` label), update this constant.\n *\n * Why a constant + predicate live in the SDK: the same logic needs to run\n * in sifa-web today and sifa-app tomorrow, and the API only forwards the\n * raw labels — the hide decision is a client concern.\n */\n\nexport const ADULT_CONTENT_LABELS = ['porn', 'sexual', 'nudity', 'graphic-media'] as const;\n\nexport type AdultContentLabel = (typeof ADULT_CONTENT_LABELS)[number];\n\n/**\n * A single label entry as emitted by `app.bsky.feed.defs#postView.labels`.\n * Matches `com.atproto.label.defs#label` (the on-wire shape after the AppView\n * resolves a `postView`). `neg: true` marks a retraction.\n */\nexport interface ActivityLabel {\n val: string;\n src: string;\n uri: string;\n cts?: string;\n neg?: boolean;\n}\n\n/**\n * Return true if the item carries at least one adult-content label that is\n * not negated. Items without a `labels` field (every non-Bluesky source,\n * legacy responses) are treated as safe.\n */\nexport function hasAdultContent(item: { labels?: ActivityLabel[] }): boolean {\n const labels = item.labels;\n if (!labels || labels.length === 0) return false;\n return labels.some(\n (label) => !label.neg && (ADULT_CONTENT_LABELS as readonly string[]).includes(label.val),\n );\n}\n"],"mappings":";AAiBO,IAAM,uBAAuB,CAAC,QAAQ,UAAU,UAAU,eAAe;AAsBzE,SAAS,gBAAgB,MAA6C;AAC3E,QAAM,SAAS,KAAK;AACpB,MAAI,CAAC,UAAU,OAAO,WAAW,EAAG,QAAO;AAC3C,SAAO,OAAO;AAAA,IACZ,CAAC,UAAU,CAAC,MAAM,OAAQ,qBAA2C,SAAS,MAAM,GAAG;AAAA,EACzF;AACF;","names":[]}