@antora/ui-loader 3.2.0-alpha.1 → 3.2.0-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/constants.js CHANGED
@@ -3,14 +3,13 @@
3
3
  module.exports = Object.freeze({
4
4
  UI_CACHE_FOLDER: 'ui',
5
5
  UI_DESC_FILENAME: 'ui.yml',
6
- UI_SRC_GLOB: '**/*[!~]',
6
+ UI_SRC_GLOB: '**/!(*~)',
7
7
  UI_SRC_OPTS: {
8
+ braceExpansion: false,
8
9
  dot: true,
9
- follow: true,
10
- ignore: ['.git/**'],
11
- nomount: true,
12
- nosort: true,
13
- nounique: true,
14
- strict: false,
10
+ ignore: ['.git'],
11
+ objectMode: true,
12
+ onlyFiles: false,
13
+ unique: false,
15
14
  },
16
15
  })
package/lib/file.js CHANGED
@@ -1,10 +1,12 @@
1
1
  'use strict'
2
2
 
3
- const { Readable } = require('stream')
4
- const { Stats } = require('fs')
3
+ const { constants: fsc } = require('node:fs')
4
+ const { posix: path } = require('node:path')
5
+ const { Readable } = require('node:stream')
5
6
  const Vinyl = require('vinyl')
6
7
 
7
8
  const DEFAULT_FILE_MODE = 0o100666 & ~process.umask()
9
+ const invariably = { true: () => true, false: () => false }
8
10
 
9
11
  class File extends Vinyl {
10
12
  get path () {
@@ -27,21 +29,92 @@ class File extends Vinyl {
27
29
  class MemoryFile extends File {
28
30
  constructor (file) {
29
31
  const contents = file.contents || Buffer.alloc(0)
30
- const stat = Object.assign(new Stats(), { mode: DEFAULT_FILE_MODE, mtime: undefined, size: contents.length })
32
+ const stat = {
33
+ mode: DEFAULT_FILE_MODE,
34
+ size: contents.length,
35
+ isDirectory: invariably.false,
36
+ isFile: invariably.true,
37
+ isSymbolicLink: invariably.false,
38
+ }
31
39
  super(Object.assign({}, file, { contents, stat }))
32
40
  }
33
41
  }
34
42
 
35
- class ReadableFile extends Readable {
36
- constructor (file) {
37
- super({ objectMode: true })
38
- this._file = file
43
+ class ZipReadable extends Readable {
44
+ constructor (zipFile, options = {}) {
45
+ super({ objectMode: true, highWaterMark: 1 })
46
+ if ((this._closeable = (this._zipFile = zipFile).reader.fd != null) && !zipFile.autoClose) {
47
+ throw new Error('ZipReadable requires file-based ZipFile to be initialized with autoClose:true option')
48
+ }
49
+ if (!zipFile.lazyEntries) {
50
+ throw new Error('ZipReadable requires ZipFile to be initialized with lazyEntries:true option')
51
+ }
52
+ if ((this._startPath = options.startPath) && (this._startPath = path.join('/', this._startPath + '/')) !== '/') {
53
+ this._startPath = this._startPath.slice(1)
54
+ } else {
55
+ this._startPath = undefined
56
+ }
57
+ this._init()
58
+ }
59
+
60
+ _init () {
61
+ const zipFile = this._zipFile
62
+ zipFile
63
+ .on('entry', (entry) => {
64
+ const mode = this.getFileMode(entry)
65
+ if ((mode & fsc.S_IFMT) === fsc.S_IFDIR) return zipFile.readEntry()
66
+ let path_ = entry.fileName
67
+ if (this._startPath) {
68
+ if (path_.length < this._startPath.length || !path_.startsWith(this._startPath)) return zipFile.readEntry()
69
+ path_ = path_.slice(this._startPath.length)
70
+ }
71
+ const isLink = (mode & fsc.S_IFMT) === fsc.S_IFLNK
72
+ const stat = {
73
+ mode,
74
+ mtime: entry.getLastModDate(),
75
+ size: entry.uncompressedSize,
76
+ isDirectory: invariably.false,
77
+ isFile: invariably[!isLink],
78
+ isSymbolicLink: invariably[isLink],
79
+ }
80
+ const file = { path: path_, stat }
81
+ if (stat.size === 0) {
82
+ file.contents = Buffer.alloc(0)
83
+ this.push(new File(file))
84
+ } else {
85
+ zipFile.openReadStream(entry, (readErr, readStream) => {
86
+ if (readErr) {
87
+ zipFile.close()
88
+ this.emit('error', readErr)
89
+ return
90
+ }
91
+ if (isLink) {
92
+ const buffer = []
93
+ readStream
94
+ .on('data', (chunk) => buffer.push(chunk))
95
+ .on('error', (readStreamErr) => this.emit('error', readStreamErr))
96
+ .on('end', () => {
97
+ file.symlink = (buffer.length === 1 ? buffer[0] : Buffer.concat(buffer)).toString()
98
+ this.push(new File(file))
99
+ })
100
+ } else {
101
+ file.contents = readStream
102
+ this.push(new File(file))
103
+ }
104
+ })
105
+ }
106
+ })
107
+ .on(this._closeable ? 'close' : 'end', () => zipFile.emittedError || this.push(null))
108
+ }
109
+
110
+ _read (_n) {
111
+ this._zipFile.readEntry()
39
112
  }
40
113
 
41
- _read () {
42
- this.push(this._file)
43
- this.push((this._file = null))
114
+ getFileMode ({ externalFileAttributes }) {
115
+ const attr = externalFileAttributes >> 16 || 33188
116
+ return [448, 56, 7].map((mask) => attr & mask).reduce((a, b) => a + b, attr & fsc.S_IFMT)
44
117
  }
45
118
  }
46
119
 
47
- module.exports = { File, MemoryFile, ReadableFile }
120
+ module.exports = { File, MemoryFile, ZipReadable }
package/lib/load-ui.js CHANGED
@@ -1,23 +1,26 @@
1
1
  'use strict'
2
2
 
3
3
  const { compile: bracesToGroup } = require('braces')
4
- const { createHash } = require('crypto')
4
+ const { createHash } = require('node:crypto')
5
5
  const expandPath = require('@antora/expand-path-helper')
6
- const { File, MemoryFile, ReadableFile } = require('./file')
7
- const { promises: fsp } = require('fs')
6
+ const { File, MemoryFile, ZipReadable } = require('./file')
7
+ const { promises: fsp } = require('node:fs')
8
8
  const { concat: get } = require('simple-get')
9
9
  const getCacheDir = require('cache-directory')
10
- const globStream = require('glob-stream')
11
- const ospath = require('path')
10
+ const { globStream } = require('fast-glob')
11
+ const { inspect } = require('node:util')
12
+ const invariably = { false: () => false, void: () => undefined }
13
+ const ospath = require('node:path')
12
14
  const { posix: path } = ospath
13
15
  const picomatch = require('picomatch')
14
16
  const posixify = ospath.sep === '\\' ? (p) => p.replace(/\\/g, '/') : undefined
15
- const { pipeline, Transform, Writable } = require('stream')
17
+ const { pipeline, PassThrough, Writable } = require('node:stream')
18
+ const drain = () => new Writable({ objectMode: true, write: (_chunk, _enc, next) => next() })
16
19
  const forEach = (write, final) => new Writable({ objectMode: true, write, final })
17
- const map = (transform) => new Transform({ objectMode: true, transform })
20
+ const through = () => new PassThrough({ objectMode: true })
18
21
  const UiCatalog = require('./ui-catalog')
19
22
  const yaml = require('js-yaml')
20
- const vzip = require('gulp-vinyl-zip')
23
+ const yauzl = require('yauzl')
21
24
 
22
25
  const STATIC_FILE_MATCHER_OPTS = {
23
26
  expandRange: (begin, end, step, opts) => bracesToGroup(opts ? `{${begin}..${end}..${step}}` : `{${begin}..${end}}`),
@@ -82,9 +85,9 @@ async function loadUi (playbook) {
82
85
  return fetch && bundle.snapshot
83
86
  ? downloadBundle(bundleUrl, cachePath, createAgent(bundleUrl, playbook.network || {}))
84
87
  : fsp.stat(cachePath).then(
85
- (stat) => new File({ path: cachePath, stat }),
86
- () => downloadBundle(bundleUrl, cachePath, createAgent(bundleUrl, playbook.network || {}))
87
- )
88
+ (stat) => new File({ path: cachePath, stat }),
89
+ () => downloadBundle(bundleUrl, cachePath, createAgent(bundleUrl, playbook.network || {}))
90
+ )
88
91
  })
89
92
  } else {
90
93
  const localPath = expandPath(bundleUrl, { dot: startDir })
@@ -103,20 +106,15 @@ async function loadUi (playbook) {
103
106
  new Promise((resolve, reject) =>
104
107
  bundleFile.isDirectory()
105
108
  ? srcFs(ospath.join(bundleFile.path, bundle.startPath || '', '.')).then(resolve, reject)
106
- : vzip
107
- .src(bundleFile.path)
108
- .on('error', (err) => reject(Object.assign(err, { message: `not a valid zip file; ${err.message}` })))
109
- .pipe(selectFilesStartingFrom(bundle.startPath))
110
- .pipe(bufferizeContents())
111
- .on('error', reject)
112
- .pipe(collectFiles(resolve))
109
+ : srcZip(bundleFile.path, { startPath: bundle.startPath })
110
+ .on('error', (err) => reject(Object.assign(err, { message: `not a valid zip file; ${err.message}` })))
111
+ .pipe(bufferizeContentsAndCollectFiles(resolve))
112
+ .on('error', reject)
113
113
  ).catch((err) => {
114
- const errWrapper = new Error(
114
+ const msg =
115
115
  `Failed to read UI ${bundleFile.isDirectory() ? 'directory' : 'bundle'}: ` +
116
- (bundleUrl === bundleFile.path ? bundleUrl : `${bundleFile.path} (resolved from url: ${bundleUrl})`)
117
- )
118
- errWrapper.stack += `\nCaused by: ${err.stack || 'unknown'}`
119
- throw errWrapper
116
+ (bundleUrl === bundleFile.path ? bundleUrl : `${bundleFile.path} (resolved from url: ${bundleUrl})`)
117
+ throw transformError(err, msg)
120
118
  })
121
119
  ),
122
120
  srcSupplementalFiles(supplementalFilesSpec, startDir),
@@ -175,13 +173,12 @@ function createAgent (url, { httpProxy, httpsProxy, noProxy }) {
175
173
  function downloadBundle (url, to, agent) {
176
174
  return new Promise((resolve, reject) => {
177
175
  get({ url, agent }, (err, response, contents) => {
178
- if (err) reject(err)
176
+ if (err) return reject(err)
179
177
  if (response.statusCode !== 200) {
180
178
  const message = `Response code ${response.statusCode} (${response.statusMessage})`
181
179
  return reject(Object.assign(new Error(message), { name: 'HTTPError' }))
182
180
  }
183
- new ReadableFile(new MemoryFile({ path: ospath.basename(to), contents }))
184
- .pipe(vzip.src())
181
+ srcZip(contents, { testOnly: true })
185
182
  .on('error', (err) =>
186
183
  reject(Object.assign(err, { message: `not a valid zip file; ${err.message}`, summary: 'Invalid UI bundle' }))
187
184
  )
@@ -189,110 +186,52 @@ function downloadBundle (url, to, agent) {
189
186
  fsp
190
187
  .mkdir(ospath.dirname(to), { recursive: true })
191
188
  .then(() => fsp.writeFile(to, contents))
192
- .then(() => resolve(new File({ path: to, stat: { isDirectory: () => false } })))
189
+ .then(() => resolve(new File({ path: to, stat: { isDirectory: invariably.false } })))
193
190
  )
194
191
  })
195
192
  }).catch((err) => {
196
- const errWrapper = new Error(`${err.summary || 'Failed to download UI bundle'}: ${url}`)
197
- errWrapper.stack += `\nCaused by: ${err.stack || 'unknown'}`
198
- throw errWrapper
199
- })
200
- }
201
-
202
- function selectFilesStartingFrom (startPath) {
203
- if (!startPath || (startPath = path.join('/', startPath + '/')) === '/') {
204
- return map((file, _, next) => {
205
- if (file.isNull()) {
206
- next()
207
- } else {
208
- next(
209
- null,
210
- new File({ path: posixify ? posixify(file.path) : file.path, contents: file.contents, stat: file.stat })
211
- )
212
- }
213
- })
214
- } else {
215
- startPath = startPath.substr(1)
216
- const startPathOffset = startPath.length
217
- return map((file, _, next) => {
218
- if (file.isNull()) {
219
- next()
220
- } else {
221
- const path_ = posixify ? posixify(file.path) : file.path
222
- if (path_.length > startPathOffset && path_.startsWith(startPath)) {
223
- next(null, new File({ path: path_.substr(startPathOffset), contents: file.contents, stat: file.stat }))
224
- } else {
225
- next()
226
- }
227
- }
228
- })
229
- }
230
- }
231
-
232
- function bufferizeContents () {
233
- return map((file, _, next) => {
234
- // NOTE gulp-vinyl-zip automatically converts the contents of an empty file to a Buffer
235
- if (file.isStream()) {
236
- const buffer = []
237
- pipeline(
238
- file.contents,
239
- forEach((chunk, _, done) => buffer.push(chunk) && done()),
240
- (err) => (err ? next(err) : next(null, Object.assign(file, { contents: Buffer.concat(buffer) })))
241
- )
242
- } else {
243
- next(null, file)
193
+ const errWrapper = transformError(err, `${err.summary || 'Failed to download UI bundle'}: ${url}`)
194
+ if (err.code === 'ECONNRESET' || (err.message || '').toLowerCase() === 'request timed out') {
195
+ Object.defineProperty(errWrapper, 'recoverable', { value: true })
244
196
  }
197
+ throw errWrapper
245
198
  })
246
199
  }
247
200
 
248
- function collectFiles (resolve, files = new Map()) {
249
- return forEach(
250
- (file, _, done) => {
251
- files.set(file.path, file)
252
- done()
253
- },
254
- (done) => done() || resolve(files)
255
- )
256
- }
257
-
258
201
  function srcSupplementalFiles (filesSpec, startDir) {
259
202
  if (!filesSpec) return new Map()
260
203
  let cwd
261
204
  return (
262
205
  Array.isArray(filesSpec)
263
206
  ? Promise.all(
264
- filesSpec.reduce((accum, { path: path_, contents: contents_ }) => {
265
- if (!path_) {
266
- return accum
267
- } else if (contents_) {
268
- if (~contents_.indexOf('\n') || !EXT_RX.test(contents_)) {
269
- accum.push(new MemoryFile({ path: path_, contents: Buffer.from(contents_) }))
207
+ filesSpec.reduce((accum, { path: path_, contents: contents_ }) => {
208
+ if (!path_) return accum
209
+ if (contents_) {
210
+ if (~contents_.indexOf('\n') || !EXT_RX.test(contents_)) {
211
+ accum.push(new MemoryFile({ path: path_, contents: Buffer.from(contents_) }))
212
+ } else {
213
+ contents_ = expandPath(contents_, { dot: startDir })
214
+ accum.push(
215
+ fsp
216
+ .stat(contents_)
217
+ .then((stat) =>
218
+ fsp.readFile(contents_).then((contents) => new File({ path: path_, contents, stat }))
219
+ )
220
+ )
221
+ }
270
222
  } else {
271
- contents_ = expandPath(contents_, { dot: startDir })
272
- accum.push(
273
- fsp
274
- .stat(contents_)
275
- .then((stat) =>
276
- fsp.readFile(contents_).then((contents) => new File({ path: path_, contents, stat }))
277
- )
278
- )
223
+ accum.push(new MemoryFile({ path: path_ }))
279
224
  }
280
- } else {
281
- accum.push(new MemoryFile({ path: path_ }))
282
- }
283
- return accum
284
- }, [])
285
- ).then((files) => files.reduce((accum, file) => accum.set(file.path, file) && accum, new Map()))
225
+ return accum
226
+ }, [])
227
+ ).then((files) => files.reduce((accum, file) => accum.set(file.path, file) && accum, new Map()))
286
228
  : fsp.access((cwd = expandPath(filesSpec, { dot: startDir }))).then(() => srcFs(cwd))
287
229
  ).catch((err) => {
288
230
  const dir = cwd ? filesSpec + (filesSpec === cwd ? '' : ` (resolved to ${cwd})`) : undefined
289
231
  if (err.code === 'ENOENT' && err.path === cwd) {
290
232
  throw new Error(`Specified ui.supplemental_files directory does not exist: ${dir}`)
291
- } else {
292
- const errWrapper = new Error(`Failed to read ui.supplemental_files ${cwd ? `directory: ${dir}` : 'entry'}`)
293
- errWrapper.stack += `\nCaused by: ${err.stack || 'unknown'}`
294
- throw errWrapper
295
233
  }
234
+ throw transformError(err, `Failed to read ui.supplemental_files ${cwd ? `directory: ${dir}` : 'entry'}`)
296
235
  })
297
236
  }
298
237
 
@@ -307,12 +246,11 @@ function loadConfig (files, outputDir) {
307
246
  files.delete(UI_DESC_FILENAME)
308
247
  const config = camelCaseKeys(yaml.load(configFile.contents.toString()))
309
248
  const staticFiles = config.staticFiles
310
- if (staticFiles && staticFiles.length) config.isStaticFile = picomatch(staticFiles, STATIC_FILE_MATCHER_OPTS)
249
+ if (staticFiles?.length) config.isStaticFile = picomatch(staticFiles, STATIC_FILE_MATCHER_OPTS)
311
250
  if (outputDir !== undefined) config.outputDir = outputDir
312
251
  return config
313
- } else {
314
- return { outputDir }
315
252
  }
253
+ return { outputDir }
316
254
  }
317
255
 
318
256
  function camelCaseKeys (o) {
@@ -326,7 +264,7 @@ function camelCaseKeys (o) {
326
264
  }
327
265
 
328
266
  function classifyFile (file, config) {
329
- if (config.isStaticFile && config.isStaticFile(file.path)) {
267
+ if (typeof config.isStaticFile === 'function' && config.isStaticFile(file.path)) {
330
268
  file.type = 'static'
331
269
  file.out = resolveOut(file, '')
332
270
  } else if (file.isDot()) {
@@ -353,17 +291,15 @@ function resolveOut (file, outputDir = '_') {
353
291
  }
354
292
 
355
293
  function srcFs (cwd) {
356
- return new Promise((resolve, reject, cache = Object.create(null), files = new Map(), relpathStart = cwd.length + 1) =>
294
+ return new Promise((resolve, reject, files = new Map()) =>
357
295
  pipeline(
358
- globStream(UI_SRC_GLOB, Object.assign({ cache, cwd }, UI_SRC_OPTS)),
359
- forEach(({ path: abspathPosix }, _, done) => {
360
- if ((cache[abspathPosix] || {}).constructor === Array) return done() // detects some directories
361
- const abspath = posixify ? ospath.normalize(abspathPosix) : abspathPosix
362
- const relpath = abspath.substr(relpathStart)
363
- symlinkAwareStat(abspath).then(
296
+ globStream(UI_SRC_GLOB, Object.assign({ cwd }, UI_SRC_OPTS)),
297
+ forEach(({ path: relpath, dirent }, _, done) => {
298
+ if (dirent.isDirectory()) return done()
299
+ const relpathPosix = relpath
300
+ const abspath = posixify ? ospath.join(cwd, (relpath = ospath.normalize(relpath))) : cwd + '/' + relpath
301
+ fsp.stat(abspath).then(
364
302
  (stat) => {
365
- if (stat.isDirectory()) return done() // detects directories that slipped through cache check
366
- const relpathPosix = posixify ? posixify(relpath) : relpath
367
303
  fsp.readFile(abspath).then(
368
304
  (contents) => {
369
305
  files.set(relpathPosix, new File({ cwd, path: relpathPosix, contents, stat, local: true }))
@@ -374,16 +310,18 @@ function srcFs (cwd) {
374
310
  }
375
311
  )
376
312
  },
377
- (statErr) => {
378
- done(
379
- Object.assign(statErr, {
380
- message: statErr.symlink
381
- ? (statErr.code === 'ELOOP' ? 'ELOOP: symbolic link cycle, ' : 'ENOENT: broken symbolic link, ') +
382
- `${relpath} -> ${statErr.symlink}`
383
- : statErr.message.replace(`'${abspath}'`, relpath),
384
- })
385
- )
386
- }
313
+ (statErr) =>
314
+ dirent.isSymbolicLink()
315
+ ? fsp
316
+ .readlink(abspath)
317
+ .then(
318
+ (symlink) =>
319
+ (statErr.code === 'ELOOP' ? 'ELOOP: symbolic link cycle, ' : 'ENOENT: broken symbolic link, ') +
320
+ `${relpath} -> ${symlink}`,
321
+ () => statErr.message.replace(`'${abspath}'`, relpath)
322
+ )
323
+ .then((message) => done(Object.assign(statErr, { message })))
324
+ : done(Object.assign(statErr, { message: statErr.message.replace(`'${abspath}'`, relpath) }))
387
325
  )
388
326
  }),
389
327
  (err) => (err ? reject(err) : resolve(files))
@@ -391,18 +329,42 @@ function srcFs (cwd) {
391
329
  )
392
330
  }
393
331
 
394
- function symlinkAwareStat (path_) {
395
- return fsp.lstat(path_).then((lstat) => {
396
- if (!lstat.isSymbolicLink()) return lstat
397
- return fsp.stat(path_).catch((statErr) =>
398
- fsp
399
- .readlink(path_)
400
- .catch(() => undefined)
401
- .then((symlink) => {
402
- throw Object.assign(statErr, { symlink })
403
- })
404
- )
332
+ function srcZip (file, options = {}) {
333
+ const result = options.testOnly
334
+ ? forEach((file_, _, done) => (file_.isStream() ? pipeline(file_.contents, through(), drain(), done) : done()))
335
+ : through()
336
+ yauzl[file instanceof Buffer ? 'fromBuffer' : 'open'](file, { lazyEntries: true }, (err, zipFile) => {
337
+ if (err) return result.emit('error', err)
338
+ new ZipReadable(zipFile, options).pipe(result)
405
339
  })
340
+ return result
341
+ }
342
+
343
+ function bufferizeContentsAndCollectFiles (resolve, files = new Map()) {
344
+ return forEach(
345
+ (file, _, done) => {
346
+ if (file.isStream()) {
347
+ const buffer = []
348
+ file.contents
349
+ .on('data', (chunk) => buffer.push(chunk))
350
+ .on('end', () => {
351
+ file.contents = buffer.length === 1 ? buffer[0] : Buffer.concat(buffer)
352
+ files.set(file.path, file)
353
+ done()
354
+ })
355
+ } else {
356
+ files.set(file.path, file)
357
+ done()
358
+ }
359
+ },
360
+ (done) => done() || resolve(files)
361
+ )
362
+ }
363
+
364
+ function transformError (err, msg) {
365
+ const errWrapper = new Error(msg)
366
+ errWrapper.stack += `\nCaused by: ${err.stack ? inspect(err).replace(/^Error \[(.+)\](?=: )/, '$1') : err}`
367
+ return errWrapper
406
368
  }
407
369
 
408
370
  module.exports = loadUi
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antora/ui-loader",
3
- "version": "3.2.0-alpha.1",
3
+ "version": "3.2.0-alpha.10",
4
4
  "description": "Downloads a UI bundle, if necessary, and loads the files into a UI catalog for use in an Antora documentation pipeline.",
5
5
  "license": "MPL-2.0",
6
6
  "author": "OpenDevise Inc. (https://opendevise.com)",
@@ -11,7 +11,11 @@
11
11
  "Guillaume Grossetie <g.grossetie@gmail.com>"
12
12
  ],
13
13
  "homepage": "https://antora.org",
14
- "repository": "gitlab:antora/antora",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://gitlab.com/antora/antora.git",
17
+ "directory": "packages/ui-loader"
18
+ },
15
19
  "bugs": {
16
20
  "url": "https://gitlab.com/antora/antora/issues"
17
21
  },
@@ -25,20 +29,20 @@
25
29
  "#constants": "./lib/constants.js"
26
30
  },
27
31
  "dependencies": {
28
- "@antora/expand-path-helper": "~2.0",
32
+ "@antora/expand-path-helper": "~3.0",
29
33
  "braces": "~3.0",
30
34
  "cache-directory": "~2.0",
31
- "glob-stream": "~7.0",
32
- "gulp-vinyl-zip": "~2.5",
33
- "hpagent": "~1.1",
35
+ "fast-glob": "~3.3",
36
+ "hpagent": "~1.2",
34
37
  "js-yaml": "~4.1",
35
- "picomatch": "~2.3",
38
+ "picomatch": "~4.0",
36
39
  "should-proxy": "~1.0",
37
40
  "simple-get": "~4.0",
38
- "vinyl": "~2.2"
41
+ "vinyl": "~3.0",
42
+ "yauzl": "~3.1"
39
43
  },
40
44
  "engines": {
41
- "node": ">=16.0.0"
45
+ "node": ">=18.0.0"
42
46
  },
43
47
  "files": [
44
48
  "lib/"
@@ -54,7 +58,7 @@
54
58
  ],
55
59
  "scripts": {
56
60
  "test": "_mocha",
57
- "prepublishOnly": "node $npm_config_local_prefix/npm/prepublishOnly.js",
58
- "postpublish": "node $npm_config_local_prefix/npm/postpublish.js"
61
+ "prepublishOnly": "npx -y downdoc@latest --prepublish",
62
+ "postpublish": "npx -y downdoc@latest --postpublish"
59
63
  }
60
64
  }