@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
@@ -0,0 +1,178 @@
1
+ // src/jsonld/works.ts
2
+ import { normalizeDoi } from "../format/doi.js";
3
+ import { buildTalkSlug } from "../format/talk-slug.js";
4
+ import { filterHidden } from "../profile/section-model.js";
5
+ import { normaliseBaseUrl, profileUrl } from "./url.js";
6
+ var DOI_RESOLVER = "https://doi.org/";
7
+ var ORCID_RESOLVER = "https://orcid.org/";
8
+ var identity = (input) => input;
9
+ function authorNode(author, baseUrl, s) {
10
+ return {
11
+ "@type": "Person",
12
+ name: s(author.displayName ?? author.handle),
13
+ url: profileUrl(baseUrl, author.handle)
14
+ };
15
+ }
16
+ function confirmedActorNodes(actors, baseUrl, s) {
17
+ return (actors ?? []).filter((a) => a.confirmed === true && Boolean(a.handle)).map((a) => ({
18
+ "@type": "Person",
19
+ name: s(a.displayName ?? a.handle),
20
+ url: profileUrl(baseUrl, a.handle)
21
+ }));
22
+ }
23
+ function visible(items) {
24
+ return filterHidden(items ? [...items] : void 0);
25
+ }
26
+ var ATTENDANCE_MODE = {
27
+ "community.lexicon.calendar.event#inperson": "https://schema.org/OfflineEventAttendanceMode",
28
+ "community.lexicon.calendar.event#virtual": "https://schema.org/OnlineEventAttendanceMode",
29
+ "community.lexicon.calendar.event#hybrid": "https://schema.org/MixedEventAttendanceMode"
30
+ };
31
+ var EVENT_STATUS = {
32
+ "community.lexicon.calendar.event#scheduled": "https://schema.org/EventScheduled",
33
+ "community.lexicon.calendar.event#cancelled": "https://schema.org/EventCancelled",
34
+ "community.lexicon.calendar.event#postponed": "https://schema.org/EventPostponed",
35
+ "community.lexicon.calendar.event#rescheduled": "https://schema.org/EventRescheduled"
36
+ };
37
+ var LINK_SLIDES = "id.sifa.defs#linkSlides";
38
+ var LINK_RECORDING = "id.sifa.defs#linkRecording";
39
+ function lookup(table, key) {
40
+ if (!key) return void 0;
41
+ return Object.prototype.hasOwnProperty.call(table, key) ? table[key] : void 0;
42
+ }
43
+ var REGION_NAMES = new Intl.DisplayNames("en", { type: "region", fallback: "code" });
44
+ function countryName(code) {
45
+ if (code.length !== 2) return code;
46
+ try {
47
+ const name = REGION_NAMES.of(code.toUpperCase());
48
+ if (!name || name === "Unknown Region") return code;
49
+ return name;
50
+ } catch {
51
+ return code;
52
+ }
53
+ }
54
+ function deliveryPlace(delivery, s) {
55
+ const addressLocality = delivery.locationLocality ?? void 0;
56
+ const addressRegion = delivery.locationRegion ?? void 0;
57
+ const addressCountry = delivery.countryCode ?? void 0;
58
+ if (addressLocality || addressRegion || addressCountry) {
59
+ const name = [addressLocality, addressRegion, addressCountry && countryName(addressCountry)].filter(Boolean).join(", ");
60
+ return {
61
+ "@type": "Place",
62
+ ...name && { name: s(name) },
63
+ address: {
64
+ "@type": "PostalAddress",
65
+ ...addressLocality && { addressLocality: s(addressLocality) },
66
+ ...addressRegion && { addressRegion: s(addressRegion) },
67
+ ...addressCountry && { addressCountry }
68
+ }
69
+ };
70
+ }
71
+ if (delivery.location) {
72
+ return { "@type": "Place", name: s(delivery.location) };
73
+ }
74
+ return void 0;
75
+ }
76
+ function linkOfType(links, type) {
77
+ return links?.find((l) => l.type === type)?.uri;
78
+ }
79
+ function buildPresentationJsonLd(presentation, author, options = {}) {
80
+ const s = options.sanitize ?? identity;
81
+ const baseUrl = normaliseBaseUrl(options.baseUrl);
82
+ const speaker = authorNode(author, baseUrl, s);
83
+ const events = visible(presentation.deliveries).map((delivery) => {
84
+ const place = deliveryPlace(delivery, s);
85
+ const recording = linkOfType(delivery.links, LINK_RECORDING);
86
+ const slides = linkOfType(delivery.links, LINK_SLIDES) ?? linkOfType(presentation.links, LINK_SLIDES);
87
+ return {
88
+ "@type": "Event",
89
+ name: s(delivery.eventName ?? delivery.title ?? presentation.title),
90
+ ...delivery.date && { startDate: delivery.date },
91
+ ...place && { location: place },
92
+ ...lookup(ATTENDANCE_MODE, delivery.mode) && {
93
+ eventAttendanceMode: lookup(ATTENDANCE_MODE, delivery.mode)
94
+ },
95
+ ...lookup(EVENT_STATUS, delivery.status) && {
96
+ eventStatus: lookup(EVENT_STATUS, delivery.status)
97
+ },
98
+ performer: [speaker, ...confirmedActorNodes(delivery.coSpeakers, baseUrl, s)],
99
+ ...recording && { recordedIn: { "@type": "VideoObject", url: recording } },
100
+ ...slides && {
101
+ workFeatured: { "@type": "PresentationDigitalDocument", url: slides }
102
+ }
103
+ };
104
+ });
105
+ return {
106
+ "@context": "https://schema.org",
107
+ "@type": "PresentationDigitalDocument",
108
+ name: s(presentation.title),
109
+ ...presentation.description && { abstract: s(presentation.description) },
110
+ ...presentation.duration && { timeRequired: `PT${presentation.duration.minMinutes}M` },
111
+ ...presentation.coverImageUrl && { image: presentation.coverImageUrl },
112
+ author: speaker,
113
+ url: `${baseUrl}/p/${author.handle}/talk/${buildTalkSlug(presentation.title, presentation.rkey)}`,
114
+ ...events.length > 0 && { subjectOf: events }
115
+ };
116
+ }
117
+ function buildPublicationJsonLd(publication, author, options = {}) {
118
+ const s = options.sanitize ?? identity;
119
+ const baseUrl = normaliseBaseUrl(options.baseUrl);
120
+ const authors = publication.contributors && publication.contributors.length > 0 ? publication.contributors.map((c) => ({
121
+ "@type": "Person",
122
+ name: s(c.name),
123
+ ...c.handle && { url: profileUrl(baseUrl, c.handle) },
124
+ ...c.orcidId && { sameAs: [`${ORCID_RESOLVER}${c.orcidId}`] }
125
+ })) : [authorNode(author, baseUrl, s)];
126
+ const doi = publication.doi ? normalizeDoi(publication.doi) : void 0;
127
+ return {
128
+ "@context": "https://schema.org",
129
+ "@type": "ScholarlyArticle",
130
+ name: s(publication.title),
131
+ ...publication.subtitle && { alternativeHeadline: s(publication.subtitle) },
132
+ ...publication.description && { abstract: s(publication.description) },
133
+ ...publication.publisher && {
134
+ publisher: { "@type": "Organization", name: s(publication.publisher) }
135
+ },
136
+ ...publication.date && { datePublished: publication.date },
137
+ ...publication.url && { url: publication.url },
138
+ author: authors,
139
+ ...doi && {
140
+ identifier: { "@type": "PropertyValue", propertyID: "DOI", value: doi },
141
+ sameAs: [`${DOI_RESOLVER}${doi}`]
142
+ }
143
+ };
144
+ }
145
+ function buildCourseJsonLd(course, options = {}) {
146
+ const s = options.sanitize ?? identity;
147
+ return {
148
+ "@context": "https://schema.org",
149
+ "@type": "Course",
150
+ name: s(course.name),
151
+ ...course.number && { courseCode: s(course.number) },
152
+ ...course.institution && {
153
+ provider: { "@type": "EducationalOrganization", name: s(course.institution) }
154
+ }
155
+ };
156
+ }
157
+ function buildProjectJsonLd(project, author, options = {}) {
158
+ const s = options.sanitize ?? identity;
159
+ const baseUrl = normaliseBaseUrl(options.baseUrl);
160
+ const members = confirmedActorNodes(project.members, baseUrl, s);
161
+ return {
162
+ "@context": "https://schema.org",
163
+ "@type": "Project",
164
+ name: s(project.name),
165
+ ...project.description && { description: s(project.description) },
166
+ ...project.url && { url: project.url },
167
+ ...project.startDate && { startDate: project.startDate },
168
+ ...project.endDate && { endDate: project.endDate },
169
+ member: [authorNode(author, baseUrl, s), ...members]
170
+ };
171
+ }
172
+ export {
173
+ buildCourseJsonLd,
174
+ buildPresentationJsonLd,
175
+ buildProjectJsonLd,
176
+ buildPublicationJsonLd
177
+ };
178
+ //# sourceMappingURL=works.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/jsonld/works.ts"],"sourcesContent":["/**\n * Schema.org JSON-LD for the things a person made: talks, publications,\n * courses and projects.\n *\n * These collections carried no structured data before this module existed;\n * a talk page emitted a four-property `CreativeWork` and publications,\n * courses and projects emitted nothing at all. Term choices are recorded in\n * `./terms.js`.\n */\n\nimport { normalizeDoi } from '../format/doi.js';\nimport { buildTalkSlug } from '../format/talk-slug.js';\nimport { filterHidden } from '../profile/section-model.js';\nimport { normaliseBaseUrl, profileUrl } from './url.js';\n\nconst DOI_RESOLVER = 'https://doi.org/';\nconst ORCID_RESOLVER = 'https://orcid.org/';\n\nexport interface WorksOptions {\n readonly baseUrl?: string;\n readonly sanitize?: (input: string) => string;\n}\n\n/** Minimal identity for the profile owner a work is attributed to. */\nexport interface WorkAuthor {\n readonly handle: string;\n readonly displayName?: string;\n}\n\n/**\n * A person named on somebody else's record. `confirmed` is load bearing: an\n * unconfirmed claim is a claim, not a fact, and must never be laundered into\n * structured data as a schema.org Person.\n */\ninterface NamedActor {\n readonly did?: string;\n readonly handle?: string;\n readonly displayName?: string;\n readonly confirmed?: boolean;\n}\n\ninterface PersonNode {\n readonly '@type': 'Person';\n readonly name: string;\n readonly url?: string;\n readonly sameAs?: string[];\n}\n\nconst identity = (input: string) => input;\n\nfunction authorNode(author: WorkAuthor, baseUrl: string, s: (v: string) => string): PersonNode {\n return {\n '@type': 'Person',\n name: s(author.displayName ?? author.handle),\n url: profileUrl(baseUrl, author.handle),\n };\n}\n\n/**\n * Confirmed named actors only, mapped to Person nodes. Anyone unconfirmed is\n * dropped entirely rather than degraded to a bare handle, because even a handle\n * in structured data asserts the association.\n */\nfunction confirmedActorNodes(\n actors: readonly NamedActor[] | undefined,\n baseUrl: string,\n s: (v: string) => string,\n): PersonNode[] {\n return (actors ?? [])\n .filter((a) => a.confirmed === true && Boolean(a.handle))\n .map((a) => ({\n '@type': 'Person' as const,\n name: s(a.displayName ?? a.handle!),\n url: profileUrl(baseUrl, a.handle!),\n }));\n}\n\n/** `filterHidden` takes a mutable array; inputs here are readonly. */\nfunction visible<T extends { hidden?: boolean }>(items: readonly T[] | undefined): T[] {\n return filterHidden(items ? [...items] : undefined);\n}\n\n// ---------------------------------------------------------------------------\n// Presentations\n// ---------------------------------------------------------------------------\n\n/**\n * The community calendar known values are already one-to-one with the\n * schema.org enumerations. An unrecognised value is left off rather than\n * guessed: a wrong attendance mode is worse than a missing one.\n */\nconst ATTENDANCE_MODE: Readonly<Record<string, string>> = {\n 'community.lexicon.calendar.event#inperson': 'https://schema.org/OfflineEventAttendanceMode',\n 'community.lexicon.calendar.event#virtual': 'https://schema.org/OnlineEventAttendanceMode',\n 'community.lexicon.calendar.event#hybrid': 'https://schema.org/MixedEventAttendanceMode',\n};\n\n/**\n * `#planned` is intentionally absent: schema.org has no equivalent, and mapping\n * it to EventScheduled would assert a commitment the record does not make.\n */\nconst EVENT_STATUS: Readonly<Record<string, string>> = {\n 'community.lexicon.calendar.event#scheduled': 'https://schema.org/EventScheduled',\n 'community.lexicon.calendar.event#cancelled': 'https://schema.org/EventCancelled',\n 'community.lexicon.calendar.event#postponed': 'https://schema.org/EventPostponed',\n 'community.lexicon.calendar.event#rescheduled': 'https://schema.org/EventRescheduled',\n};\n\nconst LINK_SLIDES = 'id.sifa.defs#linkSlides';\nconst LINK_RECORDING = 'id.sifa.defs#linkRecording';\n\nexport interface PresentationLinkInput {\n readonly uri: string;\n readonly label?: string;\n readonly type?: string;\n}\n\nexport interface PresentationDeliveryInput {\n readonly rkey: string;\n readonly title?: string | null;\n readonly role?: string | null;\n readonly eventName?: string | null;\n readonly date?: string | null;\n readonly location?: string | null;\n readonly locationLocality?: string | null;\n readonly locationRegion?: string | null;\n readonly countryCode?: string | null;\n readonly mode?: string | null;\n readonly status?: string | null;\n readonly links?: readonly PresentationLinkInput[];\n readonly coSpeakers?: readonly NamedActor[];\n readonly hidden?: boolean;\n}\n\nexport interface PresentationInput {\n readonly rkey: string;\n readonly title: string;\n readonly description?: string | null;\n readonly duration?: { minMinutes: number; maxMinutes?: number } | null;\n readonly coverImageUrl?: string | null;\n readonly links?: readonly PresentationLinkInput[];\n readonly deliveries?: readonly PresentationDeliveryInput[];\n readonly hidden?: boolean;\n}\n\nfunction lookup(table: Readonly<Record<string, string>>, key: string | null | undefined) {\n if (!key) return undefined;\n return Object.prototype.hasOwnProperty.call(table, key) ? table[key] : undefined;\n}\n\nconst REGION_NAMES = new Intl.DisplayNames('en', { type: 'region', fallback: 'code' });\n\n/** ISO 3166-1 alpha-2 code to a readable country name, falling back to the code. */\nfunction countryName(code: string): string {\n if (code.length !== 2) return code;\n try {\n const name = REGION_NAMES.of(code.toUpperCase());\n // ICU returns \"Unknown Region\" (locale pinned to 'en') for reserved or\n // unassigned codes; the raw code is a more useful label than that.\n if (!name || name === 'Unknown Region') return code;\n return name;\n } catch {\n return code;\n }\n}\n\nfunction deliveryPlace(delivery: PresentationDeliveryInput, s: (v: string) => string) {\n const addressLocality = delivery.locationLocality ?? undefined;\n const addressRegion = delivery.locationRegion ?? undefined;\n const addressCountry = delivery.countryCode ?? undefined;\n\n if (addressLocality || addressRegion || addressCountry) {\n // Google flags an Event `location` Place that has no `name`, so we compose a\n // readable label from the parts we have (venue names are not captured).\n const name = [addressLocality, addressRegion, addressCountry && countryName(addressCountry)]\n .filter(Boolean)\n .join(', ');\n return {\n '@type': 'Place' as const,\n ...(name && { name: s(name) }),\n address: {\n '@type': 'PostalAddress' as const,\n ...(addressLocality && { addressLocality: s(addressLocality) }),\n ...(addressRegion && { addressRegion: s(addressRegion) }),\n ...(addressCountry && { addressCountry }),\n },\n };\n }\n\n // Legacy free-text location, kept so deliveries recorded before the\n // structured address field still say where they happened.\n if (delivery.location) {\n return { '@type': 'Place' as const, name: s(delivery.location) };\n }\n\n return undefined;\n}\n\nfunction linkOfType(links: readonly PresentationLinkInput[] | undefined, type: string) {\n return links?.find((l) => l.type === type)?.uri;\n}\n\nexport function buildPresentationJsonLd(\n presentation: PresentationInput,\n author: WorkAuthor,\n options: WorksOptions = {},\n) {\n const s = options.sanitize ?? identity;\n const baseUrl = normaliseBaseUrl(options.baseUrl);\n const speaker = authorNode(author, baseUrl, s);\n\n const events = visible(presentation.deliveries).map((delivery) => {\n const place = deliveryPlace(delivery, s);\n const recording = linkOfType(delivery.links, LINK_RECORDING);\n const slides =\n linkOfType(delivery.links, LINK_SLIDES) ?? linkOfType(presentation.links, LINK_SLIDES);\n\n return {\n '@type': 'Event' as const,\n name: s(delivery.eventName ?? delivery.title ?? presentation.title),\n ...(delivery.date && { startDate: delivery.date }),\n ...(place && { location: place }),\n ...(lookup(ATTENDANCE_MODE, delivery.mode) && {\n eventAttendanceMode: lookup(ATTENDANCE_MODE, delivery.mode),\n }),\n ...(lookup(EVENT_STATUS, delivery.status) && {\n eventStatus: lookup(EVENT_STATUS, delivery.status),\n }),\n performer: [speaker, ...confirmedActorNodes(delivery.coSpeakers, baseUrl, s)],\n ...(recording && { recordedIn: { '@type': 'VideoObject' as const, url: recording } }),\n ...(slides && {\n workFeatured: { '@type': 'PresentationDigitalDocument' as const, url: slides },\n }),\n };\n });\n\n return {\n '@context': 'https://schema.org',\n '@type': 'PresentationDigitalDocument' as const,\n name: s(presentation.title),\n ...(presentation.description && { abstract: s(presentation.description) }),\n ...(presentation.duration && { timeRequired: `PT${presentation.duration.minMinutes}M` }),\n ...(presentation.coverImageUrl && { image: presentation.coverImageUrl }),\n author: speaker,\n url: `${baseUrl}/p/${author.handle}/talk/${buildTalkSlug(presentation.title, presentation.rkey)}`,\n ...(events.length > 0 && { subjectOf: events }),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Publications\n// ---------------------------------------------------------------------------\n\nexport interface PublicationContributorInput {\n readonly name: string;\n readonly orcidId?: string;\n readonly handle?: string;\n}\n\nexport interface PublicationInput {\n readonly rkey: string;\n readonly title: string;\n readonly subtitle?: string;\n readonly publisher?: string;\n readonly date?: string;\n readonly url?: string;\n readonly description?: string;\n readonly doi?: string;\n readonly contributors?: readonly PublicationContributorInput[];\n readonly hidden?: boolean;\n}\n\nexport function buildPublicationJsonLd(\n publication: PublicationInput,\n author: WorkAuthor,\n options: WorksOptions = {},\n) {\n const s = options.sanitize ?? identity;\n const baseUrl = normaliseBaseUrl(options.baseUrl);\n\n // Contributor order is meaningful in scholarly work, so it is preserved as\n // given rather than sorted or deduplicated.\n const authors: PersonNode[] =\n publication.contributors && publication.contributors.length > 0\n ? publication.contributors.map((c) => ({\n '@type': 'Person' as const,\n name: s(c.name),\n ...(c.handle && { url: profileUrl(baseUrl, c.handle) }),\n ...(c.orcidId && { sameAs: [`${ORCID_RESOLVER}${c.orcidId}`] }),\n }))\n : [authorNode(author, baseUrl, s)];\n\n const doi = publication.doi ? normalizeDoi(publication.doi) : undefined;\n\n return {\n '@context': 'https://schema.org',\n '@type': 'ScholarlyArticle' as const,\n name: s(publication.title),\n ...(publication.subtitle && { alternativeHeadline: s(publication.subtitle) }),\n ...(publication.description && { abstract: s(publication.description) }),\n ...(publication.publisher && {\n publisher: { '@type': 'Organization' as const, name: s(publication.publisher) },\n }),\n ...(publication.date && { datePublished: publication.date }),\n ...(publication.url && { url: publication.url }),\n author: authors,\n ...(doi && {\n identifier: { '@type': 'PropertyValue' as const, propertyID: 'DOI', value: doi },\n sameAs: [`${DOI_RESOLVER}${doi}`],\n }),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Courses and projects\n// ---------------------------------------------------------------------------\n\nexport interface CourseInput {\n readonly rkey: string;\n readonly name: string;\n readonly number?: string;\n readonly institution?: string;\n readonly hidden?: boolean;\n}\n\nexport function buildCourseJsonLd(course: CourseInput, options: WorksOptions = {}) {\n const s = options.sanitize ?? identity;\n return {\n '@context': 'https://schema.org',\n '@type': 'Course' as const,\n name: s(course.name),\n ...(course.number && { courseCode: s(course.number) }),\n ...(course.institution && {\n provider: { '@type': 'EducationalOrganization' as const, name: s(course.institution) },\n }),\n };\n}\n\nexport interface ProjectInput {\n readonly rkey: string;\n readonly name: string;\n readonly description?: string;\n readonly url?: string;\n readonly startDate?: string;\n readonly endDate?: string;\n readonly members?: readonly NamedActor[];\n readonly hidden?: boolean;\n}\n\nexport function buildProjectJsonLd(\n project: ProjectInput,\n author: WorkAuthor,\n options: WorksOptions = {},\n) {\n const s = options.sanitize ?? identity;\n const baseUrl = normaliseBaseUrl(options.baseUrl);\n const members = confirmedActorNodes(project.members, baseUrl, s);\n\n return {\n '@context': 'https://schema.org',\n '@type': 'Project' as const,\n name: s(project.name),\n ...(project.description && { description: s(project.description) }),\n ...(project.url && { url: project.url }),\n ...(project.startDate && { startDate: project.startDate }),\n ...(project.endDate && { endDate: project.endDate }),\n member: [authorNode(author, baseUrl, s), ...members],\n };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,8 @@
1
+ // src/logic/actor-identity.ts
2
+ function actorShowsIdentity(actor) {
3
+ return !!(actor.confirmed || actor.claimed);
4
+ }
5
+ export {
6
+ actorShowsIdentity
7
+ };
8
+ //# sourceMappingURL=actor-identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/logic/actor-identity.ts"],"sourcesContent":["/**\n * Whether a person named on someone else's record may render with their\n * identity attached -- display name, avatar, and a link to their profile.\n *\n * Naming someone is a claim. The default is that an unconfirmed claim shows as\n * a bare handle: it does not get to borrow that person's face until they affirm\n * it with an `id.sifa.confirmation`. This predicate widens that default in one\n * case: a person with a *claimed* Sifa account (`claimed`) is shown by name and\n * linked even before they confirm, because they can be notified of the pending\n * claim and act on it from their own account. An indexed-but-unclaimed account\n * cannot be notified, so it stays a bare handle until confirmed.\n *\n * The AppView is still the enforcement point: it only serves `displayName` /\n * `avatar` for a card when this predicate would hold, so a consumer that\n * forgets the rule cannot leak the identity. This predicate exists so web, the\n * activity surfaces, and any future consumer decide it identically.\n */\nexport function actorShowsIdentity(actor: { confirmed?: boolean; claimed?: boolean }): boolean {\n return !!(actor.confirmed || actor.claimed);\n}\n"],"mappings":";AAiBO,SAAS,mBAAmB,OAA4D;AAC7F,SAAO,CAAC,EAAE,MAAM,aAAa,MAAM;AACrC;","names":[]}
@@ -0,0 +1,24 @@
1
+ // src/logic/agent-ref.ts
2
+ function clean(value) {
3
+ const trimmed = value?.trim();
4
+ return trimmed ? trimmed : void 0;
5
+ }
6
+ function normalize(name, did, entityRef) {
7
+ const out = { name };
8
+ const d = clean(did);
9
+ const e = clean(entityRef);
10
+ if (d) out.did = d;
11
+ if (e) out.entityRef = e;
12
+ return out;
13
+ }
14
+ function resolveAgentRef(nested, flat) {
15
+ const nestedName = clean(nested?.name);
16
+ if (nestedName) return normalize(nestedName, nested?.did, nested?.entityRef);
17
+ const flatName = clean(flat?.name);
18
+ if (flatName) return normalize(flatName, flat?.did, flat?.entityRef);
19
+ return void 0;
20
+ }
21
+ export {
22
+ resolveAgentRef
23
+ };
24
+ //# sourceMappingURL=agent-ref.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/logic/agent-ref.ts"],"sourcesContent":["import type { AgentRef } from '../schemas/shared.js';\n\n/**\n * The legacy flat organization fields a record carried before it adopted the\n * shared `agentRef` shape. Field NAMES differ per record (company, institution,\n * authority, issuer, organization, via, upstream), so callers pass the values\n * positionally; this helper is name-agnostic.\n */\nexport interface AgentRefFlat {\n name?: string | null;\n did?: string | null;\n entityRef?: string | null;\n}\n\nfunction clean(value: string | null | undefined): string | undefined {\n const trimmed = value?.trim();\n return trimmed ? trimmed : undefined;\n}\n\nfunction normalize(name: string, did?: string | null, entityRef?: string | null): AgentRef {\n const out: AgentRef = { name };\n const d = clean(did);\n const e = clean(entityRef);\n if (d) out.did = d;\n if (e) out.entityRef = e;\n return out;\n}\n\n/**\n * Resolve the effective organization/person reference for a record during the\n * flat-to-agentRef migration, with object-level precedence: a nested `agentRef`\n * that carries a name wins wholesale, otherwise fall back to the legacy flat\n * fields. Empty or absent anchors are dropped so the result never carries a\n * null `did`/`entityRef`. Returns `undefined` when neither side names anything\n * (a free-text-less / self-employed entry).\n *\n * During the migration records dual-write both shapes in sync, so the two agree;\n * this precedence is what keeps legacy (flat-only) and new (both) records\n * readable through one path.\n */\nexport function resolveAgentRef(\n nested: AgentRef | null | undefined,\n flat: AgentRefFlat | null | undefined,\n): AgentRef | undefined {\n const nestedName = clean(nested?.name);\n if (nestedName) return normalize(nestedName, nested?.did, nested?.entityRef);\n\n const flatName = clean(flat?.name);\n if (flatName) return normalize(flatName, flat?.did, flat?.entityRef);\n\n return undefined;\n}\n"],"mappings":";AAcA,SAAS,MAAM,OAAsD;AACnE,QAAM,UAAU,OAAO,KAAK;AAC5B,SAAO,UAAU,UAAU;AAC7B;AAEA,SAAS,UAAU,MAAc,KAAqB,WAAqC;AACzF,QAAM,MAAgB,EAAE,KAAK;AAC7B,QAAM,IAAI,MAAM,GAAG;AACnB,QAAM,IAAI,MAAM,SAAS;AACzB,MAAI,EAAG,KAAI,MAAM;AACjB,MAAI,EAAG,KAAI,YAAY;AACvB,SAAO;AACT;AAcO,SAAS,gBACd,QACA,MACsB;AACtB,QAAM,aAAa,MAAM,QAAQ,IAAI;AACrC,MAAI,WAAY,QAAO,UAAU,YAAY,QAAQ,KAAK,QAAQ,SAAS;AAE3E,QAAM,WAAW,MAAM,MAAM,IAAI;AACjC,MAAI,SAAU,QAAO,UAAU,UAAU,MAAM,KAAK,MAAM,SAAS;AAEnE,SAAO;AACT;","names":[]}
@@ -0,0 +1,23 @@
1
+ // src/logic/company-page-indexable.ts
2
+ var COMPANY_PAGE_MIN_FIRMOGRAPHIC_FIELDS = 2;
3
+ function isNonEmptyString(value) {
4
+ return typeof value === "string" && value.trim().length > 0;
5
+ }
6
+ function isPresentNumber(value) {
7
+ return typeof value === "number" && Number.isFinite(value);
8
+ }
9
+ function isCompanyPageIndexable(company) {
10
+ if (!isNonEmptyString(company.canonicalName)) return false;
11
+ let present = 0;
12
+ if (isNonEmptyString(company.description)) present += 1;
13
+ if (isNonEmptyString(company.industry)) present += 1;
14
+ if (isNonEmptyString(company.logoUrl)) present += 1;
15
+ if (isPresentNumber(company.employeeCount)) present += 1;
16
+ if (isPresentNumber(company.founded) || isNonEmptyString(company.founded)) present += 1;
17
+ return present >= COMPANY_PAGE_MIN_FIRMOGRAPHIC_FIELDS;
18
+ }
19
+ export {
20
+ COMPANY_PAGE_MIN_FIRMOGRAPHIC_FIELDS,
21
+ isCompanyPageIndexable
22
+ };
23
+ //# sourceMappingURL=company-page-indexable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/logic/company-page-indexable.ts"],"sourcesContent":["/**\n * Firmographic subset of a company profile that decides whether its `/c/` page\n * is worth indexing. A minimal STRUCTURAL type on purpose -- it does not import\n * sifa-web's `CompanyProfile`, so this predicate stays importable by any\n * consumer (sifa-web, sifa-app, third parties). Fields mirror the external\n * firmographics that lead a `/c/` page (Wikidata / ROR / GLEIF derived), NOT\n * Sifa-aggregated stats.\n *\n * Deliberately ABSENT: claim status and Sifa-derived employee count. Company\n * page indexability is NEVER gated on either (product decision, 2026-07-16) --\n * a rich unclaimed page is indexable; a claimed but empty page is not.\n */\nexport interface CompanyFirmographics {\n /** External canonical name (Wikidata label / registry name). */\n canonicalName?: string | null;\n /** Free-text description / about. */\n description?: string | null;\n /** Industry / sector label. */\n industry?: string | null;\n /** URL of the company logo. */\n logoUrl?: string | null;\n /** External headcount figure (e.g. Wikidata P1128). NOT the Sifa employee count. */\n employeeCount?: number | null;\n /** Founding year or date. */\n founded?: string | number | null;\n}\n\n/**\n * Minimum number of the optional firmographic fields\n * ({@link CompanyFirmographics} minus `canonicalName`) that must be present for\n * a `/c/` company page to be indexable. Named + exported so the threshold is\n * tunable in one place.\n */\nexport const COMPANY_PAGE_MIN_FIRMOGRAPHIC_FIELDS = 2;\n\nfunction isNonEmptyString(value: unknown): boolean {\n return typeof value === 'string' && value.trim().length > 0;\n}\n\n/** A number counts as present when finite; a founded-string counts when non-empty. */\nfunction isPresentNumber(value: unknown): boolean {\n return typeof value === 'number' && Number.isFinite(value);\n}\n\n/**\n * Is a `/c/` company page worth indexing?\n *\n * True iff it has real firmographic content: a non-empty `canonicalName` AND at\n * least {@link COMPANY_PAGE_MIN_FIRMOGRAPHIC_FIELDS} of the optional firmographic\n * fields ({@link CompanyFirmographics.description | description},\n * `industry`, `logoUrl`, `employeeCount`, `founded`) present.\n *\n * NEVER gated on claim status or Sifa employee count -- there is no such\n * parameter (product decision, 2026-07-16). A rich unclaimed stub is indexable;\n * a name-only page (claimed or not) is not.\n *\n * Pure: no network, no I/O. Third-party importable.\n */\nexport function isCompanyPageIndexable(company: CompanyFirmographics): boolean {\n if (!isNonEmptyString(company.canonicalName)) return false;\n\n let present = 0;\n if (isNonEmptyString(company.description)) present += 1;\n if (isNonEmptyString(company.industry)) present += 1;\n if (isNonEmptyString(company.logoUrl)) present += 1;\n if (isPresentNumber(company.employeeCount)) present += 1;\n if (isPresentNumber(company.founded) || isNonEmptyString(company.founded)) present += 1;\n\n return present >= COMPANY_PAGE_MIN_FIRMOGRAPHIC_FIELDS;\n}\n"],"mappings":";AAiCO,IAAM,uCAAuC;AAEpD,SAAS,iBAAiB,OAAyB;AACjD,SAAO,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,SAAS;AAC5D;AAGA,SAAS,gBAAgB,OAAyB;AAChD,SAAO,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK;AAC3D;AAgBO,SAAS,uBAAuB,SAAwC;AAC7E,MAAI,CAAC,iBAAiB,QAAQ,aAAa,EAAG,QAAO;AAErD,MAAI,UAAU;AACd,MAAI,iBAAiB,QAAQ,WAAW,EAAG,YAAW;AACtD,MAAI,iBAAiB,QAAQ,QAAQ,EAAG,YAAW;AACnD,MAAI,iBAAiB,QAAQ,OAAO,EAAG,YAAW;AAClD,MAAI,gBAAgB,QAAQ,aAAa,EAAG,YAAW;AACvD,MAAI,gBAAgB,QAAQ,OAAO,KAAK,iBAAiB,QAAQ,OAAO,EAAG,YAAW;AAEtF,SAAO,WAAW;AACpB;","names":[]}
@@ -0,0 +1,33 @@
1
+ // src/logic/date-lookahead.ts
2
+ var DATE_LOOKAHEAD_MONTHS = {
3
+ credentialIssue: 0,
4
+ award: 0,
5
+ course: 0,
6
+ involvementStart: 0,
7
+ investmentStart: 0,
8
+ /** The end date of any finished (not ongoing) range. */
9
+ completedRangeEnd: 0,
10
+ careerStart: 6,
11
+ projectStart: 6,
12
+ publication: 6,
13
+ talk: 12,
14
+ /** Admitted to a programme that starts next year. */
15
+ educationStart: 12,
16
+ educationEnd: 96
17
+ };
18
+ function isBeyondLookahead(value, months, now = /* @__PURE__ */ new Date()) {
19
+ const match = /^(\d{4})(?:-(\d{2}))?/.exec(value);
20
+ if (!match) return false;
21
+ const index = Number(match[1]) * 12 + (match[2] ? Number(match[2]) : 1);
22
+ const limit = now.getFullYear() * 12 + now.getMonth() + 1 + months;
23
+ return index > limit;
24
+ }
25
+ function isUpcomingStart(startedAt, now = /* @__PURE__ */ new Date()) {
26
+ return !!startedAt && isBeyondLookahead(startedAt, 0, now);
27
+ }
28
+ export {
29
+ DATE_LOOKAHEAD_MONTHS,
30
+ isBeyondLookahead,
31
+ isUpcomingStart
32
+ };
33
+ //# sourceMappingURL=date-lookahead.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/logic/date-lookahead.ts"],"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"],"mappings":";AAUO,IAAM,wBAAwB;AAAA,EACnC,iBAAiB;AAAA,EACjB,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,kBAAkB;AAAA,EAClB,iBAAiB;AAAA;AAAA,EAEjB,mBAAmB;AAAA,EACnB,aAAa;AAAA,EACb,cAAc;AAAA,EACd,aAAa;AAAA,EACb,MAAM;AAAA;AAAA,EAEN,gBAAgB;AAAA,EAChB,cAAc;AAChB;AAUO,SAAS,kBAAkB,OAAe,QAAgB,MAAY,oBAAI,KAAK,GAAY;AAChG,QAAM,QAAQ,wBAAwB,KAAK,KAAK;AAChD,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,QAAQ,OAAO,MAAM,CAAC,CAAC,IAAI,MAAM,MAAM,CAAC,IAAI,OAAO,MAAM,CAAC,CAAC,IAAI;AACrE,QAAM,QAAQ,IAAI,YAAY,IAAI,KAAK,IAAI,SAAS,IAAI,IAAI;AAC5D,SAAO,QAAQ;AACjB;AAMO,SAAS,gBAAgB,WAA+B,MAAY,oBAAI,KAAK,GAAY;AAC9F,SAAO,CAAC,CAAC,aAAa,kBAAkB,WAAW,GAAG,GAAG;AAC3D;","names":[]}
@@ -0,0 +1,43 @@
1
+ // src/logic/domain-detect.ts
2
+ var MAX_DOMAIN_LEN = 253;
3
+ var LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
4
+ var TLD = /^[a-z]{2,63}$/;
5
+ function stripTrailingDots(s) {
6
+ let end = s.length;
7
+ while (end > 0 && s.charCodeAt(end - 1) === 46) end -= 1;
8
+ return s.slice(0, end);
9
+ }
10
+ function looksLikeDomain(query) {
11
+ const q = query.trim().toLowerCase();
12
+ if (!q || q.length > MAX_DOMAIN_LEN) return false;
13
+ if (q.includes(" ") || q.includes(" ") || q.includes("@")) return false;
14
+ let host = q;
15
+ const schemeAt = host.indexOf("://");
16
+ if (schemeAt !== -1) host = host.slice(schemeAt + 3);
17
+ for (let i = 0; i < host.length; i += 1) {
18
+ const ch = host[i];
19
+ if (ch === "/" || ch === "?" || ch === "#") {
20
+ host = host.slice(0, i);
21
+ break;
22
+ }
23
+ }
24
+ if (host.startsWith("www.")) host = host.slice(4);
25
+ const colonAt = host.lastIndexOf(":");
26
+ if (colonAt !== -1 && /^[0-9]{1,5}$/.test(host.slice(colonAt + 1))) {
27
+ host = host.slice(0, colonAt);
28
+ }
29
+ host = stripTrailingDots(host);
30
+ if (!host || host.length > MAX_DOMAIN_LEN) return false;
31
+ const labels = host.split(".");
32
+ if (labels.length < 2) return false;
33
+ const tld = labels[labels.length - 1];
34
+ if (tld === void 0 || !TLD.test(tld)) return false;
35
+ for (let i = 0; i < labels.length - 1; i += 1) {
36
+ if (!LABEL.test(labels[i])) return false;
37
+ }
38
+ return true;
39
+ }
40
+ export {
41
+ looksLikeDomain
42
+ };
43
+ //# sourceMappingURL=domain-detect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/logic/domain-detect.ts"],"sourcesContent":["/**\n * Lightweight \"does this typeahead query look like a domain?\" predicate, used to\n * decide whether to fire the domain grow-on-demand path (resolve-domain) on a\n * local typeahead miss. It is a heuristic, NOT a validator: the API re-validates\n * against the public suffix list. It only needs to distinguish \"randstad.com\"\n * (a domain) from \"Randstad\" / \"Acme Corp\" (a name) so a plain company name\n * never triggers a domain lookup.\n *\n * Accepts an optional scheme and path (`https://www.stripe.com/pricing`), a\n * leading `www.`, and multi-label hosts; requires at least one dot and a >=2\n * alpha final label (TLD-shaped); rejects whitespace and `@` (emails).\n *\n * Runs on uncontrolled typeahead input, so it is deliberately ReDoS-safe: the\n * input length is bounded up front (a domain is <=253 chars), the host is\n * extracted with plain string ops (no scheme/path/trailing-dot regex), and each\n * label is validated by a single anchored, bounded pattern. There is no\n * ambiguous or nested quantifier anywhere, so matching is linear in the input.\n */\n\n/** Max DNS name length; anything longer is rejected before any matching. */\nconst MAX_DOMAIN_LEN = 253;\n\n/**\n * One DNS label: 1-63 chars, alphanumeric, internal hyphens allowed. Anchored\n * and length-bounded, with a single optional group and no outer quantifier, so\n * it cannot backtrack polynomially.\n */\nconst LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;\n\n/** Final label must be TLD-shaped: 2-63 alphabetic chars. */\nconst TLD = /^[a-z]{2,63}$/;\n\n/** Strip trailing dots without a regex (avoids the `\\.+$` polynomial pattern). */\nfunction stripTrailingDots(s: string): string {\n let end = s.length;\n while (end > 0 && s.charCodeAt(end - 1) === 0x2e /* '.' */) end -= 1;\n return s.slice(0, end);\n}\n\nexport function looksLikeDomain(query: string): boolean {\n const q = query.trim().toLowerCase();\n // Bound the input BEFORE any matching. A domain is <=253 chars; a longer\n // typeahead value is never a domain, and this caps every check below to a\n // constant amount of work (ReDoS guard).\n if (!q || q.length > MAX_DOMAIN_LEN) return false;\n if (q.includes(' ') || q.includes('\\t') || q.includes('@')) return false;\n\n // Extract the host with plain string ops -- no backtracking regex on input.\n let host = q;\n const schemeAt = host.indexOf('://');\n if (schemeAt !== -1) host = host.slice(schemeAt + 3);\n // Cut at the first path / query / fragment delimiter.\n for (let i = 0; i < host.length; i += 1) {\n const ch = host[i];\n if (ch === '/' || ch === '?' || ch === '#') {\n host = host.slice(0, i);\n break;\n }\n }\n if (host.startsWith('www.')) host = host.slice(4);\n // Strip a trailing :port.\n const colonAt = host.lastIndexOf(':');\n if (colonAt !== -1 && /^[0-9]{1,5}$/.test(host.slice(colonAt + 1))) {\n host = host.slice(0, colonAt);\n }\n host = stripTrailingDots(host);\n if (!host || host.length > MAX_DOMAIN_LEN) return false;\n\n // Validate each label independently (split first, so no quantifier spans dots).\n const labels = host.split('.');\n if (labels.length < 2) return false;\n const tld = labels[labels.length - 1];\n if (tld === undefined || !TLD.test(tld)) return false;\n for (let i = 0; i < labels.length - 1; i += 1) {\n if (!LABEL.test(labels[i] as string)) return false;\n }\n return true;\n}\n"],"mappings":";AAoBA,IAAM,iBAAiB;AAOvB,IAAM,QAAQ;AAGd,IAAM,MAAM;AAGZ,SAAS,kBAAkB,GAAmB;AAC5C,MAAI,MAAM,EAAE;AACZ,SAAO,MAAM,KAAK,EAAE,WAAW,MAAM,CAAC,MAAM,GAAgB,QAAO;AACnE,SAAO,EAAE,MAAM,GAAG,GAAG;AACvB;AAEO,SAAS,gBAAgB,OAAwB;AACtD,QAAM,IAAI,MAAM,KAAK,EAAE,YAAY;AAInC,MAAI,CAAC,KAAK,EAAE,SAAS,eAAgB,QAAO;AAC5C,MAAI,EAAE,SAAS,GAAG,KAAK,EAAE,SAAS,GAAI,KAAK,EAAE,SAAS,GAAG,EAAG,QAAO;AAGnE,MAAI,OAAO;AACX,QAAM,WAAW,KAAK,QAAQ,KAAK;AACnC,MAAI,aAAa,GAAI,QAAO,KAAK,MAAM,WAAW,CAAC;AAEnD,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GAAG;AACvC,UAAM,KAAK,KAAK,CAAC;AACjB,QAAI,OAAO,OAAO,OAAO,OAAO,OAAO,KAAK;AAC1C,aAAO,KAAK,MAAM,GAAG,CAAC;AACtB;AAAA,IACF;AAAA,EACF;AACA,MAAI,KAAK,WAAW,MAAM,EAAG,QAAO,KAAK,MAAM,CAAC;AAEhD,QAAM,UAAU,KAAK,YAAY,GAAG;AACpC,MAAI,YAAY,MAAM,eAAe,KAAK,KAAK,MAAM,UAAU,CAAC,CAAC,GAAG;AAClE,WAAO,KAAK,MAAM,GAAG,OAAO;AAAA,EAC9B;AACA,SAAO,kBAAkB,IAAI;AAC7B,MAAI,CAAC,QAAQ,KAAK,SAAS,eAAgB,QAAO;AAGlD,QAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,MAAI,OAAO,SAAS,EAAG,QAAO;AAC9B,QAAM,MAAM,OAAO,OAAO,SAAS,CAAC;AACpC,MAAI,QAAQ,UAAa,CAAC,IAAI,KAAK,GAAG,EAAG,QAAO;AAChD,WAAS,IAAI,GAAG,IAAI,OAAO,SAAS,GAAG,KAAK,GAAG;AAC7C,QAAI,CAAC,MAAM,KAAK,OAAO,CAAC,CAAW,EAAG,QAAO;AAAA,EAC/C;AACA,SAAO;AACT;","names":[]}
@@ -0,0 +1,24 @@
1
+ // src/logic/entity-disambiguation.ts
2
+ function entityDisambiguationLabel(fields) {
3
+ const parts = [];
4
+ if (fields.domain) parts.push(fields.domain);
5
+ if (fields.country) parts.push(fields.country);
6
+ if (fields.parentName) parts.push(`part of ${fields.parentName}`);
7
+ return parts.join(" \xB7 ");
8
+ }
9
+ function searchResultDisambiguation(result) {
10
+ return entityDisambiguationLabel({
11
+ domain: result.domain,
12
+ country: result.country,
13
+ parentName: result.parentName
14
+ });
15
+ }
16
+ function entityResultKey(result) {
17
+ return result.source === "entity" ? `entity:${result.entityId}` : `pdl:${result.pdlId}`;
18
+ }
19
+ export {
20
+ entityDisambiguationLabel,
21
+ entityResultKey,
22
+ searchResultDisambiguation
23
+ };
24
+ //# sourceMappingURL=entity-disambiguation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/logic/entity-disambiguation.ts"],"sourcesContent":["import type { EntitySearchResult } from '../schemas/entity.js';\n\n/**\n * Disambiguation-display helpers for the org typeahead (#159, Flow 1A-2). Two\n * same-name orgs must be told apart at a glance, so each row shows its domain,\n * country, and parent where known.\n */\n\nexport interface DisambiguationFields {\n domain?: string | null;\n country?: string | null;\n parentName?: string | null;\n}\n\n/**\n * Build the muted secondary line for a typeahead row, e.g.\n * `spryker.com · DE · part of Spryker Holding`. Empty when nothing to show.\n */\nexport function entityDisambiguationLabel(fields: DisambiguationFields): string {\n const parts: string[] = [];\n if (fields.domain) parts.push(fields.domain);\n if (fields.country) parts.push(fields.country);\n if (fields.parentName) parts.push(`part of ${fields.parentName}`);\n return parts.join(' · ');\n}\n\n/** Convenience over {@link entityDisambiguationLabel} for a search result row. */\nexport function searchResultDisambiguation(result: EntitySearchResult): string {\n return entityDisambiguationLabel({\n domain: result.domain,\n country: result.country,\n parentName: result.parentName,\n });\n}\n\n/** Stable per-row identity for React keys and dedupe (entity id or PDL id). */\nexport function entityResultKey(result: EntitySearchResult): string {\n return result.source === 'entity' ? `entity:${result.entityId}` : `pdl:${result.pdlId}`;\n}\n"],"mappings":";AAkBO,SAAS,0BAA0B,QAAsC;AAC9E,QAAM,QAAkB,CAAC;AACzB,MAAI,OAAO,OAAQ,OAAM,KAAK,OAAO,MAAM;AAC3C,MAAI,OAAO,QAAS,OAAM,KAAK,OAAO,OAAO;AAC7C,MAAI,OAAO,WAAY,OAAM,KAAK,WAAW,OAAO,UAAU,EAAE;AAChE,SAAO,MAAM,KAAK,QAAK;AACzB;AAGO,SAAS,2BAA2B,QAAoC;AAC7E,SAAO,0BAA0B;AAAA,IAC/B,QAAQ,OAAO;AAAA,IACf,SAAS,OAAO;AAAA,IAChB,YAAY,OAAO;AAAA,EACrB,CAAC;AACH;AAGO,SAAS,gBAAgB,QAAoC;AAClE,SAAO,OAAO,WAAW,WAAW,UAAU,OAAO,QAAQ,KAAK,OAAO,OAAO,KAAK;AACvF;","names":[]}
@@ -0,0 +1,33 @@
1
+ // src/logic/entity-ref-anchor.ts
2
+ var ENTITY_REF_ANCHORS = ["registry", "sifa", "unlinked"];
3
+ var REGISTRY_HOSTS = /* @__PURE__ */ new Set([
4
+ "wikidata.org",
5
+ "www.wikidata.org",
6
+ "ror.org",
7
+ "gleif.org",
8
+ "www.gleif.org"
9
+ ]);
10
+ var SIFA_HOST = "sifa.id";
11
+ function classifyEntityRef(entityRef) {
12
+ if (!entityRef || !entityRef.trim()) return "unlinked";
13
+ let url;
14
+ try {
15
+ url = new URL(entityRef.trim());
16
+ } catch {
17
+ return "unlinked";
18
+ }
19
+ if (url.protocol !== "https:" && url.protocol !== "http:") return "unlinked";
20
+ const host = url.hostname.toLowerCase();
21
+ if (REGISTRY_HOSTS.has(host)) return "registry";
22
+ if (host === SIFA_HOST) return "sifa";
23
+ return "unlinked";
24
+ }
25
+ function isLinked(entityRef) {
26
+ return classifyEntityRef(entityRef) !== "unlinked";
27
+ }
28
+ export {
29
+ ENTITY_REF_ANCHORS,
30
+ classifyEntityRef,
31
+ isLinked
32
+ };
33
+ //# sourceMappingURL=entity-ref-anchor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/logic/entity-ref-anchor.ts"],"sourcesContent":["/**\n * Anchor-quality classification for a position's `entityRef` pointer (#159).\n *\n * A durable company link tiers by the quality of its anchor, which drives the\n * link indicator in the UI (registry-backed reads stronger than a Sifa-scoped\n * PDL pointer, per the ratified D5 chip policy). The classification is derived\n * purely from the ref URI's host, so it is a shared, cross-platform predicate\n * (web today, the native app later).\n *\n * registry -- a portable external id any atproto app can resolve\n * (Wikidata / ROR / GLEIF). Renders as a full \"Linked\" indicator.\n * sifa -- a Sifa-scoped `sifa.id/company/<publicId>` pointer for a\n * PDL-only company. Renders as a muted \"Linked\" (no glyph).\n * unlinked -- no ref, a malformed URL, or an unrecognized host. Free text.\n *\n * NEVER surfaced as \"verified\": a link is not a verification.\n */\nexport const ENTITY_REF_ANCHORS = ['registry', 'sifa', 'unlinked'] as const;\nexport type EntityRefAnchor = (typeof ENTITY_REF_ANCHORS)[number];\n\n/** Hosts of portable, app-neutral company registries. */\nconst REGISTRY_HOSTS = new Set([\n 'wikidata.org',\n 'www.wikidata.org',\n 'ror.org',\n 'gleif.org',\n 'www.gleif.org',\n]);\n\nconst SIFA_HOST = 'sifa.id';\n\n/**\n * Classify an entityRef by anchor quality. Uses strict host parsing (never\n * substring matching) so a hostile URL that merely contains a registry host in a\n * query string is treated as unlinked, not falsely linked.\n */\nexport function classifyEntityRef(entityRef: string | null | undefined): EntityRefAnchor {\n if (!entityRef || !entityRef.trim()) return 'unlinked';\n let url: URL;\n try {\n url = new URL(entityRef.trim());\n } catch {\n return 'unlinked';\n }\n if (url.protocol !== 'https:' && url.protocol !== 'http:') return 'unlinked';\n const host = url.hostname.toLowerCase();\n if (REGISTRY_HOSTS.has(host)) return 'registry';\n if (host === SIFA_HOST) return 'sifa';\n return 'unlinked';\n}\n\n/** Whether a position carries a durable link of any anchor quality. */\nexport function isLinked(entityRef: string | null | undefined): boolean {\n return classifyEntityRef(entityRef) !== 'unlinked';\n}\n"],"mappings":";AAiBO,IAAM,qBAAqB,CAAC,YAAY,QAAQ,UAAU;AAIjE,IAAM,iBAAiB,oBAAI,IAAI;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,IAAM,YAAY;AAOX,SAAS,kBAAkB,WAAuD;AACvF,MAAI,CAAC,aAAa,CAAC,UAAU,KAAK,EAAG,QAAO;AAC5C,MAAI;AACJ,MAAI;AACF,UAAM,IAAI,IAAI,UAAU,KAAK,CAAC;AAAA,EAChC,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,IAAI,aAAa,YAAY,IAAI,aAAa,QAAS,QAAO;AAClE,QAAM,OAAO,IAAI,SAAS,YAAY;AACtC,MAAI,eAAe,IAAI,IAAI,EAAG,QAAO;AACrC,MAAI,SAAS,UAAW,QAAO;AAC/B,SAAO;AACT;AAGO,SAAS,SAAS,WAA+C;AACtE,SAAO,kBAAkB,SAAS,MAAM;AAC1C;","names":[]}
@@ -0,0 +1,91 @@
1
+ // src/logic/index.ts
2
+ import {
3
+ COMPLETENESS_MAX_SCORE,
4
+ completenessPercent,
5
+ completenessScore
6
+ } from "./profile-completeness.js";
7
+ import {
8
+ DIMENSIONS_MAX_SCORE,
9
+ MIN_SKILLS,
10
+ countFilledDimensions,
11
+ dimensionsFromInputs,
12
+ getFilledDimensionsMap,
13
+ profileToDimensionInputs
14
+ } from "./profile-dimensions.js";
15
+ import { pickPrimaryPosition } from "./primary-position.js";
16
+ import {
17
+ DATE_LOOKAHEAD_MONTHS,
18
+ isBeyondLookahead,
19
+ isUpcomingStart
20
+ } from "./date-lookahead.js";
21
+ import { pickPrimaryFlagged } from "./primary-item.js";
22
+ import {
23
+ summarizeProfileView
24
+ } from "./profile-summary.js";
25
+ import { isPseudoEmployer } from "./pseudo-employer.js";
26
+ import { looksLikeDomain } from "./domain-detect.js";
27
+ import {
28
+ entityDisambiguationLabel,
29
+ searchResultDisambiguation,
30
+ entityResultKey
31
+ } from "./entity-disambiguation.js";
32
+ import {
33
+ classifyEntityRef,
34
+ isLinked,
35
+ ENTITY_REF_ANCHORS
36
+ } from "./entity-ref-anchor.js";
37
+ import { resolveAgentRef } from "./agent-ref.js";
38
+ import {
39
+ qualifiesAsOrg,
40
+ isRegistrableDomainHandle,
41
+ hasPersonalProfileContent,
42
+ rendersPersonalProfile,
43
+ rendersCompanyProfile,
44
+ resolveAccountFacetMode
45
+ } from "./org-floor.js";
46
+ import {
47
+ isCompanyPageIndexable,
48
+ COMPANY_PAGE_MIN_FIRMOGRAPHIC_FIELDS
49
+ } from "./company-page-indexable.js";
50
+ import { countRecentActivity } from "./recent-activity.js";
51
+ import { SELF_APP_ID, excludeSelfApp } from "./self-app.js";
52
+ import { actorShowsIdentity } from "./actor-identity.js";
53
+ export {
54
+ COMPANY_PAGE_MIN_FIRMOGRAPHIC_FIELDS,
55
+ COMPLETENESS_MAX_SCORE,
56
+ DATE_LOOKAHEAD_MONTHS,
57
+ DIMENSIONS_MAX_SCORE,
58
+ ENTITY_REF_ANCHORS,
59
+ MIN_SKILLS,
60
+ SELF_APP_ID,
61
+ actorShowsIdentity,
62
+ classifyEntityRef,
63
+ completenessPercent,
64
+ completenessScore,
65
+ countFilledDimensions,
66
+ countRecentActivity,
67
+ dimensionsFromInputs,
68
+ entityDisambiguationLabel,
69
+ entityResultKey,
70
+ excludeSelfApp,
71
+ getFilledDimensionsMap,
72
+ hasPersonalProfileContent,
73
+ isBeyondLookahead,
74
+ isCompanyPageIndexable,
75
+ isLinked,
76
+ isPseudoEmployer,
77
+ isRegistrableDomainHandle,
78
+ isUpcomingStart,
79
+ looksLikeDomain,
80
+ pickPrimaryFlagged,
81
+ pickPrimaryPosition,
82
+ profileToDimensionInputs,
83
+ qualifiesAsOrg,
84
+ rendersCompanyProfile,
85
+ rendersPersonalProfile,
86
+ resolveAccountFacetMode,
87
+ resolveAgentRef,
88
+ searchResultDisambiguation,
89
+ summarizeProfileView
90
+ };
91
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/logic/index.ts"],"sourcesContent":["export {\n COMPLETENESS_MAX_SCORE,\n completenessPercent,\n completenessScore,\n type ProfileCompletion,\n} from './profile-completeness.js';\n\nexport {\n DIMENSIONS_MAX_SCORE,\n MIN_SKILLS,\n countFilledDimensions,\n dimensionsFromInputs,\n getFilledDimensionsMap,\n profileToDimensionInputs,\n type DimensionKey,\n type DimensionMap,\n type ProfileDimensionInputs,\n} from './profile-dimensions.js';\n\nexport { pickPrimaryPosition, type PrimaryPositionCandidate } from './primary-position.js';\n\nexport {\n DATE_LOOKAHEAD_MONTHS,\n isBeyondLookahead,\n isUpcomingStart,\n type DateLookaheadKind,\n} from './date-lookahead.js';\n\nexport { pickPrimaryFlagged, type PrimaryFlagCandidate } from './primary-item.js';\n\nexport {\n summarizeProfileView,\n type ProfileSummary,\n type SummarizeProfileViewOptions,\n} from './profile-summary.js';\n\nexport { isPseudoEmployer } from './pseudo-employer.js';\nexport { looksLikeDomain } from './domain-detect.js';\nexport {\n entityDisambiguationLabel,\n searchResultDisambiguation,\n entityResultKey,\n type DisambiguationFields,\n} from './entity-disambiguation.js';\nexport {\n classifyEntityRef,\n isLinked,\n ENTITY_REF_ANCHORS,\n type EntityRefAnchor,\n} from './entity-ref-anchor.js';\nexport { resolveAgentRef, type AgentRefFlat } from './agent-ref.js';\nexport {\n qualifiesAsOrg,\n isRegistrableDomainHandle,\n hasPersonalProfileContent,\n rendersPersonalProfile,\n rendersCompanyProfile,\n resolveAccountFacetMode,\n} from './org-floor.js';\nexport type { PersonalFacetContent } from './org-floor.js';\nexport {\n isCompanyPageIndexable,\n COMPANY_PAGE_MIN_FIRMOGRAPHIC_FIELDS,\n type CompanyFirmographics,\n} from './company-page-indexable.js';\nexport { countRecentActivity, type DailyActivityCount } from './recent-activity.js';\nexport { SELF_APP_ID, excludeSelfApp, type AppWithId } from './self-app.js';\nexport { actorShowsIdentity } from './actor-identity.js';\n"],"mappings":";AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAEP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AAEP,SAAS,2BAA0D;AAEnE;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAEP,SAAS,0BAAqD;AAE9D;AAAA,EACE;AAAA,OAGK;AAEP,SAAS,wBAAwB;AACjC,SAAS,uBAAuB;AAChC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,uBAA0C;AACnD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AACP,SAAS,2BAAoD;AAC7D,SAAS,aAAa,sBAAsC;AAC5D,SAAS,0BAA0B;","names":[]}
@@ -0,0 +1,66 @@
1
+ // src/logic/org-floor.ts
2
+ import { looksLikeDomain } from "./domain-detect.js";
3
+ var KNOWN_HANDLE_HOST_SUFFIXES = [
4
+ ".bsky.social",
5
+ ".blacksky.app",
6
+ ".eurosky.social",
7
+ ".northsky.social"
8
+ ];
9
+ function isRegistrableDomainHandle(handle) {
10
+ const h = handle.trim().toLowerCase();
11
+ if (!h) return false;
12
+ if (h.startsWith("did:")) return false;
13
+ if (!looksLikeDomain(h)) return false;
14
+ for (const suffix of KNOWN_HANDLE_HOST_SUFFIXES) {
15
+ if (h.endsWith(suffix)) return false;
16
+ }
17
+ return true;
18
+ }
19
+ function qualifiesAsOrg(record, handle) {
20
+ if (record === null || record === void 0) return false;
21
+ return isRegistrableDomainHandle(handle);
22
+ }
23
+ function hasPersonalProfileContent(profile) {
24
+ const filled = (text) => typeof text === "string" && text.trim() !== "";
25
+ if (filled(profile.headline) || filled(profile.about)) return true;
26
+ const sections = [
27
+ profile.positions,
28
+ profile.education,
29
+ profile.skills,
30
+ profile.certifications,
31
+ profile.projects,
32
+ profile.publications,
33
+ profile.volunteering,
34
+ profile.involvement,
35
+ profile.honors,
36
+ profile.courses
37
+ ];
38
+ return sections.some((section) => Array.isArray(section) && section.length > 0);
39
+ }
40
+ function rendersPersonalProfile(org, renderPreference) {
41
+ if (renderPreference === "person") return true;
42
+ if (org === null || org === void 0) return true;
43
+ if (!org.isOrg && !org.recognized) return true;
44
+ return org.isOrg && org.personalProfileVisible === true;
45
+ }
46
+ function rendersCompanyProfile(org, renderPreference) {
47
+ if (renderPreference === "person") return false;
48
+ if (org === null || org === void 0) return false;
49
+ return org.isOrg || org.recognized;
50
+ }
51
+ function resolveAccountFacetMode(profile) {
52
+ if (profile.renderPreference === "person") return "person";
53
+ const org = profile.org;
54
+ if (org?.isOrg && org.personalProfileVisible === true) return "both";
55
+ if (org?.isOrg || org?.recognized || profile.renderPreference === "company") return "company";
56
+ return "person";
57
+ }
58
+ export {
59
+ hasPersonalProfileContent,
60
+ isRegistrableDomainHandle,
61
+ qualifiesAsOrg,
62
+ rendersCompanyProfile,
63
+ rendersPersonalProfile,
64
+ resolveAccountFacetMode
65
+ };
66
+ //# sourceMappingURL=org-floor.js.map