@singi-labs/sifa-sdk 0.19.110 → 0.19.112

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 (40) hide show
  1. package/dist/cards/app-url-patterns.js +37 -1
  2. package/dist/cards/app-url-patterns.js.map +1 -1
  3. package/dist/cards/resolve-card-url.js +15 -0
  4. package/dist/cards/resolve-card-url.js.map +1 -1
  5. package/dist/index.cjs +122 -6
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.d.cts +12 -4
  8. package/dist/index.d.ts +12 -4
  9. package/dist/index.js +1 -1
  10. package/dist/{network-map-CInWwHXW.d.cts → network-map-BwM-Wvxt.d.cts} +2 -2
  11. package/dist/{network-map-zDjAIXXR.d.ts → network-map-CrkLq2Gz.d.ts} +2 -2
  12. package/dist/{profile-summary-0T0VS7Rz.d.ts → profile-summary-CwJymH9d.d.ts} +1 -1
  13. package/dist/{profile-summary-C5LlxPKK.d.cts → profile-summary-TkVxvll4.d.cts} +1 -1
  14. package/dist/query/fetchers/index.d.cts +5 -5
  15. package/dist/query/fetchers/index.d.ts +5 -5
  16. package/dist/query/hooks/index.d.cts +3 -3
  17. package/dist/query/hooks/index.d.ts +3 -3
  18. package/dist/query/index.d.cts +8 -8
  19. package/dist/query/index.d.ts +8 -8
  20. package/dist/resume/funder-cv.js +1 -1
  21. package/dist/resume/funder-cv.js.map +1 -1
  22. package/dist/resume/index.cjs +1 -1
  23. package/dist/resume/index.cjs.map +1 -1
  24. package/dist/stream/to-stream-card-vm.js +26 -0
  25. package/dist/stream/to-stream-card-vm.js.map +1 -1
  26. package/dist/stream/verbs.js +5 -3
  27. package/dist/stream/verbs.js.map +1 -1
  28. package/dist/taxonomy/activity-tiers.js +25 -0
  29. package/dist/taxonomy/activity-tiers.js.map +1 -1
  30. package/dist/taxonomy/app-categories.js +1 -0
  31. package/dist/taxonomy/app-categories.js.map +1 -1
  32. package/dist/taxonomy/app-category-map.js +12 -1
  33. package/dist/taxonomy/app-category-map.js.map +1 -1
  34. package/dist/{types-B0zZiWDH.d.ts → types-CQA_vO39.d.ts} +1 -1
  35. package/dist/{types-jE1ofoPI.d.cts → types-DMTHqi2m.d.cts} +1 -1
  36. package/dist/{use-user-settings-CCAw3_Q2.d.ts → use-user-settings-1zgi6qwl.d.ts} +2 -2
  37. package/dist/{use-user-settings-DjnTECGe.d.cts → use-user-settings-BmuAd8D4.d.cts} +2 -2
  38. package/dist/{user-settings-D_DjukR6.d.ts → user-settings-Btvrox4G.d.ts} +1 -1
  39. package/dist/{user-settings-C5sOgvhL.d.cts → user-settings-D4vdBf0A.d.cts} +1 -1
  40. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/resume/funder-cv.ts"],"sourcesContent":["/**\n * Funder-format CV documents: the NIH Biographical Sketch (Common Form plus\n * NIH Supplement) and the ERC Part B1 \"Curriculum vitae and Track Record\".\n *\n * A pure mapper from a Sifa profile and the owner's pick of items to a plain\n * JSON document a typesetter lays out. Like the JSON Resume emitter next door,\n * nothing here fetches or renders.\n *\n * Funders cap how many items a CV may list. The caps live in\n * `FUNDER_CV_GROUPS`, so the export page that offers the choice and the server\n * that enforces it read one number. Sections a funder asks for that Sifa holds\n * no records for (a personal statement, contributions to science, career\n * breaks) are emitted as `null` or empty, for the template to show as a\n * placeholder the applicant fills in.\n */\n\nimport { z } from 'zod';\n\nimport { formatLocation } from '../format/location-utils.js';\nimport { formatStructuredName } from '../format/pds-utils.js';\nimport { filterHidden } from '../profile/section-model.js';\nimport { isTeachingCourse } from '../taxonomy/course-role.js';\nimport type {\n ExternalAccount,\n ProfileCourse,\n ProfileEducation,\n ProfileHonor,\n ProfilePosition,\n ProfilePresentation,\n ProfilePublication,\n ProfileVolunteering,\n} from '../types/index.js';\nimport { normalizeExportContact, type ExportContact } from './export-contact.js';\nimport { toResumeDate } from './json-resume.js';\n\nexport const FUNDER_CV_FORMATS = ['nih', 'erc'] as const;\nexport type FunderCvFormat = (typeof FUNDER_CV_FORMATS)[number];\n\nexport const FUNDER_CV_ITEM_KINDS = ['publication', 'presentation', 'honor'] as const;\nexport type FunderCvItemKind = (typeof FUNDER_CV_ITEM_KINDS)[number];\n\nexport interface FunderCvGroupSpec {\n readonly kinds: readonly FunderCvItemKind[];\n /** Most items the funder's form takes for this group. */\n readonly max: number;\n}\n\n/**\n * The pickable groups per format, with the funder's item limits.\n *\n * - NIH Common Form \"Products\": up to 5 closely related to the proposed\n * project and up to 5 other significant products. Publications, conference\n * papers and presentations all count. NIH Supplement \"Honors\": up to 15.\n * - ERC Part B1 \"Research achievements\": up to ten research outputs. \"Peer\n * recognition\" is \"selected examples\" with no number; 15 is Sifa's own cap,\n * to keep the section inside the 4-page limit.\n */\nexport const FUNDER_CV_GROUPS = {\n nih: {\n related: { kinds: ['publication', 'presentation'], max: 5 },\n other: { kinds: ['publication', 'presentation'], max: 5 },\n honors: { kinds: ['honor'], max: 15 },\n },\n erc: {\n outputs: { kinds: ['publication'], max: 10 },\n recognition: { kinds: ['honor', 'presentation'], max: 15 },\n },\n} as const satisfies Record<FunderCvFormat, Record<string, FunderCvGroupSpec>>;\n\nexport type FunderCvGroupName<F extends FunderCvFormat> = keyof (typeof FUNDER_CV_GROUPS)[F] &\n string;\n\n/** A picked item: `<kind>:<rkey>`, e.g. `publication:3kxyz`. */\nexport function funderCvItemId(kind: FunderCvItemKind, rkey: string): string {\n return `${kind}:${rkey}`;\n}\n\n/** Record key syntax (atproto): 1-512 of A-Z a-z 0-9 . - _ : ~ */\nconst ITEM_ID_RE = /^(publication|presentation|honor):[A-Za-z0-9._~:-]{1,512}$/;\n\nfunction kindOf(id: string): FunderCvItemKind | undefined {\n const kind = id.slice(0, id.indexOf(':'));\n return (FUNDER_CV_ITEM_KINDS as readonly string[]).includes(kind)\n ? (kind as FunderCvItemKind)\n : undefined;\n}\n\nfunction groupSchema(spec: FunderCvGroupSpec) {\n return z\n .array(\n z\n .string()\n .regex(ITEM_ID_RE)\n .refine((id) => spec.kinds.includes(kindOf(id) as FunderCvItemKind), {\n message: `Only ${spec.kinds.join(', ')} items fit here`,\n }),\n )\n .max(spec.max)\n .default([]);\n}\n\nfunction noRepeats(lists: readonly string[][]): boolean {\n const all = lists.flat();\n return new Set(all).size === all.length;\n}\n\nconst nihSelectionSchema = z\n .object({\n format: z.literal('nih'),\n related: groupSchema(FUNDER_CV_GROUPS.nih.related),\n other: groupSchema(FUNDER_CV_GROUPS.nih.other),\n honors: groupSchema(FUNDER_CV_GROUPS.nih.honors),\n })\n .refine((s) => noRepeats([s.related, s.other, s.honors]), {\n message: 'An item can be listed once',\n });\n\nconst ercSelectionSchema = z\n .object({\n format: z.literal('erc'),\n outputs: groupSchema(FUNDER_CV_GROUPS.erc.outputs),\n recognition: groupSchema(FUNDER_CV_GROUPS.erc.recognition),\n })\n .refine((s) => noRepeats([s.outputs, s.recognition]), {\n message: 'An item can be listed once',\n });\n\n/**\n * The owner's pick for one format. Enforces the shape and the funder's limits;\n * whether each id is one of the owner's own visible records is a separate\n * check against the profile, `findUnknownFunderCvItems`.\n */\nexport const funderCvSelectionSchema = z.discriminatedUnion('format', [\n nihSelectionSchema,\n ercSelectionSchema,\n]);\n\nexport type FunderCvSelection = z.infer<typeof funderCvSelectionSchema>;\n\n/** The subset of `Profile` the funder mapper reads. */\nexport interface FunderCvProfileInput {\n readonly handle: string;\n readonly displayName?: string;\n readonly givenName?: string;\n readonly familyName?: string;\n readonly headline?: string;\n readonly website?: string;\n readonly positions?: ProfilePosition[];\n readonly education?: ProfileEducation[];\n readonly publications?: ProfilePublication[];\n readonly presentations?: ProfilePresentation[];\n readonly honors?: ProfileHonor[];\n readonly courses?: ProfileCourse[];\n readonly volunteering?: ProfileVolunteering[];\n readonly externalAccounts?: ExternalAccount[];\n}\n\nexport interface FunderCvCandidate {\n readonly id: string;\n readonly kind: FunderCvItemKind;\n readonly title: string;\n /** `YYYY`, `YYYY-MM` or `YYYY-MM-DD`; absent when the record has no date. */\n readonly date?: string;\n}\n\n/** The most recent visible delivery of a talk, which dates it. */\nfunction latestDeliveryRecord(presentation: ProfilePresentation) {\n const dateOf = (d: { date?: string | null }) => toResumeDate(d.date ?? undefined) ?? '';\n return [...filterHidden(presentation.deliveries)]\n .sort((a, b) => dateOf(a).localeCompare(dateOf(b)))\n .at(-1);\n}\n\nfunction latestDelivery(presentation: ProfilePresentation): string | undefined {\n return toResumeDate(latestDeliveryRecord(presentation)?.date ?? undefined);\n}\n\nfunction candidatesOf(profile: FunderCvProfileInput, kind: FunderCvItemKind): FunderCvCandidate[] {\n switch (kind) {\n case 'publication':\n return filterHidden(profile.publications).map((p) => ({\n id: funderCvItemId(kind, p.rkey),\n kind,\n title: p.title,\n date: toResumeDate(p.date),\n }));\n case 'presentation':\n return filterHidden(profile.presentations).map((p) => ({\n id: funderCvItemId(kind, p.rkey),\n kind,\n title: p.title,\n date: latestDelivery(p),\n }));\n case 'honor':\n return filterHidden(profile.honors).map((h) => ({\n id: funderCvItemId(kind, h.rkey),\n kind,\n title: h.title,\n date: toResumeDate(h.date),\n }));\n }\n}\n\n/**\n * The owner's visible items of the given kinds, most recent first. Undated\n * items go last, in profile order. The sort is stable, so equal dates keep\n * profile order too.\n */\nexport function listFunderCvCandidates(\n profile: FunderCvProfileInput,\n kinds: readonly FunderCvItemKind[],\n): FunderCvCandidate[] {\n const all = kinds.flatMap((kind) => candidatesOf(profile, kind));\n return all.sort((a, b) => {\n if (a.date && b.date) return b.date.localeCompare(a.date);\n if (a.date) return -1;\n if (b.date) return 1;\n return 0;\n });\n}\n\n/**\n * What the export page pre-ticks: the most recent publications up to each\n * limit (for NIH, the first five as closely related and the next five as\n * other), and the most recent honors. Talks are offered but not pre-ticked.\n */\nexport function defaultFunderCvSelection(\n profile: FunderCvProfileInput,\n format: FunderCvFormat,\n): FunderCvSelection {\n const publications = listFunderCvCandidates(profile, ['publication']).map((c) => c.id);\n const honors = listFunderCvCandidates(profile, ['honor']).map((c) => c.id);\n if (format === 'nih') {\n const { related, other } = FUNDER_CV_GROUPS.nih;\n return {\n format,\n related: publications.slice(0, related.max),\n other: publications.slice(related.max, related.max + other.max),\n honors: honors.slice(0, FUNDER_CV_GROUPS.nih.honors.max),\n };\n }\n return {\n format,\n outputs: publications.slice(0, FUNDER_CV_GROUPS.erc.outputs.max),\n recognition: honors.slice(0, FUNDER_CV_GROUPS.erc.recognition.max),\n };\n}\n\nfunction selectedIds(selection: FunderCvSelection): string[] {\n return selection.format === 'nih'\n ? [...selection.related, ...selection.other, ...selection.honors]\n : [...selection.outputs, ...selection.recognition];\n}\n\n/**\n * Ids in the selection that are not visible records on this profile. The\n * profile is the owner's own, so an empty result means every pick is theirs.\n */\nexport function findUnknownFunderCvItems(\n profile: FunderCvProfileInput,\n selection: FunderCvSelection,\n): string[] {\n const known = new Set(listFunderCvCandidates(profile, FUNDER_CV_ITEM_KINDS).map((c) => c.id));\n return selectedIds(selection).filter((id) => !known.has(id));\n}\n\n// ---------------------------------------------------------------------------\n// Document shapes\n// ---------------------------------------------------------------------------\n\nexport interface FunderCvPerson {\n /** Full name as the profile shows it; the handle when there is no name. */\n readonly name: string;\n readonly givenName?: string;\n readonly familyName?: string;\n /** Bare ORCID iD, e.g. `0000-0002-1825-0097`. */\n readonly orcid?: string;\n readonly website?: string;\n}\n\n/**\n * The ERC person, with the contact and personal details the owner typed for\n * this export. The NIH Biographical Sketch has no such block, so only ERC carries them.\n */\nexport interface ErcFunderCvPerson extends FunderCvPerson {\n readonly email?: string;\n readonly phone?: string;\n /** Lines are separated by `\\n`. */\n readonly address?: string;\n readonly driversLicense?: string;\n readonly rightToWork?: string;\n readonly nationality?: string;\n}\n\nexport interface FunderCvOptions {\n /**\n * Contact details the owner typed for this export. Sifa stores none of\n * them. Used for ERC only.\n */\n readonly contact?: ExportContact;\n}\n\n/** One product or research output, ready to cite. */\nexport interface FunderCvCitation {\n readonly kind: 'publication' | 'presentation';\n readonly title: string;\n /** Authors in record order, comma separated. For a talk, the speaker. */\n readonly authors?: string;\n /** Journal, publisher or event. */\n readonly venue?: string;\n /** Year. */\n readonly date?: string;\n readonly doi?: string;\n readonly url?: string;\n}\n\nexport interface FunderCvHonor {\n readonly year?: string;\n readonly title: string;\n readonly organization?: string;\n}\n\nexport interface NihEducation {\n readonly organization: string;\n readonly location?: string;\n readonly degree?: string;\n /** Start date, `MM/YYYY` or `YYYY`. */\n readonly start?: string;\n /** Receipt date, `MM/YYYY` or `YYYY`. */\n readonly end?: string;\n readonly field?: string;\n}\n\nexport interface FunderCvAppointment {\n /** `YYYY`. */\n readonly start?: string;\n /** `YYYY`, or null for a current appointment. */\n readonly end: string | null;\n readonly title: string;\n readonly organization?: string;\n readonly location?: string;\n}\n\n/** NIH Biographical Sketch: Common Form plus NIH Supplement. */\nexport interface NihFunderCv {\n readonly format: 'nih';\n readonly person: FunderCvPerson;\n readonly positionTitle?: string;\n /** Primary organization: the current position's. */\n readonly organization?: string;\n readonly location?: string;\n readonly education: NihEducation[];\n readonly appointments: FunderCvAppointment[];\n readonly products: { readonly related: FunderCvCitation[]; readonly other: FunderCvCitation[] };\n readonly honors: FunderCvHonor[];\n /** Supplement \"Personal Statement\". Sifa holds no record for it. */\n readonly personalStatement: string | null;\n /** Supplement \"Contributions to Science\". Sifa holds no record for them. */\n readonly contributions: string[];\n}\n\nexport interface ErcEducation {\n /** Receipt date, `MM/YYYY` or `YYYY`. */\n readonly date?: string;\n readonly degree?: string;\n readonly field?: string;\n readonly institution: string;\n}\n\nexport interface ErcRecognition {\n readonly kind: 'honor' | 'presentation';\n readonly year?: string;\n readonly title: string;\n /** Awarding body, or the event a talk was given at. */\n readonly organization?: string;\n}\n\nexport interface ErcContribution {\n readonly kind: 'teaching' | 'service';\n readonly start?: string;\n readonly end: string | null;\n readonly title: string;\n readonly organization?: string;\n}\n\n/** ERC Part B1, section \"Curriculum vitae and Track Record\". */\nexport interface ErcFunderCv {\n readonly format: 'erc';\n readonly person: ErcFunderCvPerson;\n readonly education: ErcEducation[];\n readonly currentPositions: FunderCvAppointment[];\n readonly previousPositions: FunderCvAppointment[];\n readonly outputs: FunderCvCitation[];\n readonly recognition: ErcRecognition[];\n readonly communityContributions: ErcContribution[];\n /** Proposal acronym for the page header. Not a profile fact. */\n readonly acronym: string | null;\n /** \"Career breaks, diverse career paths and major life events\". */\n readonly careerBreaks: string | null;\n}\n\nexport type FunderCvDocument = NihFunderCv | ErcFunderCv;\n\n// ---------------------------------------------------------------------------\n// Mapping\n// ---------------------------------------------------------------------------\n\nfunction text(value: string | null | undefined): string | undefined {\n const trimmed = value?.trim();\n return trimmed ? trimmed : undefined;\n}\n\nfunction compact<T extends object>(value: T): T {\n return Object.fromEntries(Object.entries(value).filter(([, v]) => v !== undefined)) as T;\n}\n\nfunction year(value: string | null | undefined): string | undefined {\n return toResumeDate(value ?? undefined)?.slice(0, 4);\n}\n\n/** `MM/YYYY` when the month is known, else `YYYY`. */\nfunction monthYear(value: string | null | undefined): string | undefined {\n const date = toResumeDate(value ?? undefined);\n if (!date) return undefined;\n return date.length >= 7 ? `${date.slice(5, 7)}/${date.slice(0, 4)}` : date;\n}\n\n/** Newest start first; undated entries last, in profile order. */\nfunction byStartDesc<T>(items: T[], start: (item: T) => string | undefined): T[] {\n return [...items].sort((a, b) => {\n const da = toResumeDate(start(a));\n const db = toResumeDate(start(b));\n if (da && db) return db.localeCompare(da);\n if (da) return -1;\n if (db) return 1;\n return 0;\n });\n}\n\nconst ORCID_RE = /\\d{4}-\\d{4}-\\d{4}-\\d{3}[\\dX]/;\n\nfunction orcidOf(accounts: ExternalAccount[] | undefined): string | undefined {\n const account = filterHidden(accounts).find((a) => a.platform.toLowerCase() === 'orcid');\n return account ? ORCID_RE.exec(account.url)?.[0] : undefined;\n}\n\nfunction personOf(profile: FunderCvProfileInput): FunderCvPerson {\n return compact<FunderCvPerson>({\n name:\n formatStructuredName(profile.givenName, profile.familyName) ??\n text(profile.displayName) ??\n profile.handle,\n givenName: text(profile.givenName),\n familyName: text(profile.familyName),\n orcid: orcidOf(profile.externalAccounts),\n website: text(profile.website),\n });\n}\n\nfunction appointment(position: ProfilePosition): FunderCvAppointment {\n return compact<FunderCvAppointment>({\n start: year(position.startedAt),\n end: year(position.endedAt) ?? null,\n title: position.title,\n organization: text(position.entityName) ?? text(position.company),\n location: text(formatLocation(position.location)),\n });\n}\n\n/** The current position to name on the form: the flagged primary, else the newest. */\nfunction currentPosition(positions: ProfilePosition[]): ProfilePosition | undefined {\n const current = byStartDesc(\n positions.filter((p) => !text(p.endedAt)),\n (p) => p.startedAt,\n );\n return current.find((p) => p.primary) ?? current[0];\n}\n\nfunction lookup(profile: FunderCvProfileInput) {\n const publications = new Map(filterHidden(profile.publications).map((p) => [p.rkey, p]));\n const presentations = new Map(filterHidden(profile.presentations).map((p) => [p.rkey, p]));\n const honors = new Map(filterHidden(profile.honors).map((h) => [h.rkey, h]));\n const split = (id: string) => {\n const at = id.indexOf(':');\n return { kind: id.slice(0, at), rkey: id.slice(at + 1) };\n };\n return { publications, presentations, honors, split };\n}\n\nfunction citations(\n ids: readonly string[],\n profile: FunderCvProfileInput,\n person: FunderCvPerson,\n): FunderCvCitation[] {\n const { publications, presentations, split } = lookup(profile);\n return ids.flatMap((id): FunderCvCitation[] => {\n const { kind, rkey } = split(id);\n if (kind === 'publication') {\n const p = publications.get(rkey);\n if (!p) return [];\n const authors = (p.contributors ?? []).map((c) => c.name.trim()).filter(Boolean);\n return [\n compact<FunderCvCitation>({\n kind: 'publication',\n title: p.title,\n authors: authors.length > 0 ? authors.join(', ') : undefined,\n venue: text(p.publisher),\n date: year(p.date),\n doi: text(p.doi),\n url: text(p.url),\n }),\n ];\n }\n if (kind === 'presentation') {\n const talk = presentations.get(rkey);\n if (!talk) return [];\n const delivery = latestDeliveryRecord(talk);\n return [\n compact<FunderCvCitation>({\n kind: 'presentation',\n title: talk.title,\n authors: person.name,\n venue: text(delivery?.eventName),\n date: year(delivery?.date),\n url: text(talk.links?.[0]?.uri),\n }),\n ];\n }\n return [];\n });\n}\n\nfunction honorsOf(ids: readonly string[], profile: FunderCvProfileInput): FunderCvHonor[] {\n const { honors, split } = lookup(profile);\n return ids.flatMap((id) => {\n const { kind, rkey } = split(id);\n const h = kind === 'honor' ? honors.get(rkey) : undefined;\n if (!h) return [];\n return [\n compact<FunderCvHonor>({\n year: year(h.date),\n title: h.title,\n organization: text(h.entityName) ?? text(h.issuer),\n }),\n ];\n });\n}\n\n/**\n * Build the funder document from the profile and the owner's selection.\n *\n * Hidden records never appear. Selected ids that match no visible record are\n * skipped; callers that must refuse them check `findUnknownFunderCvItems`\n * first. Selected items keep the order they were picked in.\n */\nexport function profileToFunderCv(\n profile: FunderCvProfileInput,\n selection: FunderCvSelection,\n options: FunderCvOptions = {},\n): FunderCvDocument {\n const person = personOf(profile);\n const positions = filterHidden(profile.positions);\n const education = byStartDesc(filterHidden(profile.education), (e) => e.startedAt);\n\n if (selection.format === 'nih') {\n const current = currentPosition(positions);\n return compact<NihFunderCv>({\n format: 'nih',\n person,\n positionTitle: text(current?.title),\n organization: current ? appointment(current).organization : undefined,\n location: text(formatLocation(current?.location)),\n education: education.map((e) =>\n compact<NihEducation>({\n organization: text(e.entityName) ?? e.institution,\n degree: text(e.degree),\n start: monthYear(e.startedAt),\n end: monthYear(e.endedAt),\n field: text(e.fieldOfStudy),\n }),\n ),\n appointments: byStartDesc(positions, (p) => p.startedAt).map(appointment),\n products: {\n related: citations(selection.related, profile, person),\n other: citations(selection.other, profile, person),\n },\n honors: honorsOf(selection.honors, profile),\n personalStatement: null,\n contributions: [],\n });\n }\n\n const appointments = byStartDesc(positions, (p) => p.startedAt);\n const { presentations, split } = lookup(profile);\n\n const recognition = selection.recognition.flatMap((id): ErcRecognition[] => {\n const { kind, rkey } = split(id);\n if (kind === 'honor') {\n const [honor] = honorsOf([id], profile);\n return honor ? [{ kind: 'honor', ...honor }] : [];\n }\n const talk = kind === 'presentation' ? presentations.get(rkey) : undefined;\n if (!talk) return [];\n const delivery = latestDeliveryRecord(talk);\n return [\n compact<ErcRecognition>({\n kind: 'presentation',\n year: year(delivery?.date),\n title: talk.title,\n organization: text(delivery?.eventName),\n }),\n ];\n });\n\n const teaching = filterHidden(profile.courses)\n .filter(isTeachingCourse)\n .map((c) =>\n compact<ErcContribution>({\n kind: 'teaching',\n start: year(c.startedAt),\n end: year(c.endedAt) ?? null,\n title: c.name,\n organization: text(c.entityName) ?? text(c.institution),\n }),\n );\n const service = filterHidden(profile.volunteering).map((v) =>\n compact<ErcContribution>({\n kind: 'service',\n start: year(v.startDate),\n end: year(v.endDate) ?? null,\n title: text(v.role) ?? text(v.entityName) ?? v.organization,\n organization: text(v.entityName) ?? text(v.organization),\n }),\n );\n\n return {\n format: 'erc',\n person: { ...person, ...normalizeExportContact(options.contact ?? {}) },\n education: education.map((e) =>\n compact<ErcEducation>({\n date: monthYear(e.endedAt) ?? monthYear(e.startedAt),\n degree: text(e.degree),\n field: text(e.fieldOfStudy),\n institution: text(e.entityName) ?? e.institution,\n }),\n ),\n currentPositions: appointments.filter((p) => !text(p.endedAt)).map(appointment),\n previousPositions: appointments.filter((p) => !!text(p.endedAt)).map(appointment),\n outputs: citations(selection.outputs, profile, person),\n recognition,\n communityContributions: [...teaching, ...service],\n acronym: null,\n careerBreaks: null,\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/resume/funder-cv.ts"],"sourcesContent":["/**\n * Funder-format CV documents: the NIH Biographical Sketch (Common Form plus\n * NIH Supplement) and the ERC Part B1 \"Curriculum vitae and Track Record\".\n *\n * A pure mapper from a Sifa profile and the owner's pick of items to a plain\n * JSON document a typesetter lays out. Like the JSON Resume emitter next door,\n * nothing here fetches or renders.\n *\n * Funders cap how many items a CV may list. The caps live in\n * `FUNDER_CV_GROUPS`, so the export page that offers the choice and the server\n * that enforces it read one number. Sections a funder asks for that Sifa holds\n * no records for (a personal statement, contributions to science, career\n * breaks) are emitted as `null` or empty, for the template to show as a\n * placeholder the applicant fills in.\n */\n\nimport { z } from 'zod';\n\nimport { formatLocation } from '../format/location-utils.js';\nimport { formatStructuredName } from '../format/pds-utils.js';\nimport { filterHidden } from '../profile/section-model.js';\nimport { isTeachingCourse } from '../taxonomy/course-role.js';\nimport type {\n ExternalAccount,\n ProfileCourse,\n ProfileEducation,\n ProfileHonor,\n ProfilePosition,\n ProfilePresentation,\n ProfilePublication,\n ProfileVolunteering,\n} from '../types/index.js';\nimport { normalizeExportContact, type ExportContact } from './export-contact.js';\nimport { toResumeDate } from './json-resume.js';\n\nexport const FUNDER_CV_FORMATS = ['nih', 'erc'] as const;\nexport type FunderCvFormat = (typeof FUNDER_CV_FORMATS)[number];\n\nexport const FUNDER_CV_ITEM_KINDS = ['publication', 'presentation', 'honor'] as const;\nexport type FunderCvItemKind = (typeof FUNDER_CV_ITEM_KINDS)[number];\n\nexport interface FunderCvGroupSpec {\n readonly kinds: readonly FunderCvItemKind[];\n /** Most items the funder's form takes for this group. */\n readonly max: number;\n}\n\n/**\n * The pickable groups per format, with the funder's item limits.\n *\n * - NIH Common Form \"Products\": up to 5 closely related to the proposed\n * project and up to 5 other significant products. Publications, conference\n * papers and presentations all count. NIH Supplement \"Honors\": up to 15.\n * - ERC Part B1 \"Research achievements\": up to ten research outputs. \"Peer\n * recognition\" is \"selected examples\" with no number; 15 is Sifa's own cap,\n * to keep the section inside the 4-page limit.\n */\nexport const FUNDER_CV_GROUPS = {\n nih: {\n related: { kinds: ['publication', 'presentation'], max: 5 },\n other: { kinds: ['publication', 'presentation'], max: 5 },\n honors: { kinds: ['honor'], max: 15 },\n },\n erc: {\n outputs: { kinds: ['publication'], max: 10 },\n recognition: { kinds: ['honor', 'presentation'], max: 15 },\n },\n} as const satisfies Record<FunderCvFormat, Record<string, FunderCvGroupSpec>>;\n\nexport type FunderCvGroupName<F extends FunderCvFormat> = keyof (typeof FUNDER_CV_GROUPS)[F] &\n string;\n\n/** A picked item: `<kind>:<rkey>`, e.g. `publication:3kxyz`. */\nexport function funderCvItemId(kind: FunderCvItemKind, rkey: string): string {\n return `${kind}:${rkey}`;\n}\n\n/**\n * Record key syntax (atproto: 1-512 of A-Z a-z 0-9 . - _ : ~), plus `/`:\n * publications from Standard.site are keyed by their AT URI (`std-at://...`).\n */\nconst ITEM_ID_RE = /^(publication|presentation|honor):[A-Za-z0-9._~:/-]{1,512}$/;\n\nfunction kindOf(id: string): FunderCvItemKind | undefined {\n const kind = id.slice(0, id.indexOf(':'));\n return (FUNDER_CV_ITEM_KINDS as readonly string[]).includes(kind)\n ? (kind as FunderCvItemKind)\n : undefined;\n}\n\nfunction groupSchema(spec: FunderCvGroupSpec) {\n return z\n .array(\n z\n .string()\n .regex(ITEM_ID_RE)\n .refine((id) => spec.kinds.includes(kindOf(id) as FunderCvItemKind), {\n message: `Only ${spec.kinds.join(', ')} items fit here`,\n }),\n )\n .max(spec.max)\n .default([]);\n}\n\nfunction noRepeats(lists: readonly string[][]): boolean {\n const all = lists.flat();\n return new Set(all).size === all.length;\n}\n\nconst nihSelectionSchema = z\n .object({\n format: z.literal('nih'),\n related: groupSchema(FUNDER_CV_GROUPS.nih.related),\n other: groupSchema(FUNDER_CV_GROUPS.nih.other),\n honors: groupSchema(FUNDER_CV_GROUPS.nih.honors),\n })\n .refine((s) => noRepeats([s.related, s.other, s.honors]), {\n message: 'An item can be listed once',\n });\n\nconst ercSelectionSchema = z\n .object({\n format: z.literal('erc'),\n outputs: groupSchema(FUNDER_CV_GROUPS.erc.outputs),\n recognition: groupSchema(FUNDER_CV_GROUPS.erc.recognition),\n })\n .refine((s) => noRepeats([s.outputs, s.recognition]), {\n message: 'An item can be listed once',\n });\n\n/**\n * The owner's pick for one format. Enforces the shape and the funder's limits;\n * whether each id is one of the owner's own visible records is a separate\n * check against the profile, `findUnknownFunderCvItems`.\n */\nexport const funderCvSelectionSchema = z.discriminatedUnion('format', [\n nihSelectionSchema,\n ercSelectionSchema,\n]);\n\nexport type FunderCvSelection = z.infer<typeof funderCvSelectionSchema>;\n\n/** The subset of `Profile` the funder mapper reads. */\nexport interface FunderCvProfileInput {\n readonly handle: string;\n readonly displayName?: string;\n readonly givenName?: string;\n readonly familyName?: string;\n readonly headline?: string;\n readonly website?: string;\n readonly positions?: ProfilePosition[];\n readonly education?: ProfileEducation[];\n readonly publications?: ProfilePublication[];\n readonly presentations?: ProfilePresentation[];\n readonly honors?: ProfileHonor[];\n readonly courses?: ProfileCourse[];\n readonly volunteering?: ProfileVolunteering[];\n readonly externalAccounts?: ExternalAccount[];\n}\n\nexport interface FunderCvCandidate {\n readonly id: string;\n readonly kind: FunderCvItemKind;\n readonly title: string;\n /** `YYYY`, `YYYY-MM` or `YYYY-MM-DD`; absent when the record has no date. */\n readonly date?: string;\n}\n\n/** The most recent visible delivery of a talk, which dates it. */\nfunction latestDeliveryRecord(presentation: ProfilePresentation) {\n const dateOf = (d: { date?: string | null }) => toResumeDate(d.date ?? undefined) ?? '';\n return [...filterHidden(presentation.deliveries)]\n .sort((a, b) => dateOf(a).localeCompare(dateOf(b)))\n .at(-1);\n}\n\nfunction latestDelivery(presentation: ProfilePresentation): string | undefined {\n return toResumeDate(latestDeliveryRecord(presentation)?.date ?? undefined);\n}\n\nfunction candidatesOf(profile: FunderCvProfileInput, kind: FunderCvItemKind): FunderCvCandidate[] {\n switch (kind) {\n case 'publication':\n return filterHidden(profile.publications).map((p) => ({\n id: funderCvItemId(kind, p.rkey),\n kind,\n title: p.title,\n date: toResumeDate(p.date),\n }));\n case 'presentation':\n return filterHidden(profile.presentations).map((p) => ({\n id: funderCvItemId(kind, p.rkey),\n kind,\n title: p.title,\n date: latestDelivery(p),\n }));\n case 'honor':\n return filterHidden(profile.honors).map((h) => ({\n id: funderCvItemId(kind, h.rkey),\n kind,\n title: h.title,\n date: toResumeDate(h.date),\n }));\n }\n}\n\n/**\n * The owner's visible items of the given kinds, most recent first. Undated\n * items go last, in profile order. The sort is stable, so equal dates keep\n * profile order too.\n */\nexport function listFunderCvCandidates(\n profile: FunderCvProfileInput,\n kinds: readonly FunderCvItemKind[],\n): FunderCvCandidate[] {\n const all = kinds.flatMap((kind) => candidatesOf(profile, kind));\n return all.sort((a, b) => {\n if (a.date && b.date) return b.date.localeCompare(a.date);\n if (a.date) return -1;\n if (b.date) return 1;\n return 0;\n });\n}\n\n/**\n * What the export page pre-ticks: the most recent publications up to each\n * limit (for NIH, the first five as closely related and the next five as\n * other), and the most recent honors. Talks are offered but not pre-ticked.\n */\nexport function defaultFunderCvSelection(\n profile: FunderCvProfileInput,\n format: FunderCvFormat,\n): FunderCvSelection {\n const publications = listFunderCvCandidates(profile, ['publication']).map((c) => c.id);\n const honors = listFunderCvCandidates(profile, ['honor']).map((c) => c.id);\n if (format === 'nih') {\n const { related, other } = FUNDER_CV_GROUPS.nih;\n return {\n format,\n related: publications.slice(0, related.max),\n other: publications.slice(related.max, related.max + other.max),\n honors: honors.slice(0, FUNDER_CV_GROUPS.nih.honors.max),\n };\n }\n return {\n format,\n outputs: publications.slice(0, FUNDER_CV_GROUPS.erc.outputs.max),\n recognition: honors.slice(0, FUNDER_CV_GROUPS.erc.recognition.max),\n };\n}\n\nfunction selectedIds(selection: FunderCvSelection): string[] {\n return selection.format === 'nih'\n ? [...selection.related, ...selection.other, ...selection.honors]\n : [...selection.outputs, ...selection.recognition];\n}\n\n/**\n * Ids in the selection that are not visible records on this profile. The\n * profile is the owner's own, so an empty result means every pick is theirs.\n */\nexport function findUnknownFunderCvItems(\n profile: FunderCvProfileInput,\n selection: FunderCvSelection,\n): string[] {\n const known = new Set(listFunderCvCandidates(profile, FUNDER_CV_ITEM_KINDS).map((c) => c.id));\n return selectedIds(selection).filter((id) => !known.has(id));\n}\n\n// ---------------------------------------------------------------------------\n// Document shapes\n// ---------------------------------------------------------------------------\n\nexport interface FunderCvPerson {\n /** Full name as the profile shows it; the handle when there is no name. */\n readonly name: string;\n readonly givenName?: string;\n readonly familyName?: string;\n /** Bare ORCID iD, e.g. `0000-0002-1825-0097`. */\n readonly orcid?: string;\n readonly website?: string;\n}\n\n/**\n * The ERC person, with the contact and personal details the owner typed for\n * this export. The NIH Biographical Sketch has no such block, so only ERC carries them.\n */\nexport interface ErcFunderCvPerson extends FunderCvPerson {\n readonly email?: string;\n readonly phone?: string;\n /** Lines are separated by `\\n`. */\n readonly address?: string;\n readonly driversLicense?: string;\n readonly rightToWork?: string;\n readonly nationality?: string;\n}\n\nexport interface FunderCvOptions {\n /**\n * Contact details the owner typed for this export. Sifa stores none of\n * them. Used for ERC only.\n */\n readonly contact?: ExportContact;\n}\n\n/** One product or research output, ready to cite. */\nexport interface FunderCvCitation {\n readonly kind: 'publication' | 'presentation';\n readonly title: string;\n /** Authors in record order, comma separated. For a talk, the speaker. */\n readonly authors?: string;\n /** Journal, publisher or event. */\n readonly venue?: string;\n /** Year. */\n readonly date?: string;\n readonly doi?: string;\n readonly url?: string;\n}\n\nexport interface FunderCvHonor {\n readonly year?: string;\n readonly title: string;\n readonly organization?: string;\n}\n\nexport interface NihEducation {\n readonly organization: string;\n readonly location?: string;\n readonly degree?: string;\n /** Start date, `MM/YYYY` or `YYYY`. */\n readonly start?: string;\n /** Receipt date, `MM/YYYY` or `YYYY`. */\n readonly end?: string;\n readonly field?: string;\n}\n\nexport interface FunderCvAppointment {\n /** `YYYY`. */\n readonly start?: string;\n /** `YYYY`, or null for a current appointment. */\n readonly end: string | null;\n readonly title: string;\n readonly organization?: string;\n readonly location?: string;\n}\n\n/** NIH Biographical Sketch: Common Form plus NIH Supplement. */\nexport interface NihFunderCv {\n readonly format: 'nih';\n readonly person: FunderCvPerson;\n readonly positionTitle?: string;\n /** Primary organization: the current position's. */\n readonly organization?: string;\n readonly location?: string;\n readonly education: NihEducation[];\n readonly appointments: FunderCvAppointment[];\n readonly products: { readonly related: FunderCvCitation[]; readonly other: FunderCvCitation[] };\n readonly honors: FunderCvHonor[];\n /** Supplement \"Personal Statement\". Sifa holds no record for it. */\n readonly personalStatement: string | null;\n /** Supplement \"Contributions to Science\". Sifa holds no record for them. */\n readonly contributions: string[];\n}\n\nexport interface ErcEducation {\n /** Receipt date, `MM/YYYY` or `YYYY`. */\n readonly date?: string;\n readonly degree?: string;\n readonly field?: string;\n readonly institution: string;\n}\n\nexport interface ErcRecognition {\n readonly kind: 'honor' | 'presentation';\n readonly year?: string;\n readonly title: string;\n /** Awarding body, or the event a talk was given at. */\n readonly organization?: string;\n}\n\nexport interface ErcContribution {\n readonly kind: 'teaching' | 'service';\n readonly start?: string;\n readonly end: string | null;\n readonly title: string;\n readonly organization?: string;\n}\n\n/** ERC Part B1, section \"Curriculum vitae and Track Record\". */\nexport interface ErcFunderCv {\n readonly format: 'erc';\n readonly person: ErcFunderCvPerson;\n readonly education: ErcEducation[];\n readonly currentPositions: FunderCvAppointment[];\n readonly previousPositions: FunderCvAppointment[];\n readonly outputs: FunderCvCitation[];\n readonly recognition: ErcRecognition[];\n readonly communityContributions: ErcContribution[];\n /** Proposal acronym for the page header. Not a profile fact. */\n readonly acronym: string | null;\n /** \"Career breaks, diverse career paths and major life events\". */\n readonly careerBreaks: string | null;\n}\n\nexport type FunderCvDocument = NihFunderCv | ErcFunderCv;\n\n// ---------------------------------------------------------------------------\n// Mapping\n// ---------------------------------------------------------------------------\n\nfunction text(value: string | null | undefined): string | undefined {\n const trimmed = value?.trim();\n return trimmed ? trimmed : undefined;\n}\n\nfunction compact<T extends object>(value: T): T {\n return Object.fromEntries(Object.entries(value).filter(([, v]) => v !== undefined)) as T;\n}\n\nfunction year(value: string | null | undefined): string | undefined {\n return toResumeDate(value ?? undefined)?.slice(0, 4);\n}\n\n/** `MM/YYYY` when the month is known, else `YYYY`. */\nfunction monthYear(value: string | null | undefined): string | undefined {\n const date = toResumeDate(value ?? undefined);\n if (!date) return undefined;\n return date.length >= 7 ? `${date.slice(5, 7)}/${date.slice(0, 4)}` : date;\n}\n\n/** Newest start first; undated entries last, in profile order. */\nfunction byStartDesc<T>(items: T[], start: (item: T) => string | undefined): T[] {\n return [...items].sort((a, b) => {\n const da = toResumeDate(start(a));\n const db = toResumeDate(start(b));\n if (da && db) return db.localeCompare(da);\n if (da) return -1;\n if (db) return 1;\n return 0;\n });\n}\n\nconst ORCID_RE = /\\d{4}-\\d{4}-\\d{4}-\\d{3}[\\dX]/;\n\nfunction orcidOf(accounts: ExternalAccount[] | undefined): string | undefined {\n const account = filterHidden(accounts).find((a) => a.platform.toLowerCase() === 'orcid');\n return account ? ORCID_RE.exec(account.url)?.[0] : undefined;\n}\n\nfunction personOf(profile: FunderCvProfileInput): FunderCvPerson {\n return compact<FunderCvPerson>({\n name:\n formatStructuredName(profile.givenName, profile.familyName) ??\n text(profile.displayName) ??\n profile.handle,\n givenName: text(profile.givenName),\n familyName: text(profile.familyName),\n orcid: orcidOf(profile.externalAccounts),\n website: text(profile.website),\n });\n}\n\nfunction appointment(position: ProfilePosition): FunderCvAppointment {\n return compact<FunderCvAppointment>({\n start: year(position.startedAt),\n end: year(position.endedAt) ?? null,\n title: position.title,\n organization: text(position.entityName) ?? text(position.company),\n location: text(formatLocation(position.location)),\n });\n}\n\n/** The current position to name on the form: the flagged primary, else the newest. */\nfunction currentPosition(positions: ProfilePosition[]): ProfilePosition | undefined {\n const current = byStartDesc(\n positions.filter((p) => !text(p.endedAt)),\n (p) => p.startedAt,\n );\n return current.find((p) => p.primary) ?? current[0];\n}\n\nfunction lookup(profile: FunderCvProfileInput) {\n const publications = new Map(filterHidden(profile.publications).map((p) => [p.rkey, p]));\n const presentations = new Map(filterHidden(profile.presentations).map((p) => [p.rkey, p]));\n const honors = new Map(filterHidden(profile.honors).map((h) => [h.rkey, h]));\n const split = (id: string) => {\n const at = id.indexOf(':');\n return { kind: id.slice(0, at), rkey: id.slice(at + 1) };\n };\n return { publications, presentations, honors, split };\n}\n\nfunction citations(\n ids: readonly string[],\n profile: FunderCvProfileInput,\n person: FunderCvPerson,\n): FunderCvCitation[] {\n const { publications, presentations, split } = lookup(profile);\n return ids.flatMap((id): FunderCvCitation[] => {\n const { kind, rkey } = split(id);\n if (kind === 'publication') {\n const p = publications.get(rkey);\n if (!p) return [];\n const authors = (p.contributors ?? []).map((c) => c.name.trim()).filter(Boolean);\n return [\n compact<FunderCvCitation>({\n kind: 'publication',\n title: p.title,\n authors: authors.length > 0 ? authors.join(', ') : undefined,\n venue: text(p.publisher),\n date: year(p.date),\n doi: text(p.doi),\n url: text(p.url),\n }),\n ];\n }\n if (kind === 'presentation') {\n const talk = presentations.get(rkey);\n if (!talk) return [];\n const delivery = latestDeliveryRecord(talk);\n return [\n compact<FunderCvCitation>({\n kind: 'presentation',\n title: talk.title,\n authors: person.name,\n venue: text(delivery?.eventName),\n date: year(delivery?.date),\n url: text(talk.links?.[0]?.uri),\n }),\n ];\n }\n return [];\n });\n}\n\nfunction honorsOf(ids: readonly string[], profile: FunderCvProfileInput): FunderCvHonor[] {\n const { honors, split } = lookup(profile);\n return ids.flatMap((id) => {\n const { kind, rkey } = split(id);\n const h = kind === 'honor' ? honors.get(rkey) : undefined;\n if (!h) return [];\n return [\n compact<FunderCvHonor>({\n year: year(h.date),\n title: h.title,\n organization: text(h.entityName) ?? text(h.issuer),\n }),\n ];\n });\n}\n\n/**\n * Build the funder document from the profile and the owner's selection.\n *\n * Hidden records never appear. Selected ids that match no visible record are\n * skipped; callers that must refuse them check `findUnknownFunderCvItems`\n * first. Selected items keep the order they were picked in.\n */\nexport function profileToFunderCv(\n profile: FunderCvProfileInput,\n selection: FunderCvSelection,\n options: FunderCvOptions = {},\n): FunderCvDocument {\n const person = personOf(profile);\n const positions = filterHidden(profile.positions);\n const education = byStartDesc(filterHidden(profile.education), (e) => e.startedAt);\n\n if (selection.format === 'nih') {\n const current = currentPosition(positions);\n return compact<NihFunderCv>({\n format: 'nih',\n person,\n positionTitle: text(current?.title),\n organization: current ? appointment(current).organization : undefined,\n location: text(formatLocation(current?.location)),\n education: education.map((e) =>\n compact<NihEducation>({\n organization: text(e.entityName) ?? e.institution,\n degree: text(e.degree),\n start: monthYear(e.startedAt),\n end: monthYear(e.endedAt),\n field: text(e.fieldOfStudy),\n }),\n ),\n appointments: byStartDesc(positions, (p) => p.startedAt).map(appointment),\n products: {\n related: citations(selection.related, profile, person),\n other: citations(selection.other, profile, person),\n },\n honors: honorsOf(selection.honors, profile),\n personalStatement: null,\n contributions: [],\n });\n }\n\n const appointments = byStartDesc(positions, (p) => p.startedAt);\n const { presentations, split } = lookup(profile);\n\n const recognition = selection.recognition.flatMap((id): ErcRecognition[] => {\n const { kind, rkey } = split(id);\n if (kind === 'honor') {\n const [honor] = honorsOf([id], profile);\n return honor ? [{ kind: 'honor', ...honor }] : [];\n }\n const talk = kind === 'presentation' ? presentations.get(rkey) : undefined;\n if (!talk) return [];\n const delivery = latestDeliveryRecord(talk);\n return [\n compact<ErcRecognition>({\n kind: 'presentation',\n year: year(delivery?.date),\n title: talk.title,\n organization: text(delivery?.eventName),\n }),\n ];\n });\n\n const teaching = filterHidden(profile.courses)\n .filter(isTeachingCourse)\n .map((c) =>\n compact<ErcContribution>({\n kind: 'teaching',\n start: year(c.startedAt),\n end: year(c.endedAt) ?? null,\n title: c.name,\n organization: text(c.entityName) ?? text(c.institution),\n }),\n );\n const service = filterHidden(profile.volunteering).map((v) =>\n compact<ErcContribution>({\n kind: 'service',\n start: year(v.startDate),\n end: year(v.endDate) ?? null,\n title: text(v.role) ?? text(v.entityName) ?? v.organization,\n organization: text(v.entityName) ?? text(v.organization),\n }),\n );\n\n return {\n format: 'erc',\n person: { ...person, ...normalizeExportContact(options.contact ?? {}) },\n education: education.map((e) =>\n compact<ErcEducation>({\n date: monthYear(e.endedAt) ?? monthYear(e.startedAt),\n degree: text(e.degree),\n field: text(e.fieldOfStudy),\n institution: text(e.entityName) ?? e.institution,\n }),\n ),\n currentPositions: appointments.filter((p) => !text(p.endedAt)).map(appointment),\n previousPositions: appointments.filter((p) => !!text(p.endedAt)).map(appointment),\n outputs: citations(selection.outputs, profile, person),\n recognition,\n communityContributions: [...teaching, ...service],\n acronym: null,\n careerBreaks: null,\n };\n}\n"],"mappings":"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","names":[]}
@@ -413,7 +413,7 @@ var FUNDER_CV_GROUPS = {
413
413
  function funderCvItemId(kind, rkey) {
414
414
  return `${kind}:${rkey}`;
415
415
  }
416
- var ITEM_ID_RE = /^(publication|presentation|honor):[A-Za-z0-9._~:-]{1,512}$/;
416
+ var ITEM_ID_RE = /^(publication|presentation|honor):[A-Za-z0-9._~:/-]{1,512}$/;
417
417
  function kindOf(id) {
418
418
  const kind = id.slice(0, id.indexOf(":"));
419
419
  return FUNDER_CV_ITEM_KINDS.includes(kind) ? kind : void 0;