@singi-labs/sifa-sdk 0.19.44 → 0.19.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/atproto/index.js +9 -3
- package/dist/atproto/index.js.map +1 -1
- package/dist/badge/index.js +5 -141
- package/dist/badge/index.js.map +1 -1
- package/dist/cards/adult-content.js +14 -0
- package/dist/cards/adult-content.js.map +1 -0
- package/dist/cards/app-url-patterns.js +369 -0
- package/dist/cards/app-url-patterns.js.map +1 -0
- package/dist/cards/index.js +24 -0
- package/dist/cards/index.js.map +1 -0
- package/dist/cards/resolve-card-url.js +198 -0
- package/dist/cards/resolve-card-url.js.map +1 -0
- package/dist/cards/visibility.js +45 -0
- package/dist/cards/visibility.js.map +1 -0
- package/dist/flags/index.js +4 -3
- package/dist/flags/index.js.map +1 -1
- package/dist/format/collapse-contributors.js +19 -0
- package/dist/format/collapse-contributors.js.map +1 -0
- package/dist/format/company-name.js +61 -0
- package/dist/format/company-name.js.map +1 -0
- package/dist/format/compare-text.js +9 -0
- package/dist/format/compare-text.js.map +1 -0
- package/dist/format/doi.js +8 -0
- package/dist/format/doi.js.map +1 -0
- package/dist/format/embed.js +131 -0
- package/dist/format/embed.js.map +1 -0
- package/dist/format/endorsement-context.js +35 -0
- package/dist/format/endorsement-context.js.map +1 -0
- package/dist/format/format-display-url.js +42 -0
- package/dist/format/format-display-url.js.map +1 -0
- package/dist/format/format-duration.js +13 -0
- package/dist/format/format-duration.js.map +1 -0
- package/dist/format/format-time.js +30 -0
- package/dist/format/format-time.js.map +1 -0
- package/dist/format/handle-utils.js +19 -0
- package/dist/format/handle-utils.js.map +1 -0
- package/dist/format/index.js +100 -0
- package/dist/format/index.js.map +1 -0
- package/dist/format/location-segments.js +29 -0
- package/dist/format/location-segments.js.map +1 -0
- package/dist/format/location-utils.js +42 -0
- package/dist/format/location-utils.js.map +1 -0
- package/dist/format/meta-description.js +35 -0
- package/dist/format/meta-description.js.map +1 -0
- package/dist/format/normalize-company-key.js +12 -0
- package/dist/format/normalize-company-key.js.map +1 -0
- package/dist/format/normalize-legal-form.js +71 -0
- package/dist/format/normalize-legal-form.js.map +1 -0
- package/dist/format/pds-utils.js +73 -0
- package/dist/format/pds-utils.js.map +1 -0
- package/dist/format/sort-by-date.js +39 -0
- package/dist/format/sort-by-date.js.map +1 -0
- package/dist/format/summarize-deliveries.js +37 -0
- package/dist/format/summarize-deliveries.js.map +1 -0
- package/dist/format/talk-slug.js +21 -0
- package/dist/format/talk-slug.js.map +1 -0
- package/dist/format/text-sanitize.js +29 -0
- package/dist/format/text-sanitize.js.map +1 -0
- package/dist/format/text-truncate.js +14 -0
- package/dist/format/text-truncate.js.map +1 -0
- package/dist/format/time-utils.js +19 -0
- package/dist/format/time-utils.js.map +1 -0
- package/dist/format/timeline.js +29 -0
- package/dist/format/timeline.js.map +1 -0
- package/dist/format/wcag-contrast.js +34 -0
- package/dist/format/wcag-contrast.js.map +1 -0
- package/dist/import/index.js +22 -0
- package/dist/import/index.js.map +1 -0
- package/dist/import/presentation-csv.js +116 -0
- package/dist/import/presentation-csv.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +675 -7086
- package/dist/index.js.map +1 -1
- package/dist/jev/index.js +18 -5
- package/dist/jev/index.js.map +1 -1
- package/dist/jsonld/index.js +30 -987
- package/dist/jsonld/index.js.map +1 -1
- package/dist/jsonld/profile.js +222 -0
- package/dist/jsonld/profile.js.map +1 -0
- package/dist/jsonld/terms.js +496 -0
- package/dist/jsonld/terms.js.map +1 -0
- package/dist/jsonld/url.js +17 -0
- package/dist/jsonld/url.js.map +1 -0
- package/dist/jsonld/works-graph.js +41 -0
- package/dist/jsonld/works-graph.js.map +1 -0
- package/dist/jsonld/works.js +178 -0
- package/dist/jsonld/works.js.map +1 -0
- package/dist/logic/actor-identity.js +8 -0
- package/dist/logic/actor-identity.js.map +1 -0
- package/dist/logic/agent-ref.js +24 -0
- package/dist/logic/agent-ref.js.map +1 -0
- package/dist/logic/company-page-indexable.js +23 -0
- package/dist/logic/company-page-indexable.js.map +1 -0
- package/dist/logic/date-lookahead.js +31 -0
- package/dist/logic/date-lookahead.js.map +1 -0
- package/dist/logic/domain-detect.js +43 -0
- package/dist/logic/domain-detect.js.map +1 -0
- package/dist/logic/entity-disambiguation.js +24 -0
- package/dist/logic/entity-disambiguation.js.map +1 -0
- package/dist/logic/entity-ref-anchor.js +33 -0
- package/dist/logic/entity-ref-anchor.js.map +1 -0
- package/dist/logic/index.js +91 -0
- package/dist/logic/index.js.map +1 -0
- package/dist/logic/org-floor.js +66 -0
- package/dist/logic/org-floor.js.map +1 -0
- package/dist/logic/primary-item.js +9 -0
- package/dist/logic/primary-item.js.map +1 -0
- package/dist/logic/primary-position.js +16 -0
- package/dist/logic/primary-position.js.map +1 -0
- package/dist/logic/profile-completeness.js +21 -0
- package/dist/logic/profile-completeness.js.map +1 -0
- package/dist/logic/profile-dimensions.js +50 -0
- package/dist/logic/profile-dimensions.js.map +1 -0
- package/dist/logic/profile-summary.js +23 -0
- package/dist/logic/profile-summary.js.map +1 -0
- package/dist/logic/pseudo-employer.js +34 -0
- package/dist/logic/pseudo-employer.js.map +1 -0
- package/dist/logic/recent-activity.js +10 -0
- package/dist/logic/recent-activity.js.map +1 -0
- package/dist/logic/self-app.js +10 -0
- package/dist/logic/self-app.js.map +1 -0
- package/dist/{org-BCrvebcf.d.ts → org-CxTEjjrm.d.ts} +1 -1
- package/dist/{org-settings-C73Sin5E.d.ts → org-settings-BShvhyFp.d.ts} +1 -1
- package/dist/profile/headline-role-dedupe.js +12 -0
- package/dist/profile/headline-role-dedupe.js.map +1 -0
- package/dist/profile/highlights.js +200 -0
- package/dist/profile/highlights.js.map +1 -0
- package/dist/profile/index.js +66 -0
- package/dist/profile/index.js.map +1 -0
- package/dist/profile/involvement-grouping.js +31 -0
- package/dist/profile/involvement-grouping.js.map +1 -0
- package/dist/profile/language-sort.js +24 -0
- package/dist/profile/language-sort.js.map +1 -0
- package/dist/profile/publication-versions.js +98 -0
- package/dist/profile/publication-versions.js.map +1 -0
- package/dist/profile/range-sort.js +14 -0
- package/dist/profile/range-sort.js.map +1 -0
- package/dist/profile/section-model.js +122 -0
- package/dist/profile/section-model.js.map +1 -0
- package/dist/profile/section-sorts.js +33 -0
- package/dist/profile/section-sorts.js.map +1 -0
- package/dist/publishers/index.js +16 -0
- package/dist/publishers/index.js.map +1 -0
- package/dist/publishers/registry.js +87 -0
- package/dist/publishers/registry.js.map +1 -0
- package/dist/publishing/index.js +46 -184
- package/dist/publishing/index.js.map +1 -1
- package/dist/publishing/linkblog.js +42 -0
- package/dist/publishing/linkblog.js.map +1 -0
- package/dist/publishing/registry.js +32 -0
- package/dist/publishing/registry.js.map +1 -0
- package/dist/publishing/schemas.js +74 -0
- package/dist/publishing/schemas.js.map +1 -0
- package/dist/publishing/types.js +32 -0
- package/dist/publishing/types.js.map +1 -0
- package/dist/query/client.js +119 -0
- package/dist/query/client.js.map +1 -0
- package/dist/query/config.js +23 -0
- package/dist/query/config.js.map +1 -0
- package/dist/query/fetchers/accounts.js +29 -0
- package/dist/query/fetchers/accounts.js.map +1 -0
- package/dist/query/fetchers/activity-items-hide.js +31 -0
- package/dist/query/fetchers/activity-items-hide.js.map +1 -0
- package/dist/query/fetchers/activity.js +61 -0
- package/dist/query/fetchers/activity.js.map +1 -0
- package/dist/query/fetchers/actor.js +30 -0
- package/dist/query/fetchers/actor.js.map +1 -0
- package/dist/query/fetchers/admin-feature-allowlists.js +41 -0
- package/dist/query/fetchers/admin-feature-allowlists.js.map +1 -0
- package/dist/query/fetchers/admin-review-queues.js +30 -0
- package/dist/query/fetchers/admin-review-queues.js.map +1 -0
- package/dist/query/fetchers/apps.js +31 -0
- package/dist/query/fetchers/apps.js.map +1 -0
- package/dist/query/fetchers/bsky-preferences.js +55 -0
- package/dist/query/fetchers/bsky-preferences.js.map +1 -0
- package/dist/query/fetchers/confirmations.js +55 -0
- package/dist/query/fetchers/confirmations.js.map +1 -0
- package/dist/query/fetchers/destructive.js +34 -0
- package/dist/query/fetchers/destructive.js.map +1 -0
- package/dist/query/fetchers/discovery.js +67 -0
- package/dist/query/fetchers/discovery.js.map +1 -0
- package/dist/query/fetchers/education.js +23 -0
- package/dist/query/fetchers/education.js.map +1 -0
- package/dist/query/fetchers/endorsement-inbox.js +36 -0
- package/dist/query/fetchers/endorsement-inbox.js.map +1 -0
- package/dist/query/fetchers/endorsement.js +23 -0
- package/dist/query/fetchers/endorsement.js.map +1 -0
- package/dist/query/fetchers/endorsements.js +20 -0
- package/dist/query/fetchers/endorsements.js.map +1 -0
- package/dist/query/fetchers/entities.js +65 -0
- package/dist/query/fetchers/entities.js.map +1 -0
- package/dist/query/fetchers/external-accounts.js +74 -0
- package/dist/query/fetchers/external-accounts.js.map +1 -0
- package/dist/query/fetchers/follow-extras.js +38 -0
- package/dist/query/fetchers/follow-extras.js.map +1 -0
- package/dist/query/fetchers/follow.js +105 -0
- package/dist/query/fetchers/follow.js.map +1 -0
- package/dist/query/fetchers/get-profile-view.js +21 -0
- package/dist/query/fetchers/get-profile-view.js.map +1 -0
- package/dist/query/fetchers/github-prs.js +45 -0
- package/dist/query/fetchers/github-prs.js.map +1 -0
- package/dist/query/fetchers/inbox.js +29 -0
- package/dist/query/fetchers/inbox.js.map +1 -0
- package/dist/query/fetchers/index.d.ts +2 -2
- package/dist/query/fetchers/index.js +360 -2045
- package/dist/query/fetchers/index.js.map +1 -1
- package/dist/query/fetchers/investments.js +18 -0
- package/dist/query/fetchers/investments.js.map +1 -0
- package/dist/query/fetchers/keytrace-claims.js +23 -0
- package/dist/query/fetchers/keytrace-claims.js.map +1 -0
- package/dist/query/fetchers/marque-domains.js +23 -0
- package/dist/query/fetchers/marque-domains.js.map +1 -0
- package/dist/query/fetchers/network-map.js +36 -0
- package/dist/query/fetchers/network-map.js.map +1 -0
- package/dist/query/fetchers/org.js +48 -0
- package/dist/query/fetchers/org.js.map +1 -0
- package/dist/query/fetchers/positions.js +77 -0
- package/dist/query/fetchers/positions.js.map +1 -0
- package/dist/query/fetchers/profile-completeness.js +45 -0
- package/dist/query/fetchers/profile-completeness.js.map +1 -0
- package/dist/query/fetchers/profile-items-hide.js +36 -0
- package/dist/query/fetchers/profile-items-hide.js.map +1 -0
- package/dist/query/fetchers/profile-locations.js +23 -0
- package/dist/query/fetchers/profile-locations.js.map +1 -0
- package/dist/query/fetchers/profile-mutations.js +82 -0
- package/dist/query/fetchers/profile-mutations.js.map +1 -0
- package/dist/query/fetchers/profile-summary.js +14 -0
- package/dist/query/fetchers/profile-summary.js.map +1 -0
- package/dist/query/fetchers/profile.js +31 -0
- package/dist/query/fetchers/profile.js.map +1 -0
- package/dist/query/fetchers/publications.js +66 -0
- package/dist/query/fetchers/publications.js.map +1 -0
- package/dist/query/fetchers/quoted-posts.js +40 -0
- package/dist/query/fetchers/quoted-posts.js.map +1 -0
- package/dist/query/fetchers/reactions.js +85 -0
- package/dist/query/fetchers/reactions.js.map +1 -0
- package/dist/query/fetchers/received-endorsements.js +20 -0
- package/dist/query/fetchers/received-endorsements.js.map +1 -0
- package/dist/query/fetchers/reciprocity.js +23 -0
- package/dist/query/fetchers/reciprocity.js.map +1 -0
- package/dist/query/fetchers/records.js +27 -0
- package/dist/query/fetchers/records.js.map +1 -0
- package/dist/query/fetchers/repo-inventory.js +23 -0
- package/dist/query/fetchers/repo-inventory.js.map +1 -0
- package/dist/query/fetchers/roadmap.js +73 -0
- package/dist/query/fetchers/roadmap.js.map +1 -0
- package/dist/query/fetchers/rpg.js +36 -0
- package/dist/query/fetchers/rpg.js.map +1 -0
- package/dist/query/fetchers/search.js +78 -0
- package/dist/query/fetchers/search.js.map +1 -0
- package/dist/query/fetchers/section-primary.js +23 -0
- package/dist/query/fetchers/section-primary.js.map +1 -0
- package/dist/query/fetchers/skills.js +30 -0
- package/dist/query/fetchers/skills.js.map +1 -0
- package/dist/query/fetchers/speakers.js +42 -0
- package/dist/query/fetchers/speakers.js.map +1 -0
- package/dist/query/fetchers/stats.js +16 -0
- package/dist/query/fetchers/stats.js.map +1 -0
- package/dist/query/fetchers/stream.js +30 -0
- package/dist/query/fetchers/stream.js.map +1 -0
- package/dist/query/fetchers/unlinked-positions.js +30 -0
- package/dist/query/fetchers/unlinked-positions.js.map +1 -0
- package/dist/query/hooks/index.d.ts +3 -3
- package/dist/query/hooks/index.js +283 -3555
- package/dist/query/hooks/index.js.map +1 -1
- package/dist/query/hooks/use-accounts.js +19 -0
- package/dist/query/hooks/use-accounts.js.map +1 -0
- package/dist/query/hooks/use-activity-items-hide.js +52 -0
- package/dist/query/hooks/use-activity-items-hide.js.map +1 -0
- package/dist/query/hooks/use-activity.js +44 -0
- package/dist/query/hooks/use-activity.js.map +1 -0
- package/dist/query/hooks/use-admin-review-queues.js +21 -0
- package/dist/query/hooks/use-admin-review-queues.js.map +1 -0
- package/dist/query/hooks/use-apps.js +31 -0
- package/dist/query/hooks/use-apps.js.map +1 -0
- package/dist/query/hooks/use-bsky-content-prefs.js +45 -0
- package/dist/query/hooks/use-bsky-content-prefs.js.map +1 -0
- package/dist/query/hooks/use-confirmations.js +121 -0
- package/dist/query/hooks/use-confirmations.js.map +1 -0
- package/dist/query/hooks/use-create-position.js +28 -0
- package/dist/query/hooks/use-create-position.js.map +1 -0
- package/dist/query/hooks/use-destructive.js +63 -0
- package/dist/query/hooks/use-destructive.js.map +1 -0
- package/dist/query/hooks/use-discovery.js +53 -0
- package/dist/query/hooks/use-discovery.js.map +1 -0
- package/dist/query/hooks/use-education-mutations.js +67 -0
- package/dist/query/hooks/use-education-mutations.js.map +1 -0
- package/dist/query/hooks/use-endorsement-inbox.js +80 -0
- package/dist/query/hooks/use-endorsement-inbox.js.map +1 -0
- package/dist/query/hooks/use-endorsement-mutations.js +33 -0
- package/dist/query/hooks/use-endorsement-mutations.js.map +1 -0
- package/dist/query/hooks/use-endorsement.js +20 -0
- package/dist/query/hooks/use-endorsement.js.map +1 -0
- package/dist/query/hooks/use-entity-search.js +73 -0
- package/dist/query/hooks/use-entity-search.js.map +1 -0
- package/dist/query/hooks/use-external-accounts.js +143 -0
- package/dist/query/hooks/use-external-accounts.js.map +1 -0
- package/dist/query/hooks/use-feature-allowlist.js +104 -0
- package/dist/query/hooks/use-feature-allowlist.js.map +1 -0
- package/dist/query/hooks/use-follow-extras.js +38 -0
- package/dist/query/hooks/use-follow-extras.js.map +1 -0
- package/dist/query/hooks/use-follow.js +113 -0
- package/dist/query/hooks/use-follow.js.map +1 -0
- package/dist/query/hooks/use-get-profile-view.js +20 -0
- package/dist/query/hooks/use-get-profile-view.js.map +1 -0
- package/dist/query/hooks/use-github-prs.js +21 -0
- package/dist/query/hooks/use-github-prs.js.map +1 -0
- package/dist/query/hooks/use-inbox.js +19 -0
- package/dist/query/hooks/use-inbox.js.map +1 -0
- package/dist/query/hooks/use-investment-mutations.js +65 -0
- package/dist/query/hooks/use-investment-mutations.js.map +1 -0
- package/dist/query/hooks/use-keytrace-claims.js +50 -0
- package/dist/query/hooks/use-keytrace-claims.js.map +1 -0
- package/dist/query/hooks/use-location-mutations.js +71 -0
- package/dist/query/hooks/use-location-mutations.js.map +1 -0
- package/dist/query/hooks/use-marque-domains.js +50 -0
- package/dist/query/hooks/use-marque-domains.js.map +1 -0
- package/dist/query/hooks/use-org-claim.js +29 -0
- package/dist/query/hooks/use-org-claim.js.map +1 -0
- package/dist/query/hooks/use-org-domains.js +28 -0
- package/dist/query/hooks/use-org-domains.js.map +1 -0
- package/dist/query/hooks/use-org-notification-emails.js +28 -0
- package/dist/query/hooks/use-org-notification-emails.js.map +1 -0
- package/dist/query/hooks/use-org-profile-settings.js +29 -0
- package/dist/query/hooks/use-org-profile-settings.js.map +1 -0
- package/dist/query/hooks/use-org-profile.js +21 -0
- package/dist/query/hooks/use-org-profile.js.map +1 -0
- package/dist/query/hooks/use-position-mutations.js +128 -0
- package/dist/query/hooks/use-position-mutations.js.map +1 -0
- package/dist/query/hooks/use-profile-completeness.js +21 -0
- package/dist/query/hooks/use-profile-completeness.js.map +1 -0
- package/dist/query/hooks/use-profile-items-hide.js +53 -0
- package/dist/query/hooks/use-profile-items-hide.js.map +1 -0
- package/dist/query/hooks/use-profile-mutations.js +143 -0
- package/dist/query/hooks/use-profile-mutations.js.map +1 -0
- package/dist/query/hooks/use-profile.js +30 -0
- package/dist/query/hooks/use-profile.js.map +1 -0
- package/dist/query/hooks/use-publication-mutations.js +90 -0
- package/dist/query/hooks/use-publication-mutations.js.map +1 -0
- package/dist/query/hooks/use-reaction-mutations.js +48 -0
- package/dist/query/hooks/use-reaction-mutations.js.map +1 -0
- package/dist/query/hooks/use-reactions.js +32 -0
- package/dist/query/hooks/use-reactions.js.map +1 -0
- package/dist/query/hooks/use-received-endorsements.js +23 -0
- package/dist/query/hooks/use-received-endorsements.js.map +1 -0
- package/dist/query/hooks/use-reciprocity.js +19 -0
- package/dist/query/hooks/use-reciprocity.js.map +1 -0
- package/dist/query/hooks/use-record-mutations.js +67 -0
- package/dist/query/hooks/use-record-mutations.js.map +1 -0
- package/dist/query/hooks/use-repo-inventory.js +43 -0
- package/dist/query/hooks/use-repo-inventory.js.map +1 -0
- package/dist/query/hooks/use-roadmap-mutations.js +51 -0
- package/dist/query/hooks/use-roadmap-mutations.js.map +1 -0
- package/dist/query/hooks/use-roadmap.js +31 -0
- package/dist/query/hooks/use-roadmap.js.map +1 -0
- package/dist/query/hooks/use-search.js +63 -0
- package/dist/query/hooks/use-search.js.map +1 -0
- package/dist/query/hooks/use-section-primary-mutations.js +53 -0
- package/dist/query/hooks/use-section-primary-mutations.js.map +1 -0
- package/dist/query/hooks/use-skill-mutations.js +91 -0
- package/dist/query/hooks/use-skill-mutations.js.map +1 -0
- package/dist/query/hooks/use-stats.js +19 -0
- package/dist/query/hooks/use-stats.js.map +1 -0
- package/dist/query/hooks/use-stream.js +20 -0
- package/dist/query/hooks/use-stream.js.map +1 -0
- package/dist/query/hooks/use-unlinked-positions.js +45 -0
- package/dist/query/hooks/use-unlinked-positions.js.map +1 -0
- package/dist/query/index.d.ts +4 -4
- package/dist/query/index.js +685 -3744
- package/dist/query/index.js.map +1 -1
- package/dist/query/keys.js +138 -0
- package/dist/query/keys.js.map +1 -0
- package/dist/repo/groups.js +81 -0
- package/dist/repo/groups.js.map +1 -0
- package/dist/repo/index.js +9 -0
- package/dist/repo/index.js.map +1 -0
- package/dist/repo/labels.js +121 -0
- package/dist/repo/labels.js.map +1 -0
- package/dist/repo/types.js +1 -0
- package/dist/repo/types.js.map +1 -0
- package/dist/resume/index.js +5 -179
- package/dist/resume/index.js.map +1 -1
- package/dist/resume/json-resume.js +153 -0
- package/dist/resume/json-resume.js.map +1 -0
- package/dist/roadmap/index.js +101 -0
- package/dist/roadmap/index.js.map +1 -0
- package/dist/rpg/catalog.js +101 -0
- package/dist/rpg/catalog.js.map +1 -0
- package/dist/rpg/index.js +13 -116
- package/dist/rpg/index.js.map +1 -1
- package/dist/rpg/unlocks.js +21 -0
- package/dist/rpg/unlocks.js.map +1 -0
- package/dist/schemas/blob-ref.js +12 -0
- package/dist/schemas/blob-ref.js.map +1 -0
- package/dist/schemas/confirmation.js +23 -0
- package/dist/schemas/confirmation.js.map +1 -0
- package/dist/schemas/endorsement-confirmation.js +18 -0
- package/dist/schemas/endorsement-confirmation.js.map +1 -0
- package/dist/schemas/endorsement.js +19 -0
- package/dist/schemas/endorsement.js.map +1 -0
- package/dist/schemas/entity.js +74 -0
- package/dist/schemas/entity.js.map +1 -0
- package/dist/schemas/feed.js +73 -0
- package/dist/schemas/feed.js.map +1 -0
- package/dist/schemas/follow-profile.js +32 -0
- package/dist/schemas/follow-profile.js.map +1 -0
- package/dist/schemas/graph-follow.js +19 -0
- package/dist/schemas/graph-follow.js.map +1 -0
- package/dist/schemas/index.js +164 -629
- package/dist/schemas/index.js.map +1 -1
- package/dist/schemas/org-employment-attestation.js +28 -0
- package/dist/schemas/org-employment-attestation.js.map +1 -0
- package/dist/schemas/org-profile.js +64 -0
- package/dist/schemas/org-profile.js.map +1 -0
- package/dist/schemas/profile-certification.js +28 -0
- package/dist/schemas/profile-certification.js.map +1 -0
- package/dist/schemas/profile-course.js +32 -0
- package/dist/schemas/profile-course.js.map +1 -0
- package/dist/schemas/profile-education.js +33 -0
- package/dist/schemas/profile-education.js.map +1 -0
- package/dist/schemas/profile-external-account.js +15 -0
- package/dist/schemas/profile-external-account.js.map +1 -0
- package/dist/schemas/profile-honor.js +18 -0
- package/dist/schemas/profile-honor.js.map +1 -0
- package/dist/schemas/profile-investment.js +48 -0
- package/dist/schemas/profile-investment.js.map +1 -0
- package/dist/schemas/profile-involvement.js +47 -0
- package/dist/schemas/profile-involvement.js.map +1 -0
- package/dist/schemas/profile-language.js +12 -0
- package/dist/schemas/profile-language.js.map +1 -0
- package/dist/schemas/profile-position.js +42 -0
- package/dist/schemas/profile-position.js.map +1 -0
- package/dist/schemas/profile-presentation-delivery.js +38 -0
- package/dist/schemas/profile-presentation-delivery.js.map +1 -0
- package/dist/schemas/profile-presentation.js +36 -0
- package/dist/schemas/profile-presentation.js.map +1 -0
- package/dist/schemas/profile-project.js +48 -0
- package/dist/schemas/profile-project.js.map +1 -0
- package/dist/schemas/profile-publication.js +30 -0
- package/dist/schemas/profile-publication.js.map +1 -0
- package/dist/schemas/profile-self.js +22 -0
- package/dist/schemas/profile-self.js.map +1 -0
- package/dist/schemas/profile-skill.js +13 -0
- package/dist/schemas/profile-skill.js.map +1 -0
- package/dist/schemas/profile-volunteering.js +20 -0
- package/dist/schemas/profile-volunteering.js.map +1 -0
- package/dist/schemas/shared.js +56 -0
- package/dist/schemas/shared.js.map +1 -0
- package/dist/schemas/write/certification.js +16 -0
- package/dist/schemas/write/certification.js.map +1 -0
- package/dist/schemas/write/course.js +36 -0
- package/dist/schemas/write/course.js.map +1 -0
- package/dist/schemas/write/education.js +18 -0
- package/dist/schemas/write/education.js.map +1 -0
- package/dist/schemas/write/external-account.js +13 -0
- package/dist/schemas/write/external-account.js.map +1 -0
- package/dist/schemas/write/honor.js +14 -0
- package/dist/schemas/write/honor.js.map +1 -0
- package/dist/schemas/write/index.d.ts +1 -1
- package/dist/schemas/write/index.js +89 -593
- package/dist/schemas/write/index.js.map +1 -1
- package/dist/schemas/write/investment.js +32 -0
- package/dist/schemas/write/investment.js.map +1 -0
- package/dist/schemas/write/involvement.js +34 -0
- package/dist/schemas/write/involvement.js.map +1 -0
- package/dist/schemas/write/language.js +10 -0
- package/dist/schemas/write/language.js.map +1 -0
- package/dist/schemas/write/org-claim.js +56 -0
- package/dist/schemas/write/org-claim.js.map +1 -0
- package/dist/schemas/write/org-employment-attestation.js +19 -0
- package/dist/schemas/write/org-employment-attestation.js.map +1 -0
- package/dist/schemas/write/org-profile.js +54 -0
- package/dist/schemas/write/org-profile.js.map +1 -0
- package/dist/schemas/write/org-settings.js +81 -0
- package/dist/schemas/write/org-settings.js.map +1 -0
- package/dist/schemas/write/position.js +24 -0
- package/dist/schemas/write/position.js.map +1 -0
- package/dist/schemas/write/presentation-delivery.js +34 -0
- package/dist/schemas/write/presentation-delivery.js.map +1 -0
- package/dist/schemas/write/presentation.js +23 -0
- package/dist/schemas/write/presentation.js.map +1 -0
- package/dist/schemas/write/profile-location.js +21 -0
- package/dist/schemas/write/profile-location.js.map +1 -0
- package/dist/schemas/write/profile-self.js +27 -0
- package/dist/schemas/write/profile-self.js.map +1 -0
- package/dist/schemas/write/project.js +27 -0
- package/dist/schemas/write/project.js.map +1 -0
- package/dist/schemas/write/publication.js +41 -0
- package/dist/schemas/write/publication.js.map +1 -0
- package/dist/schemas/write/shared.js +96 -0
- package/dist/schemas/write/shared.js.map +1 -0
- package/dist/schemas/write/skill.js +18 -0
- package/dist/schemas/write/skill.js.map +1 -0
- package/dist/schemas/write/volunteering.js +16 -0
- package/dist/schemas/write/volunteering.js.map +1 -0
- package/dist/stream/activity-item.js +1 -0
- package/dist/stream/activity-item.js.map +1 -0
- package/dist/stream/index.js +49 -0
- package/dist/stream/index.js.map +1 -0
- package/dist/stream/stream-card-vm-schema.js +265 -0
- package/dist/stream/stream-card-vm-schema.js.map +1 -0
- package/dist/stream/stream-card-vm.js +1 -0
- package/dist/stream/stream-card-vm.js.map +1 -0
- package/dist/stream/to-stream-card-vm.js +793 -0
- package/dist/stream/to-stream-card-vm.js.map +1 -0
- package/dist/stream/two-tier.js +30 -0
- package/dist/stream/two-tier.js.map +1 -0
- package/dist/stream/verbs.js +120 -0
- package/dist/stream/verbs.js.map +1 -0
- package/dist/taxonomy/activity-tiers.js +956 -0
- package/dist/taxonomy/activity-tiers.js.map +1 -0
- package/dist/taxonomy/app-categories.js +42 -0
- package/dist/taxonomy/app-categories.js.map +1 -0
- package/dist/taxonomy/app-category-map.js +166 -0
- package/dist/taxonomy/app-category-map.js.map +1 -0
- package/dist/taxonomy/artifact-link-kind.js +29 -0
- package/dist/taxonomy/artifact-link-kind.js.map +1 -0
- package/dist/taxonomy/calendar-event.js +28 -0
- package/dist/taxonomy/calendar-event.js.map +1 -0
- package/dist/taxonomy/continents.js +215 -0
- package/dist/taxonomy/continents.js.map +1 -0
- package/dist/taxonomy/countries.js +203 -0
- package/dist/taxonomy/countries.js.map +1 -0
- package/dist/taxonomy/course-role.js +37 -0
- package/dist/taxonomy/course-role.js.map +1 -0
- package/dist/taxonomy/employment-type.js +82 -0
- package/dist/taxonomy/employment-type.js.map +1 -0
- package/dist/taxonomy/eqf-level.js +59 -0
- package/dist/taxonomy/eqf-level.js.map +1 -0
- package/dist/taxonomy/index.js +263 -0
- package/dist/taxonomy/index.js.map +1 -0
- package/dist/taxonomy/industry-taxonomy.js +281 -0
- package/dist/taxonomy/industry-taxonomy.js.map +1 -0
- package/dist/taxonomy/investment.js +54 -0
- package/dist/taxonomy/investment.js.map +1 -0
- package/dist/taxonomy/involvement-kind.js +30 -0
- package/dist/taxonomy/involvement-kind.js.map +1 -0
- package/dist/taxonomy/open-to.js +128 -0
- package/dist/taxonomy/open-to.js.map +1 -0
- package/dist/taxonomy/platforms.js +56 -0
- package/dist/taxonomy/platforms.js.map +1 -0
- package/dist/taxonomy/presentation-link-type.js +22 -0
- package/dist/taxonomy/presentation-link-type.js.map +1 -0
- package/dist/taxonomy/presentation-role.js +21 -0
- package/dist/taxonomy/presentation-role.js.map +1 -0
- package/dist/taxonomy/publication-type.js +54 -0
- package/dist/taxonomy/publication-type.js.map +1 -0
- package/dist/taxonomy/relational.js +25 -0
- package/dist/taxonomy/relational.js.map +1 -0
- package/dist/taxonomy/search-filter-defs.js +66 -0
- package/dist/taxonomy/search-filter-defs.js.map +1 -0
- package/dist/taxonomy/skill-categories.js +28 -0
- package/dist/taxonomy/skill-categories.js.map +1 -0
- package/dist/taxonomy/skill-display-groups.js +12 -0
- package/dist/taxonomy/skill-display-groups.js.map +1 -0
- package/dist/taxonomy/skill-grouping.js +90 -0
- package/dist/taxonomy/skill-grouping.js.map +1 -0
- package/dist/taxonomy/verification-providers.js +69 -0
- package/dist/taxonomy/verification-providers.js.map +1 -0
- package/dist/taxonomy/workplace-type.js +38 -0
- package/dist/taxonomy/workplace-type.js.map +1 -0
- package/dist/tokens/colors.js +11 -0
- package/dist/tokens/colors.js.map +1 -0
- package/dist/tokens/icons.js +15 -0
- package/dist/tokens/icons.js.map +1 -0
- package/dist/tokens/index.js +10 -39
- package/dist/tokens/index.js.map +1 -1
- package/dist/tokens/typography.js +23 -0
- package/dist/tokens/typography.js.map +1 -0
- package/dist/types/index.js +10 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/profile-view.js +1 -0
- package/dist/types/profile-view.js.map +1 -0
- package/dist/{use-org-notification-emails-0hjt-dEw.d.ts → use-org-notification-emails-CWWi1D8t.d.ts} +2 -2
- package/package.json +4 -3
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// src/format/collapse-contributors.ts
|
|
2
|
+
function collapseContributors(contributors, options = {}) {
|
|
3
|
+
const list = contributors ?? [];
|
|
4
|
+
const max = options.max ?? 10;
|
|
5
|
+
if (list.length <= max + 2) return { visible: [...list], hiddenCount: 0 };
|
|
6
|
+
const visible = list.slice(0, max);
|
|
7
|
+
const ownerIndex = options.isOwner ? list.findIndex(options.isOwner) : -1;
|
|
8
|
+
const owner = ownerIndex >= max ? list[ownerIndex] : void 0;
|
|
9
|
+
if (owner !== void 0) {
|
|
10
|
+
if (ownerIndex > max) visible.push(null);
|
|
11
|
+
visible.push(owner);
|
|
12
|
+
}
|
|
13
|
+
const shown = visible.filter((c) => c !== null).length;
|
|
14
|
+
return { visible, hiddenCount: list.length - shown };
|
|
15
|
+
}
|
|
16
|
+
export {
|
|
17
|
+
collapseContributors
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=collapse-contributors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/format/collapse-contributors.ts"],"sourcesContent":["/** A long contributor list reduced to what the collapsed view shows. */\nexport interface CollapsedContributors<T> {\n /**\n * Contributors to show inline, in list order. A `null` entry marks a gap of\n * skipped names (render it as an ellipsis).\n */\n visible: (T | null)[];\n /** Contributors not in `visible`, for an \"and N more\" label. */\n hiddenCount: number;\n}\n\nexport interface CollapseContributorsOptions<T> {\n /** How many contributors to show before collapsing. Defaults to 10. */\n max?: number;\n /** Identifies the profile owner, who stays visible even deep in the list. */\n isOwner?: (contributor: T) => boolean;\n}\n\n/**\n * Collapse a long author/contributor list (a consortium paper or a software\n * release can list hundreds) to the first `max` names plus a hidden count.\n *\n * The profile owner stays visible: when they fall outside the first `max`, they\n * are appended after a gap marker, because on a large author list the owner's\n * own name is the one a reader looks for. Lists at most two names longer than\n * `max` are shown in full, since \"and 1 more\" takes as much room as the name.\n */\nexport function collapseContributors<T>(\n contributors: readonly T[] | undefined,\n options: CollapseContributorsOptions<T> = {},\n): CollapsedContributors<T> {\n const list = contributors ?? [];\n const max = options.max ?? 10;\n if (list.length <= max + 2) return { visible: [...list], hiddenCount: 0 };\n\n const visible: (T | null)[] = list.slice(0, max);\n const ownerIndex = options.isOwner ? list.findIndex(options.isOwner) : -1;\n const owner = ownerIndex >= max ? list[ownerIndex] : undefined;\n if (owner !== undefined) {\n if (ownerIndex > max) visible.push(null);\n visible.push(owner);\n }\n const shown = visible.filter((c) => c !== null).length;\n return { visible, hiddenCount: list.length - shown };\n}\n"],"mappings":";AA2BO,SAAS,qBACd,cACA,UAA0C,CAAC,GACjB;AAC1B,QAAM,OAAO,gBAAgB,CAAC;AAC9B,QAAM,MAAM,QAAQ,OAAO;AAC3B,MAAI,KAAK,UAAU,MAAM,EAAG,QAAO,EAAE,SAAS,CAAC,GAAG,IAAI,GAAG,aAAa,EAAE;AAExE,QAAM,UAAwB,KAAK,MAAM,GAAG,GAAG;AAC/C,QAAM,aAAa,QAAQ,UAAU,KAAK,UAAU,QAAQ,OAAO,IAAI;AACvE,QAAM,QAAQ,cAAc,MAAM,KAAK,UAAU,IAAI;AACrD,MAAI,UAAU,QAAW;AACvB,QAAI,aAAa,IAAK,SAAQ,KAAK,IAAI;AACvC,YAAQ,KAAK,KAAK;AAAA,EACpB;AACA,QAAM,QAAQ,QAAQ,OAAO,CAAC,MAAM,MAAM,IAAI,EAAE;AAChD,SAAO,EAAE,SAAS,aAAa,KAAK,SAAS,MAAM;AACrD;","names":[]}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// src/format/company-name.ts
|
|
2
|
+
var SMALL_WORDS = /* @__PURE__ */ new Set([
|
|
3
|
+
"a",
|
|
4
|
+
"an",
|
|
5
|
+
"and",
|
|
6
|
+
"the",
|
|
7
|
+
"of",
|
|
8
|
+
"for",
|
|
9
|
+
"to",
|
|
10
|
+
"in",
|
|
11
|
+
"on",
|
|
12
|
+
"at",
|
|
13
|
+
"by",
|
|
14
|
+
"or",
|
|
15
|
+
"nor",
|
|
16
|
+
"but",
|
|
17
|
+
"as",
|
|
18
|
+
"vs",
|
|
19
|
+
"via",
|
|
20
|
+
"with",
|
|
21
|
+
"de",
|
|
22
|
+
"del",
|
|
23
|
+
"van",
|
|
24
|
+
"von",
|
|
25
|
+
"der",
|
|
26
|
+
"den",
|
|
27
|
+
"het",
|
|
28
|
+
"la",
|
|
29
|
+
"le",
|
|
30
|
+
"y"
|
|
31
|
+
]);
|
|
32
|
+
function hasNonAsciiLetter(value) {
|
|
33
|
+
for (const char of value) {
|
|
34
|
+
const codePoint = char.codePointAt(0) ?? 0;
|
|
35
|
+
if (codePoint > 127 && /\p{L}/u.test(char)) return true;
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
function capitalizeWord(word) {
|
|
40
|
+
return word.replace(
|
|
41
|
+
/^([^\p{L}\p{N}]*)(\p{L})/u,
|
|
42
|
+
(_m, lead, ch) => lead + ch.toUpperCase()
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
function formatCompanyName(name) {
|
|
46
|
+
const trimmed = name.trim();
|
|
47
|
+
if (!trimmed || hasNonAsciiLetter(trimmed) || trimmed !== trimmed.toLowerCase()) return trimmed;
|
|
48
|
+
let seenWord = false;
|
|
49
|
+
return trimmed.split(/(\s+)/).map((token) => {
|
|
50
|
+
if (token.length === 0 || /^\s+$/.test(token)) return token;
|
|
51
|
+
const isFirst = !seenWord;
|
|
52
|
+
seenWord = true;
|
|
53
|
+
if (!isFirst && SMALL_WORDS.has(token)) return token;
|
|
54
|
+
return capitalizeWord(token);
|
|
55
|
+
}).join("");
|
|
56
|
+
}
|
|
57
|
+
export {
|
|
58
|
+
formatCompanyName,
|
|
59
|
+
hasNonAsciiLetter
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=company-name.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/format/company-name.ts"],"sourcesContent":["// Connector words kept lowercase in a title unless they lead the name. Includes\n// a few common non-English ones (Dutch/German/French/Spanish) since the company\n// data is international.\nconst SMALL_WORDS = new Set([\n 'a',\n 'an',\n 'and',\n 'the',\n 'of',\n 'for',\n 'to',\n 'in',\n 'on',\n 'at',\n 'by',\n 'or',\n 'nor',\n 'but',\n 'as',\n 'vs',\n 'via',\n 'with',\n 'de',\n 'del',\n 'van',\n 'von',\n 'der',\n 'den',\n 'het',\n 'la',\n 'le',\n 'y',\n]);\n\n/**\n * True when `value` contains at least one letter outside the ASCII range\n * (accented Latin, Cyrillic, Greek, CJK, Turkish dotted/dotless i, etc).\n *\n * Iterating by code point (not `.length`) matters for astral-plane\n * characters (e.g. some CJK), which are represented as surrogate pairs.\n */\nexport function hasNonAsciiLetter(value: string): boolean {\n for (const char of value) {\n const codePoint = char.codePointAt(0) ?? 0;\n if (codePoint > 0x7f && /\\p{L}/u.test(char)) return true;\n }\n return false;\n}\n\nfunction capitalizeWord(word: string): string {\n // Uppercase the first letter/digit, leaving any leading punctuation\n // (quotes, parens) and the rest of the word untouched.\n return word.replace(\n /^([^\\p{L}\\p{N}]*)(\\p{L})/u,\n (_m, lead: string, ch: string) => lead + ch.toUpperCase(),\n );\n}\n\n/**\n * Best-effort title-case for a company display name.\n *\n * Company names from the PDL crawl are stored all-lowercase (\"spryker\",\n * \"spryker systems gmbh\"), which reads poorly in the UI. This capitalizes them\n * for display. Names that already carry any uppercase (ROR / Wikidata, e.g.\n * \"University of Testing\" or \"IBM\") are assumed correctly cased and returned\n * unchanged, so a proper name is never mangled (\"University Of Testing\").\n *\n * It is intentionally not a canonicalizer: acronyms it can't know about stay\n * word-cased (\"gmbh\" -> \"Gmbh\"). The user's own custom display name is the\n * escape hatch when the heuristic is wrong.\n *\n * Scoped to ASCII-Latin case-bearing strings (decision D8): any name\n * containing a non-ASCII letter (accented Latin, Cyrillic, Greek, CJK,\n * Turkish dotted/dotless i, etc) is returned unchanged. Locale-dependent\n * casing rules (Turkish i/İ vs ı/I is the canonical trap) make naive\n * `toUpperCase`/`toLowerCase` unsafe outside ASCII, and this formatter has\n * no reliable way to pick the right locale for an arbitrary company name.\n *\n * Known limitation: this does not (and cannot) distinguish an intentional\n * all-lowercase ASCII wordmark (\"adidas\", \"thyssenkrupp\") from a\n * PDL-lowercased name that should be title-cased (\"spryker\" -> \"Spryker\").\n * Both are pure ASCII, so both still get title-cased. See sifa-workspace#235.\n */\nexport function formatCompanyName(name: string): string {\n const trimmed = name.trim();\n if (!trimmed || hasNonAsciiLetter(trimmed) || trimmed !== trimmed.toLowerCase()) return trimmed;\n\n let seenWord = false;\n // Split on whitespace but keep the separators so original spacing survives.\n return trimmed\n .split(/(\\s+)/)\n .map((token) => {\n if (token.length === 0 || /^\\s+$/.test(token)) return token;\n const isFirst = !seenWord;\n seenWord = true;\n if (!isFirst && SMALL_WORDS.has(token)) return token;\n return capitalizeWord(token);\n })\n .join('');\n}\n"],"mappings":";AAGA,IAAM,cAAc,oBAAI,IAAI;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AASM,SAAS,kBAAkB,OAAwB;AACxD,aAAW,QAAQ,OAAO;AACxB,UAAM,YAAY,KAAK,YAAY,CAAC,KAAK;AACzC,QAAI,YAAY,OAAQ,SAAS,KAAK,IAAI,EAAG,QAAO;AAAA,EACtD;AACA,SAAO;AACT;AAEA,SAAS,eAAe,MAAsB;AAG5C,SAAO,KAAK;AAAA,IACV;AAAA,IACA,CAAC,IAAI,MAAc,OAAe,OAAO,GAAG,YAAY;AAAA,EAC1D;AACF;AA2BO,SAAS,kBAAkB,MAAsB;AACtD,QAAM,UAAU,KAAK,KAAK;AAC1B,MAAI,CAAC,WAAW,kBAAkB,OAAO,KAAK,YAAY,QAAQ,YAAY,EAAG,QAAO;AAExF,MAAI,WAAW;AAEf,SAAO,QACJ,MAAM,OAAO,EACb,IAAI,CAAC,UAAU;AACd,QAAI,MAAM,WAAW,KAAK,QAAQ,KAAK,KAAK,EAAG,QAAO;AACtD,UAAM,UAAU,CAAC;AACjB,eAAW;AACX,QAAI,CAAC,WAAW,YAAY,IAAI,KAAK,EAAG,QAAO;AAC/C,WAAO,eAAe,KAAK;AAAA,EAC7B,CAAC,EACA,KAAK,EAAE;AACZ;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/format/compare-text.ts"],"sourcesContent":["const COLLATOR = new Intl.Collator('en');\n\n/**\n * Compare two display strings for sorting, with a fixed collation locale.\n *\n * A bare `a.localeCompare(b)` follows the runtime's default locale, which is\n * the server's on the server and the visitor's on the client. Mixed-script\n * lists (Latin and Cyrillic skill names, for example) then sort differently on\n * each side, and React discards the server HTML as a hydration mismatch. Use\n * this for every name sort whose output is rendered.\n */\nexport function compareText(a: string, b: string): number {\n return COLLATOR.compare(a, b);\n}\n"],"mappings":";AAAA,IAAM,WAAW,IAAI,KAAK,SAAS,IAAI;AAWhC,SAAS,YAAY,GAAW,GAAmB;AACxD,SAAO,SAAS,QAAQ,GAAG,CAAC;AAC9B;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/format/doi.ts"],"sourcesContent":["/**\n * Reduce a DOI to its bare form: `10.1234/example`, never\n * `https://doi.org/10.1234/example` and never `doi:10.1234/example`.\n *\n * A DOI inside a URL is a link. Bare, it is an identifier, which is what lets\n * two records describing one publication be matched on it. The lexicon stores\n * the bare form for that reason.\n *\n * People paste the resolver URL, because that is what a publisher page and a\n * citation manager both hand you, and citations carry the `doi:` scheme.\n * Normalizing on the way in is the difference between a field that dedupes and\n * a field holding three spellings of one value.\n *\n * Input that is not a DOI at all is trimmed and returned as-is. This is a\n * normalizer, not a validator: rejecting here would fail a write over a field\n * nothing depends on, and older registrants produce shapes worth not policing.\n */\nexport function normalizeDoi(value: string): string {\n return value\n .trim()\n .replace(/^https?:\\/\\/(dx\\.)?doi\\.org\\//i, '')\n .replace(/^doi:\\s*/i, '');\n}\n"],"mappings":";AAiBO,SAAS,aAAa,OAAuB;AAClD,SAAO,MACJ,KAAK,EACL,QAAQ,kCAAkC,EAAE,EAC5C,QAAQ,aAAa,EAAE;AAC5B;","names":[]}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// src/format/embed.ts
|
|
2
|
+
var LINK = { kind: "link" };
|
|
3
|
+
var YOUTUBE_ID = /^[A-Za-z0-9_-]{6,}$/;
|
|
4
|
+
function youtubeVideoId(url) {
|
|
5
|
+
let parsed;
|
|
6
|
+
try {
|
|
7
|
+
parsed = new URL(url);
|
|
8
|
+
} catch {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
if (parsed.protocol !== "https:") return null;
|
|
12
|
+
const host = bareHost(parsed.hostname);
|
|
13
|
+
const segments = parsed.pathname.split("/").filter(Boolean);
|
|
14
|
+
let id = null;
|
|
15
|
+
if (host === "youtu.be") id = segments[0] ?? null;
|
|
16
|
+
else if (host === "youtube.com" || host === "m.youtube.com") {
|
|
17
|
+
if (parsed.pathname === "/watch") id = parsed.searchParams.get("v");
|
|
18
|
+
else if (segments[0] === "embed" || segments[0] === "shorts") id = segments[1] ?? null;
|
|
19
|
+
}
|
|
20
|
+
return id && YOUTUBE_ID.test(id) ? id : null;
|
|
21
|
+
}
|
|
22
|
+
function youtubeThumbnailUrl(videoId) {
|
|
23
|
+
return `https://i.ytimg.com/vi/${videoId}/hqdefault.jpg`;
|
|
24
|
+
}
|
|
25
|
+
function bareHost(hostname) {
|
|
26
|
+
return hostname.toLowerCase().replace(/^www\./, "");
|
|
27
|
+
}
|
|
28
|
+
function resolveEmbed(url) {
|
|
29
|
+
let parsed;
|
|
30
|
+
try {
|
|
31
|
+
parsed = new URL(url);
|
|
32
|
+
} catch {
|
|
33
|
+
return LINK;
|
|
34
|
+
}
|
|
35
|
+
if (parsed.protocol !== "https:") return LINK;
|
|
36
|
+
const host = bareHost(parsed.hostname);
|
|
37
|
+
const segments = parsed.pathname.split("/").filter(Boolean);
|
|
38
|
+
if (host === "youtu.be" || host === "youtube.com" || host === "m.youtube.com") {
|
|
39
|
+
const id = youtubeVideoId(url);
|
|
40
|
+
return id ? youtubeEmbed(id) : LINK;
|
|
41
|
+
}
|
|
42
|
+
if (host === "vimeo.com") {
|
|
43
|
+
const id = segments[0];
|
|
44
|
+
if (id && /^\d+$/.test(id)) {
|
|
45
|
+
return {
|
|
46
|
+
kind: "iframe",
|
|
47
|
+
provider: "vimeo",
|
|
48
|
+
src: `https://player.vimeo.com/video/${id}`,
|
|
49
|
+
aspectRatio: "16:9"
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return LINK;
|
|
53
|
+
}
|
|
54
|
+
if (host === "player.vimeo.com") {
|
|
55
|
+
if (segments[0] === "video" && segments[1] && /^\d+$/.test(segments[1])) {
|
|
56
|
+
return {
|
|
57
|
+
kind: "iframe",
|
|
58
|
+
provider: "vimeo",
|
|
59
|
+
src: `https://player.vimeo.com/video/${segments[1]}`,
|
|
60
|
+
aspectRatio: "16:9"
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
return LINK;
|
|
64
|
+
}
|
|
65
|
+
if (host === "speakerdeck.com") {
|
|
66
|
+
if (segments[0] === "player" && segments[1]) {
|
|
67
|
+
return { kind: "iframe", provider: "speakerdeck", src: parsed.href, aspectRatio: "4:3" };
|
|
68
|
+
}
|
|
69
|
+
if (segments.length >= 2) {
|
|
70
|
+
return { kind: "oembed", provider: "speakerdeck", aspectRatio: "4:3", pageUrl: parsed.href };
|
|
71
|
+
}
|
|
72
|
+
return LINK;
|
|
73
|
+
}
|
|
74
|
+
if (host === "slideshare.net") {
|
|
75
|
+
if (segments[0] === "slideshow" && segments[1] === "embed_code") {
|
|
76
|
+
return { kind: "iframe", provider: "slideshare", src: parsed.href, aspectRatio: "4:3" };
|
|
77
|
+
}
|
|
78
|
+
if (segments.length >= 2) {
|
|
79
|
+
return { kind: "oembed", provider: "slideshare", aspectRatio: "4:3", pageUrl: parsed.href };
|
|
80
|
+
}
|
|
81
|
+
return LINK;
|
|
82
|
+
}
|
|
83
|
+
if (host === "docs.google.com") {
|
|
84
|
+
if (segments[0] === "presentation" && segments[1] === "d" && segments[2]) {
|
|
85
|
+
return {
|
|
86
|
+
kind: "iframe",
|
|
87
|
+
provider: "googleslides",
|
|
88
|
+
src: `https://docs.google.com/presentation/d/${segments[2]}/embed`,
|
|
89
|
+
aspectRatio: "16:9"
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
return LINK;
|
|
93
|
+
}
|
|
94
|
+
if (host === "loom.com") {
|
|
95
|
+
if (segments[0] === "share" && segments[1]) {
|
|
96
|
+
return {
|
|
97
|
+
kind: "iframe",
|
|
98
|
+
provider: "loom",
|
|
99
|
+
src: `https://www.loom.com/embed/${segments[1]}`,
|
|
100
|
+
aspectRatio: "16:9"
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
return LINK;
|
|
104
|
+
}
|
|
105
|
+
if (host === "canva.com") {
|
|
106
|
+
if (segments[0] === "design" && segments[1] && segments[2] === "view") {
|
|
107
|
+
return {
|
|
108
|
+
kind: "iframe",
|
|
109
|
+
provider: "canva",
|
|
110
|
+
src: `https://www.canva.com/design/${segments[1]}/view?embed`,
|
|
111
|
+
aspectRatio: "16:9"
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
return LINK;
|
|
115
|
+
}
|
|
116
|
+
return LINK;
|
|
117
|
+
}
|
|
118
|
+
function youtubeEmbed(id) {
|
|
119
|
+
return {
|
|
120
|
+
kind: "iframe",
|
|
121
|
+
provider: "youtube",
|
|
122
|
+
src: `https://www.youtube-nocookie.com/embed/${id}`,
|
|
123
|
+
aspectRatio: "16:9"
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
export {
|
|
127
|
+
resolveEmbed,
|
|
128
|
+
youtubeThumbnailUrl,
|
|
129
|
+
youtubeVideoId
|
|
130
|
+
};
|
|
131
|
+
//# sourceMappingURL=embed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/format/embed.ts"],"sourcesContent":["/**\n * Resolve a slide / video URL to an embed descriptor. Pure and offline.\n *\n * Three outcomes:\n * - `iframe`: the URL carries everything an embed needs, so the iframe `src` is\n * known offline (YouTube, Vimeo, Google Slides, Loom, Canva).\n * - `oembed`: the URL is a known embeddable provider whose iframe `src` is NOT\n * in the URL and needs a server-side oEmbed lookup (SlideShare, SpeakerDeck\n * public deck URLs). The caller resolves the src via its own oEmbed step.\n * - `link`: not embeddable; render a plain link.\n */\n\n/**\n * A resolved embed: an iframe whose src is known offline, an `oembed` provider\n * whose src needs a server lookup (carries the original page URL), or a plain\n * link.\n */\nexport type EmbedResult =\n | { kind: 'iframe'; provider: string; src: string; aspectRatio: '16:9' | '4:3' }\n | { kind: 'oembed'; provider: string; aspectRatio: '16:9' | '4:3'; pageUrl: string }\n | { kind: 'link' };\n\nconst LINK: EmbedResult = { kind: 'link' };\n\n/** YouTube video IDs: 11 chars in practice, but accept the documented charset. */\nconst YOUTUBE_ID = /^[A-Za-z0-9_-]{6,}$/;\n\n/**\n * Extract a YouTube video id from any recognized YouTube URL (`youtu.be/ID`,\n * `youtube.com/watch?v=ID`, `/embed/ID`, `/shorts/ID`), or null. Pure/offline.\n */\nexport function youtubeVideoId(url: string): string | null {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return null;\n }\n if (parsed.protocol !== 'https:') return null;\n const host = bareHost(parsed.hostname);\n const segments = parsed.pathname.split('/').filter(Boolean);\n let id: string | null = null;\n if (host === 'youtu.be') id = segments[0] ?? null;\n else if (host === 'youtube.com' || host === 'm.youtube.com') {\n if (parsed.pathname === '/watch') id = parsed.searchParams.get('v');\n else if (segments[0] === 'embed' || segments[0] === 'shorts') id = segments[1] ?? null;\n }\n return id && YOUTUBE_ID.test(id) ? id : null;\n}\n\n/**\n * The public thumbnail URL for a YouTube video id (`i.ytimg.com`). Derivable\n * offline. NOTE: this host is Google — never load it directly in a privacy\n * context; fetch and re-serve it from your own origin.\n */\nexport function youtubeThumbnailUrl(videoId: string): string {\n return `https://i.ytimg.com/vi/${videoId}/hqdefault.jpg`;\n}\n\n/** Strip a leading `www.` so host matching is scheme/subdomain tolerant. */\nfunction bareHost(hostname: string): string {\n return hostname.toLowerCase().replace(/^www\\./, '');\n}\n\n/**\n * Recognize an embeddable URL and return its iframe descriptor, else a link.\n * Only https URLs are accepted.\n */\nexport function resolveEmbed(url: string): EmbedResult {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return LINK;\n }\n if (parsed.protocol !== 'https:') return LINK;\n\n const host = bareHost(parsed.hostname);\n const segments = parsed.pathname.split('/').filter(Boolean);\n\n // --- YouTube -> privacy-enhanced nocookie embed ---\n if (host === 'youtu.be' || host === 'youtube.com' || host === 'm.youtube.com') {\n const id = youtubeVideoId(url);\n return id ? youtubeEmbed(id) : LINK;\n }\n\n // --- Vimeo ---\n if (host === 'vimeo.com') {\n const id = segments[0];\n if (id && /^\\d+$/.test(id)) {\n return {\n kind: 'iframe',\n provider: 'vimeo',\n src: `https://player.vimeo.com/video/${id}`,\n aspectRatio: '16:9',\n };\n }\n return LINK;\n }\n if (host === 'player.vimeo.com') {\n if (segments[0] === 'video' && segments[1] && /^\\d+$/.test(segments[1])) {\n return {\n kind: 'iframe',\n provider: 'vimeo',\n src: `https://player.vimeo.com/video/${segments[1]}`,\n aspectRatio: '16:9',\n };\n }\n return LINK;\n }\n\n // --- SpeakerDeck ---\n // An already-resolved `speakerdeck.com/player/HEXID` embed URL is embeddable\n // offline. A public `speakerdeck.com/USER/SLUG` deck URL needs a server oEmbed\n // lookup for the data-id, so it is `oembed`, not a dead link.\n if (host === 'speakerdeck.com') {\n if (segments[0] === 'player' && segments[1]) {\n return { kind: 'iframe', provider: 'speakerdeck', src: parsed.href, aspectRatio: '4:3' };\n }\n if (segments.length >= 2) {\n return { kind: 'oembed', provider: 'speakerdeck', aspectRatio: '4:3', pageUrl: parsed.href };\n }\n return LINK;\n }\n\n // --- SlideShare ---\n // An already-resolved `slideshow/embed_code/...` URL is embeddable offline. A\n // public deck URL needs a server oEmbed lookup for the embed_code id, so it is\n // `oembed`.\n if (host === 'slideshare.net') {\n if (segments[0] === 'slideshow' && segments[1] === 'embed_code') {\n return { kind: 'iframe', provider: 'slideshare', src: parsed.href, aspectRatio: '4:3' };\n }\n if (segments.length >= 2) {\n return { kind: 'oembed', provider: 'slideshare', aspectRatio: '4:3', pageUrl: parsed.href };\n }\n return LINK;\n }\n\n // --- Google Slides ---\n if (host === 'docs.google.com') {\n if (segments[0] === 'presentation' && segments[1] === 'd' && segments[2]) {\n return {\n kind: 'iframe',\n provider: 'googleslides',\n src: `https://docs.google.com/presentation/d/${segments[2]}/embed`,\n aspectRatio: '16:9',\n };\n }\n return LINK;\n }\n\n // --- Loom ---\n if (host === 'loom.com') {\n if (segments[0] === 'share' && segments[1]) {\n return {\n kind: 'iframe',\n provider: 'loom',\n src: `https://www.loom.com/embed/${segments[1]}`,\n aspectRatio: '16:9',\n };\n }\n return LINK;\n }\n\n // --- Canva ---\n if (host === 'canva.com') {\n if (segments[0] === 'design' && segments[1] && segments[2] === 'view') {\n return {\n kind: 'iframe',\n provider: 'canva',\n src: `https://www.canva.com/design/${segments[1]}/view?embed`,\n aspectRatio: '16:9',\n };\n }\n return LINK;\n }\n\n return LINK;\n}\n\nfunction youtubeEmbed(id: string): EmbedResult {\n return {\n kind: 'iframe',\n provider: 'youtube',\n src: `https://www.youtube-nocookie.com/embed/${id}`,\n aspectRatio: '16:9',\n };\n}\n"],"mappings":";AAsBA,IAAM,OAAoB,EAAE,MAAM,OAAO;AAGzC,IAAM,aAAa;AAMZ,SAAS,eAAe,KAA4B;AACzD,MAAI;AACJ,MAAI;AACF,aAAS,IAAI,IAAI,GAAG;AAAA,EACtB,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,OAAO,aAAa,SAAU,QAAO;AACzC,QAAM,OAAO,SAAS,OAAO,QAAQ;AACrC,QAAM,WAAW,OAAO,SAAS,MAAM,GAAG,EAAE,OAAO,OAAO;AAC1D,MAAI,KAAoB;AACxB,MAAI,SAAS,WAAY,MAAK,SAAS,CAAC,KAAK;AAAA,WACpC,SAAS,iBAAiB,SAAS,iBAAiB;AAC3D,QAAI,OAAO,aAAa,SAAU,MAAK,OAAO,aAAa,IAAI,GAAG;AAAA,aACzD,SAAS,CAAC,MAAM,WAAW,SAAS,CAAC,MAAM,SAAU,MAAK,SAAS,CAAC,KAAK;AAAA,EACpF;AACA,SAAO,MAAM,WAAW,KAAK,EAAE,IAAI,KAAK;AAC1C;AAOO,SAAS,oBAAoB,SAAyB;AAC3D,SAAO,0BAA0B,OAAO;AAC1C;AAGA,SAAS,SAAS,UAA0B;AAC1C,SAAO,SAAS,YAAY,EAAE,QAAQ,UAAU,EAAE;AACpD;AAMO,SAAS,aAAa,KAA0B;AACrD,MAAI;AACJ,MAAI;AACF,aAAS,IAAI,IAAI,GAAG;AAAA,EACtB,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,OAAO,aAAa,SAAU,QAAO;AAEzC,QAAM,OAAO,SAAS,OAAO,QAAQ;AACrC,QAAM,WAAW,OAAO,SAAS,MAAM,GAAG,EAAE,OAAO,OAAO;AAG1D,MAAI,SAAS,cAAc,SAAS,iBAAiB,SAAS,iBAAiB;AAC7E,UAAM,KAAK,eAAe,GAAG;AAC7B,WAAO,KAAK,aAAa,EAAE,IAAI;AAAA,EACjC;AAGA,MAAI,SAAS,aAAa;AACxB,UAAM,KAAK,SAAS,CAAC;AACrB,QAAI,MAAM,QAAQ,KAAK,EAAE,GAAG;AAC1B,aAAO;AAAA,QACL,MAAM;AAAA,QACN,UAAU;AAAA,QACV,KAAK,kCAAkC,EAAE;AAAA,QACzC,aAAa;AAAA,MACf;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACA,MAAI,SAAS,oBAAoB;AAC/B,QAAI,SAAS,CAAC,MAAM,WAAW,SAAS,CAAC,KAAK,QAAQ,KAAK,SAAS,CAAC,CAAC,GAAG;AACvE,aAAO;AAAA,QACL,MAAM;AAAA,QACN,UAAU;AAAA,QACV,KAAK,kCAAkC,SAAS,CAAC,CAAC;AAAA,QAClD,aAAa;AAAA,MACf;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAMA,MAAI,SAAS,mBAAmB;AAC9B,QAAI,SAAS,CAAC,MAAM,YAAY,SAAS,CAAC,GAAG;AAC3C,aAAO,EAAE,MAAM,UAAU,UAAU,eAAe,KAAK,OAAO,MAAM,aAAa,MAAM;AAAA,IACzF;AACA,QAAI,SAAS,UAAU,GAAG;AACxB,aAAO,EAAE,MAAM,UAAU,UAAU,eAAe,aAAa,OAAO,SAAS,OAAO,KAAK;AAAA,IAC7F;AACA,WAAO;AAAA,EACT;AAMA,MAAI,SAAS,kBAAkB;AAC7B,QAAI,SAAS,CAAC,MAAM,eAAe,SAAS,CAAC,MAAM,cAAc;AAC/D,aAAO,EAAE,MAAM,UAAU,UAAU,cAAc,KAAK,OAAO,MAAM,aAAa,MAAM;AAAA,IACxF;AACA,QAAI,SAAS,UAAU,GAAG;AACxB,aAAO,EAAE,MAAM,UAAU,UAAU,cAAc,aAAa,OAAO,SAAS,OAAO,KAAK;AAAA,IAC5F;AACA,WAAO;AAAA,EACT;AAGA,MAAI,SAAS,mBAAmB;AAC9B,QAAI,SAAS,CAAC,MAAM,kBAAkB,SAAS,CAAC,MAAM,OAAO,SAAS,CAAC,GAAG;AACxE,aAAO;AAAA,QACL,MAAM;AAAA,QACN,UAAU;AAAA,QACV,KAAK,0CAA0C,SAAS,CAAC,CAAC;AAAA,QAC1D,aAAa;AAAA,MACf;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAGA,MAAI,SAAS,YAAY;AACvB,QAAI,SAAS,CAAC,MAAM,WAAW,SAAS,CAAC,GAAG;AAC1C,aAAO;AAAA,QACL,MAAM;AAAA,QACN,UAAU;AAAA,QACV,KAAK,8BAA8B,SAAS,CAAC,CAAC;AAAA,QAC9C,aAAa;AAAA,MACf;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAGA,MAAI,SAAS,aAAa;AACxB,QAAI,SAAS,CAAC,MAAM,YAAY,SAAS,CAAC,KAAK,SAAS,CAAC,MAAM,QAAQ;AACrE,aAAO;AAAA,QACL,MAAM;AAAA,QACN,UAAU;AAAA,QACV,KAAK,gCAAgC,SAAS,CAAC,CAAC;AAAA,QAChD,aAAa;AAAA,MACf;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAAS,aAAa,IAAyB;AAC7C,SAAO;AAAA,IACL,MAAM;AAAA,IACN,UAAU;AAAA,IACV,KAAK,0CAA0C,EAAE;AAAA,IACjD,aAAa;AAAA,EACf;AACF;","names":[]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// src/format/endorsement-context.ts
|
|
2
|
+
var RELATIONSHIP_PREFIX = /^\[(\w+?)(?:: ([^\]]+))?\]\s*/;
|
|
3
|
+
function formatRelationship(type, detail) {
|
|
4
|
+
const trimmed = detail?.trim() || void 0;
|
|
5
|
+
switch (type) {
|
|
6
|
+
case "worked_together":
|
|
7
|
+
return trimmed ? `Worked together at ${trimmed}` : "Worked together";
|
|
8
|
+
case "collaborated_in":
|
|
9
|
+
return trimmed ? `Collaborated in ${trimmed}` : "Collaborated";
|
|
10
|
+
case "supervised_by":
|
|
11
|
+
return "Manager or mentor relationship";
|
|
12
|
+
case "co_authored":
|
|
13
|
+
return "Co-authored";
|
|
14
|
+
case "other":
|
|
15
|
+
return trimmed ?? null;
|
|
16
|
+
default:
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function parseEndorsementComment(comment) {
|
|
21
|
+
if (!comment) return { relationship: null, note: null };
|
|
22
|
+
const match = comment.match(RELATIONSHIP_PREFIX);
|
|
23
|
+
if (!match) {
|
|
24
|
+
const note2 = comment.trim();
|
|
25
|
+
return { relationship: null, note: note2 || null };
|
|
26
|
+
}
|
|
27
|
+
const relationship = formatRelationship(match[1] ?? "", match[2]);
|
|
28
|
+
const note = comment.slice(match[0].length).trim();
|
|
29
|
+
return { relationship, note: note || null };
|
|
30
|
+
}
|
|
31
|
+
export {
|
|
32
|
+
formatRelationship,
|
|
33
|
+
parseEndorsementComment
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=endorsement-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/format/endorsement-context.ts"],"sourcesContent":["/**\n * Endorsement comments carry an optional relationship-context prefix written by\n * the endorse dialog, e.g. `[worked_together: Acme] great colleague`. The prefix\n * is a UI convention baked into the free-text comment, not a structured lexicon\n * field. This splits a stored comment back into a human relationship label and\n * the endorser's own note, so no surface ever shows a raw `[supervised_by]`\n * token. Shared by every Sifa client (web and app) so all of them render the\n * same friendly labels.\n */\n\n/** Leading `[type]` or `[type: detail]` group, plus any trailing whitespace. */\nconst RELATIONSHIP_PREFIX = /^\\[(\\w+?)(?:: ([^\\]]+))?\\]\\s*/;\n\nexport interface ParsedEndorsementComment {\n /** Human-readable relationship label, or null when the comment has no prefix. */\n relationship: string | null;\n /** The endorser's free-text note with the prefix removed, or null when empty. */\n note: string | null;\n}\n\n/**\n * Friendly label for a relationship-context token. The detail is appended for\n * the options that collect one. Mirrors the endorse dialog's option list; keep\n * the two in step.\n */\nexport function formatRelationship(type: string, detail?: string): string | null {\n const trimmed = detail?.trim() || undefined;\n switch (type) {\n case 'worked_together':\n return trimmed ? `Worked together at ${trimmed}` : 'Worked together';\n case 'collaborated_in':\n return trimmed ? `Collaborated in ${trimmed}` : 'Collaborated';\n case 'supervised_by':\n // Reworded from the old \"Supervised / was supervised by\": that read as\n // bidirectional and ambiguous, and it shows publicly on profiles.\n return 'Manager or mentor relationship';\n case 'co_authored':\n return 'Co-authored';\n case 'other':\n // For \"other\" the endorser's own words are the relationship.\n return trimmed ?? null;\n default:\n return null;\n }\n}\n\n/**\n * Split a stored endorsement comment into its relationship label and free-text\n * note. A comment with no prefix returns `{ relationship: null, note }`.\n */\nexport function parseEndorsementComment(comment?: string | null): ParsedEndorsementComment {\n if (!comment) return { relationship: null, note: null };\n\n const match = comment.match(RELATIONSHIP_PREFIX);\n if (!match) {\n const note = comment.trim();\n return { relationship: null, note: note || null };\n }\n\n const relationship = formatRelationship(match[1] ?? '', match[2]);\n const note = comment.slice(match[0].length).trim();\n return { relationship, note: note || null };\n}\n"],"mappings":";AAWA,IAAM,sBAAsB;AAcrB,SAAS,mBAAmB,MAAc,QAAgC;AAC/E,QAAM,UAAU,QAAQ,KAAK,KAAK;AAClC,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO,UAAU,sBAAsB,OAAO,KAAK;AAAA,IACrD,KAAK;AACH,aAAO,UAAU,mBAAmB,OAAO,KAAK;AAAA,IAClD,KAAK;AAGH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAEH,aAAO,WAAW;AAAA,IACpB;AACE,aAAO;AAAA,EACX;AACF;AAMO,SAAS,wBAAwB,SAAmD;AACzF,MAAI,CAAC,QAAS,QAAO,EAAE,cAAc,MAAM,MAAM,KAAK;AAEtD,QAAM,QAAQ,QAAQ,MAAM,mBAAmB;AAC/C,MAAI,CAAC,OAAO;AACV,UAAMA,QAAO,QAAQ,KAAK;AAC1B,WAAO,EAAE,cAAc,MAAM,MAAMA,SAAQ,KAAK;AAAA,EAClD;AAEA,QAAM,eAAe,mBAAmB,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,CAAC;AAChE,QAAM,OAAO,QAAQ,MAAM,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK;AACjD,SAAO,EAAE,cAAc,MAAM,QAAQ,KAAK;AAC5C;","names":["note"]}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// src/format/format-display-url.ts
|
|
2
|
+
var SAFE_NON_WEB_SCHEMES = /* @__PURE__ */ new Set(["mailto:", "tel:", "dns:"]);
|
|
3
|
+
function tryParseUrl(raw) {
|
|
4
|
+
try {
|
|
5
|
+
return new URL(raw);
|
|
6
|
+
} catch {
|
|
7
|
+
}
|
|
8
|
+
if (!/^[a-z][a-z0-9+.-]*:/i.test(raw)) {
|
|
9
|
+
try {
|
|
10
|
+
return new URL(`https://${raw}`);
|
|
11
|
+
} catch {
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
function formatDisplayUrl(url, options = {}) {
|
|
17
|
+
const { path = "full" } = options;
|
|
18
|
+
const raw = (url ?? "").trim();
|
|
19
|
+
if (!raw) return { display: "", href: "" };
|
|
20
|
+
const parsed = tryParseUrl(raw);
|
|
21
|
+
if (!parsed) {
|
|
22
|
+
return { display: raw.replace(/^dns:/i, ""), href: raw };
|
|
23
|
+
}
|
|
24
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
25
|
+
const display = raw.replace(/^[a-z][a-z0-9+.-]*:/i, "").replace(/^\/\//, "") || raw;
|
|
26
|
+
const href = SAFE_NON_WEB_SCHEMES.has(parsed.protocol) ? raw : "";
|
|
27
|
+
return { display, href };
|
|
28
|
+
}
|
|
29
|
+
const host = parsed.hostname.replace(/^www\./i, "");
|
|
30
|
+
const segments = parsed.pathname.split("/").filter(Boolean);
|
|
31
|
+
let pathPart = "";
|
|
32
|
+
if (path === "full" && segments.length > 0) {
|
|
33
|
+
pathPart = `/${segments.join("/")}`;
|
|
34
|
+
} else if (path === "firstSegment" && segments.length > 0) {
|
|
35
|
+
pathPart = `/${segments[0]}`;
|
|
36
|
+
}
|
|
37
|
+
return { display: `${host}${pathPart}`, href: parsed.href };
|
|
38
|
+
}
|
|
39
|
+
export {
|
|
40
|
+
formatDisplayUrl
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=format-display-url.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/format/format-display-url.ts"],"sourcesContent":["/**\n * How much of the URL path to keep in the display string.\n *\n * - `full`: keep the whole pathname (the path is identity, e.g.\n * `github.com/user/repo` or `mastodon.social/@handle`).\n * - `firstSegment`: keep only the first path segment (`gui.do/events`) -- enough\n * to say \"which page\" without the deep-slug / marketing cruft.\n * - `none`: host only (`gui.do`).\n */\nexport type UrlPathPolicy = 'full' | 'firstSegment' | 'none';\n\nexport interface FormatDisplayUrlOptions {\n path?: UrlPathPolicy;\n}\n\nexport interface DisplayUrl {\n /** The cleaned label to show (no scheme, no `www.`, no query/hash). */\n display: string;\n /** The full, navigable URL for an `<a href>` / hover title. */\n href: string;\n}\n\n/**\n * Non-web URL schemes that are safe to expose as an `href`. Anything outside\n * http(s) and this set (notably `javascript:`, `data:`, `vbscript:`, `file:`) is\n * dropped to an empty href so it can never become a navigable link.\n */\nconst SAFE_NON_WEB_SCHEMES = new Set(['mailto:', 'tel:', 'dns:']);\n\n/**\n * Best-effort parse. Tries the input as-is, then (for scheme-less input like\n * `example.com`) with an `https://` prefix. Returns `null` when neither parses,\n * so the caller can fall back instead of throwing -- an uncaught `new URL()`\n * throw inside a Server Component takes down the whole render.\n */\nfunction tryParseUrl(raw: string): URL | null {\n try {\n return new URL(raw);\n } catch {\n // fall through\n }\n if (!/^[a-z][a-z0-9+.-]*:/i.test(raw)) {\n try {\n return new URL(`https://${raw}`);\n } catch {\n // fall through\n }\n }\n return null;\n}\n\n/**\n * Canonical URL display formatter, shared by every surface that shows a link\n * (profile projects, external accounts, identity-card website). Produces a tidy\n * label plus the real destination, so consumers never re-implement the strip.\n *\n * Rules: drop the scheme and a leading `www.`, drop the query string and hash,\n * drop a trailing slash, and apply the `path` policy. `display` is the label;\n * `href` is the full canonical URL (scheme + query + hash preserved) for the\n * anchor and its hover `title`.\n *\n * Truncation/ellipsis is deliberately NOT handled here -- it is a view concern\n * that depends on the rendered width, so consumers truncate with CSS\n * (`text-overflow: ellipsis`) and keep the full value in `href`/`title`.\n */\nexport function formatDisplayUrl(url: string, options: FormatDisplayUrlOptions = {}): DisplayUrl {\n const { path = 'full' } = options;\n const raw = (url ?? '').trim();\n if (!raw) return { display: '', href: '' };\n\n const parsed = tryParseUrl(raw);\n\n // Unparseable even with a prepended scheme: return a best-effort label and\n // keep the raw value as href. Never throw.\n if (!parsed) {\n return { display: raw.replace(/^dns:/i, ''), href: raw };\n }\n\n // Non-web scheme: strip the scheme for display. Keep an href only for a\n // known-safe scheme (`mailto:`, `tel:`, `dns:`); a dangerous scheme\n // (`javascript:`, `data:`, `vbscript:`, `file:`, ...) parses fine and reaches\n // here, so it must be neutralised to an empty href rather than returned\n // verbatim, or it becomes a navigable link. Defence-in-depth for records\n // stored before write-time validation existed.\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n const display = raw.replace(/^[a-z][a-z0-9+.-]*:/i, '').replace(/^\\/\\//, '') || raw;\n const href = SAFE_NON_WEB_SCHEMES.has(parsed.protocol) ? raw : '';\n return { display, href };\n }\n\n const host = parsed.hostname.replace(/^www\\./i, '');\n const segments = parsed.pathname.split('/').filter(Boolean);\n\n let pathPart = '';\n if (path === 'full' && segments.length > 0) {\n pathPart = `/${segments.join('/')}`;\n } else if (path === 'firstSegment' && segments.length > 0) {\n pathPart = `/${segments[0]}`;\n }\n\n return { display: `${host}${pathPart}`, href: parsed.href };\n}\n"],"mappings":";AA2BA,IAAM,uBAAuB,oBAAI,IAAI,CAAC,WAAW,QAAQ,MAAM,CAAC;AAQhE,SAAS,YAAY,KAAyB;AAC5C,MAAI;AACF,WAAO,IAAI,IAAI,GAAG;AAAA,EACpB,QAAQ;AAAA,EAER;AACA,MAAI,CAAC,uBAAuB,KAAK,GAAG,GAAG;AACrC,QAAI;AACF,aAAO,IAAI,IAAI,WAAW,GAAG,EAAE;AAAA,IACjC,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;AAgBO,SAAS,iBAAiB,KAAa,UAAmC,CAAC,GAAe;AAC/F,QAAM,EAAE,OAAO,OAAO,IAAI;AAC1B,QAAM,OAAO,OAAO,IAAI,KAAK;AAC7B,MAAI,CAAC,IAAK,QAAO,EAAE,SAAS,IAAI,MAAM,GAAG;AAEzC,QAAM,SAAS,YAAY,GAAG;AAI9B,MAAI,CAAC,QAAQ;AACX,WAAO,EAAE,SAAS,IAAI,QAAQ,UAAU,EAAE,GAAG,MAAM,IAAI;AAAA,EACzD;AAQA,MAAI,OAAO,aAAa,WAAW,OAAO,aAAa,UAAU;AAC/D,UAAM,UAAU,IAAI,QAAQ,wBAAwB,EAAE,EAAE,QAAQ,SAAS,EAAE,KAAK;AAChF,UAAM,OAAO,qBAAqB,IAAI,OAAO,QAAQ,IAAI,MAAM;AAC/D,WAAO,EAAE,SAAS,KAAK;AAAA,EACzB;AAEA,QAAM,OAAO,OAAO,SAAS,QAAQ,WAAW,EAAE;AAClD,QAAM,WAAW,OAAO,SAAS,MAAM,GAAG,EAAE,OAAO,OAAO;AAE1D,MAAI,WAAW;AACf,MAAI,SAAS,UAAU,SAAS,SAAS,GAAG;AAC1C,eAAW,IAAI,SAAS,KAAK,GAAG,CAAC;AAAA,EACnC,WAAW,SAAS,kBAAkB,SAAS,SAAS,GAAG;AACzD,eAAW,IAAI,SAAS,CAAC,CAAC;AAAA,EAC5B;AAEA,SAAO,EAAE,SAAS,GAAG,IAAI,GAAG,QAAQ,IAAI,MAAM,OAAO,KAAK;AAC5D;","names":[]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// src/format/format-duration.ts
|
|
2
|
+
function formatPresentationDuration(duration) {
|
|
3
|
+
if (!duration) return void 0;
|
|
4
|
+
const { minMinutes, maxMinutes } = duration;
|
|
5
|
+
if (maxMinutes != null && maxMinutes !== minMinutes) {
|
|
6
|
+
return `${minMinutes}-${maxMinutes} min`;
|
|
7
|
+
}
|
|
8
|
+
return `${minMinutes} min`;
|
|
9
|
+
}
|
|
10
|
+
export {
|
|
11
|
+
formatPresentationDuration
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=format-duration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/format/format-duration.ts"],"sourcesContent":["import type { PresentationDuration } from '../schemas/profile-presentation.js';\n\n/**\n * Format a presentation duration for display. A fixed length renders as\n * \"30 min\"; a range renders as \"20-30 min\". Returns undefined when there is no\n * duration. A range whose bounds are equal, or whose max is missing, renders as\n * a single value.\n */\nexport function formatPresentationDuration(\n duration: PresentationDuration | undefined | null,\n): string | undefined {\n if (!duration) return undefined;\n const { minMinutes, maxMinutes } = duration;\n if (maxMinutes != null && maxMinutes !== minMinutes) {\n return `${minMinutes}-${maxMinutes} min`;\n }\n return `${minMinutes} min`;\n}\n"],"mappings":";AAQO,SAAS,2BACd,UACoB;AACpB,MAAI,CAAC,SAAU,QAAO;AACtB,QAAM,EAAE,YAAY,WAAW,IAAI;AACnC,MAAI,cAAc,QAAQ,eAAe,YAAY;AACnD,WAAO,GAAG,UAAU,IAAI,UAAU;AAAA,EACpC;AACA,SAAO,GAAG,UAAU;AACtB;","names":[]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// src/format/format-time.ts
|
|
2
|
+
function formatIsoTitle(dateString) {
|
|
3
|
+
const date = new Date(dateString);
|
|
4
|
+
if (isNaN(date.getTime())) return "";
|
|
5
|
+
return date.toISOString();
|
|
6
|
+
}
|
|
7
|
+
function formatRelativeTime(dateString) {
|
|
8
|
+
const date = new Date(dateString);
|
|
9
|
+
if (isNaN(date.getTime())) return "";
|
|
10
|
+
const now = Date.now();
|
|
11
|
+
const diffMs = now - date.getTime();
|
|
12
|
+
if (diffMs < 0) return "";
|
|
13
|
+
const seconds = Math.floor(diffMs / 1e3);
|
|
14
|
+
if (seconds < 60) return `${seconds}s ago`;
|
|
15
|
+
const minutes = Math.floor(seconds / 60);
|
|
16
|
+
if (minutes < 60) return `${minutes}m ago`;
|
|
17
|
+
const hours = Math.floor(minutes / 60);
|
|
18
|
+
if (hours < 24) return `${hours}h ago`;
|
|
19
|
+
const days = Math.floor(hours / 24);
|
|
20
|
+
if (days < 30) return `${days}d ago`;
|
|
21
|
+
const months = Math.floor(days / 30);
|
|
22
|
+
if (months < 12) return `${months}mo ago`;
|
|
23
|
+
const years = Math.floor(months / 12);
|
|
24
|
+
return `${years}y ago`;
|
|
25
|
+
}
|
|
26
|
+
export {
|
|
27
|
+
formatIsoTitle,
|
|
28
|
+
formatRelativeTime
|
|
29
|
+
};
|
|
30
|
+
//# sourceMappingURL=format-time.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/format/format-time.ts"],"sourcesContent":["/**\n * Format a date string as a full ISO-8601 instant, for a `<time title>` tooltip\n * (the exact timestamp behind a relative \"5m ago\"). Returns an empty string for\n * an invalid or unparseable date rather than throwing.\n *\n * `new Date(x).toISOString()` throws `RangeError: Invalid time value` on a bad\n * input, which crashed SSR wherever a record's timestamp fed a title attribute\n * directly (GlitchTip SIFAID-M45). Guard the conversion the same way\n * {@link formatRelativeTime} guards its own parse.\n */\nexport function formatIsoTitle(dateString: string): string {\n const date = new Date(dateString);\n if (isNaN(date.getTime())) return '';\n return date.toISOString();\n}\n\n/**\n * Format a date string as a relative time (e.g. \"5m ago\", \"3d ago\").\n * Returns an empty string for invalid or future dates.\n */\nexport function formatRelativeTime(dateString: string): string {\n const date = new Date(dateString);\n if (isNaN(date.getTime())) return '';\n\n const now = Date.now();\n const diffMs = now - date.getTime();\n if (diffMs < 0) return '';\n\n const seconds = Math.floor(diffMs / 1000);\n if (seconds < 60) return `${seconds}s ago`;\n\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m ago`;\n\n const hours = Math.floor(minutes / 60);\n if (hours < 24) return `${hours}h ago`;\n\n const days = Math.floor(hours / 24);\n if (days < 30) return `${days}d ago`;\n\n const months = Math.floor(days / 30);\n if (months < 12) return `${months}mo ago`;\n\n const years = Math.floor(months / 12);\n return `${years}y ago`;\n}\n"],"mappings":";AAUO,SAAS,eAAe,YAA4B;AACzD,QAAM,OAAO,IAAI,KAAK,UAAU;AAChC,MAAI,MAAM,KAAK,QAAQ,CAAC,EAAG,QAAO;AAClC,SAAO,KAAK,YAAY;AAC1B;AAMO,SAAS,mBAAmB,YAA4B;AAC7D,QAAM,OAAO,IAAI,KAAK,UAAU;AAChC,MAAI,MAAM,KAAK,QAAQ,CAAC,EAAG,QAAO;AAElC,QAAM,MAAM,KAAK,IAAI;AACrB,QAAM,SAAS,MAAM,KAAK,QAAQ;AAClC,MAAI,SAAS,EAAG,QAAO;AAEvB,QAAM,UAAU,KAAK,MAAM,SAAS,GAAI;AACxC,MAAI,UAAU,GAAI,QAAO,GAAG,OAAO;AAEnC,QAAM,UAAU,KAAK,MAAM,UAAU,EAAE;AACvC,MAAI,UAAU,GAAI,QAAO,GAAG,OAAO;AAEnC,QAAM,QAAQ,KAAK,MAAM,UAAU,EAAE;AACrC,MAAI,QAAQ,GAAI,QAAO,GAAG,KAAK;AAE/B,QAAM,OAAO,KAAK,MAAM,QAAQ,EAAE;AAClC,MAAI,OAAO,GAAI,QAAO,GAAG,IAAI;AAE7B,QAAM,SAAS,KAAK,MAAM,OAAO,EAAE;AACnC,MAAI,SAAS,GAAI,QAAO,GAAG,MAAM;AAEjC,QAAM,QAAQ,KAAK,MAAM,SAAS,EAAE;AACpC,SAAO,GAAG,KAAK;AACjB;","names":[]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// src/format/handle-utils.ts
|
|
2
|
+
function sanitizeHandleInput(raw) {
|
|
3
|
+
let identifier = raw.trim().replace(/^https?:\/\/bsky\.app\/profile\//i, "").replace(/^at:\/\//i, "").replace(/^@/, "").replace(/^https?:\/\//i, "");
|
|
4
|
+
if (!identifier.startsWith("did:")) {
|
|
5
|
+
identifier = identifier.split("/")[0] ?? identifier;
|
|
6
|
+
}
|
|
7
|
+
identifier = identifier.replace(/\.$/, "");
|
|
8
|
+
if (!identifier.startsWith("did:")) {
|
|
9
|
+
identifier = identifier.toLowerCase();
|
|
10
|
+
}
|
|
11
|
+
if (identifier && !identifier.startsWith("did:") && !identifier.includes(".")) {
|
|
12
|
+
identifier = `${identifier}.bsky.social`;
|
|
13
|
+
}
|
|
14
|
+
return identifier;
|
|
15
|
+
}
|
|
16
|
+
export {
|
|
17
|
+
sanitizeHandleInput
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=handle-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/format/handle-utils.ts"],"sourcesContent":["/**\n * Sanitize raw user input into a valid AT Protocol identifier (handle or DID).\n *\n * Handles common mistakes:\n * - Pasting a full Bluesky profile URL\n * - Pasting a raw HTTPS URL to a PDS domain\n * - Using an at:// URI\n * - Prefixing with @\n * - Entering a bare username without .bsky.social\n */\nexport function sanitizeHandleInput(raw: string): string {\n let identifier = raw\n .trim()\n .replace(/^https?:\\/\\/bsky\\.app\\/profile\\//i, '')\n .replace(/^at:\\/\\//i, '')\n .replace(/^@/, '')\n .replace(/^https?:\\/\\//i, '');\n if (!identifier.startsWith('did:')) {\n identifier = identifier.split('/')[0] ?? identifier;\n }\n identifier = identifier.replace(/\\.$/, '');\n if (!identifier.startsWith('did:')) {\n identifier = identifier.toLowerCase();\n }\n\n // Auto-append .bsky.social for bare usernames (no dots, not a DID)\n if (identifier && !identifier.startsWith('did:') && !identifier.includes('.')) {\n identifier = `${identifier}.bsky.social`;\n }\n\n return identifier;\n}\n"],"mappings":";AAUO,SAAS,oBAAoB,KAAqB;AACvD,MAAI,aAAa,IACd,KAAK,EACL,QAAQ,qCAAqC,EAAE,EAC/C,QAAQ,aAAa,EAAE,EACvB,QAAQ,MAAM,EAAE,EAChB,QAAQ,iBAAiB,EAAE;AAC9B,MAAI,CAAC,WAAW,WAAW,MAAM,GAAG;AAClC,iBAAa,WAAW,MAAM,GAAG,EAAE,CAAC,KAAK;AAAA,EAC3C;AACA,eAAa,WAAW,QAAQ,OAAO,EAAE;AACzC,MAAI,CAAC,WAAW,WAAW,MAAM,GAAG;AAClC,iBAAa,WAAW,YAAY;AAAA,EACtC;AAGA,MAAI,cAAc,CAAC,WAAW,WAAW,MAAM,KAAK,CAAC,WAAW,SAAS,GAAG,GAAG;AAC7E,iBAAa,GAAG,UAAU;AAAA,EAC5B;AAEA,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// src/format/index.ts
|
|
2
|
+
import { formatCompanyName } from "./company-name.js";
|
|
3
|
+
import { normalizeLegalForm } from "./normalize-legal-form.js";
|
|
4
|
+
import { normalizeCompanyKey } from "./normalize-company-key.js";
|
|
5
|
+
import { formatIsoTitle, formatRelativeTime } from "./format-time.js";
|
|
6
|
+
import { formatPresentationDuration } from "./format-duration.js";
|
|
7
|
+
import { formatDateRange, formatPositionDateRange, formatTimelineDate } from "./timeline.js";
|
|
8
|
+
import {
|
|
9
|
+
collapseContributors
|
|
10
|
+
} from "./collapse-contributors.js";
|
|
11
|
+
import {
|
|
12
|
+
summarizePresentationDeliveries
|
|
13
|
+
} from "./summarize-deliveries.js";
|
|
14
|
+
import {
|
|
15
|
+
certDateExtractor,
|
|
16
|
+
dateRangeExtractor,
|
|
17
|
+
lexiconDateExtractor,
|
|
18
|
+
singleDateExtractor,
|
|
19
|
+
sortByDateDesc
|
|
20
|
+
} from "./sort-by-date.js";
|
|
21
|
+
import { normalizeDoi } from "./doi.js";
|
|
22
|
+
import {
|
|
23
|
+
formatRelationship,
|
|
24
|
+
parseEndorsementComment
|
|
25
|
+
} from "./endorsement-context.js";
|
|
26
|
+
import { sanitizeHandleInput } from "./handle-utils.js";
|
|
27
|
+
import { countryCodeToFlag, formatLocation, parseLocationString } from "./location-utils.js";
|
|
28
|
+
import { locationSegments } from "./location-segments.js";
|
|
29
|
+
import {
|
|
30
|
+
detectPdsProvider,
|
|
31
|
+
formatStructuredName,
|
|
32
|
+
getDisplayLabel,
|
|
33
|
+
getHandleStem,
|
|
34
|
+
getPdsDisplayName,
|
|
35
|
+
pdsProviderFromApi
|
|
36
|
+
} from "./pds-utils.js";
|
|
37
|
+
import { resolveEmbed, youtubeVideoId, youtubeThumbnailUrl } from "./embed.js";
|
|
38
|
+
import { buildTalkSlug, parseTalkRkey, slugifyTitle } from "./talk-slug.js";
|
|
39
|
+
import { buildMetaDescription } from "./meta-description.js";
|
|
40
|
+
import { limitCombiningMarks, sanitizeDisplayText } from "./text-sanitize.js";
|
|
41
|
+
import { truncateGraphemes } from "./text-truncate.js";
|
|
42
|
+
import { formatDisplayUrl } from "./format-display-url.js";
|
|
43
|
+
import { formatDistanceToNow } from "./time-utils.js";
|
|
44
|
+
import {
|
|
45
|
+
contrastRatio,
|
|
46
|
+
isValidRgbColor,
|
|
47
|
+
meetsContrastAA,
|
|
48
|
+
relativeLuminance,
|
|
49
|
+
rgbToString
|
|
50
|
+
} from "./wcag-contrast.js";
|
|
51
|
+
export {
|
|
52
|
+
buildMetaDescription,
|
|
53
|
+
buildTalkSlug,
|
|
54
|
+
certDateExtractor,
|
|
55
|
+
collapseContributors,
|
|
56
|
+
contrastRatio,
|
|
57
|
+
countryCodeToFlag,
|
|
58
|
+
dateRangeExtractor,
|
|
59
|
+
detectPdsProvider,
|
|
60
|
+
formatCompanyName,
|
|
61
|
+
formatDateRange,
|
|
62
|
+
formatDisplayUrl,
|
|
63
|
+
formatDistanceToNow,
|
|
64
|
+
formatIsoTitle,
|
|
65
|
+
formatLocation,
|
|
66
|
+
formatPositionDateRange,
|
|
67
|
+
formatPresentationDuration,
|
|
68
|
+
formatRelationship,
|
|
69
|
+
formatRelativeTime,
|
|
70
|
+
formatStructuredName,
|
|
71
|
+
formatTimelineDate,
|
|
72
|
+
getDisplayLabel,
|
|
73
|
+
getHandleStem,
|
|
74
|
+
getPdsDisplayName,
|
|
75
|
+
isValidRgbColor,
|
|
76
|
+
lexiconDateExtractor,
|
|
77
|
+
limitCombiningMarks,
|
|
78
|
+
locationSegments,
|
|
79
|
+
meetsContrastAA,
|
|
80
|
+
normalizeCompanyKey,
|
|
81
|
+
normalizeDoi,
|
|
82
|
+
normalizeLegalForm,
|
|
83
|
+
parseEndorsementComment,
|
|
84
|
+
parseLocationString,
|
|
85
|
+
parseTalkRkey,
|
|
86
|
+
pdsProviderFromApi,
|
|
87
|
+
relativeLuminance,
|
|
88
|
+
resolveEmbed,
|
|
89
|
+
rgbToString,
|
|
90
|
+
sanitizeDisplayText,
|
|
91
|
+
sanitizeHandleInput,
|
|
92
|
+
singleDateExtractor,
|
|
93
|
+
slugifyTitle,
|
|
94
|
+
sortByDateDesc,
|
|
95
|
+
summarizePresentationDeliveries,
|
|
96
|
+
truncateGraphemes,
|
|
97
|
+
youtubeThumbnailUrl,
|
|
98
|
+
youtubeVideoId
|
|
99
|
+
};
|
|
100
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/format/index.ts"],"sourcesContent":["export { formatCompanyName } from './company-name.js';\nexport { normalizeLegalForm } from './normalize-legal-form.js';\nexport { normalizeCompanyKey } from './normalize-company-key.js';\nexport { formatIsoTitle, formatRelativeTime } from './format-time.js';\nexport { formatPresentationDuration } from './format-duration.js';\nexport { formatDateRange, formatPositionDateRange, formatTimelineDate } from './timeline.js';\nexport {\n collapseContributors,\n type CollapseContributorsOptions,\n type CollapsedContributors,\n} from './collapse-contributors.js';\nexport {\n summarizePresentationDeliveries,\n type PresentationDeliverySummary,\n} from './summarize-deliveries.js';\nexport {\n certDateExtractor,\n dateRangeExtractor,\n lexiconDateExtractor,\n singleDateExtractor,\n sortByDateDesc,\n} from './sort-by-date.js';\nexport { normalizeDoi } from './doi.js';\nexport {\n formatRelationship,\n parseEndorsementComment,\n type ParsedEndorsementComment,\n} from './endorsement-context.js';\nexport { sanitizeHandleInput } from './handle-utils.js';\nexport { countryCodeToFlag, formatLocation, parseLocationString } from './location-utils.js';\nexport { locationSegments } from './location-segments.js';\nexport type {\n LocationSegment,\n LocationSegmentKind,\n LocationSegmentsOptions,\n} from './location-segments.js';\nexport {\n detectPdsProvider,\n formatStructuredName,\n getDisplayLabel,\n getHandleStem,\n getPdsDisplayName,\n pdsProviderFromApi,\n type PdsProvider,\n} from './pds-utils.js';\nexport { resolveEmbed, youtubeVideoId, youtubeThumbnailUrl, type EmbedResult } from './embed.js';\nexport { buildTalkSlug, parseTalkRkey, slugifyTitle } from './talk-slug.js';\nexport { buildMetaDescription } from './meta-description.js';\nexport type { MetaDescriptionInput, MetaDescriptionPosition } from './meta-description.js';\nexport { limitCombiningMarks, sanitizeDisplayText } from './text-sanitize.js';\nexport { truncateGraphemes } from './text-truncate.js';\nexport { formatDisplayUrl } from './format-display-url.js';\nexport type { DisplayUrl, FormatDisplayUrlOptions, UrlPathPolicy } from './format-display-url.js';\nexport { formatDistanceToNow } from './time-utils.js';\nexport {\n contrastRatio,\n isValidRgbColor,\n meetsContrastAA,\n relativeLuminance,\n rgbToString,\n type RgbColor,\n} from './wcag-contrast.js';\n"],"mappings":";AAAA,SAAS,yBAAyB;AAClC,SAAS,0BAA0B;AACnC,SAAS,2BAA2B;AACpC,SAAS,gBAAgB,0BAA0B;AACnD,SAAS,kCAAkC;AAC3C,SAAS,iBAAiB,yBAAyB,0BAA0B;AAC7E;AAAA,EACE;AAAA,OAGK;AACP;AAAA,EACE;AAAA,OAEK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,oBAAoB;AAC7B;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AACP,SAAS,2BAA2B;AACpC,SAAS,mBAAmB,gBAAgB,2BAA2B;AACvE,SAAS,wBAAwB;AAMjC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,cAAc,gBAAgB,2BAA6C;AACpF,SAAS,eAAe,eAAe,oBAAoB;AAC3D,SAAS,4BAA4B;AAErC,SAAS,qBAAqB,2BAA2B;AACzD,SAAS,yBAAyB;AAClC,SAAS,wBAAwB;AAEjC,SAAS,2BAA2B;AACpC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;","names":[]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// src/format/location-segments.ts
|
|
2
|
+
function clean(value) {
|
|
3
|
+
const trimmed = value?.trim();
|
|
4
|
+
return trimmed ? trimmed : null;
|
|
5
|
+
}
|
|
6
|
+
function locationSegments(loc, options = {}) {
|
|
7
|
+
if (!loc) return [];
|
|
8
|
+
const ordered = [
|
|
9
|
+
{ kind: "locality", value: clean(loc.locality ?? loc.city) },
|
|
10
|
+
{ kind: "region", value: clean(loc.region) },
|
|
11
|
+
{ kind: "country", value: clean(loc.country) }
|
|
12
|
+
];
|
|
13
|
+
const segments = [];
|
|
14
|
+
for (const { kind, value } of ordered) {
|
|
15
|
+
if (value === null) continue;
|
|
16
|
+
const previous = segments[segments.length - 1];
|
|
17
|
+
if (previous && previous.label.toLowerCase() === value.toLowerCase()) continue;
|
|
18
|
+
segments.push({
|
|
19
|
+
kind,
|
|
20
|
+
label: value,
|
|
21
|
+
searchHref: kind === "country" && options.searchable !== false ? `/search?${new URLSearchParams({ country: value }).toString()}` : null
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
return segments;
|
|
25
|
+
}
|
|
26
|
+
export {
|
|
27
|
+
locationSegments
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=location-segments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/format/location-segments.ts"],"sourcesContent":["import type { LocationValue } from '../types/index.js';\n\nexport type LocationSegmentKind = 'locality' | 'region' | 'country';\n\nexport interface LocationSegment {\n kind: LocationSegmentKind;\n /** Text to render for this segment. */\n label: string;\n /** Search URL for this segment, or null when it should render as plain text. */\n searchHref: string | null;\n}\n\nexport interface LocationSegmentsOptions {\n /**\n * Whether the country is one the search index can filter on. Profiles\n * imported from LinkedIn sometimes hold a metro area (\"Greater Bielefeld\n * Area\") in the country field; linking one promises a result set that does\n * not exist, so those render as plain text. Defaults to true.\n */\n searchable?: boolean;\n}\n\nfunction clean(value: string | undefined): string | null {\n const trimmed = value?.trim();\n return trimmed ? trimmed : null;\n}\n\n/**\n * Split a location into the ordered segments shown on a profile, marking which\n * ones link to a search.\n *\n * Only the country links today. Region is the weakest of the three (free text,\n * no search filter) and locality has no filter yet, so both render as plain\n * text until those land.\n *\n * A segment equal to its neighbour is dropped: capital regions produce\n * \"Oslo, Oslo, Norway\", and repeating the word helps nobody. Blank segments are\n * dropped with their separator so nothing renders as \"Oslo, , Norway\".\n */\nexport function locationSegments(\n loc: LocationValue | null | undefined,\n options: LocationSegmentsOptions = {},\n): LocationSegment[] {\n if (!loc) return [];\n\n const ordered: Array<{ kind: LocationSegmentKind; value: string | null }> = [\n { kind: 'locality', value: clean(loc.locality ?? loc.city) },\n { kind: 'region', value: clean(loc.region) },\n { kind: 'country', value: clean(loc.country) },\n ];\n\n const segments: LocationSegment[] = [];\n for (const { kind, value } of ordered) {\n if (value === null) continue;\n const previous = segments[segments.length - 1];\n if (previous && previous.label.toLowerCase() === value.toLowerCase()) continue;\n\n segments.push({\n kind,\n label: value,\n searchHref:\n kind === 'country' && options.searchable !== false\n ? `/search?${new URLSearchParams({ country: value }).toString()}`\n : null,\n });\n }\n\n return segments;\n}\n"],"mappings":";AAsBA,SAAS,MAAM,OAA0C;AACvD,QAAM,UAAU,OAAO,KAAK;AAC5B,SAAO,UAAU,UAAU;AAC7B;AAcO,SAAS,iBACd,KACA,UAAmC,CAAC,GACjB;AACnB,MAAI,CAAC,IAAK,QAAO,CAAC;AAElB,QAAM,UAAsE;AAAA,IAC1E,EAAE,MAAM,YAAY,OAAO,MAAM,IAAI,YAAY,IAAI,IAAI,EAAE;AAAA,IAC3D,EAAE,MAAM,UAAU,OAAO,MAAM,IAAI,MAAM,EAAE;AAAA,IAC3C,EAAE,MAAM,WAAW,OAAO,MAAM,IAAI,OAAO,EAAE;AAAA,EAC/C;AAEA,QAAM,WAA8B,CAAC;AACrC,aAAW,EAAE,MAAM,MAAM,KAAK,SAAS;AACrC,QAAI,UAAU,KAAM;AACpB,UAAM,WAAW,SAAS,SAAS,SAAS,CAAC;AAC7C,QAAI,YAAY,SAAS,MAAM,YAAY,MAAM,MAAM,YAAY,EAAG;AAEtE,aAAS,KAAK;AAAA,MACZ;AAAA,MACA,OAAO;AAAA,MACP,YACE,SAAS,aAAa,QAAQ,eAAe,QACzC,WAAW,IAAI,gBAAgB,EAAE,SAAS,MAAM,CAAC,EAAE,SAAS,CAAC,KAC7D;AAAA,IACR,CAAC;AAAA,EACH;AAEA,SAAO;AACT;","names":[]}
|