UNPKG

meca

Version:
278 lines (277 loc) 13.9 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import fs from 'node:fs'; import path from 'node:path'; import { js2xml, xml2js } from 'xml-js'; import { convertToUnist } from 'jats-utils'; import { xmllintValidate } from 'jats-xml'; import { tic } from 'myst-cli-utils'; import fetch from 'node-fetch'; import { createTempFolder, elementWithText, removeTempFolder, select, selectAll } from './utils.js'; export const TRANSFER = 'transfer.xml'; export const TRANSFER_DTD = 'transfer-1.0.dtd'; function extractContact(node) { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m; if (!node) return undefined; const role = (node === null || node === void 0 ? void 0 : node['contact-role']) || (node === null || node === void 0 ? void 0 : node.role); const surname = (_c = (_b = (_a = select('surname', node)) === null || _a === void 0 ? void 0 : _a.children) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.value; const given = (_f = (_e = (_d = select('given-names,given-name', node)) === null || _d === void 0 ? void 0 : _d.children) === null || _e === void 0 ? void 0 : _e[0]) === null || _f === void 0 ? void 0 : _f.value; const email = (_j = (_h = (_g = select('email', node)) === null || _g === void 0 ? void 0 : _g.children) === null || _h === void 0 ? void 0 : _h[0]) === null || _j === void 0 ? void 0 : _j.value; const phone = (_m = (_l = (_k = select('phone', node)) === null || _k === void 0 ? void 0 : _k.children) === null || _l === void 0 ? void 0 : _l[0]) === null || _m === void 0 ? void 0 : _m.value; const contact = { role, name: given || surname ? { given, surname } : undefined, email, phone, }; return contact; } function extractProvider(node) { var _a, _b, _c; if (!node) return undefined; const name = (_c = (_b = (_a = select('provider-name,name', node)) === null || _a === void 0 ? void 0 : _a.children) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.value; const contact = extractContact(select('contact', node)); const provider = { name, contact }; return provider; } function extractPublication(node) { var _a, _b, _c, _d, _e, _f; if (!node) return undefined; const type = node._type; const title = (_c = (_b = (_a = select('publication-title,title', node)) === null || _a === void 0 ? void 0 : _a.children) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.value; const acronym = (_f = (_e = (_d = select('acronym', node)) === null || _d === void 0 ? void 0 : _d.children) === null || _e === void 0 ? void 0 : _e[0]) === null || _f === void 0 ? void 0 : _f.value; const contact = extractContact(select('contact', node)); const serviceProvider = { type, title, acronym, contact }; return serviceProvider; } function extractSecurity(node) { var _a, _b, _c; if (!node) return undefined; const auth = (_c = (_b = (_a = select('authentication-code', node)) === null || _a === void 0 ? void 0 : _a.children) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.value; if (!auth) return undefined; return { auth }; } export class TransferXml { constructor(data, opts) { var _a, _b; const toc = tic(); this.rawXML = data; this.log = (_a = opts === null || opts === void 0 ? void 0 : opts.log) !== null && _a !== void 0 ? _a : console; try { this.raw = xml2js(data, { compact: false }); } catch (error) { throw new Error('Problem parsing the TransferXML document, please ensure it is XML'); } const { declaration, elements } = this.raw; this.declaration = declaration === null || declaration === void 0 ? void 0 : declaration.attributes; if (!((elements === null || elements === void 0 ? void 0 : elements.length) === 2 && elements[0].type === 'doctype' && elements[1].name === 'transfer')) { throw new Error('Element <transfer> is not the only element of the transfer.xml'); } this.doctype = elements[0].doctype; const converted = convertToUnist(elements[1]); this.tree = select('transfer', converted); (_b = this.log) === null || _b === void 0 ? void 0 : _b.debug(toc('Parsed and converted transfer.xml to unist tree in %s')); } get localDtd() { // This works both compiled and in tests const dtd = fs.existsSync(path.join(__dirname, TRANSFER_DTD)) ? path.join(__dirname, TRANSFER_DTD) : path.join(__dirname, '..', 'static', TRANSFER_DTD); if (fs.existsSync(dtd)) return dtd; throw new Error(`Unable to locate transfer DTD file ${TRANSFER_DTD} in meca lib distribution`); } validateXml(remoteDtd) { return __awaiter(this, void 0, void 0, function* () { const tempFolder = createTempFolder(); fs.writeFileSync(path.join(tempFolder, TRANSFER), this.rawXML); let dtdFile = this.localDtd; if (remoteDtd) { const data = yield (yield fetch(remoteDtd)).text(); dtdFile = path.join(tempFolder, TRANSFER_DTD); fs.writeFileSync(dtdFile, data); } const manifestIsValid = yield xmllintValidate(this, path.join(tempFolder, TRANSFER), dtdFile).catch(() => { this.log.error(`${TRANSFER} DTD validation failed`); return false; }); removeTempFolder(tempFolder); return manifestIsValid; }); } get version() { return this.tree['transfer-version'] || this.tree.version; } get source() { const source = select('transfer-source', this.tree) || select('source', this.tree); if (!source) return undefined; const provider = extractProvider(select('service-provider', source)); const publication = extractPublication(select('publication', source)); const security = extractSecurity(select('security', source)); return { provider, publication, security }; } get destination() { const source = select('transfer-destination', this.tree) || select('destination', this.tree); if (!source) return undefined; const provider = extractProvider(select('service-provider', source)); const publication = extractPublication(select('publication', source)); const security = extractSecurity(select('security', source)); return { provider, publication, security }; } get instructions() { const parent = select('processing-instructions', this.tree); if (!parent) return undefined; const instructions = selectAll('processing-instruction,instruction', parent).map((node) => { var _a, _b; return ({ sequence: node['processing-sequence'] || node.sequence, instruction: (_b = (_a = node.children) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.value, }); }); const comments = selectAll('processing-comments,comments', parent).map((node) => { var _a, _b; return ((_b = (_a = node.children) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.value) || ''; }); return { instructions, comments }; } } function writeContactElement(opts, contact) { var _a, _b, _c, _d, _e, _f; if (!contact) return undefined; return { type: 'element', name: 'contact', attributes: contact.role ? { [(opts === null || opts === void 0 ? void 0 : opts.simplifiedXML) ? 'role' : 'contact-role']: contact.role } : undefined, elements: [ ((_a = contact.name) === null || _a === void 0 ? void 0 : _a.surname) || ((_b = contact.name) === null || _b === void 0 ? void 0 : _b.given) ? { type: 'element', name: 'contact-name', elements: [ ((_c = contact.name) === null || _c === void 0 ? void 0 : _c.surname) ? elementWithText('surname', (_d = contact.name) === null || _d === void 0 ? void 0 : _d.surname) : undefined, ((_e = contact.name) === null || _e === void 0 ? void 0 : _e.given) ? elementWithText('given-names', (_f = contact.name) === null || _f === void 0 ? void 0 : _f.given) : undefined, ].filter((e) => !!e), } : undefined, contact.email ? elementWithText('email', contact.email) : undefined, contact.phone ? elementWithText('phone', contact.phone) : undefined, ].filter((e) => !!e), }; } function writeServiceProviderElement(opts, provider) { if (!provider) return undefined; return { type: 'element', name: 'service-provider', elements: [ provider.name ? elementWithText('provider-name', provider.name) : undefined, writeContactElement(opts, provider.contact), ].filter((e) => !!e), }; } function writePublicationElement(opts, publication) { if (!publication) return undefined; return { type: 'element', name: 'publication', attributes: publication.type ? { type: publication.type } : undefined, elements: [ publication.title ? elementWithText((opts === null || opts === void 0 ? void 0 : opts.simplifiedXML) ? 'title' : 'publication-title', publication.title) : undefined, publication.acronym ? elementWithText('acronym', publication.acronym) : undefined, writeContactElement(opts, publication.contact), ].filter((e) => !!e), }; } function writeLocationElement(name, opts, location) { if (!location) return undefined; return { type: 'element', name: name === 'source' ? ((opts === null || opts === void 0 ? void 0 : opts.simplifiedXML) ? 'source' : 'transfer-source') : 'destination', elements: [ writeServiceProviderElement(opts, location.provider), writePublicationElement(opts, location.publication), location.security ? { type: 'element', name: 'security', elements: [ location.security.auth ? elementWithText('authentication-code', location.security.auth) : undefined, ].filter((e) => !!e), } : undefined, ].filter((e) => !!e), }; } function writeInstructionsElement(opts, instructions) { var _a, _b, _c; if (!instructions) return undefined; return { type: 'element', name: 'processing-instructions', elements: [ ...((_b = (_a = instructions.instructions) === null || _a === void 0 ? void 0 : _a.map(({ instruction, sequence }) => instruction ? elementWithText((opts === null || opts === void 0 ? void 0 : opts.simplifiedXML) ? 'instruction' : 'processing-instruction', instruction, sequence ? { [(opts === null || opts === void 0 ? void 0 : opts.simplifiedXML) ? 'sequence' : 'processing-sequence']: sequence } : undefined) : undefined).filter((e) => !!e)) !== null && _b !== void 0 ? _b : []), ...(_c = instructions.comments) === null || _c === void 0 ? void 0 : _c.map((comment) => comment ? elementWithText((opts === null || opts === void 0 ? void 0 : opts.simplifiedXML) ? 'comments' : 'processing-comments', comment) : undefined).filter((e) => !!e), ].filter((e) => !!e), }; } export function createTransferXml(transfer, opts) { var _a; const element = { type: 'element', elements: [ { type: 'doctype', doctype: `transfer PUBLIC "-//MECA//DTD Manifest v1.0//en" "${(_a = opts === null || opts === void 0 ? void 0 : opts.dtdUrl) !== null && _a !== void 0 ? _a : 'https://meca.zip/transfer-1.0.dtd'}"`, }, { type: 'element', name: 'transfer', attributes: { [(opts === null || opts === void 0 ? void 0 : opts.simplifiedXML) ? 'version' : 'transfer-version']: '1.0', xmlns: 'https://manuscriptexchange.org/schema/transfer', }, elements: [ writeLocationElement('source', opts, transfer.source), writeLocationElement('destination', opts, transfer.destination), writeInstructionsElement(opts, transfer.instructions), ].filter((e) => !!e), }, ], declaration: { attributes: { version: '1.0', encoding: 'UTF-8' } }, }; const manifest = js2xml(element, { compact: false, spaces: 2, }); return manifest; }