async-neocities
Advanced tools
Comparing version 2.1.6 to 3.0.3
@@ -10,6 +10,38 @@ # Changelog | ||
## [v2.1.6](https://github.com/bcomnes/async-neocities/compare/v2.1.5...v2.1.6) | ||
## [v3.0.3](https://github.com/bcomnes/async-neocities/compare/v3.0.2...v3.0.3) | ||
### Commits | ||
- Typo [`ad10451`](https://github.com/bcomnes/async-neocities/commit/ad104512222e442a99a86b42adc2a82f2ec27acd) | ||
- More non-critical docs tweaks [`b350ef5`](https://github.com/bcomnes/async-neocities/commit/b350ef53dc8b7c5dfb46dbc754832416907c3a61) | ||
## [v3.0.2](https://github.com/bcomnes/async-neocities/compare/v3.0.1...v3.0.2) - 2024-01-15 | ||
### Commits | ||
- Improve docs [`527990d`](https://github.com/bcomnes/async-neocities/commit/527990d25e25a2570fa9663e4c19fa24843932dc) | ||
## [v3.0.1](https://github.com/bcomnes/async-neocities/compare/v3.0.0...v3.0.1) - 2024-01-15 | ||
### Commits | ||
- Fix engines range [`ce08ada`](https://github.com/bcomnes/async-neocities/commit/ce08ada4c78f975954c587af1cc5442cef37ca3f) | ||
## [v3.0.0](https://github.com/bcomnes/async-neocities/compare/v2.1.6...v3.0.0) - 2024-01-15 | ||
### Merged | ||
- Add command line interface and convert to ESM [`#81`](https://github.com/bcomnes/async-neocities/pull/81) | ||
- build(deps): bump actions/setup-node from 3 to 4 [`#79`](https://github.com/bcomnes/async-neocities/pull/79) | ||
### Commits | ||
- Convert to esm [`032eb2c`](https://github.com/bcomnes/async-neocities/commit/032eb2cf9f345c48667657cc8444c07ba381e16f) | ||
- Add a bin for local deployments [`7f2d322`](https://github.com/bcomnes/async-neocities/commit/7f2d32229ca5b115c9e000363c1e7503334a6a16) | ||
- Switch to node test runner [`a9bd295`](https://github.com/bcomnes/async-neocities/commit/a9bd295049914a7acb55dd92e2fdd49202928cd7) | ||
## [v2.1.6](https://github.com/bcomnes/async-neocities/compare/v2.1.5...v2.1.6) - 2023-10-22 | ||
### Merged | ||
- build(deps): bump async-folder-walker from 2.2.1 to 3.0.1 [`#78`](https://github.com/bcomnes/async-neocities/pull/78) | ||
@@ -16,0 +48,0 @@ - build(deps-dev): bump tap from 16.3.8 to 18.1.5 [`#77`](https://github.com/bcomnes/async-neocities/pull/77) |
89
index.js
@@ -1,17 +0,20 @@ | ||
const { handleResponse } = require('fetch-errors') | ||
const { createReadStream } = require('fs') | ||
const afw = require('async-folder-walker') | ||
const assert = require('nanoassert') | ||
const fetch = require('node-fetch') | ||
const { URL } = require('url') | ||
const qs = require('querystring') | ||
const os = require('os') | ||
const { ErrorWithCause } = require('pony-cause') | ||
// Replace require with import statements | ||
import { request, fetch } from 'undici' | ||
// @ts-ignore | ||
import { handleResponse } from 'fetch-errors' | ||
import { createReadStream } from 'fs' | ||
import afw from 'async-folder-walker' | ||
// @ts-ignore | ||
import assert from 'webassert' | ||
import { URL } from 'url' | ||
import qs from 'querystring' | ||
import os from 'os' | ||
const { neocitiesLocalDiff } = require('./lib/folder-diff') | ||
const pkg = require('./package.json') | ||
const SimpleTimer = require('./lib/timer') | ||
const { getStreamsLength, getStreamLength, meterStream, captureStreamLength } = require('./lib/stream-meter') | ||
const statsHandler = require('./lib/stats-handler') | ||
const { createForm, createForms } = require('./lib/create-form') | ||
// Use import for local files as well | ||
import { neocitiesLocalDiff } from './lib/folder-diff.js' | ||
import { SimpleTimer } from './lib/timer.js' | ||
import { getStreamsLength, getStreamLength, meterStream, captureStreamLength } from './lib/stream-meter.js' | ||
import { statsHandler } from './lib/stats-handler.js' | ||
import { createForm, createForms } from './lib/create-form.js' | ||
import { pkg } from './pkg.cjs' | ||
@@ -34,3 +37,3 @@ const defaultURL = 'https://neocities.org' | ||
*/ | ||
class NeocitiesAPIClient { | ||
export class NeocitiesAPIClient { | ||
/** | ||
@@ -41,6 +44,6 @@ * getKey returns an apiKey from a sitename and password. | ||
* @param {Object} [opts] Options object. | ||
* @param {Object} [opts.url=https://neocities.org] Base URL to request to. | ||
* @param {string} [opts.url='https://neocities.org'] Base URL to request to. | ||
* @return {Promise<String>} An api key for the sitename.. | ||
*/ | ||
static getKey (sitename, password, opts) { | ||
static async getKey (sitename, password, opts) { | ||
assert(sitename, 'must pass sitename as first arg') | ||
@@ -58,6 +61,24 @@ assert(typeof sitename === 'string', 'user arg must be a string') | ||
if (!baseURL) throw new Error('A url base is required') | ||
const url = new URL('/api/key', baseURL) | ||
url.username = sitename | ||
url.password = password | ||
return fetch(url, opts) | ||
const response = await fetch(url, { | ||
headers: { | ||
Authorization: `Basic ${btoa(sitename + ':' + password)}` | ||
} | ||
}) | ||
if (!response.ok) { | ||
let cause | ||
try { | ||
cause = await response.text() | ||
} catch (err) { | ||
cause = err | ||
} | ||
throw new Error('Response was not okay', { cause }) | ||
} | ||
/** @type {*} */ | ||
const json = await response.json() | ||
const token = json.api_key | ||
return token | ||
} | ||
@@ -156,3 +177,3 @@ | ||
const url = new URL(`/api/${endpoint}`, this.url) | ||
return fetch(url, opts) | ||
return request(url, opts) | ||
} | ||
@@ -212,6 +233,6 @@ | ||
try { | ||
const result = await fetch(url, reqOpts).then(handleResponse) | ||
const result = await request(url, reqOpts) | ||
results.push(result) | ||
} catch (err) { | ||
const wrappedError = new ErrorWithCause('Neocities API error', { | ||
const wrappedError = new Error('Neocities API error', { | ||
cause: err | ||
@@ -265,3 +286,3 @@ }) | ||
return this.post('delete', formEntries, { statsCb: opts.statsCb }).then(handleResponse) | ||
return this.post('delete', formEntries, { statsCb: opts.statsCb }) | ||
} | ||
@@ -286,9 +307,9 @@ | ||
* Deploy a directory to neocities, skipping already uploaded files and optionally cleaning orphaned files. | ||
* @param {String} directory The path of the directory to deploy. | ||
* @param {Object} opts Options object. | ||
* @param {Boolean} opts.cleanup Boolean to delete orphaned files nor not. Defaults to false. | ||
* @param {Boolean} opts.statsCb Get access to stat info before uploading is complete. | ||
* @param {Integer} opts.batchSize The number of files to upload per request. Default to 50. | ||
* @param {Function} opts.protected FileFilter A filter function that will prevent files from being cleaned up. | ||
* @return {Promise} Promise containing stats about the deploy | ||
* @param {string} directory The path of the directory to deploy. | ||
* @param {object} opts Options object. | ||
* @param {boolean} opts.cleanup Boolean to delete orphaned files nor not. Defaults to false. | ||
* @param {boolean} opts.statsCb Get access to stat info before uploading is complete. | ||
* @param {number} opts.batchSize The number of files to upload per request. Default to 50. | ||
* @param {function} opts.protected FileFilter A filter function that will prevent files from being cleaned up. | ||
* @return {Promise<object>} Promise containing stats about the deploy | ||
*/ | ||
@@ -363,3 +384,3 @@ async deploy (directory, opts) { | ||
// Wrap error with stats so that we don't lose all that context | ||
const wrappedError = new ErrorWithCause('Error uploading files', { | ||
const wrappedError = new Error('Error uploading files', { | ||
cause: err | ||
@@ -387,3 +408,1 @@ }) | ||
} | ||
module.exports = NeocitiesAPIClient |
@@ -1,5 +0,4 @@ | ||
const FormData = require('form-data') | ||
const batch = require('lodash.chunk') | ||
import FormData from 'form-data' | ||
function createForm (formEntries) { | ||
export function createForm (formEntries) { | ||
const form = new FormData() | ||
@@ -13,8 +12,6 @@ for (const { name, value } of formEntries) { | ||
exports.createForm = createForm | ||
function createForms (formEntries, opts = {}) { | ||
export function createForms (formEntries, opts = {}) { | ||
const { batchSize = 50 } = opts | ||
const forms = [] | ||
const batchedFormEntries = batch(formEntries, batchSize) | ||
const batchedFormEntries = chunk(formEntries, batchSize) | ||
@@ -29,2 +26,17 @@ for (const formEntryBatch of batchedFormEntries) { | ||
exports.createForms = createForms | ||
function chunk (array, size) { | ||
if (!Array.isArray(array)) { | ||
throw new TypeError('First argument must be an array.') | ||
} | ||
if (!Number.isInteger(size) || size <= 0) { | ||
throw new TypeError('Size must be a positive integer.') | ||
} | ||
const chunked = [] | ||
for (let i = 0; i < array.length; i += size) { | ||
chunked.push(array.slice(i, i + size)) | ||
} | ||
return chunked | ||
} |
@@ -1,6 +0,7 @@ | ||
const crypto = require('crypto') | ||
const util = require('util') | ||
const fs = require('fs') | ||
import crypto from 'node:crypto' | ||
import util from 'node:util' | ||
import fs from 'node:fs' | ||
import pump from 'pump' | ||
const ppump = util.promisify(require('pump')) | ||
const ppump = util.promisify(pump) | ||
@@ -13,3 +14,3 @@ /** | ||
*/ | ||
async function neocitiesLocalDiff (neocitiesFiles, localListing, opts = {}) { | ||
export async function neocitiesLocalDiff (neocitiesFiles, localListing, opts = {}) { | ||
opts = { | ||
@@ -64,6 +65,2 @@ protectedFileFilter: (path) => false, | ||
module.exports = { | ||
neocitiesLocalDiff | ||
} | ||
/** | ||
@@ -70,0 +67,0 @@ * sha1FromPath returns a sha1 hex from a path |
@@ -1,7 +0,10 @@ | ||
const afw = require('async-folder-walker') | ||
const path = require('path') | ||
const tap = require('tap') | ||
const { minimatch } = require('minimatch') | ||
import afw from 'async-folder-walker' | ||
import path from 'node:path' | ||
import test from 'node:test' | ||
import assert from 'node:assert' | ||
import { minimatch } from 'minimatch' | ||
import desm from 'desm' | ||
import { neocitiesLocalDiff } from './folder-diff.js' | ||
const { neocitiesLocalDiff } = require('./folder-diff') | ||
const __dirname = desm(import.meta.url) | ||
@@ -58,3 +61,3 @@ const remoteFiles = [ | ||
tap.test('test differ', async t => { | ||
test('test differ', async t => { | ||
const localFiles = await afw.allFiles(path.join(__dirname, '../fixtures'), { | ||
@@ -66,9 +69,9 @@ shaper: f => f | ||
t.ok(['tootzzz.png', 'toot.gif', 'cat.png'].every(path => { | ||
assert.ok(['tootzzz.png', 'toot.gif', 'cat.png'].every(path => { | ||
const found = filesToUpload.find(ftu => ftu.name === path) | ||
t.ok(found.path && found.name, 'each file to upload has a name and path') | ||
assert.ok(found.path && found.name, 'each file to upload has a name and path') | ||
return found | ||
}), 'every file to upload is included') | ||
t.same(filesToDelete, [ | ||
assert.deepEqual(filesToDelete, [ | ||
'not_found.html', | ||
@@ -80,3 +83,3 @@ 'style.css', | ||
t.ok(['neocities.png'].every(path => { | ||
assert.ok(['neocities.png'].every(path => { | ||
const found = filesSkipped.find(fs => fs.relname === path) | ||
@@ -86,6 +89,6 @@ return found | ||
t.equal(protectedFiles.length, 0, 'no files are protected by default') | ||
assert.equal(protectedFiles.length, 0, 'no files are protected by default') | ||
}) | ||
tap.test('proteceted files', async t => { | ||
test('proteceted files', async t => { | ||
const localFiles = await afw.allFiles(path.join(__dirname, '../fixtures'), { | ||
@@ -99,3 +102,3 @@ shaper: f => f | ||
t.same(filesToDelete, [ | ||
assert.deepEqual(filesToDelete, [ | ||
'not_found.html', | ||
@@ -105,3 +108,3 @@ 'style.css' | ||
t.same(protectedFiles, ['a-folder/foo', 'a-folder/bar'], 'protected files are protected') | ||
assert.deepEqual(protectedFiles, ['a-folder/foo', 'a-folder/bar'], 'protected files are protected') | ||
}) |
@@ -1,2 +0,2 @@ | ||
const prettyBytes = require('pretty-bytes') | ||
import prettyBytes from 'pretty-bytes' | ||
@@ -9,3 +9,7 @@ // Progress API constants | ||
function statsHandler (opts = {}) { | ||
/** | ||
* Handler for statistics updates. | ||
* @returns {Function} - A function to handle stats updates. | ||
*/ | ||
export function statsHandler () { | ||
let progressInterval | ||
@@ -64,3 +68,1 @@ const lastStats = {} | ||
} | ||
module.exports = statsHandler |
@@ -1,4 +0,4 @@ | ||
const { Writable, Transform } = require('streamx') | ||
const pump = require('pump') | ||
const pumpify = require('pumpify') | ||
import { Writable, Transform } from 'streamx' | ||
import pump from 'pump' | ||
import pumpify from 'pumpify' | ||
@@ -56,3 +56,3 @@ async function getStreamsLength (readables) { | ||
module.exports = { | ||
export { | ||
getStreamsLength, | ||
@@ -59,0 +59,0 @@ getStreamLength, |
/** | ||
* Simple timer lets you record start and stop times, with an elapsed time getter. | ||
*/ | ||
class SimpleTimer { | ||
export class SimpleTimer { | ||
constructor (startTime) { | ||
@@ -33,3 +33,1 @@ this.start = startTime || Date.now() | ||
} | ||
module.exports = SimpleTimer |
{ | ||
"name": "async-neocities", | ||
"description": "WIP - nothing to see here", | ||
"version": "2.1.6", | ||
"description": "A library and bin to deploy to neocities", | ||
"version": "3.0.3", | ||
"author": "Bret Comnes <bcomnes@gmail.com> (https://bret.io)", | ||
"type": "module", | ||
"bin": { | ||
"async-neocities": "./bin.js", | ||
"an": "./bin.js" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/bcomnes/async-neocities/issues" | ||
}, | ||
"engines": { | ||
"node": ">=20", | ||
"npm": ">=10" | ||
}, | ||
"dependencies": { | ||
"@lukeed/ms": "^2.0.2", | ||
"application-config": "^3.0.0", | ||
"argsclopts": "^1.0.4", | ||
"async-folder-walker": "^3.0.1", | ||
"desm": "^1.3.0", | ||
"fetch-errors": "^2.0.1", | ||
"form-data": "^4.0.0", | ||
"lodash.chunk": "^4.2.0", | ||
"nanoassert": "^2.0.0", | ||
"node-fetch": "^2.6.0", | ||
"password-prompt": "1.1.3", | ||
"pony-cause": "^2.1.4", | ||
"pretty-bytes": "^5.3.0", | ||
"pretty-bytes": "^6.0.0", | ||
"pump": "^3.0.0", | ||
"pumpify": "^2.0.1", | ||
"streamx": "^2.6.0" | ||
"streamx": "^2.6.0", | ||
"undici": "^6.0.0", | ||
"webassert": "^3.0.2" | ||
}, | ||
"devDependencies": { | ||
"@voxpelli/tsconfig": "^10.0.0", | ||
"auto-changelog": "^2.2.0", | ||
"dependency-check": "^4.1.0", | ||
"c8": "^9.0.0", | ||
"gh-release": "^7.0.0", | ||
"minimatch": "^9.0.0", | ||
"minimatch": "^9.0.3", | ||
"npm-run-all": "^4.1.5", | ||
"standard": "^17.0.0", | ||
"tap": "^18.1.5" | ||
"standard": "^17.0.0" | ||
}, | ||
@@ -47,5 +60,4 @@ "homepage": "https://github.com/bcomnes/async-neocities", | ||
"test": "run-s test:*", | ||
"test:deps": "dependency-check . --no-dev --no-peer", | ||
"test:standard": "standard", | ||
"test:tape": "tap", | ||
"test:node-test": "c8 node --test --test-reporter spec", | ||
"version": "auto-changelog -p --template keepachangelog auto-changelog --breaking-pattern 'BREAKING CHANGE:' && git add CHANGELOG.md" | ||
@@ -57,15 +69,3 @@ }, | ||
] | ||
}, | ||
"tap": { | ||
"serial": [ | ||
"lib", | ||
"test.js" | ||
], | ||
"typecheck": false, | ||
"allow-incomplete-coverage": true, | ||
"coverage-report": [ | ||
"text", | ||
"lcovonly" | ||
] | ||
} | ||
} |
@@ -17,9 +17,9 @@ # async-neocities | ||
``` js | ||
const path = require('path') | ||
const Neocities = require('async-neocities') | ||
import path from 'node:path' | ||
import { NeocitiesAPIClient } from 'async-neocities' | ||
async function deploySite () { | ||
const token = await Neocities.getKey('sitename', 'password') | ||
const token = await NeocitiesAPIClient.getKey('sitename', 'password') | ||
const client = new Neocities(token) | ||
const client = new NeocitiesAPIClient(token) | ||
@@ -36,9 +36,43 @@ console.log(await client.list()) // site files | ||
## Bin | ||
`async-neocities` ships a bin that lets you deploy to neocities locally or in CI. | ||
It's interactive and will help you set up your config and keys. | ||
The site name is configured to a file in cwd called `deploy-to-neocities.json` that looks like: | ||
```json | ||
{"siteName":"the-name-of-the-site"} | ||
``` | ||
```console | ||
Usage: async-neocities [options] | ||
Example: async-neocities --src public | ||
--help, -h print help text | ||
--src, -s The directory to deploy to neocities (default: "public") | ||
--cleanup, -c Destructively clean up orphaned files on neocities | ||
--protect, -p String to minimatch files which will never be cleaned up | ||
--status Print auth status of current working directory | ||
--print-key Print api-key status of current working directory | ||
--clear-key Remove the currently assoicated API key | ||
--force-auth Force re-authorization of current working directory | ||
async-neocities (v3.0.0) | ||
``` | ||
You can set the flags with ENV vars | ||
- `ASYNC_NEOCITIES_API_KEY` or `NEOCITIES_API_TOKEN`: the API token matching the site name, but you should set and commit the `deploy-to-neocities.json` file. | ||
- `ASYNC_NEOCITIES_SITE_NAME`: the name of the site to deploy to. | ||
## API | ||
### `Neocities = require('async-neocities')` | ||
### `import { NeocitiesAPIClient } from 'async-neocities'` | ||
Import the Neocities API client. | ||
### `apiKey = await Neocities.getKey(sitename, password, [opts])` | ||
### `apiKey = await NeocitiesAPIClient.getKey(sitename, password, [opts])` | ||
@@ -55,3 +89,3 @@ Static class method that will get an API Key from a sitename and password. | ||
### `client = new Neocities(apiKey, [opts])` | ||
### `client = new NeocitiesAPIClient(apiKey, [opts])` | ||
@@ -58,0 +92,0 @@ Create a new API client for a given API key. |
55
test.js
@@ -1,7 +0,10 @@ | ||
const tap = require('tap') | ||
import test from 'node:test' | ||
import assert from 'node:assert' | ||
import { readFileSync } from 'node:fs' | ||
import { resolve } from 'node:path' | ||
import { NeocitiesAPIClient } from './index.js' | ||
import { statsHandler } from './lib/stats-handler.js' | ||
import desm from 'desm' | ||
const { readFileSync } = require('fs') | ||
const { resolve } = require('path') | ||
const NeocitiesAPIClient = require('.') | ||
const statsHanlder = require('./lib/stats-handler') | ||
const __dirname = desm(import.meta.url) | ||
@@ -15,4 +18,4 @@ let token = process.env.NEOCITIES_API_TOKEN | ||
token = config.token | ||
tap.test('token loaded', async t => { | ||
t.ok(token) | ||
test('token loaded', async t => { | ||
assert.ok(token) | ||
}) | ||
@@ -27,13 +30,13 @@ } catch (e) { | ||
tap.test('basic client api', async t => { | ||
test('basic client api', async t => { | ||
const client = new NeocitiesAPIClient(token) | ||
t.ok(client.info, 'info method available') | ||
t.ok(client.list, 'list method available') | ||
t.ok(client.get, 'get method available') | ||
t.ok(client.post, 'post method available') | ||
assert.ok(client.info, 'info method available') | ||
assert.ok(client.list, 'list method available') | ||
assert.ok(client.get, 'get method available') | ||
assert.ok(client.post, 'post method available') | ||
}) | ||
if (!fakeToken) { | ||
tap.test('can get info about site', async t => { | ||
test('can get info about site', async t => { | ||
const client = new NeocitiesAPIClient(token) | ||
@@ -43,6 +46,6 @@ | ||
// console.log(info) | ||
t.equal(info.result, 'success', 'info requesst successfull') | ||
assert.equal(info.result, 'success', 'info requesst successfull') | ||
const list = await client.list() | ||
// console.log(list) | ||
t.equal(list.result, 'success', 'list result successfull') | ||
assert.equal(list.result, 'success', 'list result successfull') | ||
}) | ||
@@ -66,3 +69,3 @@ | ||
tap.test('can upload and delete files', async t => { | ||
test('can upload and delete files', async t => { | ||
const client = new NeocitiesAPIClient(token) | ||
@@ -81,4 +84,4 @@ | ||
// console.log(uploadResults) | ||
t.equal(uploadResults[0].result, 'success', 'list result successfull') | ||
// console.log(uploadResults[0]) | ||
assert.equal(uploadResults[0].statusCode, 200, 'list result successfull') | ||
@@ -90,6 +93,6 @@ const deleteResults = await client.delete([ | ||
// console.log(deleteResults) | ||
t.equal(deleteResults.result, 'success', 'list result successfull') | ||
assert.equal(deleteResults.statusCode, 200, 'list result successfull') | ||
}) | ||
tap.test('can deploy folders', async t => { | ||
test('can deploy folders', async t => { | ||
const client = new NeocitiesAPIClient(token) | ||
@@ -100,3 +103,3 @@ | ||
{ | ||
statsCb: statsHanlder(), | ||
statsCb: statsHandler(), | ||
cleanup: false | ||
@@ -106,3 +109,3 @@ } | ||
t.ok(deployStats) | ||
assert.ok(deployStats) | ||
@@ -114,3 +117,3 @@ // console.dir(deployStats, { depth: 99, colors: true }) | ||
{ | ||
statsCb: statsHanlder(), | ||
statsCb: statsHandler(), | ||
cleanup: false | ||
@@ -120,3 +123,3 @@ } | ||
t.ok(redeployStats) | ||
assert.ok(redeployStats) | ||
@@ -128,3 +131,3 @@ // console.dir(redeployStats, { depth: 99, colors: true }) | ||
{ | ||
statsCb: statsHanlder(), | ||
statsCb: statsHandler(), | ||
cleanup: true | ||
@@ -134,3 +137,3 @@ } | ||
t.ok(cleanupStats) | ||
assert.ok(cleanupStats) | ||
@@ -137,0 +140,0 @@ // console.dir(cleanupStats, { depth: 99, colors: true }) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 4 instances in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
292
2
Yes
67100
15
16
1173
6
+ Added@lukeed/ms@^2.0.2
+ Addedapplication-config@^3.0.0
+ Addedargsclopts@^1.0.4
+ Addeddesm@^1.3.0
+ Addedpassword-prompt@1.1.3
+ Addedundici@^6.0.0
+ Addedwebassert@^3.0.2
+ Added@lukeed/ms@2.0.2(transitive)
+ Addedansi-escapes@4.3.2(transitive)
+ Addedapplication-config@3.0.0(transitive)
+ Addedapplication-config-path@1.0.0(transitive)
+ Addedargsclopts@1.0.4(transitive)
+ Addedcross-spawn@7.0.3(transitive)
+ Addeddesm@1.3.1(transitive)
+ Addeddetect-indent@7.0.1(transitive)
+ Addedimurmurhash@0.1.4(transitive)
+ Addedis-plain-obj@4.1.0(transitive)
+ Addedis-typedarray@1.0.0(transitive)
+ Addedisexe@2.0.0(transitive)
+ Addedload-json-file@7.0.1(transitive)
+ Addedpassword-prompt@1.1.3(transitive)
+ Addedpath-key@3.1.1(transitive)
+ Addedpretty-bytes@6.1.1(transitive)
+ Addedshebang-command@2.0.0(transitive)
+ Addedshebang-regex@3.0.0(transitive)
+ Addedsignal-exit@3.0.7(transitive)
+ Addedsort-keys@5.1.0(transitive)
+ Addedtype-fest@0.21.3(transitive)
+ Addedtypedarray-to-buffer@3.1.5(transitive)
+ Addedundici@6.20.1(transitive)
+ Addedwebassert@3.0.2(transitive)
+ Addedwhich@2.0.2(transitive)
+ Addedwrite-file-atomic@3.0.3(transitive)
+ Addedwrite-json-file@5.0.0(transitive)
- Removedlodash.chunk@^4.2.0
- Removednanoassert@^2.0.0
- Removednode-fetch@^2.6.0
- Removedlodash.chunk@4.2.0(transitive)
- Removednanoassert@2.0.0(transitive)
- Removednode-fetch@2.7.0(transitive)
- Removedpretty-bytes@5.6.0(transitive)
- Removedtr46@0.0.3(transitive)
- Removedwebidl-conversions@3.0.1(transitive)
- Removedwhatwg-url@5.0.0(transitive)
Updatedpretty-bytes@^6.0.0