Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

myst-frontmatter

Package Overview
Dependencies
Maintainers
3
Versions
78
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

myst-frontmatter - npm Package Compare versions

Comparing version 1.1.1 to 1.1.2

38

dist/frontmatter/types.d.ts
import type { CreditRole } from 'credit-roles';
import type { Licenses } from '../licenses/types.js';
export interface Affiliation {
id?: string;
name?: string;
institution?: string;
department?: string;
address?: string;
city?: string;
state?: string;
postal_code?: string;
country?: string;
collaboration?: boolean;
isni?: string;
ringgold?: number;
ror?: string;
url?: string;
email?: string;
phone?: string;
fax?: string;
}
export type AuthorRoles = CreditRole | string;
export interface Author {
id?: string;
name?: string;

@@ -9,10 +29,23 @@ userId?: string;

corresponding?: boolean;
equal_contributor?: boolean;
deceased?: boolean;
email?: string;
roles?: AuthorRoles[];
affiliations?: string[];
collaborations?: string[];
twitter?: string;
github?: string;
website?: string;
url?: string;
note?: string;
phone?: string;
fax?: string;
}
/**
* Object to hold items referenced in multiple parts of frontmatter
*
* These will be normalized to the top level and replaced with ids elsewhere
*/
export type ReferenceStash = {
affiliations?: Affiliation[];
authors?: Author[];
};
export type Biblio = {

@@ -115,2 +148,3 @@ volume?: string | number;

authors?: Author[];
affiliations?: Affiliation[];
venue?: Venue;

@@ -117,0 +151,0 @@ github?: string;

import type { ValidationOptions } from 'simple-validators';
import type { Author, Biblio, Export, Jupytext, KernelSpec, Numbering, PageFrontmatter, ProjectFrontmatter, SiteFrontmatter, Venue, Thebe, BinderHubOptions, JupyterServerOptions, JupyterLocalOptions } from './types.js';
import type { Author, Biblio, Export, Jupytext, KernelSpec, Numbering, PageFrontmatter, ProjectFrontmatter, SiteFrontmatter, Venue, Thebe, BinderHubOptions, JupyterServerOptions, JupyterLocalOptions, ReferenceStash, Affiliation } from './types.js';
export declare const SITE_FRONTMATTER_KEYS: string[];

@@ -9,2 +9,20 @@ export declare const PROJECT_FRONTMATTER_KEYS: string[];

/**
* Update stash of authors/affiliations based on input value
*
* Input may be:
* - string name
* - string id
* - object without id
* - object with id
*
* This function will normalize all of the above to an id and if a corresponding
* object does not yet exist in the stash, it will be added. The id is returned.
*
* This function will warn if two objects are explicitly defined with the same id.
*/
export declare function validateAndStashObject<T extends {
id?: string;
name?: string;
}>(input: any, stash: ReferenceStash, kind: keyof ReferenceStash, validateFn: (v: any, o: ValidationOptions) => T | undefined, opts: ValidationOptions): string | undefined;
/**
* Validate Venue object against the schema

@@ -16,5 +34,12 @@ *

/**
* Validate Affiliation object against the schema
*/
export declare function validateAffiliation(input: any, opts: ValidationOptions): Affiliation | {
id: string;
name: string;
} | undefined;
/**
* Validate Author object against the schema
*/
export declare function validateAuthor(input: any, opts: ValidationOptions): Author | undefined;
export declare function validateAuthor(input: any, stash: ReferenceStash, opts: ValidationOptions): Author | undefined;
/**

@@ -70,3 +95,3 @@ * Validate Biblio object

*/
export declare function fillPageFrontmatter(pageFrontmatter: PageFrontmatter, projectFrontmatter: ProjectFrontmatter): PageFrontmatter;
export declare function fillPageFrontmatter(pageFrontmatter: PageFrontmatter, projectFrontmatter: ProjectFrontmatter, opts: ValidationOptions): PageFrontmatter;
/**

@@ -73,0 +98,0 @@ * Unnest `kernelspec` from `jupyter.kernelspec`

import { doi } from 'doi-utils';
import { credit } from 'credit-roles';
import { orcid } from 'orcid';
import { defined, incrementOptions, fillMissingKeys, filterKeys, validateBoolean, validateDate, validateEmail, validateEnum, validateKeys, validateList, validateObject, validateObjectKeys, validateString, validateUrl, validationError, validationWarning, } from 'simple-validators';
import { defined, incrementOptions, fillMissingKeys, filterKeys, validateBoolean, validateDate, validateEmail, validateEnum, validateKeys, validateList, validateObject, validateObjectKeys, validateString, validateUrl, validationError, validationWarning, validateNumber, } from 'simple-validators';
import { validateLicenses } from '../licenses/validators.js';
import { BinderProviders, ExportFormats } from './types.js';
import { createHash } from 'node:crypto';
export const SITE_FRONTMATTER_KEYS = [

@@ -20,2 +21,3 @@ 'title',

'keywords',
'affiliations',
];

@@ -60,3 +62,23 @@ export const PROJECT_FRONTMATTER_KEYS = [

];
const AFFILIATION_KEYS = [
'id',
'address',
'city',
'state',
'postal_code',
'country',
'name',
'institution',
'department',
'collaboration',
'isni',
'ringgold',
'ror',
'url',
'email',
'phone',
'fax',
];
const AUTHOR_KEYS = [
'id',
'userId',

@@ -66,2 +88,4 @@ 'name',

'corresponding',
'equal_contributor',
'deceased',
'email',

@@ -73,3 +97,6 @@ 'roles',

'github',
'website',
'url',
'note',
'phone',
'fax',
];

@@ -79,3 +106,10 @@ const AUTHOR_ALIASES = {

affiliation: 'affiliations',
website: 'url',
};
const AFFILIATION_ALIASES = {
ref: 'id',
region: 'state',
province: 'state',
website: 'url',
};
const BIBLIO_KEYS = ['volume', 'issue', 'first_page', 'last_page'];

@@ -145,3 +179,68 @@ const THEBE_KEYS = [

}
function stashPlaceholder(value) {
return { id: value, name: value };
}
/**
* Return true if object:
* - has 2 keys and only 2 keys: id and name
* - the values for id and name are the same
*/
function isStashPlaceholder(object) {
return Object.keys(object).length === 2 && object.name && object.id && object.name === object.id;
}
function normalizedString(value) {
return JSON.stringify(Object.entries(value).sort());
}
/**
* Update stash of authors/affiliations based on input value
*
* Input may be:
* - string name
* - string id
* - object without id
* - object with id
*
* This function will normalize all of the above to an id and if a corresponding
* object does not yet exist in the stash, it will be added. The id is returned.
*
* This function will warn if two objects are explicitly defined with the same id.
*/
export function validateAndStashObject(input, stash, kind, validateFn, opts) {
var _a;
const lookup = {};
(_a = stash[kind]) === null || _a === void 0 ? void 0 : _a.forEach((item) => {
if (item.id)
lookup[item.id] = item;
});
if (typeof input === 'string' && Object.keys(lookup).includes(input)) {
// Handle case where input is id and object already exists
return input;
}
const value = validateFn(input, opts);
if (!value)
return;
// Only warn on duplicate if the new object is not a placeholder
let warnOnDuplicate = !isStashPlaceholder(value);
if (!value.id) {
// If object is defined without an id, generate a unique id
value.id = createHash('md5').update(normalizedString(value)).digest('hex');
// Do not warn on duplicates for hash ids; any duplicates here are identical
warnOnDuplicate = false;
}
if (!Object.keys(lookup).includes(value.id)) {
// Handle case of new id - add stash value
lookup[value.id] = value;
}
else if (isStashPlaceholder(lookup[value.id])) {
// Handle case of existing placeholder { id: value, name: value } - replace stash value
lookup[value.id] = value;
}
else if (warnOnDuplicate) {
// Warn on duplicate id - lose new object
validationWarning(`duplicate id for ${kind} found in frontmatter: ${value.id}`, opts);
}
stash[kind] = Object.values(lookup);
return value.id;
}
/**
* Validate Venue object against the schema

@@ -174,7 +273,84 @@ *

/**
* Validate Affiliation object against the schema
*/
export function validateAffiliation(input, opts) {
if (typeof input === 'string') {
input = stashPlaceholder(input);
}
const value = validateObjectKeys(input, { optional: AFFILIATION_KEYS, alias: AFFILIATION_ALIASES }, opts);
if (value === undefined)
return undefined;
const output = {};
if (defined(value.id)) {
output.id = validateString(value.id, incrementOptions('id', opts));
}
if (defined(value.name)) {
output.name = validateString(value.name, incrementOptions('name', opts));
}
if (defined(value.institution)) {
output.institution = validateString(value.institution, incrementOptions('institution', opts));
}
if (defined(value.department)) {
output.department = validateString(value.department, incrementOptions('department', opts));
}
if (defined(value.address)) {
output.address = validateString(value.address, incrementOptions('address', opts));
}
if (defined(value.city)) {
output.city = validateString(value.city, incrementOptions('city', opts));
}
if (defined(value.state)) {
output.state = validateString(value.state, incrementOptions('state', opts));
}
if (defined(value.postal_code)) {
output.postal_code = validateString(value.postal_code, incrementOptions('postal_code', opts));
}
if (defined(value.country)) {
output.country = validateString(value.country, incrementOptions('country', opts));
}
// Both ISNI and ROR validation should occur similar to orcid (maybe in that same lib?)
if (defined(value.isni)) {
output.isni = validateString(value.isni, incrementOptions('isni', opts));
}
if (defined(value.ror)) {
output.ror = validateString(value.ror, incrementOptions('ror', opts));
}
if (defined(value.ringgold)) {
output.ringgold = validateNumber(value.ringgold, {
min: 1000,
max: 999999,
...incrementOptions('ringgold', opts),
});
}
if (defined(value.collaboration)) {
output.collaboration = validateBoolean(value.collaboration, incrementOptions('collaboration', opts));
}
if (defined(value.email)) {
output.email = validateEmail(value.email, incrementOptions('email', opts));
}
if (defined(value.url)) {
output.url = validateUrl(value.url, incrementOptions('url', opts));
}
if (defined(value.phone)) {
output.phone = validateString(value.phone, incrementOptions('phone', opts));
}
if (defined(value.fax)) {
output.fax = validateString(value.fax, incrementOptions('fax', opts));
}
// If affiliation only has an id, give it a matching name; this is equivalent to the case
// where a simple string is provided as an affiliation.
if (Object.keys(output).length === 1 && output.id) {
return stashPlaceholder(output.id);
}
else if (!output.name && !output.institution) {
validationWarning('affiliation should include name or institution', opts);
}
return output;
}
/**
* Validate Author object against the schema
*/
export function validateAuthor(input, opts) {
export function validateAuthor(input, stash, opts) {
if (typeof input === 'string') {
input = { name: input };
input = { id: input, name: input };
}

@@ -185,2 +361,5 @@ const value = validateObjectKeys(input, { optional: AUTHOR_KEYS, alias: AUTHOR_ALIASES }, opts);

const output = {};
if (defined(value.id)) {
output.id = validateString(value.id, incrementOptions('id', opts));
}
if (defined(value.userId)) {

@@ -193,2 +372,5 @@ // TODO: Better userId validation - length? regex?

}
else {
validationWarning('author should include name', opts);
}
if (defined(value.orcid)) {

@@ -212,2 +394,8 @@ const orcidOpts = incrementOptions('orcid', opts);

}
if (defined(value.equal_contributor)) {
output.equal_contributor = validateBoolean(value.equal_contributor, incrementOptions('equal_contributor', opts));
}
if (defined(value.deceased)) {
output.deceased = validateBoolean(value.deceased, incrementOptions('deceased', opts));
}
if (defined(value.email)) {

@@ -234,2 +422,5 @@ output.email = validateEmail(value.email, incrementOptions('email', opts));

}
if (defined(value.collaborations)) {
validationError('collaborations must be defined in frontmatter as affiliations with "collaboration: true"', incrementOptions('collaborations', opts));
}
if (defined(value.affiliations)) {

@@ -239,20 +430,8 @@ const affiliationsOpts = incrementOptions('affiliations', opts);

if (typeof affiliations === 'string') {
affiliations = affiliations.split(';');
affiliations = affiliations.split(';').map((aff) => aff.trim());
}
output.affiliations = validateList(affiliations, affiliationsOpts, (aff) => {
var _a;
return (_a = validateString(aff, affiliationsOpts)) === null || _a === void 0 ? void 0 : _a.trim();
return validateAndStashObject(aff, stash, 'affiliations', validateAffiliation, affiliationsOpts);
});
}
if (defined(value.collaborations)) {
const collaborationsOpts = incrementOptions('collaborations', opts);
let collaborations = value.collaborations;
if (typeof collaborations === 'string') {
collaborations = collaborations.split(';');
}
output.collaborations = validateList(collaborations, collaborationsOpts, (col) => {
var _a;
return (_a = validateString(col, collaborationsOpts)) === null || _a === void 0 ? void 0 : _a.trim();
});
}
if (defined(value.twitter)) {

@@ -264,5 +443,14 @@ output.twitter = validateString(value.twitter, incrementOptions('twitter', opts));

}
if (defined(value.website)) {
output.website = validateUrl(value.website, incrementOptions('website', opts));
if (defined(value.url)) {
output.url = validateUrl(value.url, incrementOptions('url', opts));
}
if (defined(value.phone)) {
output.phone = validateString(value.phone, incrementOptions('phone', opts));
}
if (defined(value.fax)) {
output.fax = validateString(value.fax, incrementOptions('fax', opts));
}
if (defined(value.note)) {
output.note = validateString(value.note, incrementOptions('note', opts));
}
return output;

@@ -623,2 +811,13 @@ }

}
const stash = {};
if (defined(value.affiliations)) {
const affiliationsOpts = incrementOptions('affiliations', opts);
let affiliations = value.affiliations;
if (typeof affiliations === 'string') {
affiliations = affiliations.split(';').map((aff) => aff.trim());
}
validateList(affiliations, affiliationsOpts, (aff) => {
return validateAndStashObject(aff, stash, 'affiliations', validateAffiliation, affiliationsOpts);
});
}
if (defined(value.authors)) {

@@ -631,3 +830,3 @@ let authors = value.authors;

output.authors = validateList(authors, incrementOptions('authors', opts), (author, index) => {
return validateAuthor(author, incrementOptions(`authors.${index}`, opts));
return validateAuthor(author, stash, incrementOptions(`authors.${index}`, opts));
});

@@ -656,2 +855,5 @@ // Ensure there is a corresponding author if an email is provided

}
if (stash.affiliations) {
output.affiliations = stash.affiliations;
}
return output;

@@ -838,3 +1040,3 @@ }

*/
export function fillPageFrontmatter(pageFrontmatter, projectFrontmatter) {
export function fillPageFrontmatter(pageFrontmatter, projectFrontmatter, opts) {
var _a, _b, _c, _d;

@@ -859,2 +1061,37 @@ const frontmatter = fillMissingKeys(pageFrontmatter, projectFrontmatter, USE_PROJECT_FALLBACK);

}
// Replace affiliation placeholders with extra affiliations available on the project/page
let affiliations;
let extraAffiliations;
// Currently, affiliations are connected only to authors, so we look at
// which frontmatter (project or page) has authors defined, and use the
// affiliations from there. However, we still use the other affiliations
// to fill out any placeholders where affiliations have id only.
if (projectFrontmatter.authors && !pageFrontmatter.authors) {
affiliations = projectFrontmatter.affiliations;
extraAffiliations = pageFrontmatter.affiliations;
}
else {
affiliations = pageFrontmatter.affiliations;
extraAffiliations = projectFrontmatter.affiliations;
}
if (affiliations) {
const projectAffLookup = {};
extraAffiliations === null || extraAffiliations === void 0 ? void 0 : extraAffiliations.forEach((aff) => {
if (aff.id && !isStashPlaceholder(aff)) {
projectAffLookup[aff.id] = aff;
}
});
frontmatter.affiliations = affiliations.map((aff) => {
if (!aff.id || !projectAffLookup[aff.id]) {
return aff;
}
else if (isStashPlaceholder(aff)) {
return projectAffLookup[aff.id];
}
else if (normalizedString(aff) !== normalizedString(projectAffLookup[aff.id])) {
validationWarning(`Duplicate affiliation id within project: ${aff.id}`, incrementOptions('affiliations', opts));
}
return aff;
});
}
return frontmatter;

@@ -861,0 +1098,0 @@ }

4

package.json
{
"name": "myst-frontmatter",
"version": "1.1.1",
"version": "1.1.2",
"sideEffects": false,

@@ -35,3 +35,3 @@ "license": "MIT",

"dependencies": {
"credit-roles": "^2.0.0",
"credit-roles": "^2.1.0",
"doi-utils": "^2.0.0",

@@ -38,0 +38,0 @@ "orcid": "^1.0.0",

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc