convert server files to esm

- update deps
- remove midwest-service-errors
This commit is contained in:
Linus Miller 2020-09-03 21:54:23 +02:00
parent 6048991b2d
commit ed79c5b7a6
24 changed files with 253 additions and 282 deletions

View File

@ -2,18 +2,19 @@
"name": "journey", "name": "journey",
"version": "0.0.1", "version": "0.0.1",
"description": "", "description": "",
"main": "server/server.js", "main": "server/index.mjs",
"scripts": { "scripts": {
"format": "prettier --write .", "format": "prettier --write .",
"lint": "eslint --ext .js --ext .jsx --ext .mjs .", "lint": "eslint --ext .js --ext .jsx --ext .mjs .",
"start": "node server/server.js", "start": "node server/index.mjs",
"start:dev": "nodemon server/server.js", "start:dev": "nodemon server/index.mjs",
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"webpack": "webpack" "webpack": "webpack"
}, },
"author": "Linus Miller <lohfu@lohfu.io> (https://lohfu.io/)", "author": "Linus Miller <lohfu@lohfu.io> (https://lohfu.io/)",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@bmp/console": "^0.1.0",
"@bmp/pg": "^0.0.6", "@bmp/pg": "^0.0.6",
"body-parser": "^1.16.1", "body-parser": "^1.16.1",
"chalk": "^4.1.0", "chalk": "^4.1.0",
@ -23,10 +24,10 @@
"easy-tz": "^0.1.1", "easy-tz": "^0.1.1",
"express": "^4.14.1", "express": "^4.14.1",
"express-session": "^1.15.1", "express-session": "^1.15.1",
"highlight-stack": "^0.2.1",
"lodash": "^4.17.4", "lodash": "^4.17.4",
"lowline": "^0.3.0", "lowline": "^0.3.0",
"midwest": "^0.4.2", "midwest": "^0.5.2",
"midwest-service-errors": "^0.2.0",
"morgan": "^1.8.1", "morgan": "^1.8.1",
"pg": "^8.3.3", "pg": "^8.3.3",
"preact": "^10.4.8", "preact": "^10.4.8",

View File

@ -1,7 +0,0 @@
'use strict'
const p = require('path')
module.exports = {
static: p.join(process.cwd(), 'dist'),
}

6
server/config/dir.mjs Normal file
View File

@ -0,0 +1,6 @@
import p from 'path'
export default {
dist: p.join(process.cwd(), 'dist'),
static: p.join(process.cwd(), 'dist'),
}

View File

@ -1,4 +1,4 @@
'use strict' global.PWD = process.env.NODE_PWD || process.cwd()
function readFromEnv(columns, defaults) { function readFromEnv(columns, defaults) {
const missing = [] const missing = []
@ -25,6 +25,6 @@ const defaults = {
POSTGRES_HOST: 'localhost', POSTGRES_HOST: 'localhost',
} }
module.exports = { export default {
...readFromEnv(['NODE_ENV', 'POSTGRES_HOST'], defaults), ...readFromEnv(['NODE_ENV', 'POSTGRES_HOST'], defaults),
} }

View File

@ -1,56 +0,0 @@
'use strict'
const _ = require('lodash')
const errorTemplate = require('../templates/Error')
const env = require('./env')
const defaults = {
post: (req, res, next) => {
res.template = errorTemplate
next()
},
mystify: {
properties: ['errors', 'message', 'name', 'status', 'statusText'],
},
log: {
// if database = true there has to be a mongoose model name ErrorModel
ignore: [],
},
}
const store = require('midwest-service-errors/stores/postgres')
module.exports = _.merge(
defaults,
{
development: {
log: {
store,
console: true,
},
},
testing: {
log: {
store: false,
console: false,
},
},
staging: {
log: {
store,
console: false,
},
},
production: {
log: {
store,
console: false,
},
},
}[env.NODE_ENV],
)

View File

@ -0,0 +1,33 @@
import _ from 'lodash'
import errorTemplate from '../templates/Error.mjs'
import env from './env.mjs'
const defaults = {
console: true,
post: (req, res, next) => {
res.template = errorTemplate
next()
},
mystify: {
properties: ['errors', 'message', 'name', 'status', 'statusText'],
},
log: {
// if database = true there has to be a mongoose model name ErrorModel
ignore: [],
},
}
export default _.merge(
defaults,
{
development: {
log: {
console: true,
},
},
}[env.NODE_ENV],
)

View File

@ -1,11 +0,0 @@
'use strict'
module.exports = {
dir: require('./dir'),
env: require('./env'),
errorHandler: require('./error_handler'),
port: require('./port'),
postgres: require('./postgres'),
shim: require('./shim'),
site: require('./site'),
}

17
server/config/index.mjs Normal file
View File

@ -0,0 +1,17 @@
import dir from './dir.mjs'
import env from './env.mjs'
import errorHandler from './error_handler.mjs'
import port from './port.mjs'
import postgres from './postgres.mjs'
import shim from './shim.mjs'
import site from './site.mjs'
export default {
dir,
env,
errorHandler,
port,
postgres,
shim,
site,
}

View File

@ -1,10 +1,8 @@
'use strict' import env from './env.mjs'
const env = require('./env')
const basePort = 3060 const basePort = 3060
module.exports = { export default {
development: basePort, development: basePort,
testing: basePort + 1, testing: basePort + 1,
staging: basePort + 2, staging: basePort + 2,

View File

@ -1,8 +1,6 @@
'use strict' import env from './env.mjs'
const env = require('./env') export default {
module.exports = {
database: 'journey', database: 'journey',
user: 'journey', user: 'journey',
password: 'journey', password: 'journey',

View File

@ -1,6 +1,4 @@
'use strict' export default {
module.exports = {
'https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.5.9/es5-shim.min.js': [ 'https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.5.9/es5-shim.min.js': [
'chrome <= 12', 'chrome <= 12',
'firefox <= 20', 'firefox <= 20',

View File

@ -1,7 +1,6 @@
'use strict' import _ from 'lodash'
import env from './env.mjs'
const _ = require('lodash') import port from './port.mjs'
const env = require('./env')
const domain = 'journey.bitmill.io' const domain = 'journey.bitmill.io'
@ -23,17 +22,17 @@ const defaults = {
}, },
} }
module.exports = _.merge( export default _.merge(
defaults, defaults,
{ {
development: { development: {
hostname: 'localhost', hostname: 'localhost',
port: process.env.EXTERNAL_PORT || process.env.PORT || require('./port'), port: process.env.EXTERNAL_PORT || process.env.PORT || port,
}, },
testing: { testing: {
hostname: 'localhost', hostname: 'localhost',
port: process.env.PORT || require('./port'), port: process.env.PORT || port,
}, },
staging: { staging: {

34
server/console.mjs Normal file
View File

@ -0,0 +1,34 @@
import chalk from 'chalk'
import Console from '@bmp/console'
import hs from 'highlight-stack'
const highlightStack = hs.default
// make error output stack pretty
process.on('uncaughtException', (err) => {
console.error(chalk.red('UNCAUGHT EXCEPTION'))
if (err.stack) {
console.error(highlightStack(err.stack))
} else {
console.error(err)
}
process.exit(1)
})
process.on('unhandledRejection', (err) => {
console.error(chalk.red('UNHANDLED REJECTION'))
if (err.stack) {
console.error(highlightStack(err.stack))
} else {
console.error(err)
}
process.exit(1)
})
if (process.env.NODE_ENV === 'development') {
Console({ log: true, error: true, dir: true })
}

View File

@ -1,6 +0,0 @@
'use strict'
const factory = require('@bmp/pg')
const conf = require('./config/postgres')
module.exports = factory(conf)

4
server/db.mjs Normal file
View File

@ -0,0 +1,4 @@
import factory from '@bmp/pg'
import conf from './config/postgres.mjs'
module.exports = factory(conf)

11
server/index.mjs Normal file
View File

@ -0,0 +1,11 @@
import chalk from 'chalk'
import server from './server.mjs'
import config from './config/index.mjs'
export default server.listen(config.port, () => {
console.info(
`[${chalk.cyan('INIT')}] HTTP Server listening on port ${chalk.magenta(config.port)} (${chalk.yellow(
config.env.NODE_ENV,
)})`,
)
})

View File

@ -1,13 +1,12 @@
'use strict' import format from 'easy-tz/cjs/format.js'
const format = require('easy-tz/cjs/format') import request from 'superagent'
import express from 'express'
const request = require('superagent')
// import get from 'get-value'; // import get from 'get-value';
const _ = require('lodash') import _ from 'lodash'
const xml2json = require('xml2json') import xml2json from 'xml2json'
// const to = 'Lund Mellanvångsvägen'; // const to = 'Lund Mellanvångsvägen';
// const from = 'Malmö Triangeln'; // const from = 'Malmö Triangeln';
@ -45,7 +44,7 @@ const types = {
STOP_AREA: 0, STOP_AREA: 0,
} }
const router = new (require('express').Router)() const router = new express.Router()
function formatStation(json) { function formatStation(json) {
return `${encodeURIComponent(json.name)}|${json.id}|${types[json.type]}` return `${encodeURIComponent(json.name)}|${json.id}|${types[json.type]}`
@ -56,7 +55,6 @@ function formatTime(date) {
return format(null, 'YYYY-MM-DD HH:mm', date) return format(null, 'YYYY-MM-DD HH:mm', date)
} }
console.log(formatTime())
function formatLink(link) {} function formatLink(link) {}
@ -64,8 +62,6 @@ function formatResult(result) {
return _.omit(result, 'Prices') return _.omit(result, 'Prices')
} }
console.log(new Date('2017-02-24T17:16:00'.split('T').join(' ')).toLocaleString())
const journeysPath = ['soap:Envelope', 'soap:Body', 'GetJourneyResponse', 'GetJourneyResult', 'Journeys', 'Journey'] const journeysPath = ['soap:Envelope', 'soap:Body', 'GetJourneyResponse', 'GetJourneyResult', 'Journeys', 'Journey']
// http://www.labs.skanetrafiken.se/v2.2/resultspage.asp?cmdaction=next&selPointFr=malm%F6%20C|80000|0&selPointTo=landskrona|82000|0&LastStart=2017-02-23%2016:38 // http://www.labs.skanetrafiken.se/v2.2/resultspage.asp?cmdaction=next&selPointFr=malm%F6%20C|80000|0&selPointTo=landskrona|82000|0&LastStart=2017-02-23%2016:38
router.post('/trip', (req, res, next) => { router.post('/trip', (req, res, next) => {
@ -84,4 +80,4 @@ router.post('/trip', (req, res, next) => {
}) })
}) })
module.exports = router export default router

View File

@ -1,19 +0,0 @@
'use strict'
const masterTemplate = require('../templates/master')
const router = new (require('express').Router)()
const mw = {
shim: require('midwest/factories/shim')(require('../config/shim')),
}
router.get('/', mw.shim, (req, res, next) => {
res.preventFlatten = true
res.template = masterTemplate
next()
})
module.exports = router

20
server/routers/index.mjs Normal file
View File

@ -0,0 +1,20 @@
import express from 'express'
import Shim from 'midwest/factories/shim.js'
import masterTemplate from '../templates/master.mjs'
import config from '../config/index.mjs'
const router = new express.Router()
const mw = {
shim: Shim(config.shim),
}
router.get('/', mw.shim, (req, res, next) => {
res.preventFlatten = true
res.template = masterTemplate
next()
})
export default router

View File

@ -1,113 +0,0 @@
'use strict'
/*
* The main file that sets up the Express instance and node
*
* @module server/server
* @type {Express instance}
*/
global.PWD = process.env.NODE_PWD || process.cwd()
const config = require('./config')
// modules > native
const p = require('path')
if (config.env.NODE_ENV === 'development') {
// output filename in console log and colour console.dir
require('midwest/util/console')
// needed so symlinked modules get access to main projects node_modules/
require('app-module-path').addPath(p.join(process.cwd(), 'node_modules'))
}
// modules > 3rd party
const _ = require('lodash')
const chalk = require('chalk')
const express = require('express')
// modules > express middlewares
const bodyParser = require('body-parser')
// modules > midwest
const colorizeStack = require('midwest/util/colorize-stack')
// make error output stack pretty
process.on('uncaughtException', (err) => {
console.error(chalk.red('UNCAUGHT EXCEPTION'))
if (err.stack) {
console.error(colorizeStack(err.stack))
} else {
console.error(err)
}
process.exit(1)
})
const prewares = [
express.static(config.dir.static, config.env.NODE_ENV === 'production' ? { maxAge: '1 year' } : null),
bodyParser.json(),
bodyParser.urlencoded({ extended: true }),
]
if (config.env.NODE_ENV === 'development') {
// only log requests to console in development mode
prewares.unshift(require('morgan')('dev'))
// prewares.push(require('midwest-module-membership/passport/automatic-login'));
}
const postwares = [
require('midwest/middleware/ensure-found'),
// transform and log error
require('midwest/factories/error-handler')(config.errorHandler),
// respond
require('midwest/middleware/responder'),
]
const server = express()
// get IP & whatnot from nginx proxy
server.set('trust proxy', true)
_.extend(server.locals, {
site: require('./config/site'),
})
// override default response render method for
// more convenient use with marko
server.response.render = function (template) {
const locals = Object.assign({ query: this.req.query }, this.app.locals, this.locals)
this.send(template(locals))
}
try {
server.locals.js = require(p.join(process.cwd(), 'public/js.json'))
} catch (e) {}
try {
server.locals.css = require(p.join(process.cwd(), 'public/css.json'))
} catch (e) {}
// load prewares
server.use(...prewares)
// mount routers
server.use(require('./routers/index'))
server.use('/api', require('./routers/api'))
// server.use('/auth', require('midwest-module-membership/passport/router'));
// load postwares
server.use(...postwares)
// Only start Express server when it is the main module (ie not required by test)
if (require.main === module) {
server.http = server.listen(config.port, () => {
console.info(
`[${chalk.cyan('INIT')}] HTTP Server listening on port ${chalk.magenta(config.port)} (${chalk.yellow(
config.env.NODE_ENV,
)})`,
)
})
}
module.exports = server

67
server/server.mjs Normal file
View File

@ -0,0 +1,67 @@
import './console.mjs'
import config from './config/index.mjs'
// modules > native
import p from 'path'
// modules > 3rd party
import _ from 'lodash'
import chalk from 'chalk'
import express from 'express'
// modules > express middlewares
import bodyParser from 'body-parser'
// modules > midwest
import ensureFound from 'midwest/middleware/ensure-found.js'
import errorHandler from 'midwest/factories/error-handler.js'
import responder from 'midwest/middleware/responder.js'
import Morgan from 'morgan'
import apiRouter from './routers/api.mjs'
import publicRouter from './routers/index.mjs'
const server = express()
// get IP & whatnot from nginx proxy
server.set('trust proxy', true)
_.extend(server.locals, {
site: config.site,
})
// override default response render method for
// more convenient use with marko
server.response.render = function (_master, template) {
const locals = Object.assign({ query: this.req.query }, this.app.locals, this.locals)
this.send(template(locals))
}
if (config.env.NODE_ENV === 'development') {
// only log requests to console in development mode
server.use(Morgan('dev'))
}
server.use([
express.static(config.dir.dist, config.env.NODE_ENV === 'production' ? { maxAge: '1 year' } : null),
express.static(config.dir.static, config.env.NODE_ENV === 'production' ? { maxAge: '1 year' } : null),
bodyParser.json(),
bodyParser.urlencoded({ extended: true }),
])
// mount routers
server.use(publicRouter)
server.use('/api', apiRouter)
server.use([
ensureFound,
// transform and log error
errorHandler(config.errorHandler),
// respond
responder,
])
// Only start Express server when it is the main module (ie not required by test)
export default server

View File

@ -1,6 +1,4 @@
'use strict' export default ({ error = {}, regions = {} }) => `<section className="error page">
module.exports = ({ error = {}, regions = {} }) => `<section className="error page">
<h1>Oops!</h1> <h1>Oops!</h1>
<p>Ett problem har tyvärr uppstått.</p> <p>Ett problem har tyvärr uppstått.</p>
<h2>${error.status} ${error.statusText}</h2> <h2>${error.status} ${error.statusText}</h2>

View File

@ -1,15 +1,4 @@
'use strict' export default ({ articleUrl, protocol, hostname, websocketsPort, INITIAL_STATE, js, css, cssFile }) => `<!doctype html>
module.exports = ({
articleUrl,
protocol,
hostname,
websocketsPort,
INITIAL_STATE,
js,
css,
cssFile,
}) => `<!doctype html>
<html> <html>
<head> <head>
<title>Journey</title> <title>Journey</title>

View File

@ -828,6 +828,21 @@
lodash "^4.17.19" lodash "^4.17.19"
to-fast-properties "^2.0.0" to-fast-properties "^2.0.0"
"@bmp/console@^0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@bmp/console/-/console-0.1.0.tgz#56a47b720e54cd1bde6cfe36d19bc55f2d1ebf9d"
integrity sha512-RMSHV/hC1DoZ7aZzQBYyszibEjCh0pvFwUZMqrUkHLtTCh4hF7EK4h+RkfkgGPzvrEeUcJB597MkaNS8/aRkmA==
dependencies:
"@bmp/highlight-stack" "^0.1.2"
chalk "^2.4.1"
"@bmp/highlight-stack@^0.1.1", "@bmp/highlight-stack@^0.1.2":
version "0.1.2"
resolved "https://registry.yarnpkg.com/@bmp/highlight-stack/-/highlight-stack-0.1.2.tgz#ddc31316f7b08529014878d6e58d583f686d984f"
integrity sha512-NVMtYxthtvJ/WJaVFzGtdU5gBXRReQ6OSeKAdm0U9Hnh4citjuKtUXUVZoCKfAAx8Aq4l6gqnuORL53jZAXnaw==
dependencies:
chalk "^2.4.1"
"@bmp/pg@^0.0.6": "@bmp/pg@^0.0.6":
version "0.0.6" version "0.0.6"
resolved "https://registry.yarnpkg.com/@bmp/pg/-/pg-0.0.6.tgz#bd99845f0904971699f6621dc04d379f943c928c" resolved "https://registry.yarnpkg.com/@bmp/pg/-/pg-0.0.6.tgz#bd99845f0904971699f6621dc04d379f943c928c"
@ -2381,7 +2396,7 @@ cyclist@^1.0.1:
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=
debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9: debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9:
version "2.6.9" version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
@ -3117,7 +3132,7 @@ express-session@^1.15.1:
safe-buffer "5.2.0" safe-buffer "5.2.0"
uid-safe "~2.1.5" uid-safe "~2.1.5"
express@^4.14.0, express@^4.14.1, express@^4.17.1: express@^4.14.1, express@^4.16.4, express@^4.17.1:
version "4.17.1" version "4.17.1"
resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134"
integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==
@ -3659,6 +3674,13 @@ hex-color-regex@^1.1.0:
resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e"
integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==
highlight-stack@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/highlight-stack/-/highlight-stack-0.2.1.tgz#478e5197e6181f738899c4a359ff3af46e7dcae3"
integrity sha512-dL4mQWJQv6Cd6SequySUNJBQat9VMwSVChxw8q8AEgBRh8SbUiXDG27RRPgvWyniGof6wkPQcLrwwyhmF4+ayw==
dependencies:
chalk "^2.4.1"
hmac-drbg@^1.0.0: hmac-drbg@^1.0.0:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
@ -4645,24 +4667,16 @@ micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4:
snapdragon "^0.8.1" snapdragon "^0.8.1"
to-regex "^3.0.2" to-regex "^3.0.2"
midwest-service-errors@^0.2.0: midwest@^0.5.2:
version "0.2.0" version "0.5.2"
resolved "https://registry.yarnpkg.com/midwest-service-errors/-/midwest-service-errors-0.2.0.tgz#83f426b180a5d30934038232de83598d5b0006f5" resolved "https://registry.yarnpkg.com/midwest/-/midwest-0.5.2.tgz#c4ec3a7a38abd833c8226b96a328fd93066e17c9"
integrity sha1-g/QmsYCl0wk0A4Iy3oNZjVsABvU= integrity sha512-gJ1wbw5iWhLOBXNvQIeuNHCZ06srGzFPh7oc9XSsvh0xpTSWNnZuQYfQ30WIb+CuFDZjDrR7CmJ79J4BLI2D/g==
dependencies: dependencies:
express "^4.14.0" "@bmp/highlight-stack" "^0.1.1"
lodash "^4.17.4" chalk "^2.4.1"
midwest "^0.4.0" debug "^4.1.0"
express "^4.16.4"
midwest@^0.4.0, midwest@^0.4.2: lodash "^4.17.11"
version "0.4.2"
resolved "https://registry.yarnpkg.com/midwest/-/midwest-0.4.2.tgz#2666ab1f9b1c94f38fc8d375c4fd5f93550cd6fa"
integrity sha1-JmarH5sclPOPyNN1xP1fk1UM1vo=
dependencies:
chalk "^1.1.3"
debug "^2.6.0"
express "^4.14.0"
lodash "^4.17.4"
miller-rabin@^4.0.0: miller-rabin@^4.0.0:
version "4.0.1" version "4.0.1"