Write all SVG's to file. Python script to batch convert SVG->PNG following soon...

This commit is contained in:
Marco van Dijk 2022-10-07 12:56:03 +02:00
parent 633a9ee611
commit 4867c455b9
7 changed files with 917 additions and 344 deletions

2
.gitignore vendored
View File

@ -1,5 +1,5 @@
.*
node_modules
build/
*.log
notes.txt
output

View File

@ -1,29 +1,14 @@
require = require("esm")(module);
import express from 'express';
const { generateRocketeer } = require("./nft-media/rocketeer");
(async () => {
try {
// Web application framework
const app = express();
app.disable('x-powered-by');
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
// Error handler
app.use(function(err, req, res, next) {
res.locals.message = err.message;
// Also log it to the console
console.log(`${err.status || 500} - ${err.message} - ${req.originalUrl} - ${req.method} - ${req.ip}`);
// Render the error page
res.status(err.status || 500);
res.render('error');
});
// Start listening on the defined port
app.listen(4243, "0.0.0.0", function () {
console.log(`Listening on port 4243`);
});
for (var id = 1; id <= 3475; id++) {
console.log("Generating Cpn Chad " + id);
console.log(await generateRocketeer(id));
}
process.exit(0);
} catch (err) {
console.log(err);
console.error(err);
process.exit(1);
}
})();

View File

@ -1,119 +1,139 @@
// ///////////////////////////////
// Helper functions
// ///////////////////////////////
exports.dev = !!process.env.development
const log = ( ...messages ) => {
if( process.env.development ) console.log( ...messages )
}
exports.log = log
exports.dev = !!process.env.development;
const log = (...messages) => {
if (process.env.development) console.log(...messages);
};
exports.log = log;
// Wait in async
const wait = timeInMs => new Promise( resolve => setTimeout( resolve ), timeInMs )
exports.wait = wait
const wait = (timeInMs) =>
new Promise((resolve) => setTimeout(resolve), timeInMs);
exports.wait = wait;
// Pick random item from an array
const pickRandomArrayEntry = array => array[ Math.floor( Math.random() * array.length ) ]
exports.pickRandomArrayEntry = pickRandomArrayEntry
const pickRandomArrayEntry = (array) =>
array[Math.floor(Math.random() * array.length)];
exports.pickRandomArrayEntry = pickRandomArrayEntry;
// Generate random number between x and y
exports.randomNumberBetween = ( min, max ) => Math.floor( Math.random() * ( max - min + 1 ) + min )
exports.randomNumberBetween = (min, max) =>
Math.floor(Math.random() * (max - min + 1) + min);
// Random attribute picker
exports.pickRandomAttributes = ( attributes ) => {
exports.pickRandomAttributes = (attributes) => {
// Decimal accuracy, if probabilities have the lowest 0.01 then 100 is enough, for 0.001 1000 is needed
const probabilityDecimals = 3
const probabilityDecimals = 3;
// Remap the trait so it has a 'lottery ticket box' based on probs
const attributeLottery = attributes.map( ( { values, ...attribute } ) => ( {
const attributeLottery = attributes.map(({ values, ...attribute }) => ({
// Attribute meta stays the same
...attribute,
// Values are reduced from objects with probabilities to an array with elements
values: values.reduce( ( acc, val ) => {
const { probability, value } = val
values: values.reduce((acc, val) => {
const { probability, value } = val;
// Map probabilities to a flat array of items
const amountToAdd = 10 * probabilityDecimals * probability
for ( let i = 0; i < amountToAdd; i++ ) acc.push( value )
return acc
}, [] )
} ) )
const amountToAdd = 10 * probabilityDecimals * probability;
for (let i = 0; i < amountToAdd; i++) acc.push(value);
return acc;
}, []),
}));
// Pick a random element from the lottery box array items
return attributeLottery.map( ( { values, ...attribute } ) => ( {
return attributeLottery.map(({ values, ...attribute }) => ({
// Attribute meta stays the same
...attribute,
// Select random entry from array
value: pickRandomArrayEntry( values )
} ) )
value: pickRandomArrayEntry(values),
}));
};
}
const nameColor = require('color-namer')
const Color = require('color')
exports.getColorName = ( rgb ) => {
const nameColor = require("color-namer");
const Color = require("color");
exports.getColorName = (rgb) => {
try {
return nameColor( rgb ).basic[0].name
} catch( e ) {
return rgb
return nameColor(rgb).basic[0].name;
} catch (e) {
return rgb;
}
}
exports.getRgbArrayFromColorName = name => {
const { hex } = nameColor( name ).basic[0]
const color = Color( hex )
return color.rgb().array()
}
};
exports.getRgbArrayFromColorName = (name) => {
const { hex } = nameColor(name).basic[0];
const color = Color(hex);
return color.rgb().array();
};
// ///////////////////////////////
// Attribute sources
// ///////////////////////////////
exports.globalAttributes = [
{ trait_type: "helmet", values: [
{ value: 'classic', probability: .2 },
{ value: 'racer', probability: .1 },
{ value: 'punk', probability: .1 },
{ value: 'knight', probability: .2 },
{ value: 'geek', probability: .2 }
] },
{ trait_type: "patch", values: [
{ value: 'nimbus', probability: .1 },
{ value: 'teku', probability: .1 },
{ value: 'lighthouse', probability: .1 },
{ value: 'prysm', probability: .2 },
{ value: 'rocketpool', probability: .5 }
] },
{ trait_type: "backpack", values: [
{ value: 'yes', probability: .9 },
{ value: 'no', probability: .1 }
] },
{ trait_type: "panel", values: [
{ value: 'yes', probability: .9 },
{ value: 'no', probability: .1 }
] },
{ trait_type: "background", values: [
{ value: 'planets', probability: .2 },
{ value: 'system', probability: .2 },
{ value: 'playful', probability: .1 },
{ value: 'moon', probability: .05 },
{ value: 'galaxy', probability: .2 },
{ value: 'chip', probability: .05 }
] },
{ trait_type: "background complexity", values: [
{ value: 1, probability: .05 },
{ value: 2, probability: .10 },
{ value: 3, probability: .10 },
{ value: 4, probability: .75 }
] }
]
exports.heavenlyBodies = [ "Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune", "Pluto", "the Moon", "the Sun" ]
exports.web2domain = 'https://rocketeer.fans'
exports.lorem = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'
{
trait_type: "helmet",
values: [
{ value: "classic", probability: 0.2 },
{ value: "racer", probability: 0.1 },
{ value: "punk", probability: 0.1 },
{ value: "knight", probability: 0.2 },
{ value: "geek", probability: 0.2 },
],
},
{
trait_type: "patch",
values: [
{ value: "nimbus", probability: 0.1 },
{ value: "teku", probability: 0.1 },
{ value: "lighthouse", probability: 0.1 },
{ value: "prysm", probability: 0.2 },
{ value: "rocketpool", probability: 0.5 },
],
},
{
trait_type: "backpack",
values: [
{ value: "yes", probability: 0.9 },
{ value: "no", probability: 0.1 },
],
},
{
trait_type: "panel",
values: [
{ value: "yes", probability: 0.9 },
{ value: "no", probability: 0.1 },
],
},
{
trait_type: "background",
values: [
{ value: "planets", probability: 0.2 },
{ value: "system", probability: 0.2 },
{ value: "playful", probability: 0.1 },
{ value: "moon", probability: 0.05 },
{ value: "galaxy", probability: 0.2 },
{ value: "chip", probability: 0.05 },
],
},
{
trait_type: "background complexity",
values: [
{ value: 1, probability: 0.05 },
{ value: 2, probability: 0.1 },
{ value: 3, probability: 0.1 },
{ value: 4, probability: 0.75 },
],
},
];
exports.heavenlyBodies = [
"Mercury",
"Venus",
"Earth",
"Mars",
"Jupiter",
"Saturn",
"Uranus",
"Neptune",
"Pluto",
"the Moon",
"the Sun",
];

View File

@ -1,78 +1,100 @@
const name = require( 'random-name' )
const { pickRandomArrayEntry, pickRandomAttributes, randomNumberBetween, globalAttributes, heavenlyBodies, web2domain, getColorName } = require( '../modules/helpers' )
const svgFromAttributes = require( './svg-generator' )
const name = require("random-name");
const {
pickRandomArrayEntry,
pickRandomAttributes,
randomNumberBetween,
globalAttributes,
heavenlyBodies,
getColorName,
} = require("../modules/helpers");
const svgFromAttributes = require("./svg-generator");
// ///////////////////////////////
// Rocketeer generator
// ///////////////////////////////
async function generateRocketeer( id, network='mainnet' ) {
async function generateRocketeer(id) {
// The base object of a new Rocketeer
const rocketeer = {
name: `${ name.first() } ${ name.middle() } ${ name.last() } of ${ id % 42 == 0 ? 'the Towel' : pickRandomArrayEntry( heavenlyBodies ) }`,
description: '',
name: `${name.first()} ${name.middle()} ${name.last()} of ${
id % 42 == 0 ? "the Towel" : pickRandomArrayEntry(heavenlyBodies)
}`,
description: "",
image: ``,
external_url: `https://viewer.rocketeer.fans/?rocketeer=${ id }` + ( network == 'mainnet' ? '' : '&testnet=true' ),
attributes: []
}
attributes: [],
};
// Generate randomized attributes
rocketeer.attributes = pickRandomAttributes( globalAttributes )
rocketeer.attributes = pickRandomAttributes(globalAttributes);
// Set birthday
rocketeer.attributes.push( {
"display_type": "date",
"trait_type": "birthday",
"value": Math.floor( Date.now() / 1000 )
} )
rocketeer.attributes.push({
display_type: "date",
trait_type: "birthday",
value: Math.floor(Date.now() / 1000),
});
// Special editions
const edition = { "trait_type": "edition", value: "regular" }
if( id <= 50 ) edition.value = 'genesis'
if( id >= ( 3475 - 166 ) ) edition.value = 'straggler'
if( id % 42 === 0 ) edition.value = 'hitchhiker'
if( ( id - 1 ) % 42 == 0 ) edition.value = 'generous'
rocketeer.attributes.push( edition )
const edition = { trait_type: "edition", value: "regular" };
if (id <= 50) edition.value = "genesis";
if (id >= 3475 - 166) edition.value = "straggler";
if (id % 42 === 0) edition.value = "hitchhiker";
if ((id - 1) % 42 == 0) edition.value = "generous";
rocketeer.attributes.push(edition);
// Create description
rocketeer.description = `${ rocketeer.name } is a proud member of the ${ rocketeer.attributes.find( ( { trait_type } ) => trait_type == 'patch' ).value } guild.`
rocketeer.description = `${rocketeer.name} is a proud member of the ${
rocketeer.attributes.find(({ trait_type }) => trait_type == "patch").value
} guild.`;
// Generate color attributes
rocketeer.attributes.push( {
"trait_type": "outfit color",
value: `rgb( ${ randomNumberBetween( 0, 255 ) }, ${ randomNumberBetween( 0, 255 ) }, ${ randomNumberBetween( 0, 255 ) } )`
} )
rocketeer.attributes.push( {
"trait_type": "outfit accent color",
value: `rgb( ${ randomNumberBetween( 0, 255 ) }, ${ randomNumberBetween( 0, 255 ) }, ${ randomNumberBetween( 0, 255 ) } )`
} )
rocketeer.attributes.push( {
"trait_type": "backpack color",
value: `rgb( ${ randomNumberBetween( 0, 255 ) }, ${ randomNumberBetween( 0, 255 ) }, ${ randomNumberBetween( 0, 255 ) } )`
} )
rocketeer.attributes.push( {
"trait_type": "visor color",
value: `rgb( ${ randomNumberBetween( 0, 255 ) }, ${ randomNumberBetween( 0, 255 ) }, ${ randomNumberBetween( 0, 255 ) } )`
} )
rocketeer.attributes.push({
trait_type: "outfit color",
value: `rgb( ${randomNumberBetween(0, 255)}, ${randomNumberBetween(
0,
255
)}, ${randomNumberBetween(0, 255)} )`,
});
rocketeer.attributes.push({
trait_type: "outfit accent color",
value: `rgb( ${randomNumberBetween(0, 255)}, ${randomNumberBetween(
0,
255
)}, ${randomNumberBetween(0, 255)} )`,
});
rocketeer.attributes.push({
trait_type: "backpack color",
value: `rgb( ${randomNumberBetween(0, 255)}, ${randomNumberBetween(
0,
255
)}, ${randomNumberBetween(0, 255)} )`,
});
rocketeer.attributes.push({
trait_type: "visor color",
value: `rgb( ${randomNumberBetween(0, 255)}, ${randomNumberBetween(
0,
255
)}, ${randomNumberBetween(0, 255)} )`,
});
// Generate, compile and upload image
rocketeer.image = await svgFromAttributes( rocketeer.attributes, `${ network }Rocketeers/${id}` )
rocketeer.image = await svgFromAttributes(
rocketeer.attributes,
`./output/${id}`
);
// Namify the attributes
rocketeer.attributes = rocketeer.attributes.map( attribute => {
if( !attribute.trait_type.includes( 'color' ) ) return attribute
rocketeer.attributes = rocketeer.attributes.map((attribute) => {
if (!attribute.trait_type.includes("color")) return attribute;
return {
...attribute,
value: getColorName( attribute.value )
}
value: getColorName(attribute.value),
};
});
} )
return rocketeer;
return rocketeer.image;
}
module.exports = {
web2domain,
generateRocketeer
}
generateRocketeer,
};

View File

@ -1,90 +1,112 @@
const masterPath = `${ __dirname }/../assets/master.svg`
const jsdom = require("jsdom")
const { JSDOM } = jsdom
const { promises: fs } = require( 'fs' )
const { getStorage } = require( 'firebase-admin/storage' )
// SVG to JPEG
const { convert } = require("convert-svg-to-jpeg")
// Existing file checker
const failIfFilesExist = async ( svg, jpeg, path ) => {
const [ [ svgExists ], [ jpegExists ] ] = await Promise.all( [ svg.exists(), jpeg.exists() ] )
if( svgExists || jpegExists ) throw new Error( `${ svgExists ? 'SVG' : '' } ${ jpegExists ? ' and JPEG' : '' } already present at ${ path }. This should never happen!` )
}
module.exports = async function svgFromAttributes( attributes=[], path='' ) {
const masterPath = `${__dirname}/../assets/master.svg`;
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
const fs = require("fs").promises;
module.exports = async function svgFromAttributes(attributes = [], path = "") {
// Validations
if( !path.length ) throw new Error( 'svgFromAttributes missing path' )
if( !attributes.length ) throw new Error( 'svgFromAttributes missing attributes' )
if (!path.length) throw new Error("svgFromAttributes missing path");
if (!attributes.length)
throw new Error("svgFromAttributes missing attributes");
// Create file references and check whether they already exist
const storage = getStorage()
const bucket = storage.bucket()
const svgFile = bucket.file( `${path}.svg` )
const rasterFile = bucket.file( `${path}.jpg` )
await failIfFilesExist( svgFile, rasterFile, path )
console.log("Checking attributes");
// Get properties
const { value: primary_color } = attributes.find( ( { trait_type } ) => trait_type == "outfit color" )
const { value: accent_color } = attributes.find( ( { trait_type } ) => trait_type == "outfit accent color" )
const { value: backpack_color } = attributes.find( ( { trait_type } ) => trait_type == "backpack color" )
const { value: visor_color } = attributes.find( ( { trait_type } ) => trait_type == "visor color" )
const { value: backpack } = attributes.find( ( { trait_type } ) => trait_type == "backpack" )
const { value: panel } = attributes.find( ( { trait_type } ) => trait_type == "panel" )
const { value: patch } = attributes.find( ( { trait_type } ) => trait_type == "patch" )
const { value: helmet } = attributes.find( ( { trait_type } ) => trait_type == "helmet" )
const { value: background } = attributes.find( ( { trait_type } ) => trait_type == "background" )
const { value: background_complexity } = attributes.find( ( { trait_type } ) => trait_type == "background complexity" )
const { value: primary_color } = attributes.find(
({ trait_type }) => trait_type == "outfit color"
);
const { value: accent_color } = attributes.find(
({ trait_type }) => trait_type == "outfit accent color"
);
const { value: backpack_color } = attributes.find(
({ trait_type }) => trait_type == "backpack color"
);
const { value: visor_color } = attributes.find(
({ trait_type }) => trait_type == "visor color"
);
const { value: backpack } = attributes.find(
({ trait_type }) => trait_type == "backpack"
);
const { value: panel } = attributes.find(
({ trait_type }) => trait_type == "panel"
);
const { value: patch } = attributes.find(
({ trait_type }) => trait_type == "patch"
);
const { value: helmet } = attributes.find(
({ trait_type }) => trait_type == "helmet"
);
const { value: background } = attributes.find(
({ trait_type }) => trait_type == "background"
);
const { value: background_complexity } = attributes.find(
({ trait_type }) => trait_type == "background complexity"
);
console.log("Reading master file");
// Generate DOM to work with
const svgString = await fs.readFile( masterPath, 'utf8' )
const { window: { document } } = new JSDOM( svgString )
const svgString = await fs.readFile(masterPath, { encoding: "utf8" });
const {
window: { document },
} = new JSDOM(svgString);
// ///////////////////////////////
// Attribute selection
// ///////////////////////////////
console.log("Removing unused attributes from master");
// Remove obsolete patches
const obsoletePatches = [ 'nimbus', 'teku', 'lighthouse', 'prysm', 'rocketpool' ].filter( p => p !== patch )
for ( let i = obsoletePatches.length - 1; i >= 0; i-- ) {
const element = document.querySelector( `#${ obsoletePatches[i] }` )
if( element ) element.remove()
else console.log( `Could not find #${ obsoletePatches[i] }` )
const obsoletePatches = [
"nimbus",
"teku",
"lighthouse",
"prysm",
"rocketpool",
].filter((p) => p !== patch);
for (let i = obsoletePatches.length - 1; i >= 0; i--) {
const element = document.querySelector(`#${obsoletePatches[i]}`);
if (element) element.remove();
else console.log(`Could not find #${obsoletePatches[i]}`);
}
// Remove obsolete hemets
const obsoleteHelmets = [ 'classic', 'racer', 'punk', 'knight', 'geek' ].filter( p => p !== helmet )
for ( let i = obsoleteHelmets.length - 1; i >= 0; i-- ) {
const element = document.querySelector( `#${ obsoleteHelmets[i] }` )
if( element ) element.remove()
else console.log( `Could not find #${ obsoleteHelmets[i] }` )
const obsoleteHelmets = ["classic", "racer", "punk", "knight", "geek"].filter(
(p) => p !== helmet
);
for (let i = obsoleteHelmets.length - 1; i >= 0; i--) {
const element = document.querySelector(`#${obsoleteHelmets[i]}`);
if (element) element.remove();
else console.log(`Could not find #${obsoleteHelmets[i]}`);
}
// Remove panel if need be
if( panel === 'no' ) {
const element = document.querySelector( `#panel` )
if( element ) element.remove()
else console.log( `Could not find #panel` )
if (panel === "no") {
const element = document.querySelector(`#panel`);
if (element) element.remove();
else console.log(`Could not find #panel`);
}
// Remove backpack if need be
if( backpack === 'no' ) {
const element = document.querySelector( `#backpack` )
if( element ) element.remove()
else console.log( 'Could not find #backpack' )
if (backpack === "no") {
const element = document.querySelector(`#backpack`);
if (element) element.remove();
else console.log("Could not find #backpack");
}
// Remove obsolete backgrounds
const obsoleteBackgrounds = [ 'planets', 'system', 'playful', 'moon', 'galaxy', 'chip' ].filter( p => p !== background )
for ( let i = obsoleteBackgrounds.length - 1; i >= 0; i-- ) {
const element = document.querySelector( `#${ obsoleteBackgrounds[i] }` )
if( element ) element.remove()
else console.log( `Could not find #${ obsoleteBackgrounds[i] }` )
const obsoleteBackgrounds = [
"planets",
"system",
"playful",
"moon",
"galaxy",
"chip",
].filter((p) => p !== background);
for (let i = obsoleteBackgrounds.length - 1; i >= 0; i--) {
const element = document.querySelector(`#${obsoleteBackgrounds[i]}`);
if (element) element.remove();
else console.log(`Could not find #${obsoleteBackgrounds[i]}`);
}
// ///////////////////////////////
@ -92,71 +114,54 @@ module.exports = async function svgFromAttributes( attributes=[], path='' ) {
// ///////////////////////////////
// In playful, keeping things is basic, removing them is cool
if( background === 'playful' ) {
const toRemove = background_complexity
for ( let i = 1; i <= toRemove; i++ ) {
const element = document.querySelector( `#playful-element-${ 5 - i }` )
if( element ) element.remove()
else console.log( `Could not find #playful-element-${ 5 - i }` )
if (background === "playful") {
const toRemove = background_complexity;
for (let i = 1; i <= toRemove; i++) {
const element = document.querySelector(`#playful-element-${5 - i}`);
if (element) element.remove();
else console.log(`Could not find #playful-element-${5 - i}`);
}
} else {
// In others, keeping is cool, and removing is less cool
// so higher rarity means less looping
const toRemove = 4 - background_complexity
for ( let i = 1; i <= toRemove; i++ ) {
const element = document.querySelector( `#${ background }-element-${ 5 - i }` )
if( element ) element.remove()
else console.log( `Could not find #${ background }-element-${ 5 - i }` )
const toRemove = 4 - background_complexity;
for (let i = 1; i <= toRemove; i++) {
const element = document.querySelector(`#${background}-element-${5 - i}`);
if (element) element.remove();
else console.log(`Could not find #${background}-element-${5 - i}`);
}
}
// ///////////////////////////////
// Color substitutions
// ///////////////////////////////
const defaultPrimary = /rgb\( ?252 ?, ?186 ?, ?157 ?\)/ig
const defaultVisor = /rgb\( ?71 ?, ?22 ?, ?127 ?\)/ig
const defaultAccent = /rgb\( ?243 ?, ?99 ?, ?113 ?\)/ig
const defaultBackpack = /rgb\( ?195 ?, ?178 ?, ?249 ?\)/ig
console.log("Substituting colours from master");
const defaultPrimary = /rgb\( ?252 ?, ?186 ?, ?157 ?\)/gi;
const defaultVisor = /rgb\( ?71 ?, ?22 ?, ?127 ?\)/gi;
const defaultAccent = /rgb\( ?243 ?, ?99 ?, ?113 ?\)/gi;
const defaultBackpack = /rgb\( ?195 ?, ?178 ?, ?249 ?\)/gi;
// Substitutions
const replace = ( from, to ) => {
const replaced = document.querySelector( 'svg' ).innerHTML.replace( from, to )
document.querySelector( 'svg' ).innerHTML = replaced
}
replace( defaultPrimary, primary_color )
replace( defaultAccent, accent_color )
replace( defaultVisor, visor_color )
replace( defaultBackpack, backpack_color )
const replace = (from, to) => {
const replaced = document.querySelector("svg").innerHTML.replace(from, to);
document.querySelector("svg").innerHTML = replaced;
};
replace(defaultPrimary, primary_color);
replace(defaultAccent, accent_color);
replace(defaultVisor, visor_color);
replace(defaultBackpack, backpack_color);
console.log("Baking SVG now...");
const bakedSvg = [
`<?xml version="1.0" encoding="UTF-8" standalone="no"?>`,
`<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">`,
document.querySelector( 'svg' ).outerHTML
].join( '' )
document.querySelector("svg").outerHTML,
].join("");
const bakedRaster = await convert( bakedSvg, {
quality: 80,
// height: 500,
// width: 500
} )
console.log("Writing to `" + path + ".svg`...");
await fs.writeFile(`${path}.svg`, bakedSvg);
// Double check that files do not yet exist (in case of weird race condition)
await failIfFilesExist( svgFile, rasterFile, path )
// Save files
await svgFile.save( bakedSvg )
await rasterFile.save( bakedRaster )
// Make file public
await svgFile.makePublic( )
await rasterFile.makePublic( )
// Return public url
return rasterFile.publicUrl()
}
return "Paths: '" + `${path}.svg` + "'";
};

539
package-lock.json generated
View File

@ -15,6 +15,9 @@
"express": "^4.18.1",
"jsdom": "^18.1.1",
"random-name": "^0.1.2"
},
"devDependencies": {
"nodemon": "^2.0.20"
}
},
"node_modules/@tootallnate/once": {
@ -30,6 +33,12 @@
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
"integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA=="
},
"node_modules/abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
"dev": true
},
"node_modules/accepts": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
@ -137,6 +146,19 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
},
"node_modules/anymatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
"dev": true,
"dependencies": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
@ -157,6 +179,15 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"node_modules/binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/body-parser": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz",
@ -189,6 +220,18 @@
"concat-map": "0.0.1"
}
},
"node_modules/braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"dev": true,
"dependencies": {
"fill-range": "^7.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/browser-process-hrtime": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz",
@ -240,6 +283,33 @@
"node": ">=4"
}
},
"node_modules/chokidar": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
],
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.6.0"
},
"engines": {
"node": ">= 8.10.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/chroma-js": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-1.4.1.tgz",
@ -743,6 +813,18 @@
"node": ">=4"
}
},
"node_modules/fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"dev": true,
"dependencies": {
"to-regex-range": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/finalhandler": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
@ -794,6 +876,20 @@
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
},
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
@ -839,6 +935,18 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"dependencies": {
"is-glob": "^4.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
@ -973,6 +1081,12 @@
"node": ">=0.10.0"
}
},
"node_modules/ignore-by-default": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
"integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
"dev": true
},
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@ -1000,6 +1114,48 @@
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
},
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dev": true,
"dependencies": {
"binary-extensions": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"dependencies": {
"is-extglob": "^2.1.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/is-potential-custom-element-name": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
@ -1173,6 +1329,73 @@
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
"integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
},
"node_modules/nodemon": {
"version": "2.0.20",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.20.tgz",
"integrity": "sha512-Km2mWHKKY5GzRg6i1j5OxOHQtuvVsgskLfigG25yTtbyfRGn/GNvIbRyOf1PSCKJ2aT/58TiuUsuOU5UToVViw==",
"dev": true,
"dependencies": {
"chokidar": "^3.5.2",
"debug": "^3.2.7",
"ignore-by-default": "^1.0.1",
"minimatch": "^3.1.2",
"pstree.remy": "^1.1.8",
"semver": "^5.7.1",
"simple-update-notifier": "^1.0.7",
"supports-color": "^5.5.0",
"touch": "^3.1.0",
"undefsafe": "^2.0.5"
},
"bin": {
"nodemon": "bin/nodemon.js"
},
"engines": {
"node": ">=8.10.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/nodemon"
}
},
"node_modules/nodemon/node_modules/debug": {
"version": "3.2.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
"dev": true,
"dependencies": {
"ms": "^2.1.1"
}
},
"node_modules/nodemon/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true
},
"node_modules/nopt": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
"integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==",
"dev": true,
"dependencies": {
"abbrev": "1"
},
"bin": {
"nopt": "bin/nopt.js"
},
"engines": {
"node": "*"
}
},
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/nwsapi": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz",
@ -1260,6 +1483,18 @@
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
"integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="
},
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true,
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/pollock": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/pollock/-/pollock-0.2.1.tgz",
@ -1308,6 +1543,12 @@
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
"integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag=="
},
"node_modules/pstree.remy": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
"integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
"dev": true
},
"node_modules/punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
@ -1461,6 +1702,18 @@
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dev": true,
"dependencies": {
"picomatch": "^2.2.1"
},
"engines": {
"node": ">=8.10.0"
}
},
"node_modules/requires-port": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
@ -1512,6 +1765,15 @@
"node": ">=10"
}
},
"node_modules/semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true,
"bin": {
"semver": "bin/semver"
}
},
"node_modules/send": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
@ -1591,6 +1853,27 @@
"is-arrayish": "^0.3.1"
}
},
"node_modules/simple-update-notifier": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.0.7.tgz",
"integrity": "sha512-BBKgR84BJQJm6WjWFMHgLVuo61FBDSj1z/xSFUIozqO6wO7ii0JxCqlIud7Enr/+LhlbNI0whErq96P2qHNWew==",
"dev": true,
"dependencies": {
"semver": "~7.0.0"
},
"engines": {
"node": ">=8.10.0"
}
},
"node_modules/simple-update-notifier/node_modules/semver": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz",
"integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==",
"dev": true,
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@ -1648,6 +1931,18 @@
"node": ">=0.6.0"
}
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"dependencies": {
"is-number": "^7.0.0"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/toidentifier": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
@ -1656,6 +1951,18 @@
"node": ">=0.6"
}
},
"node_modules/touch": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
"integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==",
"dev": true,
"dependencies": {
"nopt": "~1.0.10"
},
"bin": {
"nodetouch": "bin/nodetouch.js"
}
},
"node_modules/tough-cookie": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz",
@ -1714,6 +2021,12 @@
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
},
"node_modules/undefsafe": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
"dev": true
},
"node_modules/universalify": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
@ -1896,6 +2209,12 @@
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
"integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA=="
},
"abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
"dev": true
},
"accepts": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
@ -1977,6 +2296,16 @@
}
}
},
"anymatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
"dev": true,
"requires": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
}
},
"array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
@ -1997,6 +2326,12 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"dev": true
},
"body-parser": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz",
@ -2025,6 +2360,15 @@
"concat-map": "0.0.1"
}
},
"braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"dev": true,
"requires": {
"fill-range": "^7.0.1"
}
},
"browser-process-hrtime": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz",
@ -2064,6 +2408,22 @@
"supports-color": "^5.3.0"
}
},
"chokidar": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
"dev": true,
"requires": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
"fsevents": "~2.3.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.6.0"
}
},
"chroma-js": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-1.4.1.tgz",
@ -2474,6 +2834,15 @@
"resolved": "https://registry.npmjs.org/file-url/-/file-url-2.0.2.tgz",
"integrity": "sha512-x3989K8a1jM6vulMigE8VngH7C5nci0Ks5d9kVjUXmNF28gmiZUNujk5HjwaS8dAzN2QmUfX56riJKgN00dNRw=="
},
"fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"dev": true,
"requires": {
"to-regex-range": "^5.0.1"
}
},
"finalhandler": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
@ -2513,6 +2882,13 @@
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
},
"fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"optional": true
},
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
@ -2546,6 +2922,15 @@
"path-is-absolute": "^1.0.0"
}
},
"glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"requires": {
"is-glob": "^4.0.1"
}
},
"has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
@ -2641,6 +3026,12 @@
"safer-buffer": ">= 2.1.2 < 3"
}
},
"ignore-by-default": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
"integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
"dev": true
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@ -2665,6 +3056,36 @@
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
},
"is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dev": true,
"requires": {
"binary-extensions": "^2.0.0"
}
},
"is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true
},
"is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"requires": {
"is-extglob": "^2.1.1"
}
},
"is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true
},
"is-potential-custom-element-name": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
@ -2797,6 +3218,56 @@
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
"integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
},
"nodemon": {
"version": "2.0.20",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.20.tgz",
"integrity": "sha512-Km2mWHKKY5GzRg6i1j5OxOHQtuvVsgskLfigG25yTtbyfRGn/GNvIbRyOf1PSCKJ2aT/58TiuUsuOU5UToVViw==",
"dev": true,
"requires": {
"chokidar": "^3.5.2",
"debug": "^3.2.7",
"ignore-by-default": "^1.0.1",
"minimatch": "^3.1.2",
"pstree.remy": "^1.1.8",
"semver": "^5.7.1",
"simple-update-notifier": "^1.0.7",
"supports-color": "^5.5.0",
"touch": "^3.1.0",
"undefsafe": "^2.0.5"
},
"dependencies": {
"debug": {
"version": "3.2.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
"dev": true,
"requires": {
"ms": "^2.1.1"
}
},
"ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true
}
}
},
"nopt": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
"integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==",
"dev": true,
"requires": {
"abbrev": "1"
}
},
"normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true
},
"nwsapi": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz",
@ -2866,6 +3337,12 @@
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
"integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="
},
"picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true
},
"pollock": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/pollock/-/pollock-0.2.1.tgz",
@ -2905,6 +3382,12 @@
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
"integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag=="
},
"pstree.remy": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
"integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
"dev": true
},
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
@ -3030,6 +3513,15 @@
}
}
},
"readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dev": true,
"requires": {
"picomatch": "^2.2.1"
}
},
"requires-port": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
@ -3061,6 +3553,12 @@
"xmlchars": "^2.2.0"
}
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
},
"send": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
@ -3127,6 +3625,23 @@
"is-arrayish": "^0.3.1"
}
},
"simple-update-notifier": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.0.7.tgz",
"integrity": "sha512-BBKgR84BJQJm6WjWFMHgLVuo61FBDSj1z/xSFUIozqO6wO7ii0JxCqlIud7Enr/+LhlbNI0whErq96P2qHNWew==",
"dev": true,
"requires": {
"semver": "~7.0.0"
},
"dependencies": {
"semver": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz",
"integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==",
"dev": true
}
}
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@ -3174,11 +3689,29 @@
"os-tmpdir": "~1.0.2"
}
},
"to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"requires": {
"is-number": "^7.0.0"
}
},
"toidentifier": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="
},
"touch": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
"integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==",
"dev": true,
"requires": {
"nopt": "~1.0.10"
}
},
"tough-cookie": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz",
@ -3225,6 +3758,12 @@
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
},
"undefsafe": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
"dev": true
},
"universalify": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",

View File

@ -14,10 +14,12 @@
"dependencies": {
"color": "^4.2.3",
"color-namer": "^1.4.0",
"convert-svg-to-jpeg": "^0.5.0",
"esm": "^3.2.25",
"express": "^4.18.1",
"jsdom": "^18.1.1",
"random-name": "^0.1.2"
},
"devDependencies": {
"nodemon": "^2.0.20"
}
}