diff --git a/.grenrc.json b/.grenrc.json new file mode 100644 index 0000000..94ded13 --- /dev/null +++ b/.grenrc.json @@ -0,0 +1,6 @@ +{ + "dataSource": "commits", + "prefix": "1.0.1", + "includeMessages": "all", + "changelogFilename": "CHANGELOG.md" +} \ No newline at end of file diff --git a/README.md b/README.md index c8e2bd4..41155d6 100644 --- a/README.md +++ b/README.md @@ -6,22 +6,11 @@ ### Method 1 -`git`, a relatively new version of `nodejs` (I am using 8.x) and `yarn`. - -```bash -$ git clone git@github.com:punkish/zenodeo.git -$ cd zenodeo -$ yarn -$ node index.js -``` - -### Method 2 - -`git` and `nodejs` are non-negotiable, but if you don’t have or can’t install `yarn`, `npm` should work as well. +`git`, `nodejs 12.x` or higher ```bash $ git clone git@github.com:punkish/zenodeo.git $ cd zenodeo $ npm install $ node index.js -``` \ No newline at end of file +``` diff --git a/api/v2/index.js b/api/v2/index.js index 9807985..f02577a 100644 --- a/api/v2/index.js +++ b/api/v2/index.js @@ -1,23 +1,80 @@ 'use strict'; +/************************************************************************************* + * + * This is the index of all the routes in this version of the + * API. Here is the overall scheme of how this works (see the + * specific notes below for more details) + * + * ┌─────────────┐ + * │ index.js │ + * │ (this file) │ + * └──────┬──────┘ + * │ + * │ + * ┌───────▼───────┐ + * │┌─────────────┐│ ┌──────────────────────────┐ + * ││ root route ◀┼────────│ ./resources/rootRoute.js │ + * │└─────────────┘│ └──────────────────────────┘ + * │ │ + * │ │ + * │┌─────────────┐│ ┌────────────────────────────┐ + * ││other routes ◀┼───────│ ./resources/otherRoutes.js │ + * │└─────────────┘│ └──────────────▲─────────────┘ + * └───────┬───────┘ │ + * │ │ + * │ │ + * │ │ + * │ │ + * ┌─────────▼─────────┐ ┌───────────────────────┐ ╔════════════╗ + * │ register with │ │ ../lib/routeMaker.js │◀───────║ Schema ║ + * │ server │ └───────────────────────┘ ╚════════════╝ + * └───────────────────┘ ▲ + * │ + * │ + * ┌─────────────────────────────┐ ┌───────────────────────┐ + * │ ../lib/dd2datadictionary.js │───────▶│ ../lib/dd2schema.js │ + * └────────────────▲────────────┘ └───────────────────────┘ + * │ + * │ + * │ + * │ + * ┌────────────────────────────────┐ + * │ ../../../dataDictionary/dd.js │ + * └────────────────────────────────┘ + * + ***********************************************************************************/ + + const config = require('config'); const cache = config.get('v2.cache'); exports.plugin = { name: 'api2', - version: '1.0.1', + version: '2.6.0', register: async function(server, options) { + // This is straightforward, just include the root route of the API const rootRoute = [{ plugin: require('./resources/rootRoute') }]; + // `otherRoutes` is a factory routine that sends back + // fully-formed array of routes const otherRoutes = require('./resources/otherRoutes'); + + // If the route requires a cache, we add the cache option to it otherRoutes.forEach(r => r.options = cache); + + // We also add the `wpsummary` route to this array of routes + // because this wasn't included in the array of routes returned + // by `otherRoutes` otherRoutes.push({ plugin: require('./resources/wpsummary'), options: cache }); + // Concatenate the root route and the other routes and register + // all of them with the server. const routes = [].concat(rootRoute, otherRoutes); await server.register(routes); } -}; \ No newline at end of file +}; diff --git a/api/v2/lib/dd2datadictionary.js b/api/v2/lib/dd2datadictionary.js index 6362b7b..ed27826 100644 --- a/api/v2/lib/dd2datadictionary.js +++ b/api/v2/lib/dd2datadictionary.js @@ -17,11 +17,25 @@ const dd2datadictionary = function() { // converting the following // // dd = { - // zenodeoCore: { treatments: [] }, + // zenodeoCore: { + // treatments: { + // cache: true, + // fields [ {}, {} … ] + // } + // }, // zenodeoRelated: { - // figureCitations: [], - // bibRefCitations: [], - // … + // treatments: { + // cache: true, + // fields [ {}, {} … ] + // }, + // … + // }, + // lookups: { + // about: { + // cache: false, + // fields [ {} ] + // }, + // … // } // } // @@ -43,20 +57,20 @@ const dd2datadictionary = function() { // them the name of the 'resource' and the name of the // 'resourceId'. This will look like // - // "zenodeoCore": [ + // zenodeoCore: [ // { - // "name": "treatments", - // "resourceId": "treatmentId" + // name: "treatments", + // resourceId: "treatmentId" // } // ], - // "zenodeoRelated": [ + // zenodeoRelated: [ // { - // "name": "figureCitations", - // "resourceId": "figureCitationId" + // name: "figureCitations", + // resourceId: "figureCitationId" // }, // { - // "name": "bibRefCitations", - // "resourceId": "bibRefCitationId" + // name: "bibRefCitations", + // resourceId: "bibRefCitationId" // }, const resourceGroups = {}; @@ -76,10 +90,10 @@ const dd2datadictionary = function() { for (let resource in groupResources) { - // Add the commonParams to 'all' the resources no matter - // which resourceGroup they are in (hence, the 'all') - let gr = groupResources[resource]; - gr.push(...commonParams['all']); + // Add the commonParams to the fields of 'all' the resources + // no matter which resourceGroup they are in (hence, the 'all') + let rf = groupResources[resource].fields; + rf.push(...commonParams['all']); // Don't add any params if a particular resourceGroup's // specific commonParams are empty. This ensures nothing @@ -91,22 +105,23 @@ const dd2datadictionary = function() { // we are using concat() instead of push() because we // want to add these parameters in front of the array // rather than at the end of the array - gr = [].concat(commonParams[rg], gr) + rf = [].concat(commonParams[rg], rf) } // For every resource, we add its name and the name of its // resourceId key to the resourceGroups hash let name = resource; let resourceId = ''; - for (let i = 0, j = gr.length; i < j; i++) { + + for (let i = 0, j = rf.length; i < j; i++) { if (rg !== 'lookups') { - if (gr[i].resourceId) { + if (rf[i].resourceId) { - if (gr[i].resourceId === true) { + if (rf[i].resourceId === true) { - resourceId = gr[i].plaziName; + resourceId = rf[i].plaziName; break; } } @@ -117,10 +132,11 @@ const dd2datadictionary = function() { resourceGroups[rg].push({ name: name, - resourceId: resourceId + resourceId: resourceId, + cache: groupResources[resource].cache }); - dataDictionary[resource] = gr; + dataDictionary[resource] = rf; } diff --git a/api/v2/lib/dd2queries.js b/api/v2/lib/dd2queries.js index d4f225d..50b0aee 100644 --- a/api/v2/lib/dd2queries.js +++ b/api/v2/lib/dd2queries.js @@ -196,8 +196,6 @@ const getDdKeys = function() { const dd2queries = function(queryObject) { - //plog.info('queryObject', queryObject); - // get a reference to the resource-specific query parts. // For example, if 'queryObject.resource' is 'treatments' // then 'r' will be a reference to the 'treatments' @@ -209,7 +207,6 @@ const dd2queries = function(queryObject) { //plog.info('ddKeys', ddKeys); const pk = ddKeys.byResourceIds[queryObject.resource]; - const [ matchTables, constraint ] = calcConstraint(ddKeys, queryObject); // make a deep copy of the resource specific queries @@ -222,11 +219,13 @@ const dd2queries = function(queryObject) { const doGroups = ['essential']; if (queryObject[pk]) { - + console.log(`pk key: ${pk}`); + console.log(`pk val: ${queryObject[pk]}`); doGroups.push(...[ 'related', 'taxonStats' ]); + } else { - + console.log('no pk'); if (queryObject.facets) doGroups.push('facets'); if (queryObject.stats) doGroups.push('stats'); } @@ -267,17 +266,17 @@ const dd2queries = function(queryObject) { const test = function() { const q1 = { - communities: ['biosyslit', 'belgiumherbarium'], - refreshCache: false, - page: 1, - size: 30, + // communities: ['biosyslit', 'belgiumherbarium'], + // refreshCache: false, + // page: 1, + // size: 30, resource: 'treatments', - facets: false, - stats: false, - xml: false, - sortBy: 'journalYear:ASC', + // facets: false, + // stats: false, + // xml: false, + // sortBy: 'journalYear:ASC', // q: 'carabus', - authorityName: 'Agosti', + // authorityName: 'Agosti', // journalYear: '1996', // format: 'xml', // treatmentTitle: 'opheys', @@ -304,9 +303,10 @@ const test = function() { }, ]; + console.log(dd2queries(q1)); + lookups.forEach(l => { - const q = dd2queries(l); - console.log(q); + //console.log(dd2queries(l)); }); }; diff --git a/api/v2/lib/dd2schema.js b/api/v2/lib/dd2schema.js index f7a8bdd..c29a540 100644 --- a/api/v2/lib/dd2schema.js +++ b/api/v2/lib/dd2schema.js @@ -9,7 +9,7 @@ **********************************************************************/ const Joi = require('@hapi/joi'); -const { dataDictionary, resourceGroups } = require('./dd2datadictionary'); +const { dataDictionary } = require('./dd2datadictionary'); const Boom = require('@hapi/boom'); diff --git a/api/v2/lib/qm.js b/api/v2/lib/qm-bak.js similarity index 100% rename from api/v2/lib/qm.js rename to api/v2/lib/qm-bak.js diff --git a/api/v2/lib/qparts.js b/api/v2/lib/qparts.js index e9bbadb..e30f390 100644 --- a/api/v2/lib/qparts.js +++ b/api/v2/lib/qparts.js @@ -1,5 +1,26 @@ 'use strict'; +/*********************************************************************** + * + * Query definitions for every resource. + * + * A query is made up of seven parts + * + * three mandatory parts + * ----------------------------- + * SELECT + * FROM + * WHERE + * + * four optional parts + * ----------------------------- + * GROUP BY + * ORDER BY + * LIMIT + * OFFSET + * + **********************************************************************/ + const queryParts = { treatments: { pk: 'treatmentId', diff --git a/api/v2/lib/routeMaker.js b/api/v2/lib/routeMaker.js index d86953b..8331160 100644 --- a/api/v2/lib/routeMaker.js +++ b/api/v2/lib/routeMaker.js @@ -1,11 +1,23 @@ 'use strict'; -const { Schema, SchemaLog } = require('./dd2schema'); -const ResponseMessages = require('../../responseMessages'); +/************************************************************* + * + * This is a factory routine that takens in a resource + * description and returns a handler + * + *************************************************************/ + +const { Schema } = require('./dd2schema'); const Utils = require('../utils'); const h = function(resource) { + // Since there are two groups of resources on zenodeo, namely the + // zenodeoCore (really, the treatments) and the zenodeoRelated ( + // the resources related to each treatment stored in FK-linked + // tables in the database), we coalesce them into one (because there) + // only one factory routine for generating the handler and the + // getRecords functions for them) const rg = resource.group.startsWith('zenodeo') ? 'zenodeo' : resource.group; const { handler, getRecords } = require(`../lib/${rg}`); @@ -20,7 +32,9 @@ const h = function(resource) { name: pluginName, register: async function(server, options) { - if (! resource.group !== 'lookups') { + // lookup resources don't use a cache + //if (! resource.group !== 'lookups') { + if (resource.cache) { // create the cache and // bind it to every route registered @@ -47,7 +61,7 @@ const h = function(resource) { plugins: { 'hapi-swagger': { order: resource.order, - responseMessages: ResponseMessages + responseMessages: require('../../responseMessages') } }, validate: { diff --git a/api/v2/lib/z2.js b/api/v2/lib/z2.js deleted file mode 100644 index 15de828..0000000 --- a/api/v2/lib/z2.js +++ /dev/null @@ -1,491 +0,0 @@ -/************************************** - * abstracted logic for the handler and other functions - * for resources that are fetched from Zenodeo - * - treatments.js - * - treatmentAuthors.js - **************************************/ - -'use strict'; - -const config = require('config'); -const plog = require(config.get('plog')); -const Schema = require('../schema.js'); -const cacheOn = config.get('v2.cache.on'); -const uriZenodeo = config.get('v2.uri.zenodeo'); -const getSql = require('../lib/qm').getSql; -const Utils = require('../utils'); -const Database = require('better-sqlite3'); -const db = new Database(config.get('data.treatments')); -const fs = require('fs'); -const qParts = require('./qparts'); - -const handler = function(plugins) { - - return async function(request, h) { - - const queryObject = request.query; - - // Add names of the resource and the resource's PK - // Note, these are *not* values, but just keys. For - // example, 'treatments' and 'treatmentId', not - // '000343DSDHSK923HHC9SKKS' (value of 'treatmentId') - queryObject.resources = plugins._resources; - queryObject.resourceId = plugins._resourceId; - - // bunch up messages to print them to the log - const messages = [{label: 'queryObject', params: queryObject}]; - - if (queryObject.resources === 'treatments') { - - // if xml is being requested, send it back and be done with it - if (queryObject.format && queryObject.format === 'xml') { - - plog.log({ header: 'WEB QUERY', messages: messages }); - return h.response(getXml(queryObject.treatmentId)) - .type('text/xml') - .header('Content-Type', 'application/xml'); - - } - } - - // cacheKey is the URL query without the refreshCache param. - const cacheKey = Utils.makeCacheKey(request); - messages.push({label: 'cacheKey', params: cacheKey}); - - let result; - if (cacheOn) { - if (queryObject.refreshCache || queryObject.refreshCache === 'true') { - messages.push({label: 'info', params: 'force emptying the cache'}); - this.cache.drop(cacheKey); - messages.push({label: 'info', params: 'refilling the cache'}); - } - - messages.push({label: 'info', params: 'getting results from the cache'}); - plog.log({ header: 'WEB QUERY', messages: messages }); - result = this.cache.get(cacheKey); - } - else { - messages.push({label: 'info', params: 'querying for fresh results'}); - plog.log({ header: 'WEB QUERY', messages: messages }); - result = getRecords(cacheKey); - } - - return result; - - }; - -}; - -const getAllLikes = function(queryObject) { - - const resourceLike = qParts[queryObject.resources].queryable.like; - for (let key in queryObject) { - if (key in resourceLike) { - queryObject[key] =queryObject[key] + '%'; - } - } -}; - -const getRecords = function(cacheKey) { - - const queryObject = Utils.makeQueryObject(cacheKey); - - // A resourceId is present. The query is for a specific - // record. All other query params are ignored - if (queryObject[queryObject.resourceId]) { - return getOneRecord(queryObject); - } - - // More complicated queries with search parameters - else { - return getManyRecords(queryObject) - } -}; - -const dataForDelivery = function(t, data) { - const [s, ns] = t; - const ms = (ns / 1000000) + (s ? s * 1000 : 0); - - if (cacheOn) { - return data; - } - else { - return { - value: data, - cached: null, - report: { - msec: ms - } - } - } -}; - -const getOneRecord = function(queryObject) { - - const q = getSql(queryObject); - const sqlLog = q.queriesLog.essential.data.sql; - const sql = q.queriesLog.essential.data.sql; - - const messages = [ {label: 'queryObject', params: queryObject} ]; - - // data will hold all the query results to be sent back - const data = { 'search-criteria': {} }; - - // The following params may get added to the queryObject but they - // are not used when making the _self, _prev, _next links, or - // the search-criteria - const exclude = ['resources', 'limit', 'offset', 'refreshCache', 'resources', 'resourceId']; - - for (let key in queryObject) { - if (! exclude.includes(key)) { - data['search-criteria'][key] = queryObject[key]; - } - } - - let t = process.hrtime(); - - try { - - // add query results to data.records. If no results are found, - // add an empty array to data.records - const records = db.prepare(sql).get(queryObject); - if (records) { - data.records = [ records ]; - data['num-of-records'] = 1; - } - else { - data.records = []; - data['num-of-records'] = 0; - } - } - catch (error) { - plog.error(error, sqlLog); - } - - t = process.hrtime(t); - - - // add a self link to the data - data._links = Utils.makeSelfLink({ - uri: uriZenodeo, - resource: queryObject.resources, - queryString: Object.entries(data['search-criteria']) - .map(e => e[0] + '=' + e[1]) - .sort() - .join('&') - }); - - messages.push({label: 'data', params: { sql: sqlLog, took: t }}); - messages.push({label: 'num-of-records', params: data['num-of-records']}); - plog.log({ header: 'ONE QUERY', messages: messages }); - - // We are done if no records found - if (! data['num-of-records']) { - return dataForDelivery(t, data); - } - - // more data from beyond the database - if (queryObject.resources === 'treatments') { - if (queryObject.xml && queryObject.xml === 'true') { - data.records[0].xml = getXml(queryObject.treatmentId); - } - - data.taxonStats = getTaxonStats(data); - } - - data['related-records'] = getRelatedRecords(q, queryObject); - - return dataForDelivery(t, data); -}; - -// calc limit and offset and add them to the queryObject -// as we will need them for the SQL query -const calcLimitAndOffset = function(queryObject) { - const page = queryObject.page ? parseInt(queryObject.page) : 1; - const limit = Schema.defaults.size; - const offset = (page - 1) * limit; - queryObject.limit = limit; - queryObject.offset = offset; -}; - -const getManyRecords = async function(queryObject) { - - // data will hold all the query results to be sent back - const data = { 'search-criteria': {} }; - - // The following params may get added to the queryObject but they - // are not used when making the _self, _prev, _next links, or - // the search-criteria - const exclude = ['resources', 'limit', 'offset', 'refreshCache', 'resources', 'resourceId']; - - for (let key in queryObject) { - if (! exclude.includes(key)) { - data['search-criteria'][key] = queryObject[key]; - } - } - - calcLimitAndOffset(queryObject); - - const id = queryObject.id ? parseInt(queryObject.id) : 0; - - // procees all the query params that are used in LIKE searches - // and add a '%' suffix to them - getAllLikes(queryObject); - - // print out the queryObject - const messages = [{label: 'queryObject', params: queryObject}]; - - const q = getSql(queryObject); - - let t = process.hrtime(); - - // first find total number of matches - const countSql = q.queries.essential.count.sql; - const countSqlLog = q.queriesLog.essential.count.sql; - - try { - data['num-of-records'] = db.prepare(countSql) - .get(queryObject) - .numOfRecords; - } - catch (error) { - plog.error(error, countSqlLog); - } - - t = process.hrtime(t); - messages.push({ label: 'count', params: { sql: countSqlLog, took: t } }); - - // add a self link to the data - data._links = {}; - data._links.self = Utils.makeLink({ - uri: uriZenodeo, - resource: queryObject.resources, - queryString: Object.entries(data['search-criteria']) - .map(e => e[0] + '=' + e[1]) - .sort() - .join('&') - }); - - // We are done if no records found - if (! data['num-of-records']) { - return dataForDelivery(t, data); - } - - t = process.hrtime(); - - // get the records - const dataSql = q.queries.essential.data.sql; - const dataSqlLog = q.queriesLog.essential.data.sql; - - try { - data.records = db.prepare(dataSql).all(queryObject) || []; - } - catch (error) { - plog.error(error, dataSqlLog); - } - - t = process.hrtime(t); - messages.push({ label: 'data', params: { sql: dataSqlLog, took: t } }); - - plog.log({ header: 'MANY QUERIES', messages: messages }); - - if (data.records.length > 0) { - data.records.forEach(rec => { - rec._links = Utils.makeSelfLink({ - uri: uriZenodeo, - resource: queryObject.resources, - queryString: Object.entries({ - treatmentId: rec[queryObject.resourceId] - }) - .map(e => e[0] + '=' + e[1]) - .sort() - .join('&') - }); - }); - - const lastrec = data.records[data.records.length - 1]; - data.nextid = lastrec.id; - } - else { - data.nextid = ''; - } - - // set some records-specific from and to for the formatted - // search criteria string - data.from = ((queryObject.page - 1) * queryObject.limit) + 1; - data.to = data.records.length < queryObject.limit ? - data.from + data.records.length - 1 : - data.from + queryObject.limit - 1; - - data.previd = id; - - data.prevpage = queryObject.page >= 1 ? queryObject.page - 1 : ''; - data.nextpage = data.records.length < queryObject.limit ? '' : parseInt(queryObject.page) + 1; - - data._links.prev = Utils.makeLink({ - uri: uriZenodeo, - resource: queryObject.resources, - queryString: Object.entries(data['search-criteria']) - .map(e => e[0] + '=' + (e[0] === 'page' ? data.prevpage : e[1])) - .sort() - .join('&') - }); - - data._links.next = Utils.makeLink({ - uri: uriZenodeo, - resource: queryObject.resources, - queryString: Object.entries(data['search-criteria']) - .map(e => e[0] + '=' + (e[0] === 'page' ? data.nextpage : e[1])) - .sort() - .join('&') - }); - - // finally, get facets and stats, if requested - const groupedQueries = ['facets', 'stats']; - groupedQueries.forEach(g => { - if (g in queryObject && queryObject[g] === 'true') { - data[g] = getStatsFacets(g, q, queryObject); - } - }); - - // all done - return dataForDelivery(t, data); -}; - -const getStatsFacets = function(type, q, queryObject) { - const result = {}; - const messages = []; - - for (let query in q.queries[type]) { - - let t = process.hrtime(); - - const sql = q.queries[type][query].sql; - const sqlLog = q.queriesLog[type][query].sql; - - try { - result[query] = db.prepare(sql).all(queryObject); - } - catch (error) { - plog.error(error, sqlLog); - } - - t = process.hrtime(t); - messages.push({ - label: query, - params: { sql: sqlLog, took: t } - }); - - } - - plog.log({ header: `${type.toUpperCase()} QUERIES`, messages: messages }); - - return result; -}; - -const getRelatedRecords = function(q, queryObject) { - - const related = {}; - const messages = []; - - for (let query in q.queries.related) { - - const pk = q.queries.related[query].pk; - const sqlLog = q.queriesLog.related[query].sql; - const sql = q.queries.related[query].sql; - - let t = process.hrtime(); - - try { - related[query] = Utils.halify({ - records: db.prepare(sql).all(queryObject), - uri: uriZenodeo, - resource: query, - id: pk - }); - } - catch (error) { - plog.error(error, sqlLog); - } - - t = process.hrtime(t); - messages.push({ - label: query, - params: { sql: sqlLog, took: t } - }); - - } - - plog.log({ header: 'ONE RELATED', messages: messages }); - return related; -}; - -const getTaxonStats = function(data) { - const rec = data.records[0]; - const taxonStats = [ - { name: 'kingdom', value: rec.kingdom, num: 0 }, - { name: 'phylum', value: rec.phylum, num: 0 }, - { name: '"order"', value: rec.order, num: 0 }, - { name: 'family', value: rec.family, num: 0 }, - { name: 'genus', value: rec.genus, num: 0 }, - { name: 'species', value: rec.species, num: 0 } - ]; - - const messages = []; - - taxonStats.forEach((taxon, index) => { - const sql = `SELECT Count(*) AS num FROM treatments WHERE deleted = 0 AND ${taxon.name} = '${taxon.value}'`; - - let t = process.hrtime(); - - try { - taxonStats[index].num = db.prepare(sql).get().num; - } - catch (error) { - plog.error(error, sql); - } - - t = process.hrtime(t); - messages.push({ - label: taxon.name, - params: { sql: sql, took: t } - }); - }) - - plog.log({ header: 'ONE TAXONSTATS', messages: messages }); - return taxonStats; -}; - -const getXml = function(treatmentId) { - const one = treatmentId.substr(0, 1); - const two = treatmentId.substr(0, 2); - const thr = treatmentId.substr(0, 3); - - plog.info(`getting the xml for ${treatmentId}`); - - return fs.readFileSync( - `data/treatments/${one}/${two}/${thr}/${treatmentId}.xml`, - 'utf8' - ) -}; - -const formatAuthors = function(authors) { - let authorsArr = authors.map(a => { return a.author }); - const numOfAuthors = authorsArr.length; - - let authorsList = ''; - if (numOfAuthors === 1) { - authorsList = authorsArr[0]; - } - else if (numOfAuthors === 2) { - authorsList = authorsArr.join(' and '); - } - else if (numOfAuthors > 2) { - authorsList = authorsArr.slice(0, 2).join(', '); - authorsList += ' and ' + authorsArr[numOfAuthors - 1] - } - - return authorsList; -}; - -module.exports = {handler, getRecords}; \ No newline at end of file diff --git a/api/v2/lib/zenodeo.js b/api/v2/lib/zenodeo.js index f36fb81..d850b67 100644 --- a/api/v2/lib/zenodeo.js +++ b/api/v2/lib/zenodeo.js @@ -2,10 +2,11 @@ /******************************************************* * - * Abstracted logic for the handler and other functions - * for the related resources that are fetched from - * Zenodeo - * - treatmentAuthors + * A factory function with abstracted logic for the + * handler and other functions for the related resources + * that are fetched from Zenodeo + * + * - treatmentAuthors (core) * - materialsCitations * - treatmentCitations * - bibRefCitations @@ -386,7 +387,7 @@ const getStatsFacets = function(type, q, queryObject, debug) { }; const getRelatedRecords = function(q, queryObject, debug) { - + const related = {}; const messages = []; @@ -480,23 +481,26 @@ const getXml = function(treatmentId) { ) }; -const formatAuthors = function(authors) { - let authorsArr = authors.map(a => { return a.author }); - const numOfAuthors = authorsArr.length; - - let authorsList = ''; - if (numOfAuthors === 1) { - authorsList = authorsArr[0]; - } - else if (numOfAuthors === 2) { - authorsList = authorsArr.join(' and '); - } - else if (numOfAuthors > 2) { - authorsList = authorsArr.slice(0, 2).join(', '); - authorsList += ' and ' + authorsArr[numOfAuthors - 1] - } - - return authorsList; -}; - -module.exports = { handler, getRecords }; \ No newline at end of file +// Not used for now, but saved for posterity +//****************************************************************/ +// +// const formatAuthors = function(authors) { +// let authorsArr = authors.map(a => { return a.author }); +// const numOfAuthors = authorsArr.length; + +// let authorsList = ''; +// if (numOfAuthors === 1) { +// authorsList = authorsArr[0]; +// } +// else if (numOfAuthors === 2) { +// authorsList = authorsArr.join(' and '); +// } +// else if (numOfAuthors > 2) { +// authorsList = authorsArr.slice(0, 2).join(', '); +// authorsList += ' and ' + authorsArr[numOfAuthors - 1] +// } + +// return authorsList; +// }; + +module.exports = { handler, getRecords }; diff --git a/api/v2/resources/otherRoutes.js b/api/v2/resources/otherRoutes.js index bcb555b..a43c163 100644 --- a/api/v2/resources/otherRoutes.js +++ b/api/v2/resources/otherRoutes.js @@ -1,14 +1,35 @@ 'use strict'; -const { dataDictionary, resourceGroups } = require('../../v2/lib/dd2datadictionary'); +/************************************************************* + * + * This is a factory routine that uses another factor routine + * called `routeMaker` to create an array of routes and + * export them. + * + *************************************************************/ + +// The resource groups +// - zenodeoCore +// - zenodeoRelated +// - zenodeo +// - lookups +const { resourceGroups } = require('../../v2/lib/dd2datadictionary'); const routes = []; + +// 'order' is a route index that is used by the Hapi-Swagger +// plugin to visibly order the routes in the auto-generated +// documentation on the website (see …/docs) let order = 0; for (let g in resourceGroups) { const routeMaker = require('../lib/routeMaker'); + // Loop over every resource (column in the database that can be + // queried by the API), add the 'order' and the group name 'group' + // to it, and push it as a plugin into the array of routes to + // be sent back const resources = resourceGroups[g]; for (let i = 0, j = resources.length; i < j; i++) { @@ -25,4 +46,4 @@ for (let g in resourceGroups) { } -module.exports = routes; \ No newline at end of file +module.exports = routes; diff --git a/api/v2/resources/rootRoute.js b/api/v2/resources/rootRoute.js index a732106..a3f199c 100644 --- a/api/v2/resources/rootRoute.js +++ b/api/v2/resources/rootRoute.js @@ -1,8 +1,15 @@ -const ResponseMessages = require('../../responseMessages') +'use strict'; + +/************************************************************* + * + * This is the root of the API. It lists all the routes to the + * resources available to the user + * + *************************************************************/ module.exports = { plugin: { - name: 'root2', + name: 'root', register: function(server, options) { @@ -10,19 +17,6 @@ module.exports = { { path: '/', method: 'GET', - config: { - description: 'API root listing all available resources', - tags: ['root', 'api'], - plugins: { - 'hapi-swagger': { - order: 1, - responseMessages: ResponseMessages - } - }, - notes: [ - 'This is the root of the API. It lists all available resources.' - ] - }, handler: function(request, h) { const routes = server.table().filter(r => { @@ -44,6 +38,19 @@ module.exports = { return routes; + }, + options: { + description: 'API root listing all available resources', + tags: ['root', 'api'], + plugins: { + 'hapi-swagger': { + order: 1, + responseMessages: require('../../responseMessages') + } + }, + notes: [ + 'This is the root of the API. It lists all available resources.' + ] } } ]); diff --git a/api/v2/resources/wpsummary.js b/api/v2/resources/wpsummary.js index 4b67dd5..d854a74 100644 --- a/api/v2/resources/wpsummary.js +++ b/api/v2/resources/wpsummary.js @@ -1,16 +1,25 @@ +'use strict'; + +/************************************************************* + * + * A route to fetch summary of the given term from the info- + * box on Wikipedia + * + *************************************************************/ + const wiki = require('wikijs').default; -//const Schema = require('../schema.js'); module.exports = { + plugin: { - name: 'wpsummary2', + name: 'wpsummary', register: async function(server, options) { const wpCache = server.cache({ cache: options.cacheName, expiresIn: options.expiresIn, generateTimeout: options.generateTimeout, - segment: 'wpsummary2', + segment: 'wpsummary', generateFunc: async (q) => { return await wp(q); }, getDecoratedValue: options.getDecoratedValue }); @@ -29,10 +38,8 @@ module.exports = { plugins: { 'hapi-swagger': { order: 13, - //responseMessages: ResponseMessages } }, - //validate: Schema.wpsummary, notes: [ 'Summary from the Wikipedia page', ] @@ -58,5 +65,7 @@ const handler = async function(request, h) { const wp = async (term) => { - return await wiki().page(term).then(page => page.summary()).then(summary => { return summary }) + return await wiki().page(term) + .then(page => page.summary()) + .then(summary => { return summary }) }; \ No newline at end of file diff --git a/api/v2/utils.js b/api/v2/utils.js index 61461d6..d6ead28 100644 --- a/api/v2/utils.js +++ b/api/v2/utils.js @@ -1,5 +1,11 @@ 'use strict'; +/*********************************************************************** + * + * A lot of common utility functions called from several other programs + * + **********************************************************************/ + const Wreck = require('@hapi/wreck'); const config = require('config'); @@ -109,23 +115,29 @@ module.exports = { .formatUnicorn(data); }, + // Convert the time from process.hrtime() into human-readable + // total ms and an English-string of "? s and ? ms" timerFormat: function(t) { - const [s, ns] = t; + // seconds and nanoseconds + let [s, ns] = t; - let ms = ns / 1000000; - const msr = Math.round(ms); + // Time taken in ms + const ms = ns / 1000000; + const timeInMs = Math.round(ms); - let str; + // Time in an English-readable string + let timeInEnglish; if (ms >= 1000) { + s = s + Math.round(ms / 1000); - str = `${s}s ${ms - (s * 1000)}ms`; + timeInEnglish = `${s}s ${ms - (s * 1000)}ms`; } else { - str = `${msr}ms` + timeInEnglish = `${timeInMs}ms` } - return { msr: msr, str: str} + return { timeInMs: timeInMs, timeInEnglish: timeInEnglish} }, find: function(pattern, source) { diff --git a/bin/truebug/database/createdb.js b/bin/truebug/database/createdb.js index a9d5d7f..5b92f67 100644 --- a/bin/truebug/database/createdb.js +++ b/bin/truebug/database/createdb.js @@ -4,6 +4,40 @@ const config = require('config'); const plog = require(config.get('plog')); const dd = require('../../../dataDictionary/dd'); + +/* +CREATE TABLE webqueries ( + id INTEGER PRIMARY KEY, + + -- stringified queryObject + qp TEXT NOT NULL UNIQUE, + + -- counter tracking queries + count INTEGER DEFAULT 1 +); + +CREATE TABLE sqlqueries ( + id INTEGER PRIMARY KEY, + + -- SQL query + sql TEXT NOT NULL UNIQUE +); + +CREATE TABLE stats ( + id INTEGER PRIMARY KEY, + + -- Foreign Keys + webqueries_id INTEGER, + sqlqueries_id INTEGER, + + -- query performance time in ms + timeTaken INTEGER, + + -- timestamp of query + created INTEGER DEFAULT (strftime('%s','now')) +); +*/ + const createTable = function(table) { const rdd = dd[table]; diff --git a/config.js b/config.js index 31a6d1d..f6fcb6e 100644 --- a/config.js +++ b/config.js @@ -1,44 +1,44 @@ -const Package = require('./package.json'); +// const Package = require('./package.json'); -module.exports = (function(mode) { - const config = { - base: { - uri: 'https://zenodo.org/api', - zenodo: 'https://zenodo.org/api', - tb: 'http://tb.plazi.org/GgServer/xml/', - port: 3030, - contenttype: 'Content-Type: application/vnd.api+json', - info: { - title: 'Zenodeo API documentation for BLR', - version: '2.0.0', - description: Package.description, - termsOfService: '/tos', - contact: { - name: Package.author, - url: 'https://punkish.org/About', - email: 'punkish@plazi.org' - }, - license: { - name: 'CC0 Public Domain Dedication', - url: 'https://creativecommons.org/publicdomain/zero/1.0/legalcode' - } - } - }, - development: { - schemes: ['http'], - cache: '/Users/punkish/Projects/zenodeo/cache' - }, - production: { - schemes: ['https'], - cache: '/home/punkish/Nodes/zenodeo/cache' - } - }; +// module.exports = (function(mode) { +// const config = { +// base: { +// uri: 'https://zenodo.org/api', +// zenodo: 'https://zenodo.org/api', +// tb: 'http://tb.plazi.org/GgServer/xml/', +// port: 3030, +// contenttype: 'Content-Type: application/vnd.api+json', +// info: { +// title: 'Zenodeo API documentation for BLR', +// version: '2.0.0', +// description: Package.description, +// termsOfService: '/tos', +// contact: { +// name: Package.author, +// url: 'https://punkish.org/About', +// email: 'punkish@plazi.org' +// }, +// license: { +// name: 'CC0 Public Domain Dedication', +// url: 'https://creativecommons.org/publicdomain/zero/1.0/legalcode' +// } +// } +// }, +// development: { +// schemes: ['http'], +// cache: '/Users/punkish/Projects/zenodeo/cache' +// }, +// production: { +// schemes: ['https'], +// cache: '/home/punkish/Nodes/zenodeo/cache' +// } +// }; - let tmp = config[mode || process.env.NODE_ENV || 'development']; +// let tmp = config[mode || process.env.NODE_ENV || 'development']; - for (let i in config.base) { - tmp[i] = config.base[i]; - } +// for (let i in config.base) { +// tmp[i] = config.base[i]; +// } - return tmp; -})(); +// return tmp; +// })(); diff --git a/config/default-old.js b/config/default-old.js deleted file mode 100644 index 326ceea..0000000 --- a/config/default-old.js +++ /dev/null @@ -1,226 +0,0 @@ -'use strict'; - -const path = require('path'); -const cwd = process.cwd(); - -/* -http://tb.plazi.org/GgServer/srsStats/stats?outputFields=doc.uuid+doc.zenodoDepId+doc.updateUser+doc.updateDate&groupingFields=doc.uuid+doc.zenodoDepId+doc.updateUser+doc.updateDate&orderingFields=doc.updateDate&FP-doc.updateDate=%222020-02-21%22-&format=JSON - - - -http://tb.plazi.org/GgServer/srsStats/stats?outputFields=doc.uuid+doc.updateDate&groupingFields=doc.uuid+doc.updateDate&orderingFields=doc.updateDate&FP-doc.updateDate=%222020-02-21%22-&format=JSON - - -tb.plazi.org/GgServer/dumps/plazi.zenodeo.zip -tb.plazi.org/GgServer/dumps/plazi.xmlHistory.zip -*/ - -module.exports = { - - info: { - title: 'Zenodeo API documentation for BLR', - description: '`nodejs` interface to the Zenodo/BLR community collection', - version: '2.5.0', - termsOfService: '/tos', - contact: { - name: 'Puneet Kishor', - url: 'https://punkish.org/About', - email: 'punkish@plazi.org' - }, - license: { - name: 'CC0 Public Domain Dedication', - url: 'https://creativecommons.org/publicdomain/zero/1.0/legalcode' - } - }, - // 'cache': { - // 'path': path.join(cwd, 'cache'), - // 'v1': { - // 'path': path.join(cwd, 'cache'), - // 'name': 'persistent', - // }, - // 'v2': { - // 'path': path.join(cwd, 'cache', 'catbox'), - // 'name': 'catbox', - // 'on': false - // } - //}, - // 'uri': { - // 'local': 'http://localhost:3000', - // 'remote': 'https://zenodo.org/api', - // 'zenodeo': 'http://localhost:3030', - // 'zenodo': 'https://zenodo.org/api' - // }, - 'download-program': { - "newTreatmentsDir": path.join(cwd, 'data', 'treatmentsNew'), - "treatmentsListDir": path.join(cwd, 'data'), - "treatmentsListFilename": "listOfTreatments.xml", - "downloadTreatmentsURL": 'http://tb.plazi.org/GgServer/xml/', - "downloadListURL": 'http://tb.plazi.org/GgServer/search?&indexName=0&resultFormat=XML&lastModifiedSince=' - }, - 'swaggered-scheme': ['http'], - port: 3030, - loglevel: 'INFO', - - v1: { - cache: { - path: path.join(cwd, 'cache'), - name: 'persistent', - on: true - }, - - uri: { - zenodeo: 'http://localhost:3030', - zenodo: 'https://zenodo.org/api' - }, - - dataDict: path.join(cwd, 'dataDictionary', 'data-dictionary.js'), - schema: path.join(cwd, 'api', 'v1', 'schema.js') - }, - - v2: { - cache: { - path: path.join(cwd, 'cache'), - name: 'catbox', - on: false, - - // expires in 1440 mins, or one day - expiresIn: 60 * 1000 * 1440, - generateTimeout: false, - getDecoratedValue: true - }, - - uri: { - zenodeo: 'http://localhost:3030', - zenodo: 'https://zenodo.org/api' - }, - - - dataDict: path.join(cwd, 'dataDictionary', 'data-dictionary.js'), - schema: path.join(cwd, 'api', 'v2', 'schema.js'), - - // 'rank' is a reserved word in FTS tables, so using it in an FTS query - // throws an error. That is why we prefix the column 'rank' with the - // table name - // facets: ['journalTitle', 'journalYear', 'journalVolume', 'kingdom', 'phylum', '"order"', 'family', 'genus', 'species', 'status', 'treatments.rank', 'collectionCode'] - - - // relatedMaterialCitations: { - // yes: integer, // count - // no: integer // count - // }, - // relatedTreatmentCitations: { - // yes: integer, // count - // no: integer // count - // }, - // hasFigures: { - // yes: integer, // count - // no: integer // count - // }, - - }, - - 'xmlDumpSrc': 'http://tb.plazi.org/GgServer/dumps/plazi.xml.zip', - //'xmlDumpDir': path.join(cwd, 'data', 'treatmentsDump'), - 'xmlDumpDir': path.join(cwd, 'data', 'treatmentsDump'), - 'dataDict': path.join(cwd, 'dataDictionary', 'data-dictionary.js'), - //'xmlDumpDir': path.join(cwd, 'data', 'treatmentsDump'), - //'xmlDumpSrc': 'http://tb.plazi.org/GgServer/dumps/plazi.xml.zip', - 'xmlRearrangedDest': path.join(cwd, 'data', 'treatments'), - 'logger': path.join(cwd, 'lib', 'logger.js'), - 'logfields': [ - {'col': 'host', 'type': 'TEXT'}, - {'col': 'start', 'type': "INTEGER DEFAULT (strftime('%s', 'now'))"}, - {'col': 'end', 'type': "INTEGER DEFAULT (strftime('%s', 'now'))"}, - {'col': 'status', 'type': 'TEXT'}, - {'col': 'resource', 'type': 'TEXT'}, - {'col': 'query', 'type': 'TEXT'}, - {'col': 'message', 'type': 'TEXT'} - ], - plog: path.join(cwd, 'lib', 'plog.js'), - httpStatusCodes: path.join(cwd, 'lib', 'httpStatusCodes.js'), - 'data': { - 'logs': path.join(cwd, 'data', 'logs.sqlite'), - 'treatments': path.join(cwd, 'data', 'treatments.sqlite'), - 'authors': path.join(cwd, 'data', 'authors'), - 'keywords': path.join(cwd, 'data', 'keywords'), - 'taxa': path.join(cwd, 'data', 'taxa.min'), - 'families': path.join(cwd, 'data', 'families.min'), - 'facets': path.join(cwd, 'data', 'facets.sqlite') - }, - - - - // 'cacheBase': '/Users/punkish/Projects/zenodeo/cache', - // 'uri': { - // 'local': 'http://localhost:3000', - // 'remote': 'https://zenodo.org/api', - // 'tb': 'http://tb.plazi.org/GgServer/xml/' - // }, - // 'index': { - // 'cacheName': 'diskCache', - // 'cachePath': '/Users/punkish/Projects/catbox/diskCache', - // 'info': { - // title: 'Zenodeo API documentation for BLR', - // description: '`nodejs` interface to the Zenodo/BLR community collection', - // version: '2.0.0', - // termsOfService: '/tos', - // contact: { - // name: 'Puneet Kishor', - // url: 'https://punkish.org/About', - // email: 'punkish@plazi.org' - // }, - // license: { - // name: 'CC0 Public Domain Dedication', - // url: 'https://creativecommons.org/publicdomain/zero/1.0/legalcode' - // } - // } - // }, - // 'api': { - // 'v1': { - // 'utils': { - // 'authors': '../../data/authors', - // 'keywords': '../../data/keywords' - // }, - // 'routes': { - // cacheBase: '/Users/punkish/Projects/zenodeo/cache', - // 'treatments': { - // 'sqliteDatabase': './data/plazi.sqlite' - // } - // } - // }, - // 'v2': { - // 'utils': { - // 'authors': '../../data/authors', - // 'keywords': '../../data/keywords' - // }, - // 'routes': { - // 'treatments': { - // 'sqliteDatabase': './data/plazi.sqlite' - // } - // } - // } - // }, - // 'bin': { - // 'renew': { - // //'xmlDumpDir': './data/treatmentsDump', - // // 'download': { - // // 'downloadDir': 'data/treatments', - // // 'fileName': 'plazi.xml.zip', - // // 'host': 'tb.plazi.org', - // // 'port': 80, - // // 'pathToFile': '/GgServer/dumps/' - // // }, - // // 'parsex': { - // // 'xmlDumpDir': './data/treatmentsDump' - // // }, - // // 'database': { - // // 'sqliteDatabase': './data/plazi.sqlite', - // // 'logDatabase': './data/logs.sqlite' - // // }, - // // 'rearrangefiles': { - // // 'srcdir': 'data/treatmentsDump', - // // 'destdir': 'data/treatments' - // // } - // } - // } -} \ No newline at end of file diff --git a/config/default.js b/config/default.js index 17cf669..06335a5 100644 --- a/config/default.js +++ b/config/default.js @@ -1,23 +1,18 @@ 'use strict'; +/************************************************************** + * + * The config options start with all the values in this file + * + **************************************************************/ + const path = require('path'); const cwd = process.cwd(); const host = 'http://localhost:3030'; -/* -http://tb.plazi.org/GgServer/srsStats/stats?outputFields=doc.uuid+doc.zenodoDepId+doc.updateUser+doc.updateDate&groupingFields=doc.uuid+doc.zenodoDepId+doc.updateUser+doc.updateDate&orderingFields=doc.updateDate&FP-doc.updateDate=%222020-02-21%22-&format=JSON - - - -http://tb.plazi.org/GgServer/srsStats/stats?outputFields=doc.uuid+doc.updateDate&groupingFields=doc.uuid+doc.updateDate&orderingFields=doc.updateDate&FP-doc.updateDate=%222020-02-21%22-&format=JSON - - -tb.plazi.org/GgServer/dumps/plazi.zenodeo.zip -tb.plazi.org/GgServer/dumps/plazi.xmlHistory.zip -*/ - module.exports = { + // API info that is used in the /docs pages by hapi-swaggered info: { title: 'Zenodeo API documentation for BLR', description: '`nodejs` interface to the Zenodo/BLR community collection', @@ -33,20 +28,17 @@ module.exports = { url: 'https://creativecommons.org/publicdomain/zero/1.0/legalcode' } }, - - 'download-program': { - "newTreatmentsDir": path.join(cwd, 'data', 'treatmentsNew'), - "treatmentsListDir": path.join(cwd, 'data'), - "treatmentsListFilename": "listOfTreatments.xml", - "downloadTreatmentsURL": 'http://tb.plazi.org/GgServer/xml/', - "downloadListURL": 'http://tb.plazi.org/GgServer/search?&indexName=0&resultFormat=XML&lastModifiedSince=' - }, - 'swaggered-scheme': ['http'], + 'swaggered-scheme': [ 'http' ], port: 3030, loglevel: 'INFO', + // all queries that take longer than the + // following (in ms) are displayed in red + // in the console log + logSlowSQLthreshold: 300, + v1: { cache: { path: path.join(cwd, 'cache'), @@ -86,33 +78,57 @@ module.exports = { }, - 'xmlDumpSrc': 'http://tb.plazi.org/GgServer/dumps/plazi.xml.zip', - 'xmlDumpDir': path.join(cwd, 'data', 'treatmentsDump'), - 'dataDict': path.join(cwd, 'dataDictionary', 'data-dictionary.js'), - //'xmlDumpDir': path.join(cwd, 'data', 'treatmentsDump'), - //'xmlDumpSrc': 'http://tb.plazi.org/GgServer/dumps/plazi.xml.zip', - 'xmlRearrangedDest': path.join(cwd, 'data', 'treatments'), - 'logger': path.join(cwd, 'lib', 'logger.js'), - 'logfields': [ - {'col': 'host', 'type': 'TEXT'}, - {'col': 'start', 'type': "INTEGER DEFAULT (strftime('%s', 'now'))"}, - {'col': 'end', 'type': "INTEGER DEFAULT (strftime('%s', 'now'))"}, - {'col': 'status', 'type': 'TEXT'}, - {'col': 'resource', 'type': 'TEXT'}, - {'col': 'query', 'type': 'TEXT'}, - {'col': 'message', 'type': 'TEXT'} - ], - plog: path.join(cwd, 'lib', 'plog.js'), - httpStatusCodes: path.join(cwd, 'lib', 'httpStatusCodes.js'), data: { logs: path.join(cwd, 'data', 'logs.sqlite'), treatments: path.join(cwd, 'data', 'treatments.sqlite'), + queryStats: path.join(cwd, 'data', 'queryStats.sqlite'), + lookups: path.join(cwd, 'data', 'facets.sqlite'), + + // The following are legacy files used by API v1 authors: path.join(cwd, 'data', 'authors'), keywords: path.join(cwd, 'data', 'keywords'), taxa: path.join(cwd, 'data', 'taxa.min'), - families: path.join(cwd, 'data', 'families.min'), - lookups: path.join(cwd, 'data', 'facets.sqlite'), - queryStats: path.join(cwd, 'data', 'queryStats.sqlite') - } + families: path.join(cwd, 'data', 'families.min') + + }, + + httpStatusCodes: path.join(cwd, 'lib', 'httpStatusCodes.js'), + plog: path.join(cwd, 'lib', 'plog.js'), + logger: path.join(cwd, 'lib', 'logger.js'), + logfields: [ + { col : 'host', type: 'TEXT' }, + { col : 'start', type: "INTEGER DEFAULT (strftime('%s', 'now'))" }, + { col : 'end', type: "INTEGER DEFAULT (strftime('%s', 'now'))" }, + { col : 'status', type: 'TEXT' }, + { col : 'resource', type: 'TEXT' }, + { col : 'query', type: 'TEXT '}, + { col : 'message', type: 'TEXT' } + ], + + 'download-program': { + "newTreatmentsDir": path.join(cwd, 'data', 'treatmentsNew'), + "treatmentsListDir": path.join(cwd, 'data'), + "treatmentsListFilename": "listOfTreatments.xml", + "downloadTreatmentsURL": 'http://tb.plazi.org/GgServer/xml/', + "downloadListURL": 'http://tb.plazi.org/GgServer/search?&indexName=0&resultFormat=XML&lastModifiedSince=' + }, + + 'xmlDumpSrc': 'http://tb.plazi.org/GgServer/dumps/plazi.xml.zip', + 'xmlDumpDir': path.join(cwd, 'data', 'treatmentsDump'), + 'dataDict': path.join(cwd, 'dataDictionary', 'data-dictionary.js'), + //'xmlDumpDir': path.join(cwd, 'data', 'treatmentsDump'), + //'xmlDumpSrc': 'http://tb.plazi.org/GgServer/dumps/plazi.xml.zip', + 'xmlRearrangedDest': path.join(cwd, 'data', 'treatments') + + + // http://tb.plazi.org/GgServer/srsStats/stats?outputFields=doc.uuid+doc.zenodoDepId+doc.updateUser+doc.updateDate&groupingFields=doc.uuid+doc.zenodoDepId+doc.updateUser+doc.updateDate&orderingFields=doc.updateDate&FP-doc.updateDate=%222020-02-21%22-&format=JSON + + + + // http://tb.plazi.org/GgServer/srsStats/stats?outputFields=doc.uuid+doc.updateDate&groupingFields=doc.uuid+doc.updateDate&orderingFields=doc.updateDate&FP-doc.updateDate=%222020-02-21%22-&format=JSON + + + // tb.plazi.org/GgServer/dumps/plazi.zenodeo.zip + // tb.plazi.org/GgServer/dumps/plazi.xmlHistory.zip }; diff --git a/config/production.js b/config/production.js index 746a464..457c80c 100644 --- a/config/production.js +++ b/config/production.js @@ -1,7 +1,11 @@ 'use strict'; -// any values set here override the same values set -// in default.js +/************************************************************** + * + * The values set here override the same values set + * in default.js + * + **************************************************************/ const host = 'https://zenodeo.punkish.org'; @@ -23,5 +27,7 @@ module.exports = { uri: { zenodeo: `${host}/v2` } - } + }, + + 'swaggered-scheme': [ 'https' ], } \ No newline at end of file diff --git a/config/test.js b/config/test.js index 48c0034..cfeff19 100644 --- a/config/test.js +++ b/config/test.js @@ -1,7 +1,11 @@ 'use strict'; -// any values set here override the same values set -// in default.js +/************************************************************** + * + * The values set here override the same values set + * in default.js + * + **************************************************************/ const host = 'http://z2.punkish.org'; diff --git a/dataDictionary/bibRefCitations.js b/dataDictionary/bibRefCitations.js deleted file mode 100644 index d50c93c..0000000 --- a/dataDictionary/bibRefCitations.js +++ /dev/null @@ -1,61 +0,0 @@ -'use strict'; - -module.exports = [ - { - plaziName : 'id', - zenodoName : '', - sqlType : 'INTEGER PRIMARY KEY', - cheerioElement: '', - description : 'pk', - queryable : '', - queryString : '', - validation : '', - resourceId : false - }, - { - plaziName : 'bibRefCitationId', - zenodoName : '', - sqlType : 'TEXT NOT NULL UNIQUE', - cheerioElement: '$("bibRefCitation").attr("id")', - description : 'The unique ID of the bibRefCitation', - queryable : 'equal', - queryString : 'bibRefCitationId', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : true - }, - { - plaziName : 'refString', - zenodoName : 'relatedIdentifiers[cites]', - sqlName : 'vbibrefcitations', - table : 'vbibrefcitations ON bibRefCitations.bibRefCitationId = vbibrefcitations.bibRefCitationId', - sqlType : 'TEXT', - cheerioElement: '$("bibRefCitation").attr("refString")', - description : 'The reference cited by this treatment', - queryable : 'match', - queryString : 'q', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'type', - zenodoName : 'relatedIdentifiers[cites]', - sqlType : 'TEXT', - cheerioElement: '$("bibRefCitation").attr("type")', - description : 'The type of reference cited by this treatment', - queryable : '', - queryString : '', - validation : '', - resourceId : false - }, - { - plaziName : 'year', - zenodoName : 'relatedIdentifiers[cites]', - sqlType : 'TEXT', - cheerioElement: '$("bibRefCitation").attr("year")', - description : 'The year of the reference cited by this treatment', - queryable : '', - queryString : '', - validation : '', - resourceId : false - } -]; \ No newline at end of file diff --git a/dataDictionary/dd.js b/dataDictionary/dd.js index 3121741..55fc5b7 100644 --- a/dataDictionary/dd.js +++ b/dataDictionary/dd.js @@ -303,84 +303,33 @@ const dd = { zenodeoCore: { - treatments: require('./treatments'), + treatments: require('./resources/treatments'), }, zenodeoRelated: { - figureCitations: require('./figureCitations'), - bibRefCitations: require('./bibRefCitations'), - treatmentCitations: require('./treatmentCitations'), - materialsCitations: require('./materialsCitations'), - treatmentAuthors: require('./treatmentAuthors') + figureCitations: require('./resources/figureCitations'), + bibRefCitations: require('./resources/bibRefCitations'), + treatmentCitations: require('./resources/treatmentCitations'), + materialsCitations: require('./resources/materialsCitations'), + treatmentAuthors: require('./resources/treatmentAuthors') }, zenodo: { - images: require('./images'), - publications: require('./publications') + images: require('./resources/images'), + publications: require('./resources/publications') }, lookups: { - authors: [ - { - plaziName : 'author', - zenodoName : '', - sqlType : '', - cheerioElement: '', - description : 'retrieve all authors starting with the provided letters', - queryable : 'like', - queryString : 'q', - validation : 'Joi.string().description(`${d}`).required().min(3).message(`a querystring «q» of at least {#limit} characters is required (for example, «?q=ago»)`)', - resourceId : false - } - ], - - keywords: [ - { - plaziName : 'keyword', - zenodoName : '', - sqlType : '', - cheerioElement: '', - description : 'retrieve all keywords starting with the provided letters', - queryable : 'like', - queryString : 'q', - validation : 'Joi.string().description(`${d}`).required().min(3).message(`a querystring «q» of at least {#limit} characters is required (for example, «?q=son»)`)', - resourceId : false - } - ], - - families: [ - { - plaziName : 'family', - zenodoName : '', - sqlType : '', - cheerioElement: '', - description : 'retrieve all families starting with the provided letters', - queryable : 'like', - queryString : 'q', - validation : 'Joi.string().description(`${d}`).required().min(3).message(`a querystring «q» of at least {#limit} characters is required (for example, «?q=ago»)`)', - resourceId : false - } - ], - - taxa: [ - { - plaziName : 'taxon', - zenodoName : '', - sqlType : '', - cheerioElement: '', - description : 'retrieve all taxa starting with the provided letters', - queryable : 'like', - queryString : 'q', - validation : 'Joi.string().description(`${d}`).required().min(3).message(`a querystring «q» of at least {#limit} characters is required (for example, «?q=ago»)`)', - resourceId : false - } - ] + authors: require('./resources/authors'), + keywords: require('./resources/keywords'), + families: require('./resources/families'), + taxa: require('./resources/taxa') } diff --git a/dataDictionary/figureCitations.js b/dataDictionary/figureCitations.js deleted file mode 100644 index d1a32c6..0000000 --- a/dataDictionary/figureCitations.js +++ /dev/null @@ -1,61 +0,0 @@ -'use strict'; - -module.exports = [ - // { - // plaziName : 'id', - // zenodoName : '', - // sqlType : 'INTEGER PRIMARY KEY', - // cheerioElement: '', - // description : 'pk', - // queryable : '', - // queryString : '', - // validation : '', - // resourceId : false - // }, - { - plaziName : 'figureCitationId', - zenodoName : '', - sqlType : 'TEXT NOT NULL UNIQUE', - cheerioElement: '$("figureCitation").attr("id")', - description : 'The unique ID of the figureCitation', - queryable : 'equal', - queryString : 'figureCitationId', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : true - }, - { - plaziName : 'captionText', - zenodoName : 'relatedIdentifiers[cites]', - sqlName : 'vfigurecitations', - table : 'vfigurecitations ON figureCitations.figureCitationId = vfigurecitations.figureCitationId', - sqlType : 'TEXT', - cheerioElement: '$("figureCitation").attr("captionText")', - description : 'The figure cited by this treatment', - queryable : 'match', - queryString : 'q', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'httpUri', - zenodoName : 'relatedIdentifiers[cites]', - sqlType : 'TEXT', - cheerioElement: '$("figureCitation").attr("httpUri")', - description : 'The URI of the figure cited by this treatment', - queryable : '', - queryString : '', - validation : '', - resourceId : false - }, - { - plaziName : 'thumbnailUri', - zenodoName : '', - sqlType : 'TEXT', - cheerioElement: '', - description : 'The thumbnail of the figure cited by this treatment', - queryable : '', - queryString : '', - validation : '', - resourceId : false - } -]; \ No newline at end of file diff --git a/dataDictionary/images.js b/dataDictionary/images.js deleted file mode 100644 index f7db3d0..0000000 --- a/dataDictionary/images.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; - -module.exports = [ - { - plaziName : 'type', - zenodoName : 'publication_subtypes', - sqlType : '', - cheerioElement: '', - description : 'The image subtype; defaults to "all"', - queryable : 'equal', - queryString : 'type', - validation : 'Joi.string().valid("all", "figure", "photo", "drawing", "diagram", "plot", "other").default("all").description(`${d}`).optional()', - resourceId : false - } -]; \ No newline at end of file diff --git a/dataDictionary/materialsCitations.js b/dataDictionary/materialsCitations.js deleted file mode 100644 index 2687254..0000000 --- a/dataDictionary/materialsCitations.js +++ /dev/null @@ -1,303 +0,0 @@ -'use strict'; - -module.exports = [ - { - plaziName : 'materialsCitationId', - zenodoName : '', - sqlType : 'TEXT NOT NULL UNIQUE', - cheerioElement: '$("materialsCitation").attr("id")', - description : 'The unique ID of the materialsCitation', - queryable : 'equal', - queryString : 'materialsCitationId', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : true - }, - { - plaziName : 'collectingDate', - zenodoName : 'date[type=collected] + range parsing', - sqlType : 'TEXT', - cheerioElement: '$("materialsCitation").attr("collectingDate")', - description : 'The date when the specimen was collected', - queryable : 'equal', - queryString : 'collectingDate', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'collectionCode', - zenodoName : 'subjects', - sqlType : 'TEXT', - cheerioElement: '$("materialsCitation").attr("collectionCode")', - description : 'The collection code for a natural history collection', - queryable : 'equal', - queryString : 'collectionCode', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'collectorName', - zenodoName : 'contributor=collector', - sqlType : 'TEXT', - cheerioElement: '$("materialsCitation").attr("collectorName")', - description : 'The person who collected the specimen', - queryable : 'like', - queryString : 'collectorName', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'country', - zenodoName : 'geo_place', - sqlType : 'TEXT', - cheerioElement: '$("materialsCitation").attr("country")', - description : 'The country where the specimen was collected', - queryable : 'like', - queryString : 'country', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'collectingRegion', - zenodoName : 'geo_place', - sqlType : 'TEXT', - cheerioElement: '$("materialsCitation").attr("collectingRegion")', - description : 'The geographic region where the specimen was collected', - queryable : 'like', - queryString : 'collectingRegion', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'municipality', - zenodoName : 'geo_place', - sqlType : 'TEXT', - cheerioElement: '$("materialsCitation").attr("municipality")', - description : 'A lower administrative region', - queryable : 'like', - queryString : 'municipality', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'county', - zenodoName : 'geo_place', - sqlType : 'TEXT', - cheerioElement: '$("materialsCitation").attr("county")', - description : 'The county where the specimen was collected', - queryable : 'like', - queryString : 'county', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'stateProvince', - zenodoName : 'geo_place', - sqlType : 'TEXT', - cheerioElement: '$("materialsCitation").attr("stateProvince")', - description : 'The state or province where the specimen was collected', - queryable : 'like', - queryString : 'stateProvince', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'location', - zenodoName : 'geo_place', - sqlType : 'TEXT', - cheerioElement: '$("materialsCitation").attr("location")', - description : 'The location where the specimen was collected', - queryable : 'like', - queryString : 'location', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'locationDeviation', - zenodoName : 'geo_place', - sqlType : 'TEXT', - cheerioElement: '$("materialsCitation").attr("locationDeviation")', - description : 'The distance to the nearest location, e.g. 23km NW from…', - queryable : '', - queryString : '', - validation : '', - resourceId : false - }, - { - plaziName : 'specimenCountFemale', - zenodoName : '', - sqlType : 'TEXT', - cheerioElement: '$("materialsCitation").attr("specimenCount-female")', - description : 'The number of listed female specimens', - queryable : '', - queryString : '', - validation : '', - resourceId : false - }, - { - plaziName : 'specimenCountMale', - zenodoName : '', - sqlType : 'TEXT', - cheerioElement: '$("materialsCitation").attr("specimenCount-male")', - description : 'The number of listed male specimens', - queryable : '', - queryString : '', - validation : '', - resourceId : false - }, - { - plaziName : 'specimenCount', - zenodoName : '', - sqlType : 'TEXT', - cheerioElement: '$("materialsCitation").attr("specimenCount")', - description : 'The number of listed specimens', - queryable : '', - queryString : '', - validation : '', - resourceId : false - }, - { - plaziName : 'specimenCode', - zenodoName : 'subjects', - sqlType : 'TEXT', - cheerioElement: '$("materialsCitation").attr("specimenCode")', - description : 'The code of the specimen', - queryable : 'equal', - queryString : 'specimenCode', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'typeStatus', - zenodoName : 'subjects', - sqlType : 'TEXT', - cheerioElement: '$("materialsCitation").attr("typeStatus")', - description : 'The nomenclatural status of the specimen, e.g. holotype, paratype', - queryable : 'equal', - queryString : 'typeStatus', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'determinerName', - zenodoName : '', - sqlType : 'TEXT', - cheerioElement: '$("materialsCitation").attr("determinerName")', - description : 'The person or agent who identified the specimen', - queryable : 'like', - queryString : 'determinerName', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'collectedFrom', - zenodoName : '', - sqlType : 'TEXT', - cheerioElement: '$("materialsCitation").attr("collectedFrom")', - description : 'The substrate where the specimen has been collected, e.g. leaf, flower', - queryable : 'like', - queryString : 'collectedFrom', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'collectingMethod', - zenodoName : 'description[method]', - sqlType : 'TEXT', - cheerioElement: '$("materialsCitation").attr("collectingMethod")', - description : 'The method used for collecting the specimen', - queryable : 'like', - queryString : 'collectingMethod', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'latitude', - zenodoName : 'geo_lat', - sqlType : 'TEXT', - cheerioElement: '$("materialsCitation").attr("latitude")', - description : `Geographic coordinates of the location where the specimen was collected. -Since it is unreasonable to expect the user to provide exact lat/lon coordinates, a delta of 0.9 -is used to find all the points contained in the resulting box. For example, if latitude 77 and -longitude 78 are provided, the two points A and B are located inside the bounding box made from -submitted coordinates +- a delta of 0.9 - - lat: 77.9 lat: 77.9 - lon: 77.1 lon: 78.9 - ┌───────────────────────────────────────────────┐ - │ │ - │ │ - │ lat: 77.53333 │ - │ lon: 78.88333 │ - │ B │ - │ │ - │ │ - │ lat: 77.2 │ - │ lon: 78.11667 │ - │ A │ - │ │ - │ │ - └───────────────────────────────────────────────┘ - lat: 76.1 lat: 76.1 - lon: 77.1 lon: 78.9`, - queryable : 'between', - queryString : 'latitude', - validation : 'Joi.number().min(-90).max(90).description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'longitude', - zenodoName : 'geo_lon', - sqlType : 'TEXT', - cheerioElement: '$("materialsCitation").attr("longitude")', - description : `Geographic coordinates of the location where the specimen was collected. - Since it is unreasonable to expect the user to provide exact lat/lon coordinates, a delta of 0.9 - is used to find all the points contained in the resulting box. For example, if latitude 77 and - longitude 78 are provided, the two points A and B are located inside the bounding box made from - submitted coordinates +- a delta of 0.9 - - lat: 77.9 lat: 77.9 - lon: 77.1 lon: 78.9 - ┌───────────────────────────────────────────────┐ - │ │ - │ │ - │ lat: 77.53333 │ - │ lon: 78.88333 │ - │ B │ - │ │ - │ │ - │ lat: 77.2 │ - │ lon: 78.11667 │ - │ A │ - │ │ - │ │ - └───────────────────────────────────────────────┘ - lat: 76.1 lat: 76.1 - lon: 77.1 lon: 78.9`, - queryable : 'between', - queryString : 'longitude', - validation : 'Joi.number().min(-180).max(180).description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'elevation', - zenodoName : '', - sqlType : 'TEXT', - cheerioElement: '$("materialsCitation").attr("elevation")', - description : 'Elevation of the location where the specimen was collected', - queryable : 'equal', - queryString : 'elevation', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'httpUri', - zenodoName : 'relatedIdentifiers[hasPart]', - sqlType : 'TEXT', - cheerioElement: '$("materialsCitation").attr("httpUri")', - description : 'The persistent identifier of the specimen', - queryable : '', - queryString : '', - validation : '', - resourceId : false - } -]; \ No newline at end of file diff --git a/dataDictionary/publications.js b/dataDictionary/publications.js deleted file mode 100644 index 14f2b6f..0000000 --- a/dataDictionary/publications.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; - -module.exports = [ - { - plaziName : 'type', - zenodoName : 'image_subtypes', - sqlType : '', - cheerioElement: '', - description : 'The publication subtype; defaults to "all"', - queryable : 'equal', - queryString : 'type', - validation : 'Joi.string().valid("all", "article", "report", "book", "thesis", "section", "workingpaper", "preprint").default("all").description(`${d}`).optional()', - resourceId : false - } -]; \ No newline at end of file diff --git a/dataDictionary/resources/authors.js b/dataDictionary/resources/authors.js new file mode 100644 index 0000000..3de263a --- /dev/null +++ b/dataDictionary/resources/authors.js @@ -0,0 +1,24 @@ +'use strict'; + +/*********************************************************************** + * + * Data dictionary for authors (used for lookups) from Zenodeo + * + **********************************************************************/ + +module.exports = { + cache: false, + fields: [ + { + plaziName : 'author', + zenodoName : '', + sqlType : '', + cheerioElement: '', + description : 'retrieve all authors starting with the provided letters', + queryable : 'like', + queryString : 'q', + validation : 'Joi.string().description(`${d}`).required().min(3).message(`a querystring «q» of at least {#limit} characters is required (for example, «?q=ago»)`)', + resourceId : false + } + ] +}; diff --git a/dataDictionary/resources/bibRefCitations.js b/dataDictionary/resources/bibRefCitations.js new file mode 100644 index 0000000..97f6ae7 --- /dev/null +++ b/dataDictionary/resources/bibRefCitations.js @@ -0,0 +1,70 @@ +'use strict'; + +/*********************************************************************** + * + * Data dictionary for bibRefCitations from Zenodeo + * + **********************************************************************/ + +module.exports = { + cache: true, + fields: [ + { + plaziName : 'id', + zenodoName : '', + sqlType : 'INTEGER PRIMARY KEY', + cheerioElement: '', + description : 'pk', + queryable : '', + queryString : '', + validation : '', + resourceId : false + }, + { + plaziName : 'bibRefCitationId', + zenodoName : '', + sqlType : 'TEXT NOT NULL UNIQUE', + cheerioElement: '$("bibRefCitation").attr("id")', + description : 'The unique ID of the bibRefCitation', + queryable : 'equal', + queryString : 'bibRefCitationId', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : true + }, + { + plaziName : 'refString', + zenodoName : 'relatedIdentifiers[cites]', + sqlName : 'vbibrefcitations', + table : 'vbibrefcitations ON bibRefCitations.bibRefCitationId = vbibrefcitations.bibRefCitationId', + sqlType : 'TEXT', + cheerioElement: '$("bibRefCitation").attr("refString")', + description : 'The reference cited by this treatment', + queryable : 'match', + queryString : 'q', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'type', + zenodoName : 'relatedIdentifiers[cites]', + sqlType : 'TEXT', + cheerioElement: '$("bibRefCitation").attr("type")', + description : 'The type of reference cited by this treatment', + queryable : '', + queryString : '', + validation : '', + resourceId : false + }, + { + plaziName : 'year', + zenodoName : 'relatedIdentifiers[cites]', + sqlType : 'TEXT', + cheerioElement: '$("bibRefCitation").attr("year")', + description : 'The year of the reference cited by this treatment', + queryable : '', + queryString : '', + validation : '', + resourceId : false + } + ] +}; \ No newline at end of file diff --git a/dataDictionary/resources/families.js b/dataDictionary/resources/families.js new file mode 100644 index 0000000..4971f60 --- /dev/null +++ b/dataDictionary/resources/families.js @@ -0,0 +1,24 @@ +'use strict'; + +/*********************************************************************** + * + * Data dictionary for families (used for lookups) from Zenodeo + * + **********************************************************************/ + +module.exports = { + cache: false, + fields: [ + { + plaziName : 'family', + zenodoName : '', + sqlType : '', + cheerioElement: '', + description : 'retrieve all families starting with the provided letters', + queryable : 'like', + queryString : 'q', + validation : 'Joi.string().description(`${d}`).required().min(3).message(`a querystring «q» of at least {#limit} characters is required (for example, «?q=ago»)`)', + resourceId : false + } + ] +}; diff --git a/dataDictionary/resources/figureCitations.js b/dataDictionary/resources/figureCitations.js new file mode 100644 index 0000000..816b197 --- /dev/null +++ b/dataDictionary/resources/figureCitations.js @@ -0,0 +1,59 @@ +'use strict'; + +/*********************************************************************** + * + * Data dictionary for figureCitations from Zenodeo + * + **********************************************************************/ + +module.exports = { + cache: true, + fields: [ + { + plaziName : 'figureCitationId', + zenodoName : '', + sqlType : 'TEXT NOT NULL UNIQUE', + cheerioElement: '$("figureCitation").attr("id")', + description : 'The unique ID of the figureCitation', + queryable : 'equal', + queryString : 'figureCitationId', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : true + }, + { + plaziName : 'captionText', + zenodoName : 'relatedIdentifiers[cites]', + sqlName : 'vfigurecitations', + table : 'vfigurecitations ON figureCitations.figureCitationId = vfigurecitations.figureCitationId', + sqlType : 'TEXT', + cheerioElement: '$("figureCitation").attr("captionText")', + description : 'The figure cited by this treatment', + queryable : 'match', + queryString : 'q', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'httpUri', + zenodoName : 'relatedIdentifiers[cites]', + sqlType : 'TEXT', + cheerioElement: '$("figureCitation").attr("httpUri")', + description : 'The URI of the figure cited by this treatment', + queryable : '', + queryString : '', + validation : '', + resourceId : false + }, + { + plaziName : 'thumbnailUri', + zenodoName : '', + sqlType : 'TEXT', + cheerioElement: '', + description : 'The thumbnail of the figure cited by this treatment', + queryable : '', + queryString : '', + validation : '', + resourceId : false + } + ] +}; \ No newline at end of file diff --git a/dataDictionary/resources/images.js b/dataDictionary/resources/images.js new file mode 100644 index 0000000..f9be140 --- /dev/null +++ b/dataDictionary/resources/images.js @@ -0,0 +1,24 @@ +'use strict'; + +/*********************************************************************** + * + * Data dictionary for images from Zenodo + * + **********************************************************************/ + +module.exports = { + cache: true, + fields: [ + { + plaziName : 'type', + zenodoName : 'publication_subtypes', + sqlType : '', + cheerioElement: '', + description : 'The image subtype; defaults to "all"', + queryable : 'equal', + queryString : 'type', + validation : 'Joi.string().valid("all", "figure", "photo", "drawing", "diagram", "plot", "other").default("all").description(`${d}`).optional()', + resourceId : false + } + ] +}; \ No newline at end of file diff --git a/dataDictionary/resources/keywords.js b/dataDictionary/resources/keywords.js new file mode 100644 index 0000000..36f84d7 --- /dev/null +++ b/dataDictionary/resources/keywords.js @@ -0,0 +1,24 @@ +'use strict'; + +/*********************************************************************** + * + * Data dictionary for keywords (used for lookups) from Zenodeo + * + **********************************************************************/ + +module.exports = { + cache: false, + fields: [ + { + plaziName : 'keyword', + zenodoName : '', + sqlType : '', + cheerioElement: '', + description : 'retrieve all keywords starting with the provided letters', + queryable : 'like', + queryString : 'q', + validation : 'Joi.string().description(`${d}`).required().min(3).message(`a querystring «q» of at least {#limit} characters is required (for example, «?q=son»)`)', + resourceId : false + } + ] +}; diff --git a/dataDictionary/resources/materialsCitations.js b/dataDictionary/resources/materialsCitations.js new file mode 100644 index 0000000..a2c5b14 --- /dev/null +++ b/dataDictionary/resources/materialsCitations.js @@ -0,0 +1,312 @@ +'use strict'; + +/*********************************************************************** + * + * Data dictionary for materialsCitations from Zenodeo + * + **********************************************************************/ + +module.exports = { + cache: true, + fields: [ + { + plaziName : 'materialsCitationId', + zenodoName : '', + sqlType : 'TEXT NOT NULL UNIQUE', + cheerioElement: '$("materialsCitation").attr("id")', + description : 'The unique ID of the materialsCitation', + queryable : 'equal', + queryString : 'materialsCitationId', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : true + }, + { + plaziName : 'collectingDate', + zenodoName : 'date[type=collected] + range parsing', + sqlType : 'TEXT', + cheerioElement: '$("materialsCitation").attr("collectingDate")', + description : 'The date when the specimen was collected', + queryable : 'equal', + queryString : 'collectingDate', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'collectionCode', + zenodoName : 'subjects', + sqlType : 'TEXT', + cheerioElement: '$("materialsCitation").attr("collectionCode")', + description : 'The collection code for a natural history collection', + queryable : 'equal', + queryString : 'collectionCode', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'collectorName', + zenodoName : 'contributor=collector', + sqlType : 'TEXT', + cheerioElement: '$("materialsCitation").attr("collectorName")', + description : 'The person who collected the specimen', + queryable : 'like', + queryString : 'collectorName', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'country', + zenodoName : 'geo_place', + sqlType : 'TEXT', + cheerioElement: '$("materialsCitation").attr("country")', + description : 'The country where the specimen was collected', + queryable : 'like', + queryString : 'country', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'collectingRegion', + zenodoName : 'geo_place', + sqlType : 'TEXT', + cheerioElement: '$("materialsCitation").attr("collectingRegion")', + description : 'The geographic region where the specimen was collected', + queryable : 'like', + queryString : 'collectingRegion', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'municipality', + zenodoName : 'geo_place', + sqlType : 'TEXT', + cheerioElement: '$("materialsCitation").attr("municipality")', + description : 'A lower administrative region', + queryable : 'like', + queryString : 'municipality', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'county', + zenodoName : 'geo_place', + sqlType : 'TEXT', + cheerioElement: '$("materialsCitation").attr("county")', + description : 'The county where the specimen was collected', + queryable : 'like', + queryString : 'county', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'stateProvince', + zenodoName : 'geo_place', + sqlType : 'TEXT', + cheerioElement: '$("materialsCitation").attr("stateProvince")', + description : 'The state or province where the specimen was collected', + queryable : 'like', + queryString : 'stateProvince', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'location', + zenodoName : 'geo_place', + sqlType : 'TEXT', + cheerioElement: '$("materialsCitation").attr("location")', + description : 'The location where the specimen was collected', + queryable : 'like', + queryString : 'location', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'locationDeviation', + zenodoName : 'geo_place', + sqlType : 'TEXT', + cheerioElement: '$("materialsCitation").attr("locationDeviation")', + description : 'The distance to the nearest location, e.g. 23km NW from…', + queryable : '', + queryString : '', + validation : '', + resourceId : false + }, + { + plaziName : 'specimenCountFemale', + zenodoName : '', + sqlType : 'TEXT', + cheerioElement: '$("materialsCitation").attr("specimenCount-female")', + description : 'The number of listed female specimens', + queryable : '', + queryString : '', + validation : '', + resourceId : false + }, + { + plaziName : 'specimenCountMale', + zenodoName : '', + sqlType : 'TEXT', + cheerioElement: '$("materialsCitation").attr("specimenCount-male")', + description : 'The number of listed male specimens', + queryable : '', + queryString : '', + validation : '', + resourceId : false + }, + { + plaziName : 'specimenCount', + zenodoName : '', + sqlType : 'TEXT', + cheerioElement: '$("materialsCitation").attr("specimenCount")', + description : 'The number of listed specimens', + queryable : '', + queryString : '', + validation : '', + resourceId : false + }, + { + plaziName : 'specimenCode', + zenodoName : 'subjects', + sqlType : 'TEXT', + cheerioElement: '$("materialsCitation").attr("specimenCode")', + description : 'The code of the specimen', + queryable : 'equal', + queryString : 'specimenCode', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'typeStatus', + zenodoName : 'subjects', + sqlType : 'TEXT', + cheerioElement: '$("materialsCitation").attr("typeStatus")', + description : 'The nomenclatural status of the specimen, e.g. holotype, paratype', + queryable : 'equal', + queryString : 'typeStatus', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'determinerName', + zenodoName : '', + sqlType : 'TEXT', + cheerioElement: '$("materialsCitation").attr("determinerName")', + description : 'The person or agent who identified the specimen', + queryable : 'like', + queryString : 'determinerName', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'collectedFrom', + zenodoName : '', + sqlType : 'TEXT', + cheerioElement: '$("materialsCitation").attr("collectedFrom")', + description : 'The substrate where the specimen has been collected, e.g. leaf, flower', + queryable : 'like', + queryString : 'collectedFrom', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'collectingMethod', + zenodoName : 'description[method]', + sqlType : 'TEXT', + cheerioElement: '$("materialsCitation").attr("collectingMethod")', + description : 'The method used for collecting the specimen', + queryable : 'like', + queryString : 'collectingMethod', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'latitude', + zenodoName : 'geo_lat', + sqlType : 'TEXT', + cheerioElement: '$("materialsCitation").attr("latitude")', + description : `Geographic coordinates of the location where the specimen was collected. +Since it is unreasonable to expect the user to provide exact lat/lon coordinates, a delta of 0.9 +is used to find all the points contained in the resulting box. For example, if latitude 77 and +longitude 78 are provided, the two points A and B are located inside the bounding box made from +submitted coordinates +- a delta of 0.9 + + lat: 77.9 lat: 77.9 + lon: 77.1 lon: 78.9 + ┌───────────────────────────────────────────────┐ + │ │ + │ │ + │ lat: 77.53333 │ + │ lon: 78.88333 │ + │ B │ + │ │ + │ │ + │ lat: 77.2 │ + │ lon: 78.11667 │ + │ A │ + │ │ + │ │ + └───────────────────────────────────────────────┘ + lat: 76.1 lat: 76.1 + lon: 77.1 lon: 78.9`, + queryable : 'between', + queryString : 'latitude', + validation : 'Joi.number().min(-90).max(90).description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'longitude', + zenodoName : 'geo_lon', + sqlType : 'TEXT', + cheerioElement: '$("materialsCitation").attr("longitude")', + description : `Geographic coordinates of the location where the specimen was collected. +Since it is unreasonable to expect the user to provide exact lat/lon coordinates, a delta of 0.9 +is used to find all the points contained in the resulting box. For example, if latitude 77 and +longitude 78 are provided, the two points A and B are located inside the bounding box made from +submitted coordinates +- a delta of 0.9 + + lat: 77.9 lat: 77.9 + lon: 77.1 lon: 78.9 + ┌───────────────────────────────────────────────┐ + │ │ + │ │ + │ lat: 77.53333 │ + │ lon: 78.88333 │ + │ B │ + │ │ + │ │ + │ lat: 77.2 │ + │ lon: 78.11667 │ + │ A │ + │ │ + │ │ + └───────────────────────────────────────────────┘ + lat: 76.1 lat: 76.1 + lon: 77.1 lon: 78.9`, + queryable : 'between', + queryString : 'longitude', + validation : 'Joi.number().min(-180).max(180).description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'elevation', + zenodoName : '', + sqlType : 'TEXT', + cheerioElement: '$("materialsCitation").attr("elevation")', + description : 'Elevation of the location where the specimen was collected', + queryable : 'equal', + queryString : 'elevation', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'httpUri', + zenodoName : 'relatedIdentifiers[hasPart]', + sqlType : 'TEXT', + cheerioElement: '$("materialsCitation").attr("httpUri")', + description : 'The persistent identifier of the specimen', + queryable : '', + queryString : '', + validation : '', + resourceId : false + } + ] +}; \ No newline at end of file diff --git a/dataDictionary/resources/publications.js b/dataDictionary/resources/publications.js new file mode 100644 index 0000000..be963cc --- /dev/null +++ b/dataDictionary/resources/publications.js @@ -0,0 +1,24 @@ +'use strict'; + +/*********************************************************************** + * + * Data dictionary for publications from Zenodo + * + **********************************************************************/ + +module.exports = { + cache: true, + fields: [ + { + plaziName : 'type', + zenodoName : 'image_subtypes', + sqlType : '', + cheerioElement: '', + description : 'The publication subtype; defaults to "all"', + queryable : 'equal', + queryString : 'type', + validation : 'Joi.string().valid("all", "article", "report", "book", "thesis", "section", "workingpaper", "preprint").default("all").description(`${d}`).optional()', + resourceId : false + } + ] +}; \ No newline at end of file diff --git a/dataDictionary/resources/taxa.js b/dataDictionary/resources/taxa.js new file mode 100644 index 0000000..85ddf69 --- /dev/null +++ b/dataDictionary/resources/taxa.js @@ -0,0 +1,24 @@ +'use strict'; + +/*********************************************************************** + * + * Data dictionary for taxa (used for lookups) from Zenodeo + * + **********************************************************************/ + +module.exports = { + cache: false, + fields: [ + { + plaziName : 'taxon', + zenodoName : '', + sqlType : '', + cheerioElement: '', + description : 'retrieve all taxa starting with the provided letters', + queryable : 'like', + queryString : 'q', + validation : 'Joi.string().description(`${d}`).required().min(3).message(`a querystring «q» of at least {#limit} characters is required (for example, «?q=ago»)`)', + resourceId : false + } + ] +}; diff --git a/dataDictionary/resources/treatmentAuthors.js b/dataDictionary/resources/treatmentAuthors.js new file mode 100644 index 0000000..9d7a56d --- /dev/null +++ b/dataDictionary/resources/treatmentAuthors.js @@ -0,0 +1,35 @@ +'use strict'; + +/*********************************************************************** + * + * Data dictionary for treatmentAuthors from Zenodeo + * + **********************************************************************/ + +module.exports = { + cache: true, + fields: [ + { + plaziName : 'treatmentAuthorId', + zenodoName : '', + sqlType : 'TEXT NOT NULL UNIQUE', + cheerioElement: '$("treatmentAuthor").attr("id")', + description : 'The unique ID of the treatmentAuthor', + queryable : 'equal', + queryString : 'treatmentAuthorId', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : true + }, + { + plaziName : 'treatmentAuthor', + zenodoName : 'creators', + sqlType : 'TEXT', + cheerioElement: 'mods\\\\:namePart', + description : 'The author of this treatment (author of the article is used if no treatment authority is found)', + queryable : 'like', + queryString : 'treatmentAuthor', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + } + ] +}; \ No newline at end of file diff --git a/dataDictionary/resources/treatmentCitations.js b/dataDictionary/resources/treatmentCitations.js new file mode 100644 index 0000000..bde5016 --- /dev/null +++ b/dataDictionary/resources/treatmentCitations.js @@ -0,0 +1,46 @@ +'use strict'; + +/*********************************************************************** + * + * Data dictionary for treatmentCitations from Zenodeo + * + **********************************************************************/ + +module.exports = { + cache: true, + fields: [ + { + plaziName : 'treatmentCitationId', + zenodoName : '', + sqlType : 'TEXT NOT NULL UNIQUE', + cheerioElement: '', + description : 'The unique ID of the treatmentCitation', + queryable : 'equal', + queryString : 'treatmentCitationId', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : true + }, + { + plaziName : 'treatmentCitation', + zenodoName : 'subjects; AND if there is a DOI for the treatmentCitation, relatedIdentifiers[cites]', + sqlType : 'TEXT', + cheerioElement: '$("subSubSection[type=reference_group] treatmentCitationGroup taxonomicName").text() + " " + $("subSubSection[type=reference_Group] treatmentCitationGroup taxonomicName").attr("authority") + " sec. " + $("subSubSection[type=reference_Group] treatmentCitationGroup bibRefCitation").text()', + description : 'The taxonomic name and the author of the species, plus the author of the treatment being cited', + queryable : '', + queryString : '', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'refString', + zenodoName : 'relatedIdentifiers[cites]', + sqlType : 'TEXT', + cheerioElement: '$("subSubSection[type=referenceGroup] treatmentCitationGroup treatmentCitation bibRefCitation").attr("refString")', + description : 'The bibliographic reference string of the treatments cited by this treatment', + queryable : '', + queryString : '', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + } + ] +}; \ No newline at end of file diff --git a/dataDictionary/resources/treatments.js b/dataDictionary/resources/treatments.js new file mode 100644 index 0000000..dcb073d --- /dev/null +++ b/dataDictionary/resources/treatments.js @@ -0,0 +1,341 @@ +'use strict'; + +/*********************************************************************** + * + * Data dictionary for treatments from Zenodeo + * + **********************************************************************/ + +module.exports = { + cache: true, + fields: [ + { + plaziName : 'id', + zenodoName : '', + sqlType : 'INTEGER PRIMARY KEY', + cheerioElement: '', + description : 'The primary key (PK) for this table', + queryable : '', + queryString : '', + validation : '', + resourceId : false + }, + { + plaziName : 'treatmentId', + zenodoName : '', + sqlType : 'TEXT NOT NULL UNIQUE', + cheerioElement: '$("document").attr("docId")', + description : 'The unique ID of the treatment', + queryable : 'equal', + queryString : 'treatmentId', + validation : 'Joi.string().guid().description(`${d}`).optional()', + resourceId : true + }, + { + plaziName : 'deleted', + zenodoName : '', + sqlType : 'INTEGER DEFAULT 0', + cheerioElement: '$("document").attr("deleted")', + description : 'A boolean that tracks whether or not this resource is considered deleted/revoked, 1 if yes, 0 if no', + queryable : '', + queryString : '', + validation : '', + resourceId : false + }, + { + plaziName : 'treatmentTitle', + zenodoName : 'title', + sqlType : 'TEXT', + cheerioElement: '$("document").attr("docTitle")', + description : 'Title of the article that contains this treatment', + queryable : 'like', + queryString : 'treatmentTitle', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'doi', + zenodoName : 'relatedidentifiers[isPartOf]', + sqlType : 'TEXT', + cheerioElement: '$("document").attr("ID-DOI")', + description : 'DOI of journal article', + queryable : 'equal', + queryString : 'doi', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'zenodoDep', + zenodoName : 'relatedidentifiers[isPartOf]', + sqlType : 'TEXT', + cheerioElement: '$("document").attr("ID-Zenodo-Dep")', + description : 'Zenodo record of journal article', + queryable : '', + queryString : '', + validation : '', + resourceId : false + }, + { + plaziName : 'zoobank', + zenodoName : 'relatedidentifiers[isPartOf]', + sqlType : 'TEXT', + cheerioElement: '$("document").attr("ID-ZooBank")', + description : 'ZooBank ID of journal article', + queryable : '', + queryString : '', + validation : '', + resourceId : false + }, + { + plaziName : 'articleTitle', + zenodoName : '', + sqlType : 'TEXT', + cheerioElement: '$("document").attr("masterDocTitle")', + description : 'The title of the article in which the treatment was found', + queryable : 'like', + queryString : 'articleTitle', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'publicationDate', + zenodoName : 'publicationDate', + sqlType : 'TEXT', + cheerioElement: '$("mods\\\\:detail[type=pubDate] mods\\\\:number").text()', + description : 'The date of the publication of the article. If a complete date is not available (for example, if only the year is available), then the last day of the year is used.', + queryable : 'equal', + queryString : 'publicationDate', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'journalTitle', + zenodoName : 'journal_title', + sqlType : 'TEXT', + cheerioElement: '$("mods\\\\:relatedItem[type=host] mods\\\\:titleInfo mods\\\\:title").text()', + description : 'The title of the journal', + queryable : 'like', + queryString : 'journalTitle', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'journalYear', + zenodoName : 'journal_year', + sqlType : 'TEXT', + cheerioElement: '$("mods\\\\:relatedItem[type=host] mods\\\\:part mods\\\\:date").text()', + description : 'The year of the journal', + queryable : 'equal', + queryString : 'journalYear', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'journalVolume', + zenodoName : 'journal_volume', + sqlType : 'TEXT', + cheerioElement: '$("mods\\\\:relatedItem[type=host] mods\\\\:part mods\\\\:detail[type=volume] mods\\\\:number").text()', + description : 'The volume of the journal', + queryable : 'equal', + queryString : 'journalVolume', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'journalIssue', + zenodoName : 'journal_issue', + sqlType : 'TEXT', + cheerioElement: '$("mods\\\\:relatedItem[type=host] mods\\\\:part mods\\\\:detail[type=issue] mods\\\\:number").text()', + description : 'The issue of the journal', + queryable : '', + queryString : '', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'pages', + zenodoName : 'pages', + sqlType : 'TEXT', + cheerioElement: '$("mods\\\\:relatedItem[type=host] mods\\\\:part mods\\\\:extent[unit=page] mods\\\\:start").text() + "–" + $("mods\\\\:relatedItem[type=host] mods\\\\:part mods\\\\:extent[unit=page] mods\\\\:end").text()', + description : 'The "from" and "to" pages where the treatment occurs in the article', + queryable : '', + queryString : '', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'authorityName', + + // this should be subject: scientificName authority: dwc http://rs.tdwg.org/dwc/terms/scientificNameAuthorship + zenodoName : 'creators', + sqlType : 'TEXT', + cheerioElement: '$("subSubSection[type=nomenclature] taxonomicName").attr("authorityName")', + description : 'The name of the author(s) of the taxon (not necessarily the same as the authors of the journal article, but ommited if same as article authors)', + queryable : 'like', + queryString : 'authorityName', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'authorityYear', + zenodoName : '', + sqlType : 'TEXT', + cheerioElement: '$("subSubSection[type=nomenclature] taxonomicName").attr("authorityYear")', + description : 'The year when the taxon name was published', + queryable : 'equal', + queryString : 'authorityYear', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'kingdom', + zenodoName : 'subjects', + sqlType : 'TEXT', + cheerioElement: '$("subSubSection[type=nomenclature] taxonomicName").attr("kingdom")', + description : 'The higher category of the taxonomicName', + queryable : 'like', + queryString : 'kingdom', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'phylum', + zenodoName : 'subjects', + sqlType : 'TEXT', + cheerioElement: '$("subSubSection[type=nomenclature] taxonomicName").attr("phylum")', + description : 'The higher category of the taxonomicName', + queryable : 'like', + queryString : 'phylum', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'order', + zenodoName : 'subjects', + sqlName : '"order"', + sqlType : 'TEXT', + cheerioElement: '$("subSubSection[type=nomenclature] taxonomicName").attr("order")', + description : 'The higher category of the taxonomicName', + queryable : 'like', + queryString : 'order', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'family', + zenodoName : 'subjects', + sqlType : 'TEXT', + cheerioElement: '$("subSubSection[type=nomenclature] taxonomicName").attr("family")', + description : 'The higher category of the taxonomicName', + queryable : 'like', + queryString : 'family', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'genus', + zenodoName : 'subjects', + sqlType : 'TEXT', + cheerioElement: '$("subSubSection[type=nomenclature] taxonomicName").attr("genus")', + description : 'The higher category of the taxonomicName', + queryable : 'like', + queryString : 'genus', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'species', + zenodoName : 'subjects', + sqlType : 'TEXT', + cheerioElement: '$("subSubSection[type=nomenclature] taxonomicName").attr("species")', + description : 'The higher category of the taxonomicName', + queryable : 'like', + queryString : 'species', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'status', + zenodoName : 'subjects', + sqlType : 'TEXT', + cheerioElement: '$("subSubSection[type=nomenclature] taxonomicName").attr("status")', + description : 'The descriptor for the taxonomic status proposed by a given treatment (can be new species, or new combination, or new combination and new synonym)', + queryable : 'like', + queryString : 'status', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'taxonomicNameLabel', + zenodoName : 'subjects', + sqlType : 'TEXT', + cheerioElement: '$("subSubSection[type=nomenclature] taxonomicName").text()', + description : 'The Taxonomic Name Label, present if the species is a new species', + queryable : 'like', + queryString : 'taxonomicNameLabel', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'rank', + zenodoName : 'subjects', + sqlName : 'treatments.rank', + sqlType : 'TEXT', + cheerioElement: '$("subSubSection[type=nomenclature] taxonomicName").attr("rank")', + description : 'The taxonomic rank of the taxon, e.g. species, family', + queryable : 'like', + queryString : 'rank', + validation : 'Joi.string().description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'fulltext', + zenodoName : '', + sqlName : 'vtreatments', + table : 'vtreatments ON treatments.treatmentId = vtreatments.treatmentId', + sqlType : 'TEXT', + cheerioElement: '$("treatment").text()', + description : 'The full text of the treatment', + queryable : 'match', + resourceId : false, + queryString : 'q', + validation : 'Joi.string().description(`${d}`).optional()' + }, + + /***************** */ + + { + plaziName : 'format', + zenodoName : '', + sqlType : '', + cheerioElement: '', + description : 'Response format', + queryable : '', + queryString : 'format', + validation : 'Joi.string().valid("xml").description(`${d}`).optional()', + resourceId : false + }, + { + plaziName : 'xml', + zenodoName : '', + sqlType : '', + cheerioElement: '', + description : 'Whether or not to fetch xml', + queryable : '', + queryString : 'xml', + validation : 'Joi.boolean().default(false).description(`${d}`)', + resourceId : false + }, + { + plaziName : 'sortBy', + zenodoName : '', + sqlType : '', + cheerioElement: '', + description : 'sort column:sort order', + queryable : '', + queryString : 'sortBy', + validation : 'Joi.string().default("treatmentId:ASC").description(`${d}`).optional()', + resourceId : false + } + ] +}; \ No newline at end of file diff --git a/dataDictionary/treatmentAuthors.js b/dataDictionary/treatmentAuthors.js deleted file mode 100644 index d1bf2a1..0000000 --- a/dataDictionary/treatmentAuthors.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict'; - -module.exports = [ - // { - // plaziName : 'id', - // zenodoName : '', - // sqlType : 'INTEGER PRIMARY KEY', - // cheerioElement: '', - // description : 'pk', - // queryable : '', - // queryString : '', - // validation : '', - // resourceId : false - // }, - { - plaziName : 'treatmentAuthorId', - zenodoName : '', - sqlType : 'TEXT NOT NULL UNIQUE', - cheerioElement: '$("treatmentAuthor").attr("id")', - description : 'The unique ID of the treatmentAuthor', - queryable : 'equal', - queryString : 'treatmentAuthorId', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : true - }, - { - plaziName : 'treatmentAuthor', - zenodoName : 'creators', - sqlType : 'TEXT', - cheerioElement: 'mods\\\\:namePart', - description : 'The author of this treatment (author of the article is used if no treatment authority is found)', - queryable : 'like', - queryString : 'treatmentAuthor', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - } -]; \ No newline at end of file diff --git a/dataDictionary/treatmentCitations.js b/dataDictionary/treatmentCitations.js deleted file mode 100644 index 5357c06..0000000 --- a/dataDictionary/treatmentCitations.js +++ /dev/null @@ -1,48 +0,0 @@ -'use strict'; - -module.exports = [ - // { - // plaziName : 'id', - // zenodoName : '', - // sqlType : 'INTEGER PRIMARY KEY', - // cheerioElement: '', - // description : 'pk', - // queryable : '', - // queryString : '', - // validation : '', - // resourceId : false - // }, - { - plaziName : 'treatmentCitationId', - zenodoName : '', - sqlType : 'TEXT NOT NULL UNIQUE', - cheerioElement: '', - description : 'The unique ID of the treatmentCitation', - queryable : 'equal', - queryString : 'treatmentCitationId', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : true - }, - { - plaziName : 'treatmentCitation', - zenodoName : 'subjects; AND if there is a DOI for the treatmentCitation, relatedIdentifiers[cites]', - sqlType : 'TEXT', - cheerioElement: '$("subSubSection[type=reference_group] treatmentCitationGroup taxonomicName").text() + " " + $("subSubSection[type=reference_Group] treatmentCitationGroup taxonomicName").attr("authority") + " sec. " + $("subSubSection[type=reference_Group] treatmentCitationGroup bibRefCitation").text()', - description : 'The taxonomic name and the author of the species, plus the author of the treatment being cited', - queryable : '', - queryString : '', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'refString', - zenodoName : 'relatedIdentifiers[cites]', - sqlType : 'TEXT', - cheerioElement: '$("subSubSection[type=referenceGroup] treatmentCitationGroup treatmentCitation bibRefCitation").attr("refString")', - description : 'The bibliographic reference string of the treatments cited by this treatment', - queryable : '', - queryString : '', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - } -]; \ No newline at end of file diff --git a/dataDictionary/treatments.js b/dataDictionary/treatments.js deleted file mode 100644 index a8d15eb..0000000 --- a/dataDictionary/treatments.js +++ /dev/null @@ -1,332 +0,0 @@ -'use strict'; - -module.exports = [ - { - plaziName : 'id', - zenodoName : '', - sqlType : 'INTEGER PRIMARY KEY', - cheerioElement: '', - description : 'The primary key (PK) for this table', - queryable : '', - queryString : '', - validation : '', - resourceId : false - }, - { - plaziName : 'treatmentId', - zenodoName : '', - sqlType : 'TEXT NOT NULL UNIQUE', - cheerioElement: '$("document").attr("docId")', - description : 'The unique ID of the treatment', - queryable : 'equal', - queryString : 'treatmentId', - validation : 'Joi.string().guid().description(`${d}`).optional()', - resourceId : true - }, - { - plaziName : 'deleted', - zenodoName : '', - sqlType : 'INTEGER DEFAULT 0', - cheerioElement: '$("document").attr("deleted")', - description : 'A boolean that tracks whether or not this resource is considered deleted/revoked, 1 if yes, 0 if no', - queryable : '', - queryString : '', - validation : '', - resourceId : false - }, - { - plaziName : 'treatmentTitle', - zenodoName : 'title', - sqlType : 'TEXT', - cheerioElement: '$("document").attr("docTitle")', - description : 'Title of the article that contains this treatment', - queryable : 'like', - queryString : 'treatmentTitle', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'doi', - zenodoName : 'relatedidentifiers[isPartOf]', - sqlType : 'TEXT', - cheerioElement: '$("document").attr("ID-DOI")', - description : 'DOI of journal article', - queryable : 'equal', - queryString : 'doi', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'zenodoDep', - zenodoName : 'relatedidentifiers[isPartOf]', - sqlType : 'TEXT', - cheerioElement: '$("document").attr("ID-Zenodo-Dep")', - description : 'Zenodo record of journal article', - queryable : '', - queryString : '', - validation : '', - resourceId : false - }, - { - plaziName : 'zoobank', - zenodoName : 'relatedidentifiers[isPartOf]', - sqlType : 'TEXT', - cheerioElement: '$("document").attr("ID-ZooBank")', - description : 'ZooBank ID of journal article', - queryable : '', - queryString : '', - validation : '', - resourceId : false - }, - { - plaziName : 'articleTitle', - zenodoName : '', - sqlType : 'TEXT', - cheerioElement: '$("document").attr("masterDocTitle")', - description : 'The title of the article in which the treatment was found', - queryable : 'like', - queryString : 'articleTitle', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'publicationDate', - zenodoName : 'publicationDate', - sqlType : 'TEXT', - cheerioElement: '$("mods\\\\:detail[type=pubDate] mods\\\\:number").text()', - description : 'The date of the publication of the article. If a complete date is not available (for example, if only the year is available), then the last day of the year is used.', - queryable : 'equal', - queryString : 'publicationDate', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'journalTitle', - zenodoName : 'journal_title', - sqlType : 'TEXT', - cheerioElement: '$("mods\\\\:relatedItem[type=host] mods\\\\:titleInfo mods\\\\:title").text()', - description : 'The title of the journal', - queryable : 'like', - queryString : 'journalTitle', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'journalYear', - zenodoName : 'journal_year', - sqlType : 'TEXT', - cheerioElement: '$("mods\\\\:relatedItem[type=host] mods\\\\:part mods\\\\:date").text()', - description : 'The year of the journal', - queryable : 'equal', - queryString : 'journalYear', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'journalVolume', - zenodoName : 'journal_volume', - sqlType : 'TEXT', - cheerioElement: '$("mods\\\\:relatedItem[type=host] mods\\\\:part mods\\\\:detail[type=volume] mods\\\\:number").text()', - description : 'The volume of the journal', - queryable : 'equal', - queryString : 'journalVolume', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'journalIssue', - zenodoName : 'journal_issue', - sqlType : 'TEXT', - cheerioElement: '$("mods\\\\:relatedItem[type=host] mods\\\\:part mods\\\\:detail[type=issue] mods\\\\:number").text()', - description : 'The issue of the journal', - queryable : '', - queryString : '', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'pages', - zenodoName : 'pages', - sqlType : 'TEXT', - cheerioElement: '$("mods\\\\:relatedItem[type=host] mods\\\\:part mods\\\\:extent[unit=page] mods\\\\:start").text() + "–" + $("mods\\\\:relatedItem[type=host] mods\\\\:part mods\\\\:extent[unit=page] mods\\\\:end").text()', - description : 'The "from" and "to" pages where the treatment occurs in the article', - queryable : '', - queryString : '', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'authorityName', - - // this should be subject: scientificName authority: dwc http://rs.tdwg.org/dwc/terms/scientificNameAuthorship - zenodoName : 'creators', - sqlType : 'TEXT', - cheerioElement: '$("subSubSection[type=nomenclature] taxonomicName").attr("authorityName")', - description : 'The name of the author(s) of the taxon (not necessarily the same as the authors of the journal article, but ommited if same as article authors)', - queryable : 'like', - queryString : 'authorityName', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'authorityYear', - zenodoName : '', - sqlType : 'TEXT', - cheerioElement: '$("subSubSection[type=nomenclature] taxonomicName").attr("authorityYear")', - description : 'The year when the taxon name was published', - queryable : 'equal', - queryString : 'authorityYear', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'kingdom', - zenodoName : 'subjects', - sqlType : 'TEXT', - cheerioElement: '$("subSubSection[type=nomenclature] taxonomicName").attr("kingdom")', - description : 'The higher category of the taxonomicName', - queryable : 'like', - queryString : 'kingdom', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'phylum', - zenodoName : 'subjects', - sqlType : 'TEXT', - cheerioElement: '$("subSubSection[type=nomenclature] taxonomicName").attr("phylum")', - description : 'The higher category of the taxonomicName', - queryable : 'like', - queryString : 'phylum', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'order', - zenodoName : 'subjects', - sqlName : '"order"', - sqlType : 'TEXT', - cheerioElement: '$("subSubSection[type=nomenclature] taxonomicName").attr("order")', - description : 'The higher category of the taxonomicName', - queryable : 'like', - queryString : 'order', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'family', - zenodoName : 'subjects', - sqlType : 'TEXT', - cheerioElement: '$("subSubSection[type=nomenclature] taxonomicName").attr("family")', - description : 'The higher category of the taxonomicName', - queryable : 'like', - queryString : 'family', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'genus', - zenodoName : 'subjects', - sqlType : 'TEXT', - cheerioElement: '$("subSubSection[type=nomenclature] taxonomicName").attr("genus")', - description : 'The higher category of the taxonomicName', - queryable : 'like', - queryString : 'genus', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'species', - zenodoName : 'subjects', - sqlType : 'TEXT', - cheerioElement: '$("subSubSection[type=nomenclature] taxonomicName").attr("species")', - description : 'The higher category of the taxonomicName', - queryable : 'like', - queryString : 'species', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'status', - zenodoName : 'subjects', - sqlType : 'TEXT', - cheerioElement: '$("subSubSection[type=nomenclature] taxonomicName").attr("status")', - description : 'The descriptor for the taxonomic status proposed by a given treatment (can be new species, or new combination, or new combination and new synonym)', - queryable : 'like', - queryString : 'status', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'taxonomicNameLabel', - zenodoName : 'subjects', - sqlType : 'TEXT', - cheerioElement: '$("subSubSection[type=nomenclature] taxonomicName").text()', - description : 'The Taxonomic Name Label, present if the species is a new species', - queryable : 'like', - queryString : 'taxonomicNameLabel', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'rank', - zenodoName : 'subjects', - sqlName : 'treatments.rank', - sqlType : 'TEXT', - cheerioElement: '$("subSubSection[type=nomenclature] taxonomicName").attr("rank")', - description : 'The taxonomic rank of the taxon, e.g. species, family', - queryable : 'like', - queryString : 'rank', - validation : 'Joi.string().description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'fulltext', - zenodoName : '', - sqlName : 'vtreatments', - table : 'vtreatments ON treatments.treatmentId = vtreatments.treatmentId', - sqlType : 'TEXT', - cheerioElement: '$("treatment").text()', - description : 'The full text of the treatment', - queryable : 'match', - resourceId : false, - queryString : 'q', - validation : 'Joi.string().description(`${d}`).optional()' - }, - - /***************** */ - - { - plaziName : 'format', - zenodoName : '', - sqlType : '', - cheerioElement: '', - description : 'Response format', - queryable : '', - queryString : 'format', - validation : 'Joi.string().valid("xml").description(`${d}`).optional()', - resourceId : false - }, - { - plaziName : 'xml', - zenodoName : '', - sqlType : '', - cheerioElement: '', - description : 'Whether or not to fetch xml', - queryable : '', - queryString : 'xml', - validation : 'Joi.boolean().default(false).description(`${d}`)', - resourceId : false - }, - { - plaziName : 'sortBy', - zenodoName : '', - sqlType : '', - cheerioElement: '', - description : 'sort column:sort order', - queryable : '', - queryString : 'sortBy', - validation : 'Joi.string().default("treatmentId:ASC").description(`${d}`).optional()', - resourceId : false - } -]; \ No newline at end of file diff --git a/index.js b/index.js index 1bf34b4..112b5df 100644 --- a/index.js +++ b/index.js @@ -4,10 +4,8 @@ // Start this program from the command line with `pm2` // ~/Nodes/punkish$ NODE_ENV=production pm2 start index.js --name zenodeo -// ~/Nodes/punkish$ NODE_ENV=production pm2 restart zenodeo +// ~/Nodes/punkish$ pm2 restart zenodeo - -/*** hapi 18.4.0 *************************************/ const Hapi = require('@hapi/hapi'); const Inert = require('@hapi/inert'); const Vision = require('@hapi/vision'); @@ -16,7 +14,6 @@ const HapiSwagger = require('hapi-swagger'); const Disk = require('catbox-disk'); const Blipp = require('blipp'); -//const debug = require('debug')('v2: index'); const config = require('config'); const cacheName = config.get('v2.cache.name'); const cachePath = config.get('v2.cache.path'); @@ -25,12 +22,12 @@ const info = config.get('info'); const swaggeredScheme = config.get('swaggered-scheme'); const port = config.get('port'); -const logger = require(config.get('logger')); - -/* - * Generate Swagger-compatible documentation for the app - */ +//const logger = require(config.get('logger')); +const plog = require(config.get('plog')); +// Generate Swagger-compatible documentation for the app +// The commented options below are listed for completion +// sakes. We don't use them here. const swaggerOptions = { /******** URLs and plugin **************/ @@ -53,6 +50,7 @@ const swaggerOptions = { validatorUrl: null }; +// Create the server. Everything begins here. const server = new Hapi.server({ port: port, host: 'localhost', @@ -74,34 +72,35 @@ const server = new Hapi.server({ const start = async () => { await server.register([ + + // Inert serves static files { plugin: Inert, options: {} }, + + // Vision provides templating support (see server.view() below { plugin: Vision, options: {} }, - { plugin: Blipp, options: {} } , + + // Blipp just prints out to the console all the routes when + // the server starts. This is *only* in development mode + { plugin: Blipp, options: {} }, + + // Generate Swagger-compaitible swagger-files and docs auto- + // magically { plugin: HapiSwagger, options: swaggerOptions } ]); + // API v1 await server.register( { plugin: require('./api/v1/index.js'), options: {} }, { routes: { prefix: '/v1' } } ); + // API v2 await server.register( { plugin: require('./api/v2/index.js'), options: {} }, { routes: { prefix: '/v2' } } ); - - server.views({ - engines: { - html: require('handlebars') - }, - relativeTo: __dirname, - path: './views', - layoutPath: './views/layouts', - partialsPath: './views/partials', - layout: 'main', - isCached: false - }); + // Static pages server.route([ require('./resources/inert'), require('./resources/docs'), @@ -110,6 +109,10 @@ const start = async () => { require('./resources/examples'), require('./resources/about'), require('./resources/releases'), + + // A catch-all route for any request that hasn't been satisfied + // by any of the routes so far. Send back a 404 and suggest + // a starting point. { method: '*', path: '/{any*}', @@ -122,20 +125,40 @@ const start = async () => { } } ]); + + // Use handlebars for templating + server.views({ + engines: { + html: require('handlebars') + }, + relativeTo: __dirname, + path: './views', + layoutPath: './views/layouts', + partialsPath: './views/partials', + layout: 'main', + isCached: false + }); await server.start(); if (process.env.NODE_ENV) { - console.log('Server running in %s mode on %s', process.env.NODE_ENV.toUpperCase(), server.info.uri); + console.log( + 'Server running in %s mode on %s', + process.env.NODE_ENV.toUpperCase(), + server.info.uri + ); } else { - console.log('Server running in DEVELOPMENT mode on %s', server.info.uri); + console.log( + 'Server running in DEVELOPMENT mode on %s', + server.info.uri + ); } server.events.on('log', (event, tags) => { if (tags.error) { - logger({ + plog.logger({ host: server.info.uri, start: '', end: '', @@ -150,7 +173,7 @@ const start = async () => { server.events.on('response', function (request) { - logger({ + plog.logger({ host: request.info.host, start: request.info.received, end: request.info.completed, @@ -164,4 +187,4 @@ const start = async () => { }; -start(); \ No newline at end of file +start(); diff --git a/lib/logger.js b/lib/logger.js index 191a23c..cc197b0 100644 --- a/lib/logger.js +++ b/lib/logger.js @@ -30,162 +30,155 @@ const insert = db.prepare(insertStmt); // query: query, // message: 'all good' // }; -module.exports = function(params) { - - // logparams[] ensures the params are in the right order for - // the database insert statement - const logparams = []; - logfields.forEach(c => logparams.push(params[c.col])); - - // set default start and end UTC times in ms, if not provided - if (params.start === '') params.start = new Date().getTime(); - if (params.end === '') params.end = new Date().getTime(); - - // calculate duration of event - const t = params.end - params.start; - - const subject = 'zenodeo log'; - const msgParts = [ - ['Date', new Date().toUTCString()], - ['Host', params.host], - ['Resource/Action', params.resource], - ['Action', params.query], - ['Status', httpStatusCodes[params.status]], - ['Took', t ? `${t}ms` : 'n/a'], - ['Message', params.message] - ]; - - // only if in production - if (process.env.NODE_ENV === 'production') { - - // insert in the database - insert.run(logparams); - - if (parseInt(params.status) >= 500) { - - try { - emailer({ - subject: subject, - message: msgParts.map(e => `${e[0].padStart(10)}: ${e[1]}`).join('\n') - }); - } - catch(error) { - plog.error(error); - } +// module.exports = function(params) { + +// // logparams[] ensures the params are in the right order for +// // the database insert statement +// const logparams = []; +// logfields.forEach(c => logparams.push(params[c.col])); + +// // set default start and end UTC times in ms, if not provided +// if (params.start === '') params.start = new Date().getTime(); +// if (params.end === '') params.end = new Date().getTime(); + +// // calculate duration of event +// const t = params.end - params.start; + +// const subject = 'zenodeo log'; +// const msgParts = [ +// ['Date', new Date().toUTCString()], +// ['Host', params.host], +// ['Resource/Action', params.resource], +// ['Action', params.query], +// ['Status', httpStatusCodes[params.status]], +// ['Took', t ? `${t}ms` : 'n/a'], +// ['Message', params.message] +// ]; + +// // only if in production +// if (process.env.NODE_ENV === 'production') { + +// // insert in the database +// insert.run(logparams); + +// if (parseInt(params.status) >= 500) { + +// try { +// emailer({ +// subject: subject, +// message: msgParts.map(e => `${e[0].padStart(10)}: ${e[1]}`).join('\n') +// }); +// } +// catch(error) { +// plog.error(error); +// } - } - } +// } +// } - // show in the console - else { - plog.info('subject', subject, 'green'); - plog.info('line', '-'); - msgParts.forEach(e => plog.info(e[0].padStart(10), e[1], 'green')); - plog.info('line', '='); - } +// // show in the console +// else { +// plog.info('subject', subject, 'green'); +// plog.info('line', '-'); +// msgParts.forEach(e => plog.info(e[0].padStart(10), e[1], 'green')); +// plog.info('line', '='); +// } -}; +// }; -// For future reference, this is what Hapi request params looks like, -// the meaningful parts anyway -//**************************************************************** -// -// request = { -// headers: { -// host: 'localhost:3030', -// 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:67.0) Gecko/20100101 Firefox/67.0', -// accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', -// 'accept-language': 'en-US,en;q=0.5', -// 'accept-encoding': 'gzip, deflate', -// connection: 'keep-alive', -// 'upgrade-insecure-requests': '1', -// 'cache-control': 'max-age=0' -// }, -// info: { -// received: 1559564795546, -// remoteAddress: '127.0.0.1', -// remotePort: 50999, -// referrer: '', -// host: 'localhost:3030', -// hostname: 'localhost', -// id: '1559564795546:lucknow.local:32455:jwgclzyj:10000', -// acceptEncoding: 'gzip', -// cors: { isOriginMatch: true }, -// responded: 1559564795589, -// completed: 1559564795590 -// }, -// method: 'get', -// orig: { -// query: { -// size: '30', -// communities: 'biosyslit', -// access_right: 'open', -// type: 'image', -// summary: 'false', -// images: 'true', -// q: 'maratus', -// page: '1' -// } -// }, -// path: '/v1/records', -// raw: { -// req: { -// url: '/v1/records?size=30&communities=biosyslit&access_right=open&type=image&summary=false&images=true&q=maratus&page=1', -// method: 'GET' -// }, -// res: { -// _header: 'HTTP/1.1 200 OK\r\ncontent-type: application/json; charset=utf-8\r\nvary: origin,accept-encoding\r\naccess-control-expose-headers: WWW-Authenticate,Server-Authorization\r\ncache-control: no-cache\r\ncontent-encoding: gzip\r\nDate: Mon, 03 Jun 2019 12:26:35 GMT\r\nConnection: keep-alive\r\nTransfer-Encoding: chunked\r\n\r\n' -// } -// }, -// response: { statusCode: 200 }, -// route: { -// method: 'get', -// path: '/v1/records' -// }, -// query: { -// size: 30, -// communities: 'biosyslit', -// access_right: 'open', -// type: 'image', -// summary: false, -// images: true, -// q: 'maratus', -// page: 1, -// refreshCache: false -// }, -// server: { -// info: { -// created: 1559564793163, -// started: 1559564794322, -// host: 'localhost', -// port: 3030, -// protocol: 'http', -// id: 'lucknow.local:32455:jwgclzyj', -// uri: 'http://localhost:3030', -// address: '127.0.0.1' -// } -// }, -// url: { -// href: 'http://localhost:3030/v1/records?size=30&communities=biosyslit&access_right=open&type=image&summary=false&images=true&q=maratus&page=1', -// origin: 'http://localhost:3030', -// protocol: 'http:', -// username: '', -// password: '', -// host: 'localhost:3030', -// hostname: 'localhost', -// port: '3030', -// pathname: '/v1/records', -// search: '?size=30&communities=biosyslit&access_right=open&type=image&summary=false&images=true&q=maratus&page=1', -// searchParams: { -// 'size' => '30', -// 'communities' => 'biosyslit', -// 'access_right' => 'open', -// 'type' => 'image', -// 'summary' => 'false', -// 'images' => 'true', -// 'q' => 'maratus', -// 'page' => '1' -// }, -// hash: '' -// } -// } \ No newline at end of file +/* +For future reference, this is what Hapi request params looks like, +the meaningful parts anyway +=================================================================== + +request = { + headers: { … }, + info: { + received: 1559564795546, + remoteAddress: '127.0.0.1', + remotePort: 50999, + referrer: '', + host: 'localhost:3030', + hostname: 'localhost', + id: '1559564795546:lucknow.local:32455:jwgclzyj:10000', + acceptEncoding: 'gzip', + cors: { isOriginMatch: true }, + responded: 1559564795589, + completed: 1559564795590 + }, + method: 'get', + orig: { + query: { + size: '30', + communities: 'biosyslit', + access_right: 'open', + type: 'image', + summary: 'false', + images: 'true', + q: 'maratus', + page: '1' + } + }, + path: '/v1/records', + raw: { + req: { + url: '/v1/records?size=30&communities=biosyslit&access_right=open&type=image&summary=false&images=true&q=maratus&page=1', + method: 'GET' + }, + res: { + _header: 'HTTP/1.1 200 OK\r\ncontent-type: application/json; charset=utf-8\r\nvary: origin,accept-encoding\r\naccess-control-expose-headers: WWW-Authenticate,Server-Authorization\r\ncache-control: no-cache\r\ncontent-encoding: gzip\r\nDate: Mon, 03 Jun 2019 12:26:35 GMT\r\nConnection: keep-alive\r\nTransfer-Encoding: chunked\r\n\r\n' + } + }, + response: { statusCode: 200 }, + route: { + method: 'get', + path: '/v1/records' + }, + query: { + size: 30, + communities: 'biosyslit', + access_right: 'open', + type: 'image', + summary: false, + images: true, + q: 'maratus', + page: 1, + refreshCache: false + }, + server: { + info: { + created: 1559564793163, + started: 1559564794322, + host: 'localhost', + port: 3030, + protocol: 'http', + id: 'lucknow.local:32455:jwgclzyj', + uri: 'http://localhost:3030', + address: '127.0.0.1' + } + }, + url: { + href: 'http://localhost:3030/v1/records?size=30&communities=biosyslit&access_right=open&type=image&summary=false&images=true&q=maratus&page=1', + origin: 'http://localhost:3030', + protocol: 'http:', + username: '', + password: '', + host: 'localhost:3030', + hostname: 'localhost', + port: '3030', + pathname: '/v1/records', + search: '?size=30&communities=biosyslit&access_right=open&type=image&summary=false&images=true&q=maratus&page=1', + searchParams: { + 'size' => '30', + 'communities' => 'biosyslit', + 'access_right' => 'open', + 'type' => 'image', + 'summary' => 'false', + 'images' => 'true', + 'q' => 'maratus', + 'page' => '1' + }, + hash: '' + } +} +*/ \ No newline at end of file diff --git a/lib/plog.js b/lib/plog.js index 746b07e..c50404b 100644 --- a/lib/plog.js +++ b/lib/plog.js @@ -1,172 +1,201 @@ 'use strict'; +/*********************************************************************** + * + * A custom-logger based on picolog and chalk + * + **********************************************************************/ + +const log = require('picolog'); +const chalk = require('chalk'); const config = require('config'); const loglevel = config.get('loglevel'); -const chalk = require('chalk'); -const log = require('picolog'); +const logSlowSQLthreshold = config.get('logSlowSQLthreshold'); const Utils = require('../api/v2/utils'); -//const crypto = require('crypto'); - - -// all queries that take longer than the -// following (in ms) are displayed in red -const sqlThreshHold = 300; - -log.level = log[loglevel]; +const httpStatusCodes = require(config.get('httpStatusCodes')); +const Database = require('better-sqlite3'); +const db = new Database(config.get('data.logs')); +const logfields = config.get('logfields'); +const emailer = require('./emailer'); -const prepareMsg = function(param1, param2) { - let prefix = 'info'; - let msg = ''; +const insertStmt = `INSERT INTO log (${logfields.map(c => { return c.col }).join(', ')}) VALUES (${logfields.map(c => '?').join(', ')})`; - if (param2) { +// create table +// const columns = logfields.map(c => { return `${c.col} ${c.type}` }); +// columns.unshift("id INTEGER PRIMARY KEY", "logged INTEGER DEFAULT (strftime('%s', 'now'))"); +// db.prepare(`CREATE TABLE IF NOT EXISTS log (${columns.join(', ')})`).run(); - prefix = param1; +const insert = db.prepare(insertStmt); - if (Array.isArray(param2)) { - msg = param2.join(''); - } - else if (typeof(param2) === 'object') { - msg = JSON.stringify(param2, null, ' '); - } - else { - msg = param2; - } - - } - else { - msg = param1; - } - - return [prefix.padStart(19), msg]; -}; +// Set the log level (based on whether the app is running in development +// or testing or production mode) that determines what is printed and how +log.level = log[loglevel]; const colors = { - info: 'blue', - error: 'red', - perf: 'green' + info: chalk['blue'], + error: chalk['red'], + perf: chalk['green'] }; -module.exports = { +// overall length of message string in the console (or in the log file) +// this is the length of the label + the length of the message +const r = 70; - log: function({header, messages, queryObject}) { - // overall length of message string in the console (or in the log file) - // this is the length of the label + the length of the message - const r = 200; +module.exports = { - // calculate label length - let ll = 0; + logger: function(params) { - let i = 0; - const j = messages.length; - for (; i < j; i++) { - const {label, params} = messages[i]; - const l = label.length; - if (l > ll) { - ll = l; + // logparams[] ensures the params are in the right order for + // the database insert statement + const logparams = logfields.map(f => params[f.col]); + + // Timestamp + const d = new Date(); + + // set default start and end UTC times in ms, if not provided + if (params.start === '') params.start = d.getTime(); + if (params.end === '') params.end = d.getTime(); + + // calculate duration of event + const t = params.end - params.start; + + const subject = 'zenodeo log'; + const msgParts = [ + [ 'Date', d.toUTCString() ], + [ 'Host', params.host ], + [ 'Resource/Action', params.resource ], + [ 'Action', params.query ], + [ 'Status', httpStatusCodes[ params.status ] ], + [ 'Took', t ? `${t}ms` : 'n/a' ], + [ 'Message', params.message ] + ]; + + // only if in production + if (process.env.NODE_ENV === 'production') { + + // insert in the database + insert.run(logparams); + + if (parseInt(params.status) >= 500) { + + try { + emailer({ + subject: subject, + message: msgParts.map(e => `${e[0].padStart(10)}: ${e[1]}`) + .join('\n') + }); + } + catch(error) { + plog.error(error); + } + } } - ll += 1; + + // show in the console + else { + log.info('\n'); + log.info(`subject: ${subject}`); + log.info('-'.repeat(r)); + msgParts.forEach(e => log.info(e[0].padStart(10), e[1])); + log.info('='.repeat(r)); + } + + }, + + + // `log` prints out messages grouped by the header. Here is an example + // where the details of the labels "cacheKey" and "info" are grouped + // under the WEB QUERY header + // + // WEB QUERY + // ------------------------------------------------------------------- + // cacheKey: /v2/treatments?facets=true&limit=30&offset=… + // info: querying for fresh results + // =================================================================== + log: function({ header, messages, queryObject }) { - log.info('\n ' + header); + // calculate apprpropiate label length by finding the longest label + // in the group and adding 1 to it for padding + const ll = Math.max( ...messages.map( e => e.label.length ) ) + 1; + + log.info('\n'); + log.info(chalk.bold(header)); log.info('-'.repeat(r)); - - messages.forEach(({label, params}) => { if (typeof(params) === 'object') { + + // 'sql' is treated special if ('sql' in params) { + + // Replace the bind param placeholders in the SQL with the + // actual values from the queryObject so the actualy SQL + // that is run is printed in the log. This makes it easy to + // debug a specific SQL in case of an error const sql = Utils.strfmt(params.sql, queryObject); - const t = params.took; + const { timeInMs, timeInEnglish } = params.took; - let sqlColor = chalk[colors.perf]; - if (t.msr > sqlThreshHold) { - sqlColor = chalk[colors.error]; - } + const sqlColor = timeInMs > logSlowSQLthreshold ? colors.error + : colors.perf - log.info(`${chalk[colors.info].bold(label.padStart(ll))}: ${sql} ${sqlColor.bold('[')} ${sqlColor(t.str)} ${sqlColor.bold(']')}`); - -/* -CREATE TABLE webqueries ( - id INTEGER PRIMARY KEY, - - -- stringified queryObject - qp TEXT NOT NULL UNIQUE, - - -- counter tracking queries - count INTEGER DEFAULT 1 -); - -CREATE TABLE sqlqueries ( - id INTEGER PRIMARY KEY, - - -- SQL query - sql TEXT NOT NULL UNIQUE -); - -CREATE TABLE stats ( - id INTEGER PRIMARY KEY, - - -- Foreign Keys - webqueries_id INTEGER, - sqlqueries_id INTEGER, - - -- query performance time in ms - timeTaken INTEGER, - - -- timestamp of query - created INTEGER DEFAULT (strftime('%s','now')) -); -*/ + log.info(`${colors.info.bold(label.padStart(ll))}: ${sql} ${sqlColor.bold('[')} ${sqlColor(timeInEnglish)} ${sqlColor.bold(']')}`); + } + else { + log.info(colors.info.bold(label.padStart(ll)) + ': ' + JSON.stringify(params)); } } else { - const m = typeof(params) === 'object' ? JSON.stringify(params) : params; - log.info(chalk[colors.info].bold(label.padStart(ll)) + ': ' + m); + + log.info(colors.info.bold(label.padStart(ll)) + ': ' + params); } }); - log.info('='.repeat(r) + '\n'); + log.info('='.repeat(r)); - }, + info: function(param1, param2) { - if (param1 === 'line') { - log.info(param2.repeat(100)); - } - else { - let [prefix, msg] = prepareMsg(param1, param2); - prefix = prefix + ': '; - prefix = chalk[colors.info].bold(prefix); - - log.info(`${prefix} ${msg}`); - } + const prefix = param2 ? param1 : 'info'; + let msg = param1; - }, + if (param2) { - error: function(param1, param2) { + if (Array.isArray(param2)) { + msg = param2.join(''); + } + else if (param2.constructor === Object) { + msg = JSON.stringify(param2, null, ' '); + } + else { + msg = param2; + } + } - if (param1 === 'line') { + log.info(colors.info.bold(prefix + ': ') + msg); - log.info(param2.repeat(100)); - } - else if (typeof(param1) === 'object') { + }, - const e = JSON.stringify(param1); + error: function(param1, param2) { + + // https://stackoverflow.com/a/61333525/183692 + if (param1.constructor === Object || Error) { - log.info(chalk[colors.error].bold(`error : ${e}`)); + const e = param1.stack === undefined ? JSON.stringify(param1.message, null, 2) : param1.stack; + log.info(colors.error.bold('error: ') + colors.error(e)); if (param2) { - log.info(chalk[colors.error].bold(`more : ${param2}`)); + log.info(colors.error.bold('more: ') + param2); } } diff --git a/package-lock.json b/package-lock.json index e401bd5..cce67f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -340,6 +340,16 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-13.11.1.tgz", "integrity": "sha512-eWQGP3qtxwL8FGneRrC5DwrJLGN4/dH1clNTuLfN81HCrxVtxRjygDTUoZJ5ASlDEeo0ppYFQjQIlXhtXpOn6g==" }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==" + }, "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", @@ -390,6 +400,33 @@ "sprintf-js": "~1.0.2" } }, + "axios": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", + "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", + "requires": { + "follow-redirects": "1.5.10" + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base-64": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz", + "integrity": "sha1-eAqZyE59YAJgNhURxId2E78k9rs=" + }, "base64-js": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", @@ -506,6 +543,15 @@ "hoek": "6.x.x" } }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "buffer": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", @@ -663,6 +709,11 @@ "resolved": "https://registry.npmjs.org/chance/-/chance-1.1.4.tgz", "integrity": "sha512-pXPDSu3knKlb6H7ahQfpq//J9mSOxYK8SMtp8MV/nRJh8aLRDIl0ipLH8At8+nVogVwtvPZzyIzY/EbcY/cLuQ==" }, + "chardet": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", + "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=" + }, "cheerio": { "version": "1.0.0-rc.3", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.3.tgz", @@ -681,6 +732,24 @@ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-spinners": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-1.3.1.tgz", + "integrity": "sha512-1QL4544moEsDVH9T/l6Cemov/37iv1RtoKf7NJ04A60+4MREXNfx/QvavbH6QoGdsD4N4Mwy49cmaINR/o2mdg==" + }, + "cli-width": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==" + }, "clone": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", @@ -728,6 +797,11 @@ "graceful-readlink": ">= 1.0.0" } }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, "config": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/config/-/config-3.3.1.tgz", @@ -745,6 +819,14 @@ "proto-list": "~1.2.1" } }, + "connectivity": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/connectivity/-/connectivity-1.0.2.tgz", + "integrity": "sha512-tJNDVKFu1Ep15egdPC6ivGGZsNjriYTUaALoXTPdbsJdcekI5HTsi5DjoP43yEOl5Pjae7fQeeeNGugEzzNpWw==", + "requires": { + "once": "^1.3.0" + } + }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", @@ -1044,6 +1126,46 @@ } } }, + "editorconfig": { + "version": "0.15.3", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz", + "integrity": "sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==", + "requires": { + "commander": "^2.19.0", + "lru-cache": "^4.1.5", + "semver": "^5.6.0", + "sigmund": "^1.0.1" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + } + } + }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "requires": { + "iconv-lite": "~0.4.13" + } + }, "end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -1089,6 +1211,16 @@ "sort-keys-length": "^1.0.0" } }, + "external-editor": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", + "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "requires": { + "chardet": "^0.4.0", + "iconv-lite": "^0.4.17", + "tmp": "^0.0.33" + } + }, "fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", @@ -1097,6 +1229,14 @@ "pend": "~1.2.0" } }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, "file-type": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-8.1.0.tgz", @@ -1122,6 +1262,29 @@ "trim-repeated": "^1.0.0" } }, + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "requires": { + "debug": "=3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, "foreachasync": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/foreachasync/-/foreachasync-3.0.0.tgz", @@ -1154,6 +1317,11 @@ "minipass": "^2.6.0" } }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, "gauge": { "version": "2.7.4", "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", @@ -1182,11 +1350,102 @@ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" }, + "github-api": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/github-api/-/github-api-3.3.0.tgz", + "integrity": "sha512-30pABj/1ciHmlqmjnWXn+A4JL8j9qB2IcQgibrJ7euGbaNRkAj+T6QhJwjLcPx4Hxlj+BP1TcdvaQ/7resw+VA==", + "requires": { + "axios": "^0.19.0", + "debug": "^2.2.0", + "js-base64": "^2.1.9", + "utf8": "^2.1.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, "github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=" }, + "github-release-notes": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/github-release-notes/-/github-release-notes-0.17.1.tgz", + "integrity": "sha512-G+dEM7N8TtFDuMK2VJ9meIMYFkiTV7n3q/OhG3R2dJ/DH+qs+QicuyHLMAi/RIDX41YBIl9C5RGnw5y3xWhWfQ==", + "requires": { + "babel-runtime": "^6.26.0", + "base-64": "^0.1.0", + "chalk": "^2.1.0", + "commander": "^2.11.0", + "connectivity": "^1.0.0", + "github-api": "^3.3.0", + "inquirer": "^3.3.0", + "install": "^0.10.1", + "js-beautify": "^1.7.4", + "json2yaml": "^1.1.0", + "minimist": "^1.2.0", + "node-fetch": "^1.7.3", + "npm": "^6.13.4", + "object-assign-deep": "^0.3.1", + "ora": "^1.3.0", + "regex-match-all": "^1.0.2", + "require-from-url": "^3.1.3", + "require-yaml": "0.0.1", + "valid-url": "^1.0.9" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "requires": { + "encoding": "^0.1.11", + "is-stream": "^1.0.1" + } + } + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "got": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/got/-/got-8.3.2.tgz", @@ -1342,11 +1601,28 @@ "resolved": "https://registry.npmjs.org/http-status/-/http-status-1.4.2.tgz", "integrity": "sha512-mBnIohUwRw9NyXMEMMv8/GANnzEYUj0Y8d3uL01zDWFkxUjYyZ6rgCaAI2zZ1Wb34Oqtbx/nFZolPRDc8Xlm5A==" }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, "ieee754": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "infobox-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/infobox-parser/-/infobox-parser-3.3.1.tgz", @@ -1365,6 +1641,71 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" }, + "inquirer": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", + "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", + "requires": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^2.0.4", + "figures": "^2.0.0", + "lodash": "^4.3.0", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rx-lite": "^4.0.8", + "rx-lite-aggregates": "^4.0.8", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "install": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/install/-/install-0.10.4.tgz", + "integrity": "sha512-+IRyOastuPmLVx9zlVXJoKErSqz1Ma5at9A7S8yfsj3W+Kg95faPoh3bPDtMrZ/grz4PRmXzrswmlzfLlYyLOw==" + }, "integer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/integer/-/integer-3.0.1.tgz", @@ -1411,6 +1752,11 @@ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" + }, "is-retry-allowed": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", @@ -1453,6 +1799,23 @@ "topo": "3.x.x" } }, + "js-base64": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.2.tgz", + "integrity": "sha512-Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ==" + }, + "js-beautify": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.11.0.tgz", + "integrity": "sha512-a26B+Cx7USQGSWnz9YxgJNMmML/QG2nqIaL7VVYPCXbqiKz8PN0waSNvroMtvAK6tY7g/wPdNWGEP+JTNIBr6A==", + "requires": { + "config-chain": "^1.1.12", + "editorconfig": "^0.15.3", + "glob": "^7.1.3", + "mkdirp": "~1.0.3", + "nopt": "^4.0.3" + } + }, "js-yaml": { "version": "3.13.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", @@ -1477,6 +1840,14 @@ "ono": "^4.0.11" } }, + "json2yaml": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/json2yaml/-/json2yaml-1.1.0.tgz", + "integrity": "sha1-VBTZB/mBZYa4DFE+wuOusquBmmw=", + "requires": { + "remedial": "1.x" + } + }, "json5": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", @@ -1508,6 +1879,26 @@ "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" }, + "log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "requires": { + "chalk": "^2.0.1" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + } + } + }, "lowercase-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", @@ -1534,11 +1925,24 @@ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==" }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" + }, "mimic-response": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==" }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, "minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", @@ -1581,6 +1985,11 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" + }, "napi-build-utils": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", @@ -1631,6 +2040,15 @@ "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=" }, + "nopt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", + "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, "normalize-url": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz", @@ -1651,90 +2069,3251 @@ } } }, - "npm-conf": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", - "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", - "requires": { - "config-chain": "^1.1.11", - "pify": "^3.0.0" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", - "requires": { - "boolbase": "~1.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "object-keys": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", - "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "ono": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/ono/-/ono-4.0.11.tgz", - "integrity": "sha512-jQ31cORBFE6td25deYeD80wxKBMj+zBmHTrVxnc6CKhx8gho6ipmWM5zj/oeoqioZ99yqBls9Z/9Nss7J26G2g==", - "requires": { - "format-util": "^1.0.3" - } - }, - "p-cancelable": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz", - "integrity": "sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ==" - }, - "p-event": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/p-event/-/p-event-2.3.1.tgz", - "integrity": "sha512-NQCqOFhbpVTMX4qMe8PF8lbGtzZ+LCiN7pcNrb/413Na7+TRoe1xkKUzuWa/YEJdGQ0FvKtj35EEbDoVPO2kbA==", - "requires": { - "p-timeout": "^2.0.1" - } - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" - }, - "p-is-promise": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", - "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=" - }, - "p-timeout": { - "version": "2.0.1", + "npm": { + "version": "6.14.4", + "resolved": "https://registry.npmjs.org/npm/-/npm-6.14.4.tgz", + "integrity": "sha512-B8UDDbWvdkW6RgXFn8/h2cHJP/u/FPa4HWeGzW23aNEBARN3QPrRaHqPIZW2NSN3fW649gtgUDNZpaRs0zTMPw==", + "requires": { + "JSONStream": "^1.3.5", + "abbrev": "~1.1.1", + "ansicolors": "~0.3.2", + "ansistyles": "~0.1.3", + "aproba": "^2.0.0", + "archy": "~1.0.0", + "bin-links": "^1.1.7", + "bluebird": "^3.5.5", + "byte-size": "^5.0.1", + "cacache": "^12.0.3", + "call-limit": "^1.1.1", + "chownr": "^1.1.4", + "ci-info": "^2.0.0", + "cli-columns": "^3.1.2", + "cli-table3": "^0.5.1", + "cmd-shim": "^3.0.3", + "columnify": "~1.5.4", + "config-chain": "^1.1.12", + "debuglog": "*", + "detect-indent": "~5.0.0", + "detect-newline": "^2.1.0", + "dezalgo": "~1.0.3", + "editor": "~1.0.0", + "figgy-pudding": "^3.5.1", + "find-npm-prefix": "^1.0.2", + "fs-vacuum": "~1.2.10", + "fs-write-stream-atomic": "~1.0.10", + "gentle-fs": "^2.3.0", + "glob": "^7.1.6", + "graceful-fs": "^4.2.3", + "has-unicode": "~2.0.1", + "hosted-git-info": "^2.8.8", + "iferr": "^1.0.2", + "imurmurhash": "*", + "infer-owner": "^1.0.4", + "inflight": "~1.0.6", + "inherits": "^2.0.4", + "ini": "^1.3.5", + "init-package-json": "^1.10.3", + "is-cidr": "^3.0.0", + "json-parse-better-errors": "^1.0.2", + "lazy-property": "~1.0.0", + "libcipm": "^4.0.7", + "libnpm": "^3.0.1", + "libnpmaccess": "^3.0.2", + "libnpmhook": "^5.0.3", + "libnpmorg": "^1.0.1", + "libnpmsearch": "^2.0.2", + "libnpmteam": "^1.0.2", + "libnpx": "^10.2.2", + "lock-verify": "^2.1.0", + "lockfile": "^1.0.4", + "lodash._baseindexof": "*", + "lodash._baseuniq": "~4.6.0", + "lodash._bindcallback": "*", + "lodash._cacheindexof": "*", + "lodash._createcache": "*", + "lodash._getnative": "*", + "lodash.clonedeep": "~4.5.0", + "lodash.restparam": "*", + "lodash.union": "~4.6.0", + "lodash.uniq": "~4.5.0", + "lodash.without": "~4.4.0", + "lru-cache": "^5.1.1", + "meant": "~1.0.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.4", + "move-concurrently": "^1.0.1", + "node-gyp": "^5.1.0", + "nopt": "~4.0.1", + "normalize-package-data": "^2.5.0", + "npm-audit-report": "^1.3.2", + "npm-cache-filename": "~1.0.2", + "npm-install-checks": "^3.0.2", + "npm-lifecycle": "^3.1.4", + "npm-package-arg": "^6.1.1", + "npm-packlist": "^1.4.8", + "npm-pick-manifest": "^3.0.2", + "npm-profile": "^4.0.4", + "npm-registry-fetch": "^4.0.3", + "npm-user-validate": "~1.0.0", + "npmlog": "~4.1.2", + "once": "~1.4.0", + "opener": "^1.5.1", + "osenv": "^0.1.5", + "pacote": "^9.5.12", + "path-is-inside": "~1.0.2", + "promise-inflight": "~1.0.1", + "qrcode-terminal": "^0.12.0", + "query-string": "^6.8.2", + "qw": "~1.0.1", + "read": "~1.0.7", + "read-cmd-shim": "^1.0.5", + "read-installed": "~4.0.3", + "read-package-json": "^2.1.1", + "read-package-tree": "^5.3.1", + "readable-stream": "^3.6.0", + "readdir-scoped-modules": "^1.1.0", + "request": "^2.88.0", + "retry": "^0.12.0", + "rimraf": "^2.7.1", + "safe-buffer": "^5.1.2", + "semver": "^5.7.1", + "sha": "^3.0.0", + "slide": "~1.1.6", + "sorted-object": "~2.0.1", + "sorted-union-stream": "~2.1.3", + "ssri": "^6.0.1", + "stringify-package": "^1.0.1", + "tar": "^4.4.13", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "uid-number": "0.0.6", + "umask": "~1.1.0", + "unique-filename": "^1.1.1", + "unpipe": "~1.0.0", + "update-notifier": "^2.5.0", + "uuid": "^3.3.3", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "~3.0.0", + "which": "^1.3.1", + "worker-farm": "^1.7.0", + "write-file-atomic": "^2.4.3" + }, + "dependencies": { + "JSONStream": { + "version": "1.3.5", + "bundled": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "abbrev": { + "version": "1.1.1", + "bundled": true + }, + "agent-base": { + "version": "4.3.0", + "bundled": true, + "requires": { + "es6-promisify": "^5.0.0" + } + }, + "agentkeepalive": { + "version": "3.5.2", + "bundled": true, + "requires": { + "humanize-ms": "^1.2.1" + } + }, + "ajv": { + "version": "5.5.2", + "bundled": true, + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "ansi-align": { + "version": "2.0.0", + "bundled": true, + "requires": { + "string-width": "^2.0.0" + } + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true + }, + "ansi-styles": { + "version": "3.2.1", + "bundled": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "ansicolors": { + "version": "0.3.2", + "bundled": true + }, + "ansistyles": { + "version": "0.1.3", + "bundled": true + }, + "aproba": { + "version": "2.0.0", + "bundled": true + }, + "archy": { + "version": "1.0.0", + "bundled": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "asap": { + "version": "2.0.6", + "bundled": true + }, + "asn1": { + "version": "0.2.4", + "bundled": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "bundled": true + }, + "asynckit": { + "version": "0.4.0", + "bundled": true + }, + "aws-sign2": { + "version": "0.7.0", + "bundled": true + }, + "aws4": { + "version": "1.8.0", + "bundled": true + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "bundled": true, + "optional": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bin-links": { + "version": "1.1.7", + "bundled": true, + "requires": { + "bluebird": "^3.5.3", + "cmd-shim": "^3.0.0", + "gentle-fs": "^2.3.0", + "graceful-fs": "^4.1.15", + "npm-normalize-package-bin": "^1.0.0", + "write-file-atomic": "^2.3.0" + } + }, + "bluebird": { + "version": "3.5.5", + "bundled": true + }, + "boxen": { + "version": "1.3.0", + "bundled": true, + "requires": { + "ansi-align": "^2.0.0", + "camelcase": "^4.0.0", + "chalk": "^2.0.1", + "cli-boxes": "^1.0.0", + "string-width": "^2.0.0", + "term-size": "^1.2.0", + "widest-line": "^2.0.0" + } + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "buffer-from": { + "version": "1.0.0", + "bundled": true + }, + "builtins": { + "version": "1.0.3", + "bundled": true + }, + "byline": { + "version": "5.0.0", + "bundled": true + }, + "byte-size": { + "version": "5.0.1", + "bundled": true + }, + "cacache": { + "version": "12.0.3", + "bundled": true, + "requires": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + } + }, + "call-limit": { + "version": "1.1.1", + "bundled": true + }, + "camelcase": { + "version": "4.1.0", + "bundled": true + }, + "capture-stack-trace": { + "version": "1.0.0", + "bundled": true + }, + "caseless": { + "version": "0.12.0", + "bundled": true + }, + "chalk": { + "version": "2.4.1", + "bundled": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chownr": { + "version": "1.1.4", + "bundled": true + }, + "ci-info": { + "version": "2.0.0", + "bundled": true + }, + "cidr-regex": { + "version": "2.0.10", + "bundled": true, + "requires": { + "ip-regex": "^2.1.0" + } + }, + "cli-boxes": { + "version": "1.0.0", + "bundled": true + }, + "cli-columns": { + "version": "3.1.2", + "bundled": true, + "requires": { + "string-width": "^2.0.0", + "strip-ansi": "^3.0.1" + } + }, + "cli-table3": { + "version": "0.5.1", + "bundled": true, + "requires": { + "colors": "^1.1.2", + "object-assign": "^4.1.0", + "string-width": "^2.1.1" + } + }, + "cliui": { + "version": "4.1.0", + "bundled": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "bundled": true + }, + "strip-ansi": { + "version": "4.0.0", + "bundled": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "clone": { + "version": "1.0.4", + "bundled": true + }, + "cmd-shim": { + "version": "3.0.3", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.2", + "mkdirp": "~0.5.0" + } + }, + "co": { + "version": "4.6.0", + "bundled": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true + }, + "color-convert": { + "version": "1.9.1", + "bundled": true, + "requires": { + "color-name": "^1.1.1" + } + }, + "color-name": { + "version": "1.1.3", + "bundled": true + }, + "colors": { + "version": "1.3.3", + "bundled": true, + "optional": true + }, + "columnify": { + "version": "1.5.4", + "bundled": true, + "requires": { + "strip-ansi": "^3.0.0", + "wcwidth": "^1.0.0" + } + }, + "combined-stream": { + "version": "1.0.6", + "bundled": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "bundled": true + }, + "concat-stream": { + "version": "1.6.2", + "bundled": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "config-chain": { + "version": "1.1.12", + "bundled": true, + "requires": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "configstore": { + "version": "3.1.2", + "bundled": true, + "requires": { + "dot-prop": "^4.1.0", + "graceful-fs": "^4.1.2", + "make-dir": "^1.0.0", + "unique-string": "^1.0.0", + "write-file-atomic": "^2.0.0", + "xdg-basedir": "^3.0.0" + } + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true + }, + "copy-concurrently": { + "version": "1.0.5", + "bundled": true, + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + }, + "dependencies": { + "aproba": { + "version": "1.2.0", + "bundled": true + }, + "iferr": { + "version": "0.1.5", + "bundled": true + } + } + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true + }, + "create-error-class": { + "version": "3.0.2", + "bundled": true, + "requires": { + "capture-stack-trace": "^1.0.0" + } + }, + "cross-spawn": { + "version": "5.1.0", + "bundled": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "dependencies": { + "lru-cache": { + "version": "4.1.5", + "bundled": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "yallist": { + "version": "2.1.2", + "bundled": true + } + } + }, + "crypto-random-string": { + "version": "1.0.0", + "bundled": true + }, + "cyclist": { + "version": "0.2.2", + "bundled": true + }, + "dashdash": { + "version": "1.14.1", + "bundled": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "3.1.0", + "bundled": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "bundled": true + } + } + }, + "debuglog": { + "version": "1.0.1", + "bundled": true + }, + "decamelize": { + "version": "1.2.0", + "bundled": true + }, + "decode-uri-component": { + "version": "0.2.0", + "bundled": true + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true + }, + "defaults": { + "version": "1.0.3", + "bundled": true, + "requires": { + "clone": "^1.0.2" + } + }, + "define-properties": { + "version": "1.1.3", + "bundled": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "delayed-stream": { + "version": "1.0.0", + "bundled": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true + }, + "detect-indent": { + "version": "5.0.0", + "bundled": true + }, + "detect-newline": { + "version": "2.1.0", + "bundled": true + }, + "dezalgo": { + "version": "1.0.3", + "bundled": true, + "requires": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "dot-prop": { + "version": "4.2.0", + "bundled": true, + "requires": { + "is-obj": "^1.0.0" + } + }, + "dotenv": { + "version": "5.0.1", + "bundled": true + }, + "duplexer3": { + "version": "0.1.4", + "bundled": true + }, + "duplexify": { + "version": "3.6.0", + "bundled": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "bundled": true, + "optional": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "editor": { + "version": "1.0.0", + "bundled": true + }, + "encoding": { + "version": "0.1.12", + "bundled": true, + "requires": { + "iconv-lite": "~0.4.13" + } + }, + "end-of-stream": { + "version": "1.4.1", + "bundled": true, + "requires": { + "once": "^1.4.0" + } + }, + "env-paths": { + "version": "2.2.0", + "bundled": true + }, + "err-code": { + "version": "1.1.2", + "bundled": true + }, + "errno": { + "version": "0.1.7", + "bundled": true, + "requires": { + "prr": "~1.0.1" + } + }, + "es-abstract": { + "version": "1.12.0", + "bundled": true, + "requires": { + "es-to-primitive": "^1.1.1", + "function-bind": "^1.1.1", + "has": "^1.0.1", + "is-callable": "^1.1.3", + "is-regex": "^1.0.4" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "bundled": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es6-promise": { + "version": "4.2.8", + "bundled": true + }, + "es6-promisify": { + "version": "5.0.0", + "bundled": true, + "requires": { + "es6-promise": "^4.0.3" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "bundled": true + }, + "execa": { + "version": "0.7.0", + "bundled": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "dependencies": { + "get-stream": { + "version": "3.0.0", + "bundled": true + } + } + }, + "extend": { + "version": "3.0.2", + "bundled": true + }, + "extsprintf": { + "version": "1.3.0", + "bundled": true + }, + "fast-deep-equal": { + "version": "1.1.0", + "bundled": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "bundled": true + }, + "figgy-pudding": { + "version": "3.5.1", + "bundled": true + }, + "find-npm-prefix": { + "version": "1.0.2", + "bundled": true + }, + "find-up": { + "version": "2.1.0", + "bundled": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "flush-write-stream": { + "version": "1.0.3", + "bundled": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.4" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "forever-agent": { + "version": "0.6.1", + "bundled": true + }, + "form-data": { + "version": "2.3.2", + "bundled": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "1.0.6", + "mime-types": "^2.1.12" + } + }, + "from2": { + "version": "2.3.0", + "bundled": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "fs-minipass": { + "version": "1.2.7", + "bundled": true, + "requires": { + "minipass": "^2.6.0" + }, + "dependencies": { + "minipass": { + "version": "2.9.0", + "bundled": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + } + } + }, + "fs-vacuum": { + "version": "1.2.10", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.2", + "path-is-inside": "^1.0.1", + "rimraf": "^2.5.2" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + }, + "dependencies": { + "iferr": { + "version": "0.1.5", + "bundled": true + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true + }, + "function-bind": { + "version": "1.1.1", + "bundled": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + }, + "dependencies": { + "aproba": { + "version": "1.2.0", + "bundled": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "genfun": { + "version": "5.0.0", + "bundled": true + }, + "gentle-fs": { + "version": "2.3.0", + "bundled": true, + "requires": { + "aproba": "^1.1.2", + "chownr": "^1.1.2", + "cmd-shim": "^3.0.3", + "fs-vacuum": "^1.2.10", + "graceful-fs": "^4.1.11", + "iferr": "^0.1.5", + "infer-owner": "^1.0.4", + "mkdirp": "^0.5.1", + "path-is-inside": "^1.0.2", + "read-cmd-shim": "^1.0.1", + "slide": "^1.1.6" + }, + "dependencies": { + "aproba": { + "version": "1.2.0", + "bundled": true + }, + "iferr": { + "version": "0.1.5", + "bundled": true + } + } + }, + "get-caller-file": { + "version": "1.0.3", + "bundled": true + }, + "get-stream": { + "version": "4.1.0", + "bundled": true, + "requires": { + "pump": "^3.0.0" + } + }, + "getpass": { + "version": "0.1.7", + "bundled": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.6", + "bundled": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "global-dirs": { + "version": "0.1.1", + "bundled": true, + "requires": { + "ini": "^1.3.4" + } + }, + "got": { + "version": "6.7.1", + "bundled": true, + "requires": { + "create-error-class": "^3.0.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-redirect": "^1.0.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "lowercase-keys": "^1.0.0", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "unzip-response": "^2.0.1", + "url-parse-lax": "^1.0.0" + }, + "dependencies": { + "get-stream": { + "version": "3.0.0", + "bundled": true + } + } + }, + "graceful-fs": { + "version": "4.2.3", + "bundled": true + }, + "har-schema": { + "version": "2.0.0", + "bundled": true + }, + "har-validator": { + "version": "5.1.0", + "bundled": true, + "requires": { + "ajv": "^5.3.0", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "bundled": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "bundled": true + }, + "has-symbols": { + "version": "1.0.0", + "bundled": true + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true + }, + "hosted-git-info": { + "version": "2.8.8", + "bundled": true + }, + "http-cache-semantics": { + "version": "3.8.1", + "bundled": true + }, + "http-proxy-agent": { + "version": "2.1.0", + "bundled": true, + "requires": { + "agent-base": "4", + "debug": "3.1.0" + } + }, + "http-signature": { + "version": "1.2.0", + "bundled": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-proxy-agent": { + "version": "2.2.4", + "bundled": true, + "requires": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + } + }, + "humanize-ms": { + "version": "1.2.1", + "bundled": true, + "requires": { + "ms": "^2.0.0" + } + }, + "iconv-lite": { + "version": "0.4.23", + "bundled": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "iferr": { + "version": "1.0.2", + "bundled": true + }, + "ignore-walk": { + "version": "3.0.3", + "bundled": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "import-lazy": { + "version": "2.1.0", + "bundled": true + }, + "imurmurhash": { + "version": "0.1.4", + "bundled": true + }, + "infer-owner": { + "version": "1.0.4", + "bundled": true + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "bundled": true + }, + "ini": { + "version": "1.3.5", + "bundled": true + }, + "init-package-json": { + "version": "1.10.3", + "bundled": true, + "requires": { + "glob": "^7.1.1", + "npm-package-arg": "^4.0.0 || ^5.0.0 || ^6.0.0", + "promzard": "^0.3.0", + "read": "~1.0.1", + "read-package-json": "1 || 2", + "semver": "2.x || 3.x || 4 || 5", + "validate-npm-package-license": "^3.0.1", + "validate-npm-package-name": "^3.0.0" + } + }, + "invert-kv": { + "version": "2.0.0", + "bundled": true + }, + "ip": { + "version": "1.1.5", + "bundled": true + }, + "ip-regex": { + "version": "2.1.0", + "bundled": true + }, + "is-callable": { + "version": "1.1.4", + "bundled": true + }, + "is-ci": { + "version": "1.2.1", + "bundled": true, + "requires": { + "ci-info": "^1.5.0" + }, + "dependencies": { + "ci-info": { + "version": "1.6.0", + "bundled": true + } + } + }, + "is-cidr": { + "version": "3.0.0", + "bundled": true, + "requires": { + "cidr-regex": "^2.0.10" + } + }, + "is-date-object": { + "version": "1.0.1", + "bundled": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-installed-globally": { + "version": "0.1.0", + "bundled": true, + "requires": { + "global-dirs": "^0.1.0", + "is-path-inside": "^1.0.0" + } + }, + "is-npm": { + "version": "1.0.0", + "bundled": true + }, + "is-obj": { + "version": "1.0.1", + "bundled": true + }, + "is-path-inside": { + "version": "1.0.1", + "bundled": true, + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "is-redirect": { + "version": "1.0.0", + "bundled": true + }, + "is-regex": { + "version": "1.0.4", + "bundled": true, + "requires": { + "has": "^1.0.1" + } + }, + "is-retry-allowed": { + "version": "1.2.0", + "bundled": true + }, + "is-stream": { + "version": "1.1.0", + "bundled": true + }, + "is-symbol": { + "version": "1.0.2", + "bundled": true, + "requires": { + "has-symbols": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "bundled": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true + }, + "isexe": { + "version": "2.0.0", + "bundled": true + }, + "isstream": { + "version": "0.1.2", + "bundled": true + }, + "jsbn": { + "version": "0.1.1", + "bundled": true, + "optional": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "bundled": true + }, + "json-schema": { + "version": "0.2.3", + "bundled": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "bundled": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "bundled": true + }, + "jsonparse": { + "version": "1.3.1", + "bundled": true + }, + "jsprim": { + "version": "1.4.1", + "bundled": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "latest-version": { + "version": "3.1.0", + "bundled": true, + "requires": { + "package-json": "^4.0.0" + } + }, + "lazy-property": { + "version": "1.0.0", + "bundled": true + }, + "lcid": { + "version": "2.0.0", + "bundled": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "libcipm": { + "version": "4.0.7", + "bundled": true, + "requires": { + "bin-links": "^1.1.2", + "bluebird": "^3.5.1", + "figgy-pudding": "^3.5.1", + "find-npm-prefix": "^1.0.2", + "graceful-fs": "^4.1.11", + "ini": "^1.3.5", + "lock-verify": "^2.0.2", + "mkdirp": "^0.5.1", + "npm-lifecycle": "^3.0.0", + "npm-logical-tree": "^1.2.1", + "npm-package-arg": "^6.1.0", + "pacote": "^9.1.0", + "read-package-json": "^2.0.13", + "rimraf": "^2.6.2", + "worker-farm": "^1.6.0" + } + }, + "libnpm": { + "version": "3.0.1", + "bundled": true, + "requires": { + "bin-links": "^1.1.2", + "bluebird": "^3.5.3", + "find-npm-prefix": "^1.0.2", + "libnpmaccess": "^3.0.2", + "libnpmconfig": "^1.2.1", + "libnpmhook": "^5.0.3", + "libnpmorg": "^1.0.1", + "libnpmpublish": "^1.1.2", + "libnpmsearch": "^2.0.2", + "libnpmteam": "^1.0.2", + "lock-verify": "^2.0.2", + "npm-lifecycle": "^3.0.0", + "npm-logical-tree": "^1.2.1", + "npm-package-arg": "^6.1.0", + "npm-profile": "^4.0.2", + "npm-registry-fetch": "^4.0.0", + "npmlog": "^4.1.2", + "pacote": "^9.5.3", + "read-package-json": "^2.0.13", + "stringify-package": "^1.0.0" + } + }, + "libnpmaccess": { + "version": "3.0.2", + "bundled": true, + "requires": { + "aproba": "^2.0.0", + "get-stream": "^4.0.0", + "npm-package-arg": "^6.1.0", + "npm-registry-fetch": "^4.0.0" + } + }, + "libnpmconfig": { + "version": "1.2.1", + "bundled": true, + "requires": { + "figgy-pudding": "^3.5.1", + "find-up": "^3.0.0", + "ini": "^1.3.5" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "bundled": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "bundled": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.2.0", + "bundled": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "bundled": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "bundled": true + } + } + }, + "libnpmhook": { + "version": "5.0.3", + "bundled": true, + "requires": { + "aproba": "^2.0.0", + "figgy-pudding": "^3.4.1", + "get-stream": "^4.0.0", + "npm-registry-fetch": "^4.0.0" + } + }, + "libnpmorg": { + "version": "1.0.1", + "bundled": true, + "requires": { + "aproba": "^2.0.0", + "figgy-pudding": "^3.4.1", + "get-stream": "^4.0.0", + "npm-registry-fetch": "^4.0.0" + } + }, + "libnpmpublish": { + "version": "1.1.2", + "bundled": true, + "requires": { + "aproba": "^2.0.0", + "figgy-pudding": "^3.5.1", + "get-stream": "^4.0.0", + "lodash.clonedeep": "^4.5.0", + "normalize-package-data": "^2.4.0", + "npm-package-arg": "^6.1.0", + "npm-registry-fetch": "^4.0.0", + "semver": "^5.5.1", + "ssri": "^6.0.1" + } + }, + "libnpmsearch": { + "version": "2.0.2", + "bundled": true, + "requires": { + "figgy-pudding": "^3.5.1", + "get-stream": "^4.0.0", + "npm-registry-fetch": "^4.0.0" + } + }, + "libnpmteam": { + "version": "1.0.2", + "bundled": true, + "requires": { + "aproba": "^2.0.0", + "figgy-pudding": "^3.4.1", + "get-stream": "^4.0.0", + "npm-registry-fetch": "^4.0.0" + } + }, + "libnpx": { + "version": "10.2.2", + "bundled": true, + "requires": { + "dotenv": "^5.0.1", + "npm-package-arg": "^6.0.0", + "rimraf": "^2.6.2", + "safe-buffer": "^5.1.0", + "update-notifier": "^2.3.0", + "which": "^1.3.0", + "y18n": "^4.0.0", + "yargs": "^11.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "bundled": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lock-verify": { + "version": "2.1.0", + "bundled": true, + "requires": { + "npm-package-arg": "^6.1.0", + "semver": "^5.4.1" + } + }, + "lockfile": { + "version": "1.0.4", + "bundled": true, + "requires": { + "signal-exit": "^3.0.2" + } + }, + "lodash._baseindexof": { + "version": "3.1.0", + "bundled": true + }, + "lodash._baseuniq": { + "version": "4.6.0", + "bundled": true, + "requires": { + "lodash._createset": "~4.0.0", + "lodash._root": "~3.0.0" + } + }, + "lodash._bindcallback": { + "version": "3.0.1", + "bundled": true + }, + "lodash._cacheindexof": { + "version": "3.0.2", + "bundled": true + }, + "lodash._createcache": { + "version": "3.1.2", + "bundled": true, + "requires": { + "lodash._getnative": "^3.0.0" + } + }, + "lodash._createset": { + "version": "4.0.3", + "bundled": true + }, + "lodash._getnative": { + "version": "3.9.1", + "bundled": true + }, + "lodash._root": { + "version": "3.0.1", + "bundled": true + }, + "lodash.clonedeep": { + "version": "4.5.0", + "bundled": true + }, + "lodash.restparam": { + "version": "3.6.1", + "bundled": true + }, + "lodash.union": { + "version": "4.6.0", + "bundled": true + }, + "lodash.uniq": { + "version": "4.5.0", + "bundled": true + }, + "lodash.without": { + "version": "4.4.0", + "bundled": true + }, + "lowercase-keys": { + "version": "1.0.1", + "bundled": true + }, + "lru-cache": { + "version": "5.1.1", + "bundled": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "make-dir": { + "version": "1.3.0", + "bundled": true, + "requires": { + "pify": "^3.0.0" + } + }, + "make-fetch-happen": { + "version": "5.0.2", + "bundled": true, + "requires": { + "agentkeepalive": "^3.4.1", + "cacache": "^12.0.0", + "http-cache-semantics": "^3.8.1", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "node-fetch-npm": "^2.0.2", + "promise-retry": "^1.1.1", + "socks-proxy-agent": "^4.0.0", + "ssri": "^6.0.0" + } + }, + "map-age-cleaner": { + "version": "0.1.3", + "bundled": true, + "requires": { + "p-defer": "^1.0.0" + } + }, + "meant": { + "version": "1.0.1", + "bundled": true + }, + "mem": { + "version": "4.3.0", + "bundled": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + }, + "dependencies": { + "mimic-fn": { + "version": "2.1.0", + "bundled": true + } + } + }, + "mime-db": { + "version": "1.35.0", + "bundled": true + }, + "mime-types": { + "version": "2.1.19", + "bundled": true, + "requires": { + "mime-db": "~1.35.0" + } + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minizlib": { + "version": "1.3.3", + "bundled": true, + "requires": { + "minipass": "^2.9.0" + }, + "dependencies": { + "minipass": { + "version": "2.9.0", + "bundled": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + } + } + }, + "mississippi": { + "version": "3.0.0", + "bundled": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mkdirp": { + "version": "0.5.4", + "bundled": true, + "requires": { + "minimist": "^1.2.5" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "bundled": true + } + } + }, + "move-concurrently": { + "version": "1.0.1", + "bundled": true, + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + }, + "dependencies": { + "aproba": { + "version": "1.2.0", + "bundled": true + } + } + }, + "ms": { + "version": "2.1.1", + "bundled": true + }, + "mute-stream": { + "version": "0.0.7", + "bundled": true + }, + "nice-try": { + "version": "1.0.5", + "bundled": true + }, + "node-fetch-npm": { + "version": "2.0.2", + "bundled": true, + "requires": { + "encoding": "^0.1.11", + "json-parse-better-errors": "^1.0.0", + "safe-buffer": "^5.1.1" + } + }, + "node-gyp": { + "version": "5.1.0", + "bundled": true, + "requires": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.2", + "mkdirp": "^0.5.1", + "nopt": "^4.0.1", + "npmlog": "^4.1.2", + "request": "^2.88.0", + "rimraf": "^2.6.3", + "semver": "^5.7.1", + "tar": "^4.4.12", + "which": "^1.3.1" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "bundled": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "resolve": { + "version": "1.10.0", + "bundled": true, + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "npm-audit-report": { + "version": "1.3.2", + "bundled": true, + "requires": { + "cli-table3": "^0.5.0", + "console-control-strings": "^1.1.0" + } + }, + "npm-bundled": { + "version": "1.1.1", + "bundled": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-cache-filename": { + "version": "1.0.2", + "bundled": true + }, + "npm-install-checks": { + "version": "3.0.2", + "bundled": true, + "requires": { + "semver": "^2.3.0 || 3.x || 4 || 5" + } + }, + "npm-lifecycle": { + "version": "3.1.4", + "bundled": true, + "requires": { + "byline": "^5.0.0", + "graceful-fs": "^4.1.15", + "node-gyp": "^5.0.2", + "resolve-from": "^4.0.0", + "slide": "^1.1.6", + "uid-number": "0.0.6", + "umask": "^1.1.0", + "which": "^1.3.1" + } + }, + "npm-logical-tree": { + "version": "1.2.1", + "bundled": true + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "bundled": true + }, + "npm-package-arg": { + "version": "6.1.1", + "bundled": true, + "requires": { + "hosted-git-info": "^2.7.1", + "osenv": "^0.1.5", + "semver": "^5.6.0", + "validate-npm-package-name": "^3.0.0" + } + }, + "npm-packlist": { + "version": "1.4.8", + "bundled": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-pick-manifest": { + "version": "3.0.2", + "bundled": true, + "requires": { + "figgy-pudding": "^3.5.1", + "npm-package-arg": "^6.0.0", + "semver": "^5.4.1" + } + }, + "npm-profile": { + "version": "4.0.4", + "bundled": true, + "requires": { + "aproba": "^1.1.2 || 2", + "figgy-pudding": "^3.4.1", + "npm-registry-fetch": "^4.0.0" + } + }, + "npm-registry-fetch": { + "version": "4.0.3", + "bundled": true, + "requires": { + "JSONStream": "^1.3.4", + "bluebird": "^3.5.1", + "figgy-pudding": "^3.4.1", + "lru-cache": "^5.1.1", + "make-fetch-happen": "^5.0.0", + "npm-package-arg": "^6.1.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.0", + "bundled": true + } + } + }, + "npm-run-path": { + "version": "2.0.2", + "bundled": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "npm-user-validate": { + "version": "1.0.0", + "bundled": true + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true + }, + "oauth-sign": { + "version": "0.9.0", + "bundled": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true + }, + "object-keys": { + "version": "1.0.12", + "bundled": true + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "bundled": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + } + }, + "once": { + "version": "1.4.0", + "bundled": true, + "requires": { + "wrappy": "1" + } + }, + "opener": { + "version": "1.5.1", + "bundled": true + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true + }, + "os-locale": { + "version": "3.1.0", + "bundled": true, + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "bundled": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "1.0.0", + "bundled": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + } + } + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "p-defer": { + "version": "1.0.0", + "bundled": true + }, + "p-finally": { + "version": "1.0.0", + "bundled": true + }, + "p-is-promise": { + "version": "2.1.0", + "bundled": true + }, + "p-limit": { + "version": "1.2.0", + "bundled": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "bundled": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "bundled": true + }, + "package-json": { + "version": "4.0.1", + "bundled": true, + "requires": { + "got": "^6.7.1", + "registry-auth-token": "^3.0.1", + "registry-url": "^3.0.3", + "semver": "^5.1.0" + } + }, + "pacote": { + "version": "9.5.12", + "bundled": true, + "requires": { + "bluebird": "^3.5.3", + "cacache": "^12.0.2", + "chownr": "^1.1.2", + "figgy-pudding": "^3.5.1", + "get-stream": "^4.1.0", + "glob": "^7.1.3", + "infer-owner": "^1.0.4", + "lru-cache": "^5.1.1", + "make-fetch-happen": "^5.0.0", + "minimatch": "^3.0.4", + "minipass": "^2.3.5", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "normalize-package-data": "^2.4.0", + "npm-normalize-package-bin": "^1.0.0", + "npm-package-arg": "^6.1.0", + "npm-packlist": "^1.1.12", + "npm-pick-manifest": "^3.0.0", + "npm-registry-fetch": "^4.0.0", + "osenv": "^0.1.5", + "promise-inflight": "^1.0.1", + "promise-retry": "^1.1.1", + "protoduck": "^5.0.1", + "rimraf": "^2.6.2", + "safe-buffer": "^5.1.2", + "semver": "^5.6.0", + "ssri": "^6.0.1", + "tar": "^4.4.10", + "unique-filename": "^1.1.1", + "which": "^1.3.1" + }, + "dependencies": { + "minipass": { + "version": "2.9.0", + "bundled": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + } + } + }, + "parallel-transform": { + "version": "1.1.0", + "bundled": true, + "requires": { + "cyclist": "~0.2.2", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "path-exists": { + "version": "3.0.0", + "bundled": true + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true + }, + "path-is-inside": { + "version": "1.0.2", + "bundled": true + }, + "path-key": { + "version": "2.0.1", + "bundled": true + }, + "path-parse": { + "version": "1.0.6", + "bundled": true + }, + "performance-now": { + "version": "2.1.0", + "bundled": true + }, + "pify": { + "version": "3.0.0", + "bundled": true + }, + "prepend-http": { + "version": "1.0.4", + "bundled": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true + }, + "promise-inflight": { + "version": "1.0.1", + "bundled": true + }, + "promise-retry": { + "version": "1.1.1", + "bundled": true, + "requires": { + "err-code": "^1.0.0", + "retry": "^0.10.0" + }, + "dependencies": { + "retry": { + "version": "0.10.1", + "bundled": true + } + } + }, + "promzard": { + "version": "0.3.0", + "bundled": true, + "requires": { + "read": "1" + } + }, + "proto-list": { + "version": "1.2.4", + "bundled": true + }, + "protoduck": { + "version": "5.0.1", + "bundled": true, + "requires": { + "genfun": "^5.0.0" + } + }, + "prr": { + "version": "1.0.1", + "bundled": true + }, + "pseudomap": { + "version": "1.0.2", + "bundled": true + }, + "psl": { + "version": "1.1.29", + "bundled": true + }, + "pump": { + "version": "3.0.0", + "bundled": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "bundled": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "bundled": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "punycode": { + "version": "1.4.1", + "bundled": true + }, + "qrcode-terminal": { + "version": "0.12.0", + "bundled": true + }, + "qs": { + "version": "6.5.2", + "bundled": true + }, + "query-string": { + "version": "6.8.2", + "bundled": true, + "requires": { + "decode-uri-component": "^0.2.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + } + }, + "qw": { + "version": "1.0.1", + "bundled": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "bundled": true + } + } + }, + "read": { + "version": "1.0.7", + "bundled": true, + "requires": { + "mute-stream": "~0.0.4" + } + }, + "read-cmd-shim": { + "version": "1.0.5", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.2" + } + }, + "read-installed": { + "version": "4.0.3", + "bundled": true, + "requires": { + "debuglog": "^1.0.1", + "graceful-fs": "^4.1.2", + "read-package-json": "^2.0.0", + "readdir-scoped-modules": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "slide": "~1.1.3", + "util-extend": "^1.0.1" + } + }, + "read-package-json": { + "version": "2.1.1", + "bundled": true, + "requires": { + "glob": "^7.1.1", + "graceful-fs": "^4.1.2", + "json-parse-better-errors": "^1.0.1", + "normalize-package-data": "^2.0.0", + "npm-normalize-package-bin": "^1.0.0" + } + }, + "read-package-tree": { + "version": "5.3.1", + "bundled": true, + "requires": { + "read-package-json": "^2.0.0", + "readdir-scoped-modules": "^1.0.0", + "util-promisify": "^2.1.0" + } + }, + "readable-stream": { + "version": "3.6.0", + "bundled": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdir-scoped-modules": { + "version": "1.1.0", + "bundled": true, + "requires": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" + } + }, + "registry-auth-token": { + "version": "3.4.0", + "bundled": true, + "requires": { + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" + } + }, + "registry-url": { + "version": "3.1.0", + "bundled": true, + "requires": { + "rc": "^1.0.1" + } + }, + "request": { + "version": "2.88.0", + "bundled": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "require-directory": { + "version": "2.1.1", + "bundled": true + }, + "require-main-filename": { + "version": "1.0.1", + "bundled": true + }, + "resolve-from": { + "version": "4.0.0", + "bundled": true + }, + "retry": { + "version": "0.12.0", + "bundled": true + }, + "rimraf": { + "version": "2.7.1", + "bundled": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-queue": { + "version": "1.0.3", + "bundled": true, + "requires": { + "aproba": "^1.1.1" + }, + "dependencies": { + "aproba": { + "version": "1.2.0", + "bundled": true + } + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true + }, + "semver": { + "version": "5.7.1", + "bundled": true + }, + "semver-diff": { + "version": "2.1.0", + "bundled": true, + "requires": { + "semver": "^5.0.3" + } + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true + }, + "sha": { + "version": "3.0.0", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.2" + } + }, + "shebang-command": { + "version": "1.2.0", + "bundled": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "bundled": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true + }, + "slide": { + "version": "1.1.6", + "bundled": true + }, + "smart-buffer": { + "version": "4.1.0", + "bundled": true + }, + "socks": { + "version": "2.3.3", + "bundled": true, + "requires": { + "ip": "1.1.5", + "smart-buffer": "^4.1.0" + } + }, + "socks-proxy-agent": { + "version": "4.0.2", + "bundled": true, + "requires": { + "agent-base": "~4.2.1", + "socks": "~2.3.2" + }, + "dependencies": { + "agent-base": { + "version": "4.2.1", + "bundled": true, + "requires": { + "es6-promisify": "^5.0.0" + } + } + } + }, + "sorted-object": { + "version": "2.0.1", + "bundled": true + }, + "sorted-union-stream": { + "version": "2.1.3", + "bundled": true, + "requires": { + "from2": "^1.3.0", + "stream-iterate": "^1.1.0" + }, + "dependencies": { + "from2": { + "version": "1.3.0", + "bundled": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "~1.1.10" + } + }, + "isarray": { + "version": "0.0.1", + "bundled": true + }, + "readable-stream": { + "version": "1.1.14", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "bundled": true + } + } + }, + "spdx-correct": { + "version": "3.0.0", + "bundled": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.1.0", + "bundled": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "bundled": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.3", + "bundled": true + }, + "split-on-first": { + "version": "1.1.0", + "bundled": true + }, + "sshpk": { + "version": "1.14.2", + "bundled": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "6.0.1", + "bundled": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "stream-each": { + "version": "1.2.2", + "bundled": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-iterate": { + "version": "1.2.0", + "bundled": true, + "requires": { + "readable-stream": "^2.1.5", + "stream-shift": "^1.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "stream-shift": { + "version": "1.0.0", + "bundled": true + }, + "strict-uri-encode": { + "version": "2.0.0", + "bundled": true + }, + "string-width": { + "version": "2.1.1", + "bundled": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "bundled": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "bundled": true + }, + "strip-ansi": { + "version": "4.0.0", + "bundled": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "string_decoder": { + "version": "1.3.0", + "bundled": true, + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.0", + "bundled": true + } + } + }, + "stringify-package": { + "version": "1.0.1", + "bundled": true + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "bundled": true + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true + }, + "supports-color": { + "version": "5.4.0", + "bundled": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "tar": { + "version": "4.4.13", + "bundled": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + }, + "dependencies": { + "minipass": { + "version": "2.9.0", + "bundled": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + } + } + }, + "term-size": { + "version": "1.2.0", + "bundled": true, + "requires": { + "execa": "^0.7.0" + } + }, + "text-table": { + "version": "0.2.0", + "bundled": true + }, + "through": { + "version": "2.3.8", + "bundled": true + }, + "through2": { + "version": "2.0.3", + "bundled": true, + "requires": { + "readable-stream": "^2.1.5", + "xtend": "~4.0.1" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "timed-out": { + "version": "4.0.1", + "bundled": true + }, + "tiny-relative-date": { + "version": "1.3.0", + "bundled": true + }, + "tough-cookie": { + "version": "2.4.3", + "bundled": true, + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "bundled": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "bundled": true, + "optional": true + }, + "typedarray": { + "version": "0.0.6", + "bundled": true + }, + "uid-number": { + "version": "0.0.6", + "bundled": true + }, + "umask": { + "version": "1.1.0", + "bundled": true + }, + "unique-filename": { + "version": "1.1.1", + "bundled": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.0", + "bundled": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unique-string": { + "version": "1.0.0", + "bundled": true, + "requires": { + "crypto-random-string": "^1.0.0" + } + }, + "unpipe": { + "version": "1.0.0", + "bundled": true + }, + "unzip-response": { + "version": "2.0.1", + "bundled": true + }, + "update-notifier": { + "version": "2.5.0", + "bundled": true, + "requires": { + "boxen": "^1.2.1", + "chalk": "^2.0.1", + "configstore": "^3.0.0", + "import-lazy": "^2.1.0", + "is-ci": "^1.0.10", + "is-installed-globally": "^0.1.0", + "is-npm": "^1.0.0", + "latest-version": "^3.0.0", + "semver-diff": "^2.0.0", + "xdg-basedir": "^3.0.0" + } + }, + "url-parse-lax": { + "version": "1.0.0", + "bundled": true, + "requires": { + "prepend-http": "^1.0.1" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true + }, + "util-extend": { + "version": "1.0.3", + "bundled": true + }, + "util-promisify": { + "version": "2.1.0", + "bundled": true, + "requires": { + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "uuid": { + "version": "3.3.3", + "bundled": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "bundled": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "validate-npm-package-name": { + "version": "3.0.0", + "bundled": true, + "requires": { + "builtins": "^1.0.3" + } + }, + "verror": { + "version": "1.10.0", + "bundled": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "wcwidth": { + "version": "1.0.1", + "bundled": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "which": { + "version": "1.3.1", + "bundled": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "bundled": true + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "requires": { + "string-width": "^1.0.2" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "widest-line": { + "version": "2.0.1", + "bundled": true, + "requires": { + "string-width": "^2.1.1" + } + }, + "worker-farm": { + "version": "1.7.0", + "bundled": true, + "requires": { + "errno": "~0.1.7" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "bundled": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true + }, + "write-file-atomic": { + "version": "2.4.3", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "xdg-basedir": { + "version": "3.0.0", + "bundled": true + }, + "xtend": { + "version": "4.0.1", + "bundled": true + }, + "y18n": { + "version": "4.0.0", + "bundled": true + }, + "yallist": { + "version": "3.0.3", + "bundled": true + }, + "yargs": { + "version": "11.1.1", + "bundled": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.1.1", + "find-up": "^2.1.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.1.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^9.0.2" + }, + "dependencies": { + "y18n": { + "version": "3.2.1", + "bundled": true + } + } + }, + "yargs-parser": { + "version": "9.0.2", + "bundled": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, + "npm-conf": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", + "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", + "requires": { + "config-chain": "^1.1.11", + "pify": "^3.0.0" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "requires": { + "boolbase": "~1.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-assign-deep": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/object-assign-deep/-/object-assign-deep-0.3.1.tgz", + "integrity": "sha1-tXt1ctmjKNZEij9LbA//yLEMYaI=" + }, + "object-keys": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", + "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "ono": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/ono/-/ono-4.0.11.tgz", + "integrity": "sha512-jQ31cORBFE6td25deYeD80wxKBMj+zBmHTrVxnc6CKhx8gho6ipmWM5zj/oeoqioZ99yqBls9Z/9Nss7J26G2g==", + "requires": { + "format-util": "^1.0.3" + } + }, + "ora": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-1.4.0.tgz", + "integrity": "sha512-iMK1DOQxzzh2MBlVsU42G80mnrvUhqsMh74phHtDlrcTZPK0pH6o7l7DRshK+0YsxDyEuaOkziVdvM3T0QTzpw==", + "requires": { + "chalk": "^2.1.0", + "cli-cursor": "^2.1.0", + "cli-spinners": "^1.0.1", + "log-symbols": "^2.1.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + } + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "p-cancelable": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz", + "integrity": "sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ==" + }, + "p-event": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/p-event/-/p-event-2.3.1.tgz", + "integrity": "sha512-NQCqOFhbpVTMX4qMe8PF8lbGtzZ+LCiN7pcNrb/413Na7+TRoe1xkKUzuWa/YEJdGQ0FvKtj35EEbDoVPO2kbA==", + "requires": { + "p-timeout": "^2.0.1" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-is-promise": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", + "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=" + }, + "p-timeout": { + "version": "2.0.1", "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz", "integrity": "sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==", "requires": { @@ -1749,6 +5328,11 @@ "@types/node": "*" } }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, "pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -1838,6 +5422,11 @@ "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=" }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -1887,6 +5476,45 @@ "util-deprecate": "~1.0.1" } }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + }, + "regex-match-all": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-match-all/-/regex-match-all-1.0.2.tgz", + "integrity": "sha1-+c8/tJaUtBcxTRgzbSBAkUJ7SvM=" + }, + "remedial": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/remedial/-/remedial-1.0.8.tgz", + "integrity": "sha512-/62tYiOe6DzS5BqVsNpH/nkGlX45C/Sp6V+NtiN6JQNS1Viay7cWkazmRkrQrdFj2eshDe96SIQNIoMxqhzBOg==" + }, + "require-from-url": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/require-from-url/-/require-from-url-3.1.3.tgz", + "integrity": "sha512-SWYVQr6rZMumhsE0MGL3caGtBNDBPQRm7JV4fsxb8Nc+LR42QkmLPP56P+Y9jncZLNrrk4SpE/Ozaf8Jo3ialA==" + }, + "require-yaml": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/require-yaml/-/require-yaml-0.0.1.tgz", + "integrity": "sha1-LhsY2RPDuqcqWk03O28Tjd0sMr0=", + "requires": { + "js-yaml": "^3.13.1" + }, + "dependencies": { + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + } + } + }, "responselike": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", @@ -1895,6 +5523,15 @@ "lowercase-keys": "^1.0.0" } }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, "rmdir": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/rmdir/-/rmdir-1.2.0.tgz", @@ -1903,11 +5540,37 @@ "node.flow": "1.2.3" } }, + "run-async": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.0.tgz", + "integrity": "sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg==", + "requires": { + "is-promise": "^2.1.0" + } + }, + "rx-lite": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", + "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=" + }, + "rx-lite-aggregates": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", + "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", + "requires": { + "rx-lite": "*" + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, "seek-bzip": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.5.tgz", @@ -1926,6 +5589,11 @@ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" + }, "signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", @@ -2151,6 +5819,14 @@ "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=" }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "requires": { + "os-tmpdir": "~1.0.2" + } + }, "to-buffer": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", @@ -2219,11 +5895,21 @@ "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=" }, + "utf8": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-2.1.2.tgz", + "integrity": "sha1-H6DZJw6b6FDZsFAn9jUZv0ZFfZY=" + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, + "valid-url": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz", + "integrity": "sha1-HBRHm0DxOXp1eC8RXkCGRHQzogA=" + }, "validator": { "version": "10.11.0", "resolved": "https://registry.npmjs.org/validator/-/validator-10.11.0.tgz", diff --git a/package.json b/package.json index 1b30458..f60e026 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "csvjson": "^5.1.0", "debug": "^4.1.1", "download": "^7.1.0", + "github-release-notes": "^0.17.1", "handlebars": "^4.7.6", "hapi-swagger": "^13.0.0", "minimist": "^1.2.5", diff --git a/resources/about.js b/resources/about.js index a98cee2..6139e5a 100644 --- a/resources/about.js +++ b/resources/about.js @@ -1,11 +1,13 @@ -const about = { +'use strict'; + +module.exports = { method: 'GET', path: '/about', config: { - description: "about", + description: 'About', tags: ['zenodeo'], validate: {}, notes: [ @@ -27,5 +29,3 @@ const about = { ); } }; - -module.exports = about; \ No newline at end of file diff --git a/resources/docs.js b/resources/docs.js index 8789ad2..3f6e493 100644 --- a/resources/docs.js +++ b/resources/docs.js @@ -1,11 +1,13 @@ -const docs = { +'use strict'; + +module.exports = { method: 'GET', path: '/docs', config: { - description: "docs", + description: 'Documentation', tags: ['zenodeo'], validate: {}, notes: [ @@ -27,5 +29,3 @@ const docs = { ); } }; - -module.exports = docs; \ No newline at end of file diff --git a/resources/examples.js b/resources/examples.js index 19a62f2..e2a4bc5 100644 --- a/resources/examples.js +++ b/resources/examples.js @@ -1,11 +1,13 @@ -const examples = { +'use strict'; + +module.exports = { method: 'GET', path: '/examples', config: { - description: "Examples", + description: 'Examples', tags: ['zenodeo'], validate: {}, notes: [ @@ -27,5 +29,3 @@ const examples = { ); } }; - -module.exports = examples; \ No newline at end of file diff --git a/resources/inert.js b/resources/inert.js index d58750e..fdd3e33 100644 --- a/resources/inert.js +++ b/resources/inert.js @@ -1,4 +1,7 @@ -const inert = { +'use strict'; + +// Support for serving static files +module.exports = { method: 'GET', path: '/public/{param*}', config: { @@ -13,5 +16,3 @@ const inert = { } } }; - -module.exports = inert; \ No newline at end of file diff --git a/resources/install.js b/resources/install.js index 422fb4c..1d53f1d 100644 --- a/resources/install.js +++ b/resources/install.js @@ -1,11 +1,13 @@ -const install = { +'use strict'; + +module.exports = { method: 'GET', path: '/install', config: { - description: "installation instructions", + description: 'Installation instructions', tags: ['zenodeo'], validate: {}, notes: [ @@ -27,5 +29,3 @@ const install = { ); } }; - -module.exports = install; \ No newline at end of file diff --git a/resources/releases.js b/resources/releases.js index 02a6a51..5b8df78 100644 --- a/resources/releases.js +++ b/resources/releases.js @@ -1,4 +1,6 @@ -const notes = { +'use strict'; + +module.exports = { method: 'GET', @@ -201,6 +203,4 @@ lon: 77.1 lon: 78.9 ] }; -} - -module.exports = notes; \ No newline at end of file +}; \ No newline at end of file diff --git a/resources/tos.js b/resources/tos.js index 6c8ec6e..fe9b8ef 100644 --- a/resources/tos.js +++ b/resources/tos.js @@ -1,11 +1,13 @@ -const tos = { +'use strict'; + +module.exports = { method: 'GET', path: '/tos', config: { - description: "terms of service", + description: 'Terms of service', tags: ['zenodeo'], validate: {}, notes: [ @@ -27,5 +29,3 @@ const tos = { ); } }; - -module.exports = tos; \ No newline at end of file