import {z} from 'zod' export type GetApiWorksArgs = {rows?: number | undefined, order?: string | undefined, facet?: string | undefined, sample?: number | undefined, sort?: string | undefined, offset?: number | undefined, mailto?: string | undefined, select?: string | undefined, query?: string | undefined, filter?: string | undefined, cursor?: string | undefined}; export type GetApiWorksOptions = {baseUrl: string, fetch?: typeof fetch | undefined}; export const query = z.object({"start-index": z.number().int(), "search-terms": z.number().int()}); export const affiliationIdentifier = z.object({id: z.string(), "id-type": z.string(), "asserted-by": z.string()}); export const workInstitution = z.object({name: z.string(), place: z.array(z.string()).optional(), department: z.array(z.string()).optional(), acronym: z.array(z.string()).optional(), id: z.array(affiliationIdentifier).optional()}); export const dateAndVersion = z.object({"date-parts": z.array(z.array(z.number().int())), version: z.string()}); export const dateParts = z.object({"date-parts": z.array(z.array(z.number().int()))}); export const date = z.object({"date-parts": z.array(z.array(z.number().int())), "date-time": z.string(), timestamp: z.number().int()}); export const workUpdate = z.object({label: z.string(), DOI: z.string(), type: z.string(), source: z.string(), updated: date, "record-id": z.string().optional()}); export const workStandardsBody = z.object({name: z.string().optional(), acronym: z.string().optional()}); export const workISSNType = z.object({type: z.string(), value: z.string()}); export const workLicense = z.object({URL: z.string(), start: date.optional(), "delay-in-days": z.number().int(), "content-version": z.string()}); export const funderIdentifier = z.object({id: z.string(), "id-type": z.string(), "asserted-by": z.string()}); export const workFunder = z.object({name: z.string().optional(), DOI: z.string().optional(), "doi-asserted-by": z.string().optional(), award: z.array(z.string()).optional(), id: z.array(funderIdentifier).optional()}); export const workDomain = z.object({domain: z.array(z.string()), "crossmark-restriction": z.boolean().optional()}); export const contributorRole = z.object({role: z.string(), vocabulary: z.string()}); export const affiliation = z.object({name: z.string(), place: z.array(z.string()).optional(), department: z.array(z.string()).optional(), acronym: z.array(z.string()).optional(), id: z.array(affiliationIdentifier).optional()}); export const author = z.object({role: z.array(contributorRole).optional(), ORCID: z.string().optional(), suffix: z.string().optional(), given: z.string().optional(), family: z.string().optional(), affiliation: z.array(affiliation), name: z.string().optional(), "authenticated-orcid": z.boolean().optional(), prefix: z.string().optional(), sequence: z.string()}); export const workClinicalTrial = z.object({"clinical-trial-number": z.string(), registry: z.string(), type: z.string().optional()}); export const reference = z.object({issn: z.string().optional(), "standards-body": z.string().optional(), issue: z.string().optional(), key: z.string().optional(), "series-title": z.string().optional(), "isbn-type": z.string().optional(), "doi-asserted-by": z.string().optional(), "first-page": z.string().optional(), DOI: z.string().optional(), type: z.string().optional(), isbn: z.string().optional(), component: z.string().optional(), "article-title": z.string().optional(), "volume-title": z.string().optional(), volume: z.string().optional(), author: z.string().optional(), "standard-designator": z.string().optional(), year: z.string().optional(), unstructured: z.string().optional(), edition: z.string().optional(), "journal-title": z.string().optional(), "issn-type": z.string().optional()}); export const workEvent = z.object({name: z.string(), location: z.string().optional(), start: dateParts.optional(), end: dateParts.optional()}); export const workReview = z.object({type: z.string().optional(), "running-number": z.string().optional(), "revision-round": z.string().optional(), stage: z.string().optional(), "competing-interest-statement": z.string().optional(), recommendation: z.string().optional(), language: z.string().optional()}); export const textValue = z.object({value: z.string(), language: z.string().optional()}); export const statement = z.object({type: z.string(), "statement-text": z.array(textValue)}); export const investigatorAffiliation = z.object({id: z.array(affiliationIdentifier), name: z.string()}); export const investigator = z.object({ORCID: z.string().optional(), suffix: z.string().optional(), given: z.string().optional(), family: z.string().optional(), affiliation: z.array(investigatorAffiliation), name: z.string().optional(), "role-start": dateParts.optional(), "authenticated-orcid": z.boolean().optional(), prefix: z.string().optional(), "alternate-name": z.string().optional(), sequence: z.string().optional(), "role-end": dateParts.optional()}); export const awardAmount = z.object({amount: z.number(), currency: z.string().optional(), percentage: z.number().int().optional()}); export const funding = z.object({type: z.string(), scheme: z.string().optional(), "award-amount": awardAmount.optional(), funder: workFunder}); export const workProjectIdentifier = z.object({id: z.string(), "id-type": z.string(), "asserted-by": z.string()}); export const projectTitle = z.object({title: z.string(), language: z.string().optional()}); export const worksProjectDescription = z.object({description: z.string().optional(), language: z.string().optional()}); export const workProject = z.object({"award-end": z.array(dateParts).optional(), statement: z.array(statement).optional(), "award-planned-start": z.array(dateParts).optional(), "award-start": z.array(dateParts).optional(), "lead-investigator": z.array(investigator).optional(), "award-planned-end": z.array(dateParts).optional(), investigator: z.array(investigator).optional(), funding: z.array(funding).optional(), id: z.array(workProjectIdentifier).optional(), "project-title": z.array(projectTitle), "award-amount": awardAmount.optional(), "co-lead-investigator": z.array(investigator).optional(), "project-description": z.array(worksProjectDescription).optional()}); export const statusDescription = z.object({language: z.string().optional(), description: z.string().optional()}); export const postedContentStatus = z.object({type: z.string(), update: dateParts, "status-description": z.array(statusDescription)}); export const workLink = z.object({URL: z.string(), "content-type": z.string(), "content-version": z.string(), "intended-application": z.string()}); export const primaryResource = z.object({URL: z.string()}); export const secondaryResource = z.object({URL: z.string(), label: z.string()}); export const resources = z.object({primary: primaryResource, secondary: z.array(secondaryResource).optional()}); export const workFreeToRead = z.object({"start-date": dateParts.optional(), "end-date": dateParts.optional()}); export const workJournalIssue = z.object({issue: z.string(), "published-online": dateParts.optional(), "published-print": dateParts.optional()}); export const versionInfoDescription = z.object({language: z.string().optional(), description: z.string().optional()}); export const versionInfo = z.object({version: z.string(), language: z.string().optional(), "version-description": z.array(versionInfoDescription).optional()}); export const workRelationObject = z.object({"id-type": z.string(), id: z.string(), "asserted-by": z.string()}); export const workRelation = z.record(z.string(), z.array(workRelationObject)); export const workAssertionGroup = z.object({name: z.string(), label: z.string().optional()}); export const workAssertionExplanation = z.object({URL: z.string()}); export const workAssertion = z.object({group: workAssertionGroup.optional(), explanation: workAssertionExplanation.optional(), name: z.string(), value: z.string().optional(), URL: z.string().optional(), order: z.number().int().optional(), label: z.string().optional()}); export const work = z.object({institution: z.array(workInstitution).optional(), indexed: dateAndVersion, description: z.string().optional(), posted: dateParts.optional(), "publisher-location": z.string().optional(), "update-to": z.array(workUpdate).optional(), "standards-body": workStandardsBody.optional(), "edition-number": z.string().optional(), "group-title": z.string().optional(), "reference-count": z.number().int().optional(), publisher: z.string(), issue: z.string().optional(), "isbn-type": z.array(workISSNType).optional(), license: z.array(workLicense).optional(), funder: z.array(workFunder).optional(), "content-domain": workDomain.optional(), chair: z.array(author).optional(), "short-container-title": z.array(z.string()).optional(), accepted: dateParts.optional(), "special-numbering": z.string().optional(), "content-updated": dateParts.optional(), "published-print": dateParts.optional(), abstract: z.string().optional(), DOI: z.string(), type: z.string(), created: date, approved: dateParts.optional(), page: z.string().optional(), "update-policy": z.string().optional(), source: z.string(), "is-referenced-by-count": z.number().int().optional(), title: z.array(z.string()).optional(), prefix: z.string(), volume: z.string().optional(), "clinical-trial-number": z.array(workClinicalTrial).optional(), author: z.array(author).optional(), member: z.string(), "content-created": dateParts.optional(), "published-online": dateParts.optional(), reference: z.array(reference).optional(), "updated-by": z.array(workUpdate).optional(), event: workEvent.optional(), "container-title": z.array(z.string()).optional(), review: workReview.optional(), project: z.array(workProject).optional(), "original-title": z.array(z.string()).optional(), status: postedContentStatus.optional(), language: z.string().optional(), link: z.array(workLink).optional(), deposited: date, score: z.number(), degree: z.array(z.string()).optional(), resource: resources, subtitle: z.array(z.string()).optional(), translator: z.array(author).optional(), "free-to-read": workFreeToRead.optional(), editor: z.array(author).optional(), "proceedings-subject": z.string().optional(), "component-number": z.string().optional(), "short-title": z.array(z.string()).optional(), issued: dateParts, ISBN: z.array(z.string()).optional(), "references-count": z.number().int().optional(), "part-number": z.string().optional(), aliases: z.array(z.string()).optional(), "issue-title": z.array(z.string()).optional(), "journal-issue": workJournalIssue.optional(), "alternative-id": z.array(z.string()).optional(), version: versionInfo.optional(), URL: z.string(), archive: z.array(z.string()).optional(), relation: workRelation.optional(), ISSN: z.array(z.string()).optional(), "issn-type": z.array(workISSNType).optional(), subject: z.array(z.string()).optional(), "published-other": dateParts.optional(), published: dateParts.optional(), assertion: z.array(workAssertion).optional(), subtype: z.string().optional(), "article-number": z.string().optional()}); export const works = z.object({"items-per-page": z.number().int(), query: query, "total-results": z.number().int(), "next-cursor": z.string().optional(), facets: z.unknown(), items: z.array(work)}); export const worksMessage = z.object({status: z.string(), "message-type": z.string(), "message-version": z.string(), message: works}); export const validateResponse = async ( schema: { readonly '~standard': { readonly validate: (value: unknown) => ({ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }) | Promise<{ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }> } }, value: unknown ): Promise => { const result = await schema['~standard'].validate(value) if (result.issues !== undefined) { const detail = result.issues .map(({ message, path }) => path?.length ? `${path.map((segment) => String(typeof segment === 'object' ? segment.key : segment)).join('.')}: ${message}` : message ) .join('; ') throw new Error(`Response does not match the schema: ${detail}`, { cause: { issues: result.issues } }) } return result.value }; /** * Returns a list of all works (journal articles, conference proceedings, books, components, etc), 20 per page by default. * * GET /works */ export const getApiWorks = async (args: GetApiWorksArgs, options: GetApiWorksOptions) => { const {baseUrl, fetch: fetchFn = fetch} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('accept', "application/json") if (args.rows != null) query.append("rows", String(args.rows)) if (args.order != null) query.append("order", String(args.order)) if (args.facet != null) query.append("facet", String(args.facet)) if (args.sample != null) query.append("sample", String(args.sample)) if (args.sort != null) query.append("sort", String(args.sort)) if (args.offset != null) query.append("offset", String(args.offset)) if (args.mailto != null) query.append("mailto", String(args.mailto)) if (args.select != null) query.append("select", String(args.select)) if (args.query != null) query.append("query", String(args.query)) if (args.filter != null) query.append("filter", String(args.filter)) if (args.cursor != null) query.append("cursor", String(args.cursor)) const url = `${baseUrl.replace(/\/+$/, '')}/works${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "GET", headers}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(worksMessage, await res.json()) };