This commit is contained in:
CHEVALLIER Abel
2025-11-13 16:23:22 +01:00
parent de9c515a47
commit cb235644dc
34924 changed files with 3811102 additions and 0 deletions

15
node_modules/pacote/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,15 @@
The ISC License
Copyright (c) Isaac Z. Schlueter, Kat Marchán, npm, Inc., and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

283
node_modules/pacote/README.md generated vendored Normal file
View File

@@ -0,0 +1,283 @@
# pacote
Fetches package manifests and tarballs from the npm registry.
## USAGE
```js
const pacote = require('pacote')
// get a package manifest
pacote.manifest('foo@1.x').then(manifest => console.log('got it', manifest))
// extract a package into a folder
pacote.extract('github:npm/cli', 'some/path', options)
.then(({from, resolved, integrity}) => {
console.log('extracted!', from, resolved, integrity)
})
pacote.tarball('https://server.com/package.tgz').then(data => {
console.log('got ' + data.length + ' bytes of tarball data')
})
```
`pacote` works with any kind of package specifier that npm can install. If
you can pass it to the npm CLI, you can pass it to pacote. (In fact, that's
exactly what the npm CLI does.)
Anything that you can do with one kind of package, you can do with another.
Data that isn't relevant (like a packument for a tarball) will be
simulated.
`prepare` scripts will be run when generating tarballs from `git` and
`directory` locations, to simulate what _would_ be published to the
registry, so that you get a working package instead of just raw source
code that might need to be transpiled.
## CLI
This module exports a command line interface that can do most of what is
described below. Run `pacote -h` to learn more.
```
Pacote - The JavaScript Package Handler, v10.1.1
Usage:
pacote resolve <spec>
Resolve a specifier and output the fully resolved target
Returns integrity and from if '--long' flag is set.
pacote manifest <spec>
Fetch a manifest and print to stdout
pacote packument <spec>
Fetch a full packument and print to stdout
pacote tarball <spec> [<filename>]
Fetch a package tarball and save to <filename>
If <filename> is missing or '-', the tarball will be streamed to stdout.
pacote extract <spec> <folder>
Extract a package to the destination folder.
Configuration values all match the names of configs passed to npm, or
options passed to Pacote. Additional flags for this executable:
--long Print an object from 'resolve', including integrity and spec.
--json Print result objects as JSON rather than node's default.
(This is the default if stdout is not a TTY.)
--help -h Print this helpful text.
For example '--cache=/path/to/folder' will use that folder as the cache.
```
## API
The `spec` refers to any kind of package specifier that npm can install.
If you can pass it to the npm CLI, you can pass it to pacote. (In fact,
that's exactly what the npm CLI does.)
See below for valid `opts` values.
* `pacote.resolve(spec, opts)` Resolve a specifier like `foo@latest` or
`github:user/project` all the way to a tarball url, tarball file, or git
repo with commit hash.
* `pacote.extract(spec, dest, opts)` Extract a package's tarball into a
destination folder. Returns a promise that resolves to the
`{from,resolved,integrity}` of the extracted package.
* `pacote.manifest(spec, opts)` Fetch (or simulate) a package's manifest
(basically, the `package.json` file, plus a bit of metadata).
See below for more on manifests and packuments. Returns a Promise that
resolves to the manifest object.
* `pacote.packument(spec, opts)` Fetch (or simulate) a package's packument
(basically, the top-level package document listing all the manifests that
the registry returns). See below for more on manifests and packuments.
Returns a Promise that resolves to the packument object.
* `pacote.tarball(spec, opts)` Get a package tarball data as a buffer in
memory. Returns a Promise that resolves to the tarball data Buffer, with
`from`, `resolved`, and `integrity` fields attached.
* `pacote.tarball.file(spec, dest, opts)` Save a package tarball data to
a file on disk. Returns a Promise that resolves to
`{from,integrity,resolved}` of the fetched tarball.
* `pacote.tarball.stream(spec, streamHandler, opts)` Fetch a tarball and
make the stream available to the `streamHandler` function.
This is mostly an internal function, but it is exposed because it does
provide some functionality that may be difficult to achieve otherwise.
The `streamHandler` function MUST return a Promise that resolves when
the stream (and all associated work) is ended, or rejects if the stream
has an error.
The `streamHandler` function MAY be called multiple times, as Pacote
retries requests in some scenarios, such as cache corruption or
retriable network failures.
### Options
Options are passed to
[`npm-registry-fetch`](http://npm.im/npm-registry-fetch) and
[`cacache`](http://npm.im/cacache), so in addition to these, anything for
those modules can be given to pacote as well.
Options object is cloned, and mutated along the way to add integrity,
resolved, and other properties, as they are determined.
* `cache` Where to store cache entries and temp files. Passed to
[`cacache`](http://npm.im/cacache). Defaults to the same cache directory
that npm will use by default, based on platform and environment.
* `where` Base folder for resolving relative `file:` dependencies.
* `resolved` Shortcut for looking up resolved values. Should be specified
if known.
* `integrity` Expected integrity of fetched package tarball. If specified,
tarballs with mismatched integrity values will raise an `EINTEGRITY`
error.
* `umask` Permission mode mask for extracted files and directories.
Defaults to `0o22`. See "Extracted File Modes" below.
* `fmode` Minimum permission mode for extracted files. Defaults to
`0o666`. See "Extracted File Modes" below.
* `dmode` Minimum permission mode for extracted directories. Defaults to
`0o777`. See "Extracted File Modes" below.
* `preferOnline` Prefer to revalidate cache entries, even when it would not
be strictly necessary. Default `false`.
* `before` When picking a manifest from a packument, only consider
packages published before the specified date. Default `null`.
* `defaultTag` The default `dist-tag` to use when choosing a manifest from a
packument. Defaults to `latest`.
* `registry` The npm registry to use by default. Defaults to
`https://registry.npmjs.org/`.
* `fullMetadata` Fetch the full metadata from the registry for packuments,
including information not strictly required for installation (author,
description, etc.) Defaults to `true` when `before` is set, since the
version publish time is part of the extended packument metadata.
* `fullReadJson` Use the slower `read-package-json` package insted of
`read-package-json-fast` in order to include extra fields like "readme" in
the manifest. Defaults to `false`.
* `packumentCache` For registry packuments only, you may provide a `Map`
object which will be used to cache packument requests between pacote
calls. This allows you to easily avoid hitting the registry multiple
times (even just to validate the cache) for a given packument, since it
is unlikely to change in the span of a single command.
* `verifySignatures` A boolean that will make pacote verify the
integrity signature of a manifest, if present. There must be a
configured `_keys` entry in the config that is scoped to the
registry the manifest is being fetched from.
* `verifyAttestations` A boolean that will make pacote verify Sigstore
attestations, if present. There must be a configured `_keys` entry in the
config that is scoped to the registry the manifest is being fetched from.
* `tufCache` Where to store metadata/target files when retrieving the package
attestation key material via TUF. Defaults to the same cache directory that
npm will use by default, based on platform and environment.
### Advanced API
Each different type of fetcher is exposed for more advanced usage such as
using helper methods from this classes:
* `DirFetcher`
* `FileFetcher`
* `GitFetcher`
* `RegistryFetcher`
* `RemoteFetcher`
## Extracted File Modes
Files are extracted with a mode matching the following formula:
```
( (tarball entry mode value) | (minimum mode option) ) ~ (umask)
```
This is in order to prevent unreadable files or unlistable directories from
cluttering a project's `node_modules` folder, even if the package tarball
specifies that the file should be inaccessible.
It also prevents files from being group- or world-writable without explicit
opt-in by the user, because all file and directory modes are masked against
the `umask` value.
So, a file which is `0o771` in the tarball, using the default `fmode` of
`0o666` and `umask` of `0o22`, will result in a file mode of `0o755`:
```
(0o771 | 0o666) => 0o777
(0o777 ~ 0o22) => 0o755
```
In almost every case, the defaults are appropriate. To respect exactly
what is in the package tarball (even if this makes an unusable system), set
both `dmode` and `fmode` options to `0`. Otherwise, the `umask` config
should be used in most cases where file mode modifications are required,
and this functions more or less the same as the `umask` value in most Unix
systems.
## Extracted File Ownership
When running as `root` on Unix systems, all extracted files and folders
will have their owning `uid` and `gid` values set to match the ownership
of the containing folder.
This prevents `root`-owned files showing up in a project's `node_modules`
folder when a user runs `sudo npm install`.
## Manifests
A `manifest` is similar to a `package.json` file. However, it has a few
pieces of extra metadata, and sometimes lacks metadata that is inessential
to package installation.
In addition to the common `package.json` fields, manifests include:
* `manifest._resolved` The tarball url or file path where the package
artifact can be found.
* `manifest._from` A normalized form of the spec passed in as an argument.
* `manifest._integrity` The integrity value for the package artifact.
* `manifest._id` The canonical spec of this package version: name@version.
* `manifest.dist` Registry manifests (those included in a packument) have a
`dist` object. Only `tarball` is required, though at least one of
`shasum` or `integrity` is almost always present.
* `tarball` The url to the associated package artifact. (Copied by
Pacote to `manifest._resolved`.)
* `integrity` The integrity SRI string for the artifact. This may not
be present for older packages on the npm registry. (Copied by Pacote
to `manifest._integrity`.)
* `shasum` Legacy integrity value. Hexadecimal-encoded sha1 hash.
(Converted to an SRI string and copied by Pacote to
`manifest._integrity` when `dist.integrity` is not present.)
* `fileCount` Number of files in the tarball.
* `unpackedSize` Size on disk of the package when unpacked.
* `signatures` Signatures of the shasum. Includes the keyid that
correlates to a [`key from the npm
registry`](https://registry.npmjs.org/-/npm/v1/keys)
## Packuments
A packument is the top-level package document that lists the set of
manifests for available versions for a package.
When a packument is fetched with `accept:
application/vnd.npm.install-v1+json` in the HTTP headers, only the most
minimum necessary metadata is returned. Additional metadata is returned
when fetched with only `accept: application/json`.
For Pacote's purposes, the following fields are relevant:
* `versions` An object where each key is a version, and each value is the
manifest for that version.
* `dist-tags` An object mapping dist-tags to version numbers. This is how
`foo@latest` gets turned into `foo@1.2.3`.
* `time` In the full packument, an object mapping version numbers to
publication times, for the `opts.before` functionality.
Pacote adds the following field, regardless of the accept header:
* `_contentLength` The size of the packument.

158
node_modules/pacote/bin/index.js generated vendored Executable file
View File

@@ -0,0 +1,158 @@
#!/usr/bin/env node
const run = conf => {
const pacote = require('../')
switch (conf._[0]) {
case 'resolve':
case 'manifest':
case 'packument':
if (conf._[0] === 'resolve' && conf.long) {
return pacote.manifest(conf._[1], conf).then(mani => ({
resolved: mani._resolved,
integrity: mani._integrity,
from: mani._from,
}))
}
return pacote[conf._[0]](conf._[1], conf)
case 'tarball':
if (!conf._[2] || conf._[2] === '-') {
return pacote.tarball.stream(conf._[1], stream => {
stream.pipe(
conf.testStdout ||
/* istanbul ignore next */
process.stdout
)
// make sure it resolves something falsey
return stream.promise().then(() => {
return false
})
}, conf)
} else {
return pacote.tarball.file(conf._[1], conf._[2], conf)
}
case 'extract':
return pacote.extract(conf._[1], conf._[2], conf)
default: /* istanbul ignore next */ {
throw new Error(`bad command: ${conf._[0]}`)
}
}
}
const version = require('../package.json').version
const usage = () =>
`Pacote - The JavaScript Package Handler, v${version}
Usage:
pacote resolve <spec>
Resolve a specifier and output the fully resolved target
Returns integrity and from if '--long' flag is set.
pacote manifest <spec>
Fetch a manifest and print to stdout
pacote packument <spec>
Fetch a full packument and print to stdout
pacote tarball <spec> [<filename>]
Fetch a package tarball and save to <filename>
If <filename> is missing or '-', the tarball will be streamed to stdout.
pacote extract <spec> <folder>
Extract a package to the destination folder.
Configuration values all match the names of configs passed to npm, or
options passed to Pacote. Additional flags for this executable:
--long Print an object from 'resolve', including integrity and spec.
--json Print result objects as JSON rather than node's default.
(This is the default if stdout is not a TTY.)
--help -h Print this helpful text.
For example '--cache=/path/to/folder' will use that folder as the cache.
`
const shouldJSON = (conf, result) =>
conf.json ||
!process.stdout.isTTY &&
conf.json === undefined &&
result &&
typeof result === 'object'
const pretty = (conf, result) =>
shouldJSON(conf, result) ? JSON.stringify(result, 0, 2) : result
let addedLogListener = false
const main = args => {
const conf = parse(args)
if (conf.help || conf.h) {
return console.log(usage())
}
if (!addedLogListener) {
process.on('log', console.error)
addedLogListener = true
}
try {
return run(conf)
.then(result => result && console.log(pretty(conf, result)))
.catch(er => {
console.error(er)
process.exit(1)
})
} catch (er) {
console.error(er.message)
console.error(usage())
}
}
const parseArg = arg => {
const split = arg.slice(2).split('=')
const k = split.shift()
const v = split.join('=')
const no = /^no-/.test(k) && !v
const key = (no ? k.slice(3) : k)
.replace(/^tag$/, 'defaultTag')
.replace(/-([a-z])/g, (_, c) => c.toUpperCase())
const value = v ? v.replace(/^~/, process.env.HOME) : !no
return { key, value }
}
const parse = args => {
const conf = {
_: [],
cache: process.env.HOME + '/.npm/_cacache',
}
let dashdash = false
args.forEach(arg => {
if (dashdash) {
conf._.push(arg)
} else if (arg === '--') {
dashdash = true
} else if (arg === '-h') {
conf.help = true
} else if (/^--/.test(arg)) {
const { key, value } = parseArg(arg)
conf[key] = value
} else {
conf._.push(arg)
}
})
return conf
}
if (module === require.main) {
main(process.argv.slice(2))
} else {
module.exports = {
main,
run,
usage,
parseArg,
parse,
}
}

105
node_modules/pacote/lib/dir.js generated vendored Normal file
View File

@@ -0,0 +1,105 @@
const { resolve } = require('node:path')
const packlist = require('npm-packlist')
const runScript = require('@npmcli/run-script')
const tar = require('tar')
const { Minipass } = require('minipass')
const Fetcher = require('./fetcher.js')
const FileFetcher = require('./file.js')
const _ = require('./util/protected.js')
const tarCreateOptions = require('./util/tar-create-options.js')
class DirFetcher extends Fetcher {
constructor (spec, opts) {
super(spec, opts)
// just the fully resolved filename
this.resolved = this.spec.fetchSpec
this.tree = opts.tree || null
this.Arborist = opts.Arborist || null
}
// exposes tarCreateOptions as public API
static tarCreateOptions (manifest) {
return tarCreateOptions(manifest)
}
get types () {
return ['directory']
}
#prepareDir () {
return this.manifest().then(mani => {
if (!mani.scripts || !mani.scripts.prepare) {
return
}
if (this.opts.ignoreScripts) {
return
}
// we *only* run prepare.
// pre/post-pack is run by the npm CLI for publish and pack,
// but this function is *also* run when installing git deps
const stdio = this.opts.foregroundScripts ? 'inherit' : 'pipe'
return runScript({
// this || undefined is because runScript will be unhappy with the default null value
scriptShell: this.opts.scriptShell || undefined,
pkg: mani,
event: 'prepare',
path: this.resolved,
stdio,
env: {
npm_package_resolved: this.resolved,
npm_package_integrity: this.integrity,
npm_package_json: resolve(this.resolved, 'package.json'),
},
})
})
}
[_.tarballFromResolved] () {
if (!this.tree && !this.Arborist) {
throw new Error('DirFetcher requires either a tree or an Arborist constructor to pack')
}
const stream = new Minipass()
stream.resolved = this.resolved
stream.integrity = this.integrity
const { prefix, workspaces } = this.opts
// run the prepare script, get the list of files, and tar it up
// pipe to the stream, and proxy errors the chain.
this.#prepareDir()
.then(async () => {
if (!this.tree) {
const arb = new this.Arborist({ path: this.resolved })
this.tree = await arb.loadActual()
}
return packlist(this.tree, { path: this.resolved, prefix, workspaces })
})
.then(files => tar.c(tarCreateOptions(this.package), files)
.on('error', er => stream.emit('error', er)).pipe(stream))
.catch(er => stream.emit('error', er))
return stream
}
manifest () {
if (this.package) {
return Promise.resolve(this.package)
}
return this[_.readPackageJson](this.resolved)
.then(mani => this.package = {
...mani,
_integrity: this.integrity && String(this.integrity),
_resolved: this.resolved,
_from: this.from,
})
}
packument () {
return FileFetcher.prototype.packument.apply(this)
}
}
module.exports = DirFetcher

497
node_modules/pacote/lib/fetcher.js generated vendored Normal file
View File

@@ -0,0 +1,497 @@
// This is the base class that the other fetcher types in lib
// all descend from.
// It handles the unpacking and retry logic that is shared among
// all of the other Fetcher types.
const { basename, dirname } = require('node:path')
const { rm, mkdir } = require('node:fs/promises')
const PackageJson = require('@npmcli/package-json')
const cacache = require('cacache')
const fsm = require('fs-minipass')
const getContents = require('@npmcli/installed-package-contents')
const npa = require('npm-package-arg')
const retry = require('promise-retry')
const ssri = require('ssri')
const tar = require('tar')
const { Minipass } = require('minipass')
const { log } = require('proc-log')
const _ = require('./util/protected.js')
const cacheDir = require('./util/cache-dir.js')
const isPackageBin = require('./util/is-package-bin.js')
const removeTrailingSlashes = require('./util/trailing-slashes.js')
// Pacote is only concerned with the package.json contents
const packageJsonPrepare = (p) => PackageJson.prepare(p).then(pkg => pkg.content)
const packageJsonNormalize = (p) => PackageJson.normalize(p).then(pkg => pkg.content)
class FetcherBase {
constructor (spec, opts) {
if (!opts || typeof opts !== 'object') {
throw new TypeError('options object is required')
}
this.spec = npa(spec, opts.where)
this.allowGitIgnore = !!opts.allowGitIgnore
// a bit redundant because presumably the caller already knows this,
// but it makes it easier to not have to keep track of the requested
// spec when we're dispatching thousands of these at once, and normalizing
// is nice. saveSpec is preferred if set, because it turns stuff like
// x/y#committish into github:x/y#committish. use name@rawSpec for
// registry deps so that we turn xyz and xyz@ -> xyz@
this.from = this.spec.registry
? `${this.spec.name}@${this.spec.rawSpec}` : this.spec.saveSpec
this.#assertType()
// clone the opts object so that others aren't upset when we mutate it
// by adding/modifying the integrity value.
this.opts = { ...opts }
this.cache = opts.cache || cacheDir().cacache
this.tufCache = opts.tufCache || cacheDir().tufcache
this.resolved = opts.resolved || null
// default to caching/verifying with sha512, that's what we usually have
// need to change this default, or start overriding it, when sha512
// is no longer strong enough.
this.defaultIntegrityAlgorithm = opts.defaultIntegrityAlgorithm || 'sha512'
if (typeof opts.integrity === 'string') {
this.opts.integrity = ssri.parse(opts.integrity)
}
this.package = null
this.type = this.constructor.name
this.fmode = opts.fmode || 0o666
this.dmode = opts.dmode || 0o777
// we don't need a default umask, because we don't chmod files coming
// out of package tarballs. they're forced to have a mode that is
// valid, regardless of what's in the tarball entry, and then we let
// the process's umask setting do its job. but if configured, we do
// respect it.
this.umask = opts.umask || 0
this.preferOnline = !!opts.preferOnline
this.preferOffline = !!opts.preferOffline
this.offline = !!opts.offline
this.before = opts.before
this.fullMetadata = this.before ? true : !!opts.fullMetadata
this.fullReadJson = !!opts.fullReadJson
this[_.readPackageJson] = this.fullReadJson
? packageJsonPrepare
: packageJsonNormalize
// rrh is a registry hostname or 'never' or 'always'
// defaults to registry.npmjs.org
this.replaceRegistryHost = (!opts.replaceRegistryHost || opts.replaceRegistryHost === 'npmjs') ?
'registry.npmjs.org' : opts.replaceRegistryHost
this.defaultTag = opts.defaultTag || 'latest'
this.registry = removeTrailingSlashes(opts.registry || 'https://registry.npmjs.org')
// command to run 'prepare' scripts on directories and git dirs
// To use pacote with yarn, for example, set npmBin to 'yarn'
// and npmCliConfig with yarn's equivalents.
this.npmBin = opts.npmBin || 'npm'
// command to install deps for preparing
this.npmInstallCmd = opts.npmInstallCmd || ['install', '--force']
// XXX fill more of this in based on what we know from this.opts
// we explicitly DO NOT fill in --tag, though, since we are often
// going to be packing in the context of a publish, which may set
// a dist-tag, but certainly wants to keep defaulting to latest.
this.npmCliConfig = opts.npmCliConfig || [
`--cache=${dirname(this.cache)}`,
`--prefer-offline=${!!this.preferOffline}`,
`--prefer-online=${!!this.preferOnline}`,
`--offline=${!!this.offline}`,
...(this.before ? [`--before=${this.before.toISOString()}`] : []),
'--no-progress',
'--no-save',
'--no-audit',
// override any omit settings from the environment
'--include=dev',
'--include=peer',
'--include=optional',
// we need the actual things, not just the lockfile
'--no-package-lock-only',
'--no-dry-run',
]
}
get integrity () {
return this.opts.integrity || null
}
set integrity (i) {
if (!i) {
return
}
i = ssri.parse(i)
const current = this.opts.integrity
// do not ever update an existing hash value, but do
// merge in NEW algos and hashes that we don't already have.
if (current) {
current.merge(i)
} else {
this.opts.integrity = i
}
}
get notImplementedError () {
return new Error('not implemented in this fetcher type: ' + this.type)
}
// override in child classes
// Returns a Promise that resolves to this.resolved string value
resolve () {
return this.resolved ? Promise.resolve(this.resolved)
: Promise.reject(this.notImplementedError)
}
packument () {
return Promise.reject(this.notImplementedError)
}
// override in child class
// returns a manifest containing:
// - name
// - version
// - _resolved
// - _integrity
// - plus whatever else was in there (corgi, full metadata, or pj file)
manifest () {
return Promise.reject(this.notImplementedError)
}
// private, should be overridden.
// Note that they should *not* calculate or check integrity or cache,
// but *just* return the raw tarball data stream.
[_.tarballFromResolved] () {
throw this.notImplementedError
}
// public, should not be overridden
tarball () {
return this.tarballStream(stream => stream.concat().then(data => {
data.integrity = this.integrity && String(this.integrity)
data.resolved = this.resolved
data.from = this.from
return data
}))
}
// private
// Note: cacache will raise a EINTEGRITY error if the integrity doesn't match
#tarballFromCache () {
const startTime = Date.now()
const stream = cacache.get.stream.byDigest(this.cache, this.integrity, this.opts)
const elapsedTime = Date.now() - startTime
// cache is good, so log it as a hit in particular since there was no fetch logged
log.http(
'cache',
`${this.spec} ${elapsedTime}ms (cache hit)`
)
return stream
}
get [_.cacheFetches] () {
return true
}
#istream (stream) {
// if not caching this, just return it
if (!this.opts.cache || !this[_.cacheFetches]) {
// instead of creating a new integrity stream, we only piggyback on the
// provided stream's events
if (stream.hasIntegrityEmitter) {
stream.on('integrity', i => this.integrity = i)
return stream
}
const istream = ssri.integrityStream(this.opts)
istream.on('integrity', i => this.integrity = i)
stream.on('error', err => istream.emit('error', err))
return stream.pipe(istream)
}
// we have to return a stream that gets ALL the data, and proxies errors,
// but then pipe from the original tarball stream into the cache as well.
// To do this without losing any data, and since the cacache put stream
// is not a passthrough, we have to pipe from the original stream into
// the cache AFTER we pipe into the middleStream. Since the cache stream
// has an asynchronous flush to write its contents to disk, we need to
// defer the middleStream end until the cache stream ends.
const middleStream = new Minipass()
stream.on('error', err => middleStream.emit('error', err))
stream.pipe(middleStream, { end: false })
const cstream = cacache.put.stream(
this.opts.cache,
`pacote:tarball:${this.from}`,
this.opts
)
cstream.on('integrity', i => this.integrity = i)
cstream.on('error', err => stream.emit('error', err))
stream.pipe(cstream)
// eslint-disable-next-line promise/catch-or-return
cstream.promise().catch(() => {}).then(() => middleStream.end())
return middleStream
}
pickIntegrityAlgorithm () {
return this.integrity ? this.integrity.pickAlgorithm(this.opts)
: this.defaultIntegrityAlgorithm
}
// TODO: check error class, once those are rolled out to our deps
isDataCorruptionError (er) {
return er.code === 'EINTEGRITY' || er.code === 'Z_DATA_ERROR'
}
// override the types getter
get types () {
return false
}
#assertType () {
if (this.types && !this.types.includes(this.spec.type)) {
throw new TypeError(`Wrong spec type (${
this.spec.type
}) for ${
this.constructor.name
}. Supported types: ${this.types.join(', ')}`)
}
}
// We allow ENOENTs from cacache, but not anywhere else.
// An ENOENT trying to read a tgz file, for example, is Right Out.
isRetriableError (er) {
// TODO: check error class, once those are rolled out to our deps
return this.isDataCorruptionError(er) ||
er.code === 'ENOENT' ||
er.code === 'EISDIR'
}
// Mostly internal, but has some uses
// Pass in a function which returns a promise
// Function will be called 1 or more times with streams that may fail.
// Retries:
// Function MUST handle errors on the stream by rejecting the promise,
// so that retry logic can pick it up and either retry or fail whatever
// promise it was making (ie, failing extraction, etc.)
//
// The return value of this method is a Promise that resolves the same
// as whatever the streamHandler resolves to.
//
// This should never be overridden by child classes, but it is public.
tarballStream (streamHandler) {
// Only short-circuit via cache if we have everything else we'll need,
// and the user has not expressed a preference for checking online.
const fromCache = (
!this.preferOnline &&
this.integrity &&
this.resolved
) ? streamHandler(this.#tarballFromCache()).catch(er => {
if (this.isDataCorruptionError(er)) {
log.warn('tarball', `cached data for ${
this.spec
} (${this.integrity}) seems to be corrupted. Refreshing cache.`)
return this.cleanupCached().then(() => {
throw er
})
} else {
throw er
}
}) : null
const fromResolved = er => {
if (er) {
if (!this.isRetriableError(er)) {
throw er
}
log.silly('tarball', `no local data for ${
this.spec
}. Extracting by manifest.`)
}
return this.resolve().then(() => retry(tryAgain =>
streamHandler(this.#istream(this[_.tarballFromResolved]()))
.catch(streamErr => {
// Most likely data integrity. A cache ENOENT error is unlikely
// here, since we're definitely not reading from the cache, but it
// IS possible that the fetch subsystem accessed the cache, and the
// entry got blown away or something. Try one more time to be sure.
if (this.isRetriableError(streamErr)) {
log.warn('tarball', `tarball data for ${
this.spec
} (${this.integrity}) seems to be corrupted. Trying again.`)
return this.cleanupCached().then(() => tryAgain(streamErr))
}
throw streamErr
}), { retries: 1, minTimeout: 0, maxTimeout: 0 }))
}
return fromCache ? fromCache.catch(fromResolved) : fromResolved()
}
cleanupCached () {
return cacache.rm.content(this.cache, this.integrity, this.opts)
}
#empty (path) {
return getContents({ path, depth: 1 }).then(contents => Promise.all(
contents.map(entry => rm(entry, { recursive: true, force: true }))))
}
async #mkdir (dest) {
await this.#empty(dest)
return await mkdir(dest, { recursive: true })
}
// extraction is always the same. the only difference is where
// the tarball comes from.
async extract (dest) {
await this.#mkdir(dest)
return this.tarballStream((tarball) => this.#extract(dest, tarball))
}
#toFile (dest) {
return this.tarballStream(str => new Promise((res, rej) => {
const writer = new fsm.WriteStream(dest)
str.on('error', er => writer.emit('error', er))
writer.on('error', er => rej(er))
writer.on('close', () => res({
integrity: this.integrity && String(this.integrity),
resolved: this.resolved,
from: this.from,
}))
str.pipe(writer)
}))
}
// don't use this.#mkdir because we don't want to rimraf anything
async tarballFile (dest) {
const dir = dirname(dest)
await mkdir(dir, { recursive: true })
return this.#toFile(dest)
}
#extract (dest, tarball) {
const extractor = tar.x(this.#tarxOptions({ cwd: dest }))
const p = new Promise((resolve, reject) => {
extractor.on('end', () => {
resolve({
resolved: this.resolved,
integrity: this.integrity && String(this.integrity),
from: this.from,
})
})
extractor.on('error', er => {
log.warn('tar', er.message)
log.silly('tar', er)
reject(er)
})
tarball.on('error', er => reject(er))
})
tarball.pipe(extractor)
return p
}
// always ensure that entries are at least as permissive as our configured
// dmode/fmode, but never more permissive than the umask allows.
#entryMode (path, mode, type) {
const m = /Directory|GNUDumpDir/.test(type) ? this.dmode
: /File$/.test(type) ? this.fmode
: /* istanbul ignore next - should never happen in a pkg */ 0
// make sure package bins are executable
const exe = isPackageBin(this.package, path) ? 0o111 : 0
// always ensure that files are read/writable by the owner
return ((mode | m) & ~this.umask) | exe | 0o600
}
#tarxOptions ({ cwd }) {
const sawIgnores = new Set()
return {
cwd,
noChmod: true,
noMtime: true,
filter: (name, entry) => {
if (/Link$/.test(entry.type)) {
return false
}
entry.mode = this.#entryMode(entry.path, entry.mode, entry.type)
// this replicates the npm pack behavior where .gitignore files
// are treated like .npmignore files, but only if a .npmignore
// file is not present.
if (/File$/.test(entry.type)) {
const base = basename(entry.path)
if (base === '.npmignore') {
sawIgnores.add(entry.path)
} else if (base === '.gitignore' && !this.allowGitIgnore) {
// rename, but only if there's not already a .npmignore
const ni = entry.path.replace(/\.gitignore$/, '.npmignore')
if (sawIgnores.has(ni)) {
return false
}
entry.path = ni
}
return true
}
},
strip: 1,
onwarn: /* istanbul ignore next - we can trust that tar logs */
(code, msg, data) => {
log.warn('tar', code, msg)
log.silly('tar', code, msg, data)
},
umask: this.umask,
// always ignore ownership info from tarball metadata
preserveOwner: false,
}
}
}
module.exports = FetcherBase
// Child classes
const GitFetcher = require('./git.js')
const RegistryFetcher = require('./registry.js')
const FileFetcher = require('./file.js')
const DirFetcher = require('./dir.js')
const RemoteFetcher = require('./remote.js')
// Get an appropriate fetcher object from a spec and options
FetcherBase.get = (rawSpec, opts = {}) => {
const spec = npa(rawSpec, opts.where)
switch (spec.type) {
case 'git':
return new GitFetcher(spec, opts)
case 'remote':
return new RemoteFetcher(spec, opts)
case 'version':
case 'range':
case 'tag':
case 'alias':
return new RegistryFetcher(spec.subSpec || spec, opts)
case 'file':
return new FileFetcher(spec, opts)
case 'directory':
return new DirFetcher(spec, opts)
default:
throw new TypeError('Unknown spec type: ' + spec.type)
}
}

94
node_modules/pacote/lib/file.js generated vendored Normal file
View File

@@ -0,0 +1,94 @@
const { resolve } = require('node:path')
const { stat, chmod } = require('node:fs/promises')
const cacache = require('cacache')
const fsm = require('fs-minipass')
const Fetcher = require('./fetcher.js')
const _ = require('./util/protected.js')
class FileFetcher extends Fetcher {
constructor (spec, opts) {
super(spec, opts)
// just the fully resolved filename
this.resolved = this.spec.fetchSpec
}
get types () {
return ['file']
}
manifest () {
if (this.package) {
return Promise.resolve(this.package)
}
// have to unpack the tarball for this.
return cacache.tmp.withTmp(this.cache, this.opts, dir =>
this.extract(dir)
.then(() => this[_.readPackageJson](dir))
.then(mani => this.package = {
...mani,
_integrity: this.integrity && String(this.integrity),
_resolved: this.resolved,
_from: this.from,
}))
}
#exeBins (pkg, dest) {
if (!pkg.bin) {
return Promise.resolve()
}
return Promise.all(Object.keys(pkg.bin).map(async k => {
const script = resolve(dest, pkg.bin[k])
// Best effort. Ignore errors here, the only result is that
// a bin script is not executable. But if it's missing or
// something, we just leave it for a later stage to trip over
// when we can provide a more useful contextual error.
try {
const st = await stat(script)
const mode = st.mode | 0o111
if (mode === st.mode) {
return
}
await chmod(script, mode)
} catch {
// Ignore errors here
}
}))
}
extract (dest) {
// if we've already loaded the manifest, then the super got it.
// but if not, read the unpacked manifest and chmod properly.
return super.extract(dest)
.then(result => this.package ? result
: this[_.readPackageJson](dest).then(pkg =>
this.#exeBins(pkg, dest)).then(() => result))
}
[_.tarballFromResolved] () {
// create a read stream and return it
return new fsm.ReadStream(this.resolved)
}
packument () {
// simulate based on manifest
return this.manifest().then(mani => ({
name: mani.name,
'dist-tags': {
[this.defaultTag]: mani.version,
},
versions: {
[mani.version]: {
...mani,
dist: {
tarball: `file:${this.resolved}`,
integrity: this.integrity && String(this.integrity),
},
},
},
}))
}
}
module.exports = FileFetcher

317
node_modules/pacote/lib/git.js generated vendored Normal file
View File

@@ -0,0 +1,317 @@
const cacache = require('cacache')
const git = require('@npmcli/git')
const npa = require('npm-package-arg')
const pickManifest = require('npm-pick-manifest')
const { Minipass } = require('minipass')
const { log } = require('proc-log')
const DirFetcher = require('./dir.js')
const Fetcher = require('./fetcher.js')
const FileFetcher = require('./file.js')
const RemoteFetcher = require('./remote.js')
const _ = require('./util/protected.js')
const addGitSha = require('./util/add-git-sha.js')
const npm = require('./util/npm.js')
const hashre = /^[a-f0-9]{40}$/
// get the repository url.
// prefer https if there's auth, since ssh will drop that.
// otherwise, prefer ssh if available (more secure).
// We have to add the git+ back because npa suppresses it.
const repoUrl = (h, opts) =>
h.sshurl && !(h.https && h.auth) && addGitPlus(h.sshurl(opts)) ||
h.https && addGitPlus(h.https(opts))
// add git+ to the url, but only one time.
const addGitPlus = url => url && `git+${url}`.replace(/^(git\+)+/, 'git+')
class GitFetcher extends Fetcher {
constructor (spec, opts) {
super(spec, opts)
// we never want to compare integrity for git dependencies: npm/rfcs#525
if (this.opts.integrity) {
delete this.opts.integrity
log.warn(`skipping integrity check for git dependency ${this.spec.fetchSpec}`)
}
this.resolvedRef = null
if (this.spec.hosted) {
this.from = this.spec.hosted.shortcut({ noCommittish: false })
}
// shortcut: avoid full clone when we can go straight to the tgz
// if we have the full sha and it's a hosted git platform
if (this.spec.gitCommittish && hashre.test(this.spec.gitCommittish)) {
this.resolvedSha = this.spec.gitCommittish
// use hosted.tarball() when we shell to RemoteFetcher later
this.resolved = this.spec.hosted
? repoUrl(this.spec.hosted, { noCommittish: false })
: this.spec.rawSpec
} else {
this.resolvedSha = ''
}
this.Arborist = opts.Arborist || null
}
// just exposed to make it easier to test all the combinations
static repoUrl (hosted, opts) {
return repoUrl(hosted, opts)
}
get types () {
return ['git']
}
resolve () {
// likely a hosted git repo with a sha, so get the tarball url
// but in general, no reason to resolve() more than necessary!
if (this.resolved) {
return super.resolve()
}
// fetch the git repo and then look at the current hash
const h = this.spec.hosted
// try to use ssh, fall back to git.
return h
? this.#resolvedFromHosted(h)
: this.#resolvedFromRepo(this.spec.fetchSpec)
}
// first try https, since that's faster and passphrase-less for
// public repos, and supports private repos when auth is provided.
// Fall back to SSH to support private repos
// NB: we always store the https url in resolved field if auth
// is present, otherwise ssh if the hosted type provides it
#resolvedFromHosted (hosted) {
return this.#resolvedFromRepo(hosted.https && hosted.https()).catch(er => {
// Throw early since we know pathspec errors will fail again if retried
if (er instanceof git.errors.GitPathspecError) {
throw er
}
const ssh = hosted.sshurl && hosted.sshurl()
// no fallthrough if we can't fall through or have https auth
if (!ssh || hosted.auth) {
throw er
}
return this.#resolvedFromRepo(ssh)
})
}
#resolvedFromRepo (gitRemote) {
// XXX make this a custom error class
if (!gitRemote) {
return Promise.reject(new Error(`No git url for ${this.spec}`))
}
const gitRange = this.spec.gitRange
const name = this.spec.name
return git.revs(gitRemote, this.opts).then(remoteRefs => {
return gitRange ? pickManifest({
versions: remoteRefs.versions,
'dist-tags': remoteRefs['dist-tags'],
name,
}, gitRange, this.opts)
: this.spec.gitCommittish ?
remoteRefs.refs[this.spec.gitCommittish] ||
remoteRefs.refs[remoteRefs.shas[this.spec.gitCommittish]]
: remoteRefs.refs.HEAD // no git committish, get default head
}).then(revDoc => {
// the committish provided isn't in the rev list
// things like HEAD~3 or @yesterday can land here.
if (!revDoc || !revDoc.sha) {
return this.#resolvedFromClone()
}
this.resolvedRef = revDoc
this.resolvedSha = revDoc.sha
this.#addGitSha(revDoc.sha)
return this.resolved
})
}
#setResolvedWithSha (withSha) {
// we haven't cloned, so a tgz download is still faster
// of course, if it's not a known host, we can't do that.
this.resolved = !this.spec.hosted ? withSha
: repoUrl(npa(withSha).hosted, { noCommittish: false })
}
// when we get the git sha, we affix it to our spec to build up
// either a git url with a hash, or a tarball download URL
#addGitSha (sha) {
this.#setResolvedWithSha(addGitSha(this.spec, sha))
}
#resolvedFromClone () {
// do a full or shallow clone, then look at the HEAD
// kind of wasteful, but no other option, really
return this.#clone(() => this.resolved)
}
#prepareDir (dir) {
return this[_.readPackageJson](dir).then(mani => {
// no need if we aren't going to do any preparation.
const scripts = mani.scripts
if (!mani.workspaces && (!scripts || !(
scripts.postinstall ||
scripts.build ||
scripts.preinstall ||
scripts.install ||
scripts.prepack ||
scripts.prepare))) {
return
}
// to avoid cases where we have an cycle of git deps that depend
// on one another, we only ever do preparation for one instance
// of a given git dep along the chain of installations.
// Note that this does mean that a dependency MAY in theory end up
// trying to run its prepare script using a dependency that has not
// been properly prepared itself, but that edge case is smaller
// and less hazardous than a fork bomb of npm and git commands.
const noPrepare = !process.env._PACOTE_NO_PREPARE_ ? []
: process.env._PACOTE_NO_PREPARE_.split('\n')
if (noPrepare.includes(this.resolved)) {
log.info('prepare', 'skip prepare, already seen', this.resolved)
return
}
noPrepare.push(this.resolved)
// the DirFetcher will do its own preparation to run the prepare scripts
// All we have to do is put the deps in place so that it can succeed.
return npm(
this.npmBin,
[].concat(this.npmInstallCmd).concat(this.npmCliConfig),
dir,
{ ...process.env, _PACOTE_NO_PREPARE_: noPrepare.join('\n') },
{ message: 'git dep preparation failed' }
)
})
}
[_.tarballFromResolved] () {
const stream = new Minipass()
stream.resolved = this.resolved
stream.from = this.from
// check it out and then shell out to the DirFetcher tarball packer
this.#clone(dir => this.#prepareDir(dir)
.then(() => new Promise((res, rej) => {
if (!this.Arborist) {
throw new Error('GitFetcher requires an Arborist constructor to pack a tarball')
}
const df = new DirFetcher(`file:${dir}`, {
...this.opts,
Arborist: this.Arborist,
resolved: null,
integrity: null,
})
const dirStream = df[_.tarballFromResolved]()
dirStream.on('error', rej)
dirStream.on('end', res)
dirStream.pipe(stream)
}))).catch(
/* istanbul ignore next: very unlikely and hard to test */
er => stream.emit('error', er)
)
return stream
}
// clone a git repo into a temp folder (or fetch and unpack if possible)
// handler accepts a directory, and returns a promise that resolves
// when we're done with it, at which point, cacache deletes it
//
// TODO: after cloning, create a tarball of the folder, and add to the cache
// with cacache.put.stream(), using a key that's deterministic based on the
// spec and repo, so that we don't ever clone the same thing multiple times.
#clone (handler, tarballOk = true) {
const o = { tmpPrefix: 'git-clone' }
const ref = this.resolvedSha || this.spec.gitCommittish
const h = this.spec.hosted
const resolved = this.resolved
// can be set manually to false to fall back to actual git clone
tarballOk = tarballOk &&
h && resolved === repoUrl(h, { noCommittish: false }) && h.tarball
return cacache.tmp.withTmp(this.cache, o, async tmp => {
// if we're resolved, and have a tarball url, shell out to RemoteFetcher
if (tarballOk) {
const nameat = this.spec.name ? `${this.spec.name}@` : ''
return new RemoteFetcher(h.tarball({ noCommittish: false }), {
...this.opts,
allowGitIgnore: true,
pkgid: `git:${nameat}${this.resolved}`,
resolved: this.resolved,
integrity: null, // it'll always be different, if we have one
}).extract(tmp).then(() => handler(tmp), er => {
// fall back to ssh download if tarball fails
if (er.constructor.name.match(/^Http/)) {
return this.#clone(handler, false)
} else {
throw er
}
})
}
const sha = await (
h ? this.#cloneHosted(ref, tmp)
: this.#cloneRepo(this.spec.fetchSpec, ref, tmp)
)
this.resolvedSha = sha
if (!this.resolved) {
await this.#addGitSha(sha)
}
return handler(tmp)
})
}
// first try https, since that's faster and passphrase-less for
// public repos, and supports private repos when auth is provided.
// Fall back to SSH to support private repos
// NB: we always store the https url in resolved field if auth
// is present, otherwise ssh if the hosted type provides it
#cloneHosted (ref, tmp) {
const hosted = this.spec.hosted
return this.#cloneRepo(hosted.https({ noCommittish: true }), ref, tmp)
.catch(er => {
// Throw early since we know pathspec errors will fail again if retried
if (er instanceof git.errors.GitPathspecError) {
throw er
}
const ssh = hosted.sshurl && hosted.sshurl({ noCommittish: true })
// no fallthrough if we can't fall through or have https auth
if (!ssh || hosted.auth) {
throw er
}
return this.#cloneRepo(ssh, ref, tmp)
})
}
#cloneRepo (repo, ref, tmp) {
const { opts, spec } = this
return git.clone(repo, ref, tmp, { ...opts, spec })
}
manifest () {
if (this.package) {
return Promise.resolve(this.package)
}
return this.spec.hosted && this.resolved
? FileFetcher.prototype.manifest.apply(this)
: this.#clone(dir =>
this[_.readPackageJson](dir)
.then(mani => this.package = {
...mani,
_resolved: this.resolved,
_from: this.from,
}))
}
packument () {
return FileFetcher.prototype.packument.apply(this)
}
}
module.exports = GitFetcher

23
node_modules/pacote/lib/index.js generated vendored Normal file
View File

@@ -0,0 +1,23 @@
const { get } = require('./fetcher.js')
const GitFetcher = require('./git.js')
const RegistryFetcher = require('./registry.js')
const FileFetcher = require('./file.js')
const DirFetcher = require('./dir.js')
const RemoteFetcher = require('./remote.js')
const tarball = (spec, opts) => get(spec, opts).tarball()
tarball.stream = (spec, handler, opts) => get(spec, opts).tarballStream(handler)
tarball.file = (spec, dest, opts) => get(spec, opts).tarballFile(dest)
module.exports = {
GitFetcher,
RegistryFetcher,
FileFetcher,
DirFetcher,
RemoteFetcher,
resolve: (spec, opts) => get(spec, opts).resolve(),
extract: (spec, dest, opts) => get(spec, opts).extract(dest),
manifest: (spec, opts) => get(spec, opts).manifest(),
packument: (spec, opts) => get(spec, opts).packument(),
tarball,
}

369
node_modules/pacote/lib/registry.js generated vendored Normal file
View File

@@ -0,0 +1,369 @@
const crypto = require('node:crypto')
const PackageJson = require('@npmcli/package-json')
const pickManifest = require('npm-pick-manifest')
const ssri = require('ssri')
const npa = require('npm-package-arg')
const sigstore = require('sigstore')
const fetch = require('npm-registry-fetch')
const Fetcher = require('./fetcher.js')
const RemoteFetcher = require('./remote.js')
const pacoteVersion = require('../package.json').version
const removeTrailingSlashes = require('./util/trailing-slashes.js')
const _ = require('./util/protected.js')
// Corgis are cute. 🐕🐶
const corgiDoc = 'application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*'
const fullDoc = 'application/json'
// Some really old packages have no time field in their packument so we need a
// cutoff date.
const MISSING_TIME_CUTOFF = '2015-01-01T00:00:00.000Z'
class RegistryFetcher extends Fetcher {
#cacheKey
constructor (spec, opts) {
super(spec, opts)
// you usually don't want to fetch the same packument multiple times in
// the span of a given script or command, no matter how many pacote calls
// are made, so this lets us avoid doing that. It's only relevant for
// registry fetchers, because other types simulate their packument from
// the manifest, which they memoize on this.package, so it's very cheap
// already.
this.packumentCache = this.opts.packumentCache || null
this.registry = fetch.pickRegistry(spec, opts)
this.packumentUrl = `${removeTrailingSlashes(this.registry)}/${this.spec.escapedName}`
this.#cacheKey = `${this.fullMetadata ? 'full' : 'corgi'}:${this.packumentUrl}`
const parsed = new URL(this.registry)
const regKey = `//${parsed.host}${parsed.pathname}`
// unlike the nerf-darted auth keys, this one does *not* allow a mismatch
// of trailing slashes. It must match exactly.
if (this.opts[`${regKey}:_keys`]) {
this.registryKeys = this.opts[`${regKey}:_keys`]
}
// XXX pacote <=9 has some logic to ignore opts.resolved if
// the resolved URL doesn't go to the same registry.
// Consider reproducing that here, to throw away this.resolved
// in that case.
}
async resolve () {
// fetching the manifest sets resolved and (if present) integrity
await this.manifest()
if (!this.resolved) {
throw Object.assign(
new Error('Invalid package manifest: no `dist.tarball` field'),
{ package: this.spec.toString() }
)
}
return this.resolved
}
#headers () {
return {
// npm will override UA, but ensure that we always send *something*
'user-agent': this.opts.userAgent ||
`pacote/${pacoteVersion} node/${process.version}`,
...(this.opts.headers || {}),
'pacote-version': pacoteVersion,
'pacote-req-type': 'packument',
'pacote-pkg-id': `registry:${this.spec.name}`,
accept: this.fullMetadata ? fullDoc : corgiDoc,
}
}
async packument () {
// note this might be either an in-flight promise for a request,
// or the actual packument, but we never want to make more than
// one request at a time for the same thing regardless.
if (this.packumentCache?.has(this.#cacheKey)) {
return this.packumentCache.get(this.#cacheKey)
}
// npm-registry-fetch the packument
// set the appropriate header for corgis if fullMetadata isn't set
// return the res.json() promise
try {
const res = await fetch(this.packumentUrl, {
...this.opts,
headers: this.#headers(),
spec: this.spec,
// never check integrity for packuments themselves
integrity: null,
})
const packument = await res.json()
const contentLength = res.headers.get('content-length')
if (contentLength) {
packument._contentLength = Number(contentLength)
}
this.packumentCache?.set(this.#cacheKey, packument)
return packument
} catch (err) {
this.packumentCache?.delete(this.#cacheKey)
if (err.code !== 'E404' || this.fullMetadata) {
throw err
}
// possible that corgis are not supported by this registry
this.fullMetadata = true
return this.packument()
}
}
async manifest () {
if (this.package) {
return this.package
}
// When verifying signatures, we need to fetch the full/uncompressed
// packument to get publish time as this is not included in the
// corgi/compressed packument.
if (this.opts.verifySignatures) {
this.fullMetadata = true
}
const packument = await this.packument()
const steps = PackageJson.normalizeSteps.filter(s => s !== '_attributes')
const mani = await new PackageJson().fromContent(pickManifest(packument, this.spec.fetchSpec, {
...this.opts,
defaultTag: this.defaultTag,
before: this.before,
})).normalize({ steps }).then(p => p.content)
/* XXX add ETARGET and E403 revalidation of cached packuments here */
// add _time from packument if fetched with fullMetadata
const time = packument.time?.[mani.version]
if (time) {
mani._time = time
}
// add _resolved and _integrity from dist object
const { dist } = mani
if (dist) {
this.resolved = mani._resolved = dist.tarball
mani._from = this.from
const distIntegrity = dist.integrity ? ssri.parse(dist.integrity)
: dist.shasum ? ssri.fromHex(dist.shasum, 'sha1', { ...this.opts })
: null
if (distIntegrity) {
if (this.integrity && !this.integrity.match(distIntegrity)) {
// only bork if they have algos in common.
// otherwise we end up breaking if we have saved a sha512
// previously for the tarball, but the manifest only
// provides a sha1, which is possible for older publishes.
// Otherwise, this is almost certainly a case of holding it
// wrong, and will result in weird or insecure behavior
// later on when building package tree.
for (const algo of Object.keys(this.integrity)) {
if (distIntegrity[algo]) {
throw Object.assign(new Error(
`Integrity checksum failed when using ${algo}: ` +
`wanted ${this.integrity} but got ${distIntegrity}.`
), { code: 'EINTEGRITY' })
}
}
}
// made it this far, the integrity is worthwhile. accept it.
// the setter here will take care of merging it into what we already
// had.
this.integrity = distIntegrity
}
}
if (this.integrity) {
mani._integrity = String(this.integrity)
if (dist.signatures) {
if (this.opts.verifySignatures) {
// validate and throw on error, then set _signatures
const message = `${mani._id}:${mani._integrity}`
for (const signature of dist.signatures) {
const publicKey = this.registryKeys &&
this.registryKeys.filter(key => (key.keyid === signature.keyid))[0]
if (!publicKey) {
throw Object.assign(new Error(
`${mani._id} has a registry signature with keyid: ${signature.keyid} ` +
'but no corresponding public key can be found'
), { code: 'EMISSINGSIGNATUREKEY' })
}
const publishedTime = Date.parse(mani._time || MISSING_TIME_CUTOFF)
const validPublicKey = !publicKey.expires ||
publishedTime < Date.parse(publicKey.expires)
if (!validPublicKey) {
throw Object.assign(new Error(
`${mani._id} has a registry signature with keyid: ${signature.keyid} ` +
`but the corresponding public key has expired ${publicKey.expires}`
), { code: 'EEXPIREDSIGNATUREKEY' })
}
const verifier = crypto.createVerify('SHA256')
verifier.write(message)
verifier.end()
const valid = verifier.verify(
publicKey.pemkey,
signature.sig,
'base64'
)
if (!valid) {
throw Object.assign(new Error(
`${mani._id} has an invalid registry signature with ` +
`keyid: ${publicKey.keyid} and signature: ${signature.sig}`
), {
code: 'EINTEGRITYSIGNATURE',
keyid: publicKey.keyid,
signature: signature.sig,
resolved: mani._resolved,
integrity: mani._integrity,
})
}
}
mani._signatures = dist.signatures
} else {
mani._signatures = dist.signatures
}
}
if (dist.attestations) {
if (this.opts.verifyAttestations) {
// Always fetch attestations from the current registry host
const attestationsPath = new URL(dist.attestations.url).pathname
const attestationsUrl = removeTrailingSlashes(this.registry) + attestationsPath
const res = await fetch(attestationsUrl, {
...this.opts,
// disable integrity check for attestations json payload, we check the
// integrity in the verification steps below
integrity: null,
})
const { attestations } = await res.json()
const bundles = attestations.map(({ predicateType, bundle }) => {
const statement = JSON.parse(
Buffer.from(bundle.dsseEnvelope.payload, 'base64').toString('utf8')
)
const keyid = bundle.dsseEnvelope.signatures[0].keyid
const signature = bundle.dsseEnvelope.signatures[0].sig
return {
predicateType,
bundle,
statement,
keyid,
signature,
}
})
const attestationKeyIds = bundles.map((b) => b.keyid).filter((k) => !!k)
const attestationRegistryKeys = (this.registryKeys || [])
.filter(key => attestationKeyIds.includes(key.keyid))
if (!attestationRegistryKeys.length) {
throw Object.assign(new Error(
`${mani._id} has attestations but no corresponding public key(s) can be found`
), { code: 'EMISSINGSIGNATUREKEY' })
}
for (const { predicateType, bundle, keyid, signature, statement } of bundles) {
const publicKey = attestationRegistryKeys.find(key => key.keyid === keyid)
// Publish attestations have a keyid set and a valid public key must be found
if (keyid) {
if (!publicKey) {
throw Object.assign(new Error(
`${mani._id} has attestations with keyid: ${keyid} ` +
'but no corresponding public key can be found'
), { code: 'EMISSINGSIGNATUREKEY' })
}
const integratedTime = new Date(
Number(
bundle.verificationMaterial.tlogEntries[0].integratedTime
) * 1000
)
const validPublicKey = !publicKey.expires ||
(integratedTime < Date.parse(publicKey.expires))
if (!validPublicKey) {
throw Object.assign(new Error(
`${mani._id} has attestations with keyid: ${keyid} ` +
`but the corresponding public key has expired ${publicKey.expires}`
), { code: 'EEXPIREDSIGNATUREKEY' })
}
}
const subject = {
name: statement.subject[0].name,
sha512: statement.subject[0].digest.sha512,
}
// Only type 'version' can be turned into a PURL
const purl = this.spec.type === 'version' ? npa.toPurl(this.spec) : this.spec
// Verify the statement subject matches the package, version
if (subject.name !== purl) {
throw Object.assign(new Error(
`${mani._id} package name and version (PURL): ${purl} ` +
`doesn't match what was signed: ${subject.name}`
), { code: 'EATTESTATIONSUBJECT' })
}
// Verify the statement subject matches the tarball integrity
const integrityHexDigest = ssri.parse(this.integrity).hexDigest()
if (subject.sha512 !== integrityHexDigest) {
throw Object.assign(new Error(
`${mani._id} package integrity (hex digest): ` +
`${integrityHexDigest} ` +
`doesn't match what was signed: ${subject.sha512}`
), { code: 'EATTESTATIONSUBJECT' })
}
try {
// Provenance attestations are signed with a signing certificate
// (including the key) so we don't need to return a public key.
//
// Publish attestations are signed with a keyid so we need to
// specify a public key from the keys endpoint: `registry-host.tld/-/npm/v1/keys`
const options = {
tufCachePath: this.tufCache,
tufForceCache: true,
keySelector: publicKey ? () => publicKey.pemkey : undefined,
}
await sigstore.verify(bundle, options)
} catch (e) {
throw Object.assign(new Error(
`${mani._id} failed to verify attestation: ${e.message}`
), {
code: 'EATTESTATIONVERIFY',
predicateType,
keyid,
signature,
resolved: mani._resolved,
integrity: mani._integrity,
})
}
}
mani._attestations = dist.attestations
} else {
mani._attestations = dist.attestations
}
}
}
this.package = mani
return this.package
}
[_.tarballFromResolved] () {
// we use a RemoteFetcher to get the actual tarball stream
return new RemoteFetcher(this.resolved, {
...this.opts,
resolved: this.resolved,
pkgid: `registry:${this.spec.name}@${this.resolved}`,
})[_.tarballFromResolved]()
}
get types () {
return [
'tag',
'version',
'range',
]
}
}
module.exports = RegistryFetcher

89
node_modules/pacote/lib/remote.js generated vendored Normal file
View File

@@ -0,0 +1,89 @@
const fetch = require('npm-registry-fetch')
const { Minipass } = require('minipass')
const Fetcher = require('./fetcher.js')
const FileFetcher = require('./file.js')
const _ = require('./util/protected.js')
const pacoteVersion = require('../package.json').version
class RemoteFetcher extends Fetcher {
constructor (spec, opts) {
super(spec, opts)
this.resolved = this.spec.fetchSpec
const resolvedURL = new URL(this.resolved)
if (this.replaceRegistryHost !== 'never'
&& (this.replaceRegistryHost === 'always'
|| this.replaceRegistryHost === resolvedURL.host)) {
this.resolved = new URL(resolvedURL.pathname, this.registry).href
}
// nam is a fermented pork sausage that is good to eat
const nameat = this.spec.name ? `${this.spec.name}@` : ''
this.pkgid = opts.pkgid ? opts.pkgid : `remote:${nameat}${this.resolved}`
}
// Don't need to cache tarball fetches in pacote, because make-fetch-happen
// will write into cacache anyway.
get [_.cacheFetches] () {
return false
}
[_.tarballFromResolved] () {
const stream = new Minipass()
stream.hasIntegrityEmitter = true
const fetchOpts = {
...this.opts,
headers: this.#headers(),
spec: this.spec,
integrity: this.integrity,
algorithms: [this.pickIntegrityAlgorithm()],
}
// eslint-disable-next-line promise/always-return
fetch(this.resolved, fetchOpts).then(res => {
res.body.on('error',
/* istanbul ignore next - exceedingly rare and hard to simulate */
er => stream.emit('error', er)
)
res.body.on('integrity', i => {
this.integrity = i
stream.emit('integrity', i)
})
res.body.pipe(stream)
}).catch(er => stream.emit('error', er))
return stream
}
#headers () {
return {
// npm will override this, but ensure that we always send *something*
'user-agent': this.opts.userAgent ||
`pacote/${pacoteVersion} node/${process.version}`,
...(this.opts.headers || {}),
'pacote-version': pacoteVersion,
'pacote-req-type': 'tarball',
'pacote-pkg-id': this.pkgid,
...(this.integrity ? { 'pacote-integrity': String(this.integrity) }
: {}),
...(this.opts.headers || {}),
}
}
get types () {
return ['remote']
}
// getting a packument and/or manifest is the same as with a file: spec.
// unpack the tarball stream, and then read from the package.json file.
packument () {
return FileFetcher.prototype.packument.apply(this)
}
manifest () {
return FileFetcher.prototype.manifest.apply(this)
}
}
module.exports = RemoteFetcher

15
node_modules/pacote/lib/util/add-git-sha.js generated vendored Normal file
View File

@@ -0,0 +1,15 @@
// add a sha to a git remote url spec
const addGitSha = (spec, sha) => {
if (spec.hosted) {
const h = spec.hosted
const opt = { noCommittish: true }
const base = h.https && h.auth ? h.https(opt) : h.shortcut(opt)
return `${base}#${sha}`
} else {
// don't use new URL for this, because it doesn't handle scp urls
return spec.rawSpec.replace(/#.*$/, '') + `#${sha}`
}
}
module.exports = addGitSha

15
node_modules/pacote/lib/util/cache-dir.js generated vendored Normal file
View File

@@ -0,0 +1,15 @@
const { resolve } = require('node:path')
const { tmpdir, homedir } = require('node:os')
module.exports = (fakePlatform = false) => {
const temp = tmpdir()
const uidOrPid = process.getuid ? process.getuid() : process.pid
const home = homedir() || resolve(temp, 'npm-' + uidOrPid)
const platform = fakePlatform || process.platform
const cacheExtra = platform === 'win32' ? 'npm-cache' : '.npm'
const cacheRoot = (platform === 'win32' && process.env.LOCALAPPDATA) || home
return {
cacache: resolve(cacheRoot, cacheExtra, '_cacache'),
tufcache: resolve(cacheRoot, cacheExtra, '_tuf'),
}
}

25
node_modules/pacote/lib/util/is-package-bin.js generated vendored Normal file
View File

@@ -0,0 +1,25 @@
// Function to determine whether a path is in the package.bin set.
// Used to prevent issues when people publish a package from a
// windows machine, and then install with --no-bin-links.
//
// Note: this is not possible in remote or file fetchers, since
// we don't have the manifest until AFTER we've unpacked. But the
// main use case is registry fetching with git a distant second,
// so that's an acceptable edge case to not handle.
const binObj = (name, bin) =>
typeof bin === 'string' ? { [name]: bin } : bin
const hasBin = (pkg, path) => {
const bin = binObj(pkg.name, pkg.bin)
const p = path.replace(/^[^\\/]*\//, '')
for (const kv of Object.entries(bin)) {
if (kv[1] === p) {
return true
}
}
return false
}
module.exports = (pkg, path) =>
pkg && pkg.bin ? hasBin(pkg, path) : false

14
node_modules/pacote/lib/util/npm.js generated vendored Normal file
View File

@@ -0,0 +1,14 @@
// run an npm command
const spawn = require('@npmcli/promise-spawn')
module.exports = (npmBin, npmCommand, cwd, env, extra) => {
const isJS = npmBin.endsWith('.js')
const cmd = isJS ? process.execPath : npmBin
const args = (isJS ? [npmBin] : []).concat(npmCommand)
// when installing to run the `prepare` script for a git dep, we need
// to ensure that we don't run into a cycle of checking out packages
// in temp directories. this lets us link previously-seen repos that
// are also being prepared.
return spawn(cmd, args, { cwd, env }, extra)
}

5
node_modules/pacote/lib/util/protected.js generated vendored Normal file
View File

@@ -0,0 +1,5 @@
module.exports = {
cacheFetches: Symbol.for('pacote.Fetcher._cacheFetches'),
readPackageJson: Symbol.for('package.Fetcher._readPackageJson'),
tarballFromResolved: Symbol.for('pacote.Fetcher._tarballFromResolved'),
}

31
node_modules/pacote/lib/util/tar-create-options.js generated vendored Normal file
View File

@@ -0,0 +1,31 @@
const isPackageBin = require('./is-package-bin.js')
const tarCreateOptions = manifest => ({
cwd: manifest._resolved,
prefix: 'package/',
portable: true,
gzip: {
// forcing the level to 9 seems to avoid some
// platform specific optimizations that cause
// integrity mismatch errors due to differing
// end results after compression
level: 9,
},
// ensure that package bins are always executable
// Note that npm-packlist is already filtering out
// anything that is not a regular file, ignored by
// .npmignore or package.json "files", etc.
filter: (path, stat) => {
if (isPackageBin(manifest, path)) {
stat.mode |= 0o111
}
return true
},
// Provide a specific date in the 1980s for the benefit of zip,
// which is confounded by files dated at the Unix epoch 0.
mtime: new Date('1985-10-26T08:15:00.000Z'),
})
module.exports = tarCreateOptions

10
node_modules/pacote/lib/util/trailing-slashes.js generated vendored Normal file
View File

@@ -0,0 +1,10 @@
const removeTrailingSlashes = (input) => {
// in order to avoid regexp redos detection
let output = input
while (output.endsWith('/')) {
output = output.slice(0, -1)
}
return output
}
module.exports = removeTrailingSlashes

View File

@@ -0,0 +1,13 @@
Copyright (c) 2015, Rebecca Turner
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.

View File

@@ -0,0 +1,138 @@
# hosted-git-info
This will let you identify and transform various git hosts URLs between
protocols. It also can tell you what the URL is for the raw path for
particular file for direct access without git.
## Example
```javascript
const hostedGitInfo = require("hosted-git-info")
const info = hostedGitInfo.fromUrl("git@github.com:npm/hosted-git-info.git", opts)
/* info looks like:
{
type: "github",
domain: "github.com",
user: "npm",
project: "hosted-git-info"
}
*/
```
If the URL can't be matched with a git host, `null` will be returned. We
can match git, ssh and https urls. Additionally, we can match ssh connect
strings (`git@github.com:npm/hosted-git-info`) and shortcuts (eg,
`github:npm/hosted-git-info`). GitHub specifically, is detected in the case
of a third, unprefixed, form: `npm/hosted-git-info`.
If it does match, the returned object has properties of:
* info.type -- The short name of the service
* info.domain -- The domain for git protocol use
* info.user -- The name of the user/org on the git host
* info.project -- The name of the project on the git host
## Version Contract
The major version will be bumped any time…
* The constructor stops accepting URLs that it previously accepted.
* A method is removed.
* A method can no longer accept the number and type of arguments it previously accepted.
* A method can return a different type than it currently returns.
Implications:
* I do not consider the specific format of the urls returned from, say
`.https()` to be a part of the contract. The contract is that it will
return a string that can be used to fetch the repo via HTTPS. But what
that string looks like, specifically, can change.
* Dropping support for a hosted git provider would constitute a breaking
change.
## Usage
### const info = hostedGitInfo.fromUrl(gitSpecifier[, options])
* *gitSpecifer* is a URL of a git repository or a SCP-style specifier of one.
* *options* is an optional object. It can have the following properties:
* *noCommittish* — If true then committishes won't be included in generated URLs.
* *noGitPlus* — If true then `git+` won't be prefixed on URLs.
### const infoOrURL = hostedGitInfo.fromManifest(manifest[, options])
* *manifest* is a package manifest, such as that returned by [`pacote.manifest()`](https://npmjs.com/pacote)
* *options* is an optional object. It can have the same properties as `fromUrl` above.
## Methods
All of the methods take the same options as the `fromUrl` factory. Options
provided to a method override those provided to the constructor.
* info.file(path, opts)
Given the path of a file relative to the repository, returns a URL for
directly fetching it from the githost. If no committish was set then
`HEAD` will be used as the default.
For example `hostedGitInfo.fromUrl("git@github.com:npm/hosted-git-info.git#v1.0.0").file("package.json")`
would return `https://raw.githubusercontent.com/npm/hosted-git-info/v1.0.0/package.json`
* info.shortcut(opts)
eg, `github:npm/hosted-git-info`
* info.browse(path, fragment, opts)
eg, `https://github.com/npm/hosted-git-info/tree/v1.2.0`,
`https://github.com/npm/hosted-git-info/tree/v1.2.0/package.json`,
`https://github.com/npm/hosted-git-info/tree/v1.2.0/README.md#supported-hosts`
* info.bugs(opts)
eg, `https://github.com/npm/hosted-git-info/issues`
* info.docs(opts)
eg, `https://github.com/npm/hosted-git-info/tree/v1.2.0#readme`
* info.https(opts)
eg, `git+https://github.com/npm/hosted-git-info.git`
* info.sshurl(opts)
eg, `git+ssh://git@github.com/npm/hosted-git-info.git`
* info.ssh(opts)
eg, `git@github.com:npm/hosted-git-info.git`
* info.path(opts)
eg, `npm/hosted-git-info`
* info.tarball(opts)
eg, `https://github.com/npm/hosted-git-info/archive/v1.2.0.tar.gz`
* info.getDefaultRepresentation()
Returns the default output type. The default output type is based on the
string you passed in to be parsed
* info.toString(opts)
Uses the getDefaultRepresentation to call one of the other methods to get a URL for
this resource. As such `hostedGitInfo.fromUrl(url).toString()` will give
you a normalized version of the URL that still uses the same protocol.
Shortcuts will still be returned as shortcuts, but the special case github
form of `org/project` will be normalized to `github:org/project`.
SSH connect strings will be normalized into `git+ssh` URLs.
## Supported hosts
Currently this supports GitHub (including Gists), Bitbucket, GitLab and Sourcehut.
Pull requests for additional hosts welcome.

View File

@@ -0,0 +1,122 @@
'use strict'
const parseUrl = require('./parse-url')
// look for github shorthand inputs, such as npm/cli
const isGitHubShorthand = (arg) => {
// it cannot contain whitespace before the first #
// it cannot start with a / because that's probably an absolute file path
// but it must include a slash since repos are username/repository
// it cannot start with a . because that's probably a relative file path
// it cannot start with an @ because that's a scoped package if it passes the other tests
// it cannot contain a : before a # because that tells us that there's a protocol
// a second / may not exist before a #
const firstHash = arg.indexOf('#')
const firstSlash = arg.indexOf('/')
const secondSlash = arg.indexOf('/', firstSlash + 1)
const firstColon = arg.indexOf(':')
const firstSpace = /\s/.exec(arg)
const firstAt = arg.indexOf('@')
const spaceOnlyAfterHash = !firstSpace || (firstHash > -1 && firstSpace.index > firstHash)
const atOnlyAfterHash = firstAt === -1 || (firstHash > -1 && firstAt > firstHash)
const colonOnlyAfterHash = firstColon === -1 || (firstHash > -1 && firstColon > firstHash)
const secondSlashOnlyAfterHash = secondSlash === -1 || (firstHash > -1 && secondSlash > firstHash)
const hasSlash = firstSlash > 0
// if a # is found, what we really want to know is that the character
// immediately before # is not a /
const doesNotEndWithSlash = firstHash > -1 ? arg[firstHash - 1] !== '/' : !arg.endsWith('/')
const doesNotStartWithDot = !arg.startsWith('.')
return spaceOnlyAfterHash && hasSlash && doesNotEndWithSlash &&
doesNotStartWithDot && atOnlyAfterHash && colonOnlyAfterHash &&
secondSlashOnlyAfterHash
}
module.exports = (giturl, opts, { gitHosts, protocols }) => {
if (!giturl) {
return
}
const correctedUrl = isGitHubShorthand(giturl) ? `github:${giturl}` : giturl
const parsed = parseUrl(correctedUrl, protocols)
if (!parsed) {
return
}
const gitHostShortcut = gitHosts.byShortcut[parsed.protocol]
const gitHostDomain = gitHosts.byDomain[parsed.hostname.startsWith('www.')
? parsed.hostname.slice(4)
: parsed.hostname]
const gitHostName = gitHostShortcut || gitHostDomain
if (!gitHostName) {
return
}
const gitHostInfo = gitHosts[gitHostShortcut || gitHostDomain]
let auth = null
if (protocols[parsed.protocol]?.auth && (parsed.username || parsed.password)) {
auth = `${parsed.username}${parsed.password ? ':' + parsed.password : ''}`
}
let committish = null
let user = null
let project = null
let defaultRepresentation = null
try {
if (gitHostShortcut) {
let pathname = parsed.pathname.startsWith('/') ? parsed.pathname.slice(1) : parsed.pathname
const firstAt = pathname.indexOf('@')
// we ignore auth for shortcuts, so just trim it out
if (firstAt > -1) {
pathname = pathname.slice(firstAt + 1)
}
const lastSlash = pathname.lastIndexOf('/')
if (lastSlash > -1) {
user = decodeURIComponent(pathname.slice(0, lastSlash))
// we want nulls only, never empty strings
if (!user) {
user = null
}
project = decodeURIComponent(pathname.slice(lastSlash + 1))
} else {
project = decodeURIComponent(pathname)
}
if (project.endsWith('.git')) {
project = project.slice(0, -4)
}
if (parsed.hash) {
committish = decodeURIComponent(parsed.hash.slice(1))
}
defaultRepresentation = 'shortcut'
} else {
if (!gitHostInfo.protocols.includes(parsed.protocol)) {
return
}
const segments = gitHostInfo.extract(parsed)
if (!segments) {
return
}
user = segments.user && decodeURIComponent(segments.user)
project = decodeURIComponent(segments.project)
committish = decodeURIComponent(segments.committish)
defaultRepresentation = protocols[parsed.protocol]?.name || parsed.protocol.slice(0, -1)
}
} catch (err) {
/* istanbul ignore else */
if (err instanceof URIError) {
return
} else {
throw err
}
}
return [gitHostName, user, auth, project, committish, defaultRepresentation, opts]
}

View File

@@ -0,0 +1,231 @@
/* eslint-disable max-len */
'use strict'
const maybeJoin = (...args) => args.every(arg => arg) ? args.join('') : ''
const maybeEncode = (arg) => arg ? encodeURIComponent(arg) : ''
const formatHashFragment = (f) => f.toLowerCase()
.replace(/^\W+/g, '') // strip leading non-characters
.replace(/(?<!\W)\W+$/, '') // strip trailing non-characters
.replace(/\//g, '') // strip all slashes
.replace(/\W+/g, '-') // replace remaining non-characters with '-'
const defaults = {
sshtemplate: ({ domain, user, project, committish }) =>
`git@${domain}:${user}/${project}.git${maybeJoin('#', committish)}`,
sshurltemplate: ({ domain, user, project, committish }) =>
`git+ssh://git@${domain}/${user}/${project}.git${maybeJoin('#', committish)}`,
edittemplate: ({ domain, user, project, committish, editpath, path }) =>
`https://${domain}/${user}/${project}${maybeJoin('/', editpath, '/', maybeEncode(committish || 'HEAD'), '/', path)}`,
browsetemplate: ({ domain, user, project, committish, treepath }) =>
`https://${domain}/${user}/${project}${maybeJoin('/', treepath, '/', maybeEncode(committish))}`,
browsetreetemplate: ({ domain, user, project, committish, treepath, path, fragment, hashformat }) =>
`https://${domain}/${user}/${project}/${treepath}/${maybeEncode(committish || 'HEAD')}/${path}${maybeJoin('#', hashformat(fragment || ''))}`,
browseblobtemplate: ({ domain, user, project, committish, blobpath, path, fragment, hashformat }) =>
`https://${domain}/${user}/${project}/${blobpath}/${maybeEncode(committish || 'HEAD')}/${path}${maybeJoin('#', hashformat(fragment || ''))}`,
docstemplate: ({ domain, user, project, treepath, committish }) =>
`https://${domain}/${user}/${project}${maybeJoin('/', treepath, '/', maybeEncode(committish))}#readme`,
httpstemplate: ({ auth, domain, user, project, committish }) =>
`git+https://${maybeJoin(auth, '@')}${domain}/${user}/${project}.git${maybeJoin('#', committish)}`,
filetemplate: ({ domain, user, project, committish, path }) =>
`https://${domain}/${user}/${project}/raw/${maybeEncode(committish || 'HEAD')}/${path}`,
shortcuttemplate: ({ type, user, project, committish }) =>
`${type}:${user}/${project}${maybeJoin('#', committish)}`,
pathtemplate: ({ user, project, committish }) =>
`${user}/${project}${maybeJoin('#', committish)}`,
bugstemplate: ({ domain, user, project }) =>
`https://${domain}/${user}/${project}/issues`,
hashformat: formatHashFragment,
}
const hosts = {}
hosts.github = {
// First two are insecure and generally shouldn't be used any more, but
// they are still supported.
protocols: ['git:', 'http:', 'git+ssh:', 'git+https:', 'ssh:', 'https:'],
domain: 'github.com',
treepath: 'tree',
blobpath: 'blob',
editpath: 'edit',
filetemplate: ({ auth, user, project, committish, path }) =>
`https://${maybeJoin(auth, '@')}raw.githubusercontent.com/${user}/${project}/${maybeEncode(committish || 'HEAD')}/${path}`,
gittemplate: ({ auth, domain, user, project, committish }) =>
`git://${maybeJoin(auth, '@')}${domain}/${user}/${project}.git${maybeJoin('#', committish)}`,
tarballtemplate: ({ domain, user, project, committish }) =>
`https://codeload.${domain}/${user}/${project}/tar.gz/${maybeEncode(committish || 'HEAD')}`,
extract: (url) => {
let [, user, project, type, committish] = url.pathname.split('/', 5)
if (type && type !== 'tree') {
return
}
if (!type) {
committish = url.hash.slice(1)
}
if (project && project.endsWith('.git')) {
project = project.slice(0, -4)
}
if (!user || !project) {
return
}
return { user, project, committish }
},
}
hosts.bitbucket = {
protocols: ['git+ssh:', 'git+https:', 'ssh:', 'https:'],
domain: 'bitbucket.org',
treepath: 'src',
blobpath: 'src',
editpath: '?mode=edit',
edittemplate: ({ domain, user, project, committish, treepath, path, editpath }) =>
`https://${domain}/${user}/${project}${maybeJoin('/', treepath, '/', maybeEncode(committish || 'HEAD'), '/', path, editpath)}`,
tarballtemplate: ({ domain, user, project, committish }) =>
`https://${domain}/${user}/${project}/get/${maybeEncode(committish || 'HEAD')}.tar.gz`,
extract: (url) => {
let [, user, project, aux] = url.pathname.split('/', 4)
if (['get'].includes(aux)) {
return
}
if (project && project.endsWith('.git')) {
project = project.slice(0, -4)
}
if (!user || !project) {
return
}
return { user, project, committish: url.hash.slice(1) }
},
}
hosts.gitlab = {
protocols: ['git+ssh:', 'git+https:', 'ssh:', 'https:'],
domain: 'gitlab.com',
treepath: 'tree',
blobpath: 'tree',
editpath: '-/edit',
httpstemplate: ({ auth, domain, user, project, committish }) =>
`git+https://${maybeJoin(auth, '@')}${domain}/${user}/${project}.git${maybeJoin('#', committish)}`,
tarballtemplate: ({ domain, user, project, committish }) =>
`https://${domain}/${user}/${project}/repository/archive.tar.gz?ref=${maybeEncode(committish || 'HEAD')}`,
extract: (url) => {
const path = url.pathname.slice(1)
if (path.includes('/-/') || path.includes('/archive.tar.gz')) {
return
}
const segments = path.split('/')
let project = segments.pop()
if (project.endsWith('.git')) {
project = project.slice(0, -4)
}
const user = segments.join('/')
if (!user || !project) {
return
}
return { user, project, committish: url.hash.slice(1) }
},
}
hosts.gist = {
protocols: ['git:', 'git+ssh:', 'git+https:', 'ssh:', 'https:'],
domain: 'gist.github.com',
editpath: 'edit',
sshtemplate: ({ domain, project, committish }) =>
`git@${domain}:${project}.git${maybeJoin('#', committish)}`,
sshurltemplate: ({ domain, project, committish }) =>
`git+ssh://git@${domain}/${project}.git${maybeJoin('#', committish)}`,
edittemplate: ({ domain, user, project, committish, editpath }) =>
`https://${domain}/${user}/${project}${maybeJoin('/', maybeEncode(committish))}/${editpath}`,
browsetemplate: ({ domain, project, committish }) =>
`https://${domain}/${project}${maybeJoin('/', maybeEncode(committish))}`,
browsetreetemplate: ({ domain, project, committish, path, hashformat }) =>
`https://${domain}/${project}${maybeJoin('/', maybeEncode(committish))}${maybeJoin('#', hashformat(path))}`,
browseblobtemplate: ({ domain, project, committish, path, hashformat }) =>
`https://${domain}/${project}${maybeJoin('/', maybeEncode(committish))}${maybeJoin('#', hashformat(path))}`,
docstemplate: ({ domain, project, committish }) =>
`https://${domain}/${project}${maybeJoin('/', maybeEncode(committish))}`,
httpstemplate: ({ domain, project, committish }) =>
`git+https://${domain}/${project}.git${maybeJoin('#', committish)}`,
filetemplate: ({ user, project, committish, path }) =>
`https://gist.githubusercontent.com/${user}/${project}/raw${maybeJoin('/', maybeEncode(committish))}/${path}`,
shortcuttemplate: ({ type, project, committish }) =>
`${type}:${project}${maybeJoin('#', committish)}`,
pathtemplate: ({ project, committish }) =>
`${project}${maybeJoin('#', committish)}`,
bugstemplate: ({ domain, project }) =>
`https://${domain}/${project}`,
gittemplate: ({ domain, project, committish }) =>
`git://${domain}/${project}.git${maybeJoin('#', committish)}`,
tarballtemplate: ({ project, committish }) =>
`https://codeload.github.com/gist/${project}/tar.gz/${maybeEncode(committish || 'HEAD')}`,
extract: (url) => {
let [, user, project, aux] = url.pathname.split('/', 4)
if (aux === 'raw') {
return
}
if (!project) {
if (!user) {
return
}
project = user
user = null
}
if (project.endsWith('.git')) {
project = project.slice(0, -4)
}
return { user, project, committish: url.hash.slice(1) }
},
hashformat: function (fragment) {
return fragment && 'file-' + formatHashFragment(fragment)
},
}
hosts.sourcehut = {
protocols: ['git+ssh:', 'https:'],
domain: 'git.sr.ht',
treepath: 'tree',
blobpath: 'tree',
filetemplate: ({ domain, user, project, committish, path }) =>
`https://${domain}/${user}/${project}/blob/${maybeEncode(committish) || 'HEAD'}/${path}`,
httpstemplate: ({ domain, user, project, committish }) =>
`https://${domain}/${user}/${project}.git${maybeJoin('#', committish)}`,
tarballtemplate: ({ domain, user, project, committish }) =>
`https://${domain}/${user}/${project}/archive/${maybeEncode(committish) || 'HEAD'}.tar.gz`,
bugstemplate: () => null,
extract: (url) => {
let [, user, project, aux] = url.pathname.split('/', 4)
// tarball url
if (['archive'].includes(aux)) {
return
}
if (project && project.endsWith('.git')) {
project = project.slice(0, -4)
}
if (!user || !project) {
return
}
return { user, project, committish: url.hash.slice(1) }
},
}
for (const [name, host] of Object.entries(hosts)) {
hosts[name] = Object.assign({}, defaults, host)
}
module.exports = hosts

View File

@@ -0,0 +1,227 @@
'use strict'
const { LRUCache } = require('lru-cache')
const hosts = require('./hosts.js')
const fromUrl = require('./from-url.js')
const parseUrl = require('./parse-url.js')
const cache = new LRUCache({ max: 1000 })
function unknownHostedUrl (url) {
try {
const {
protocol,
hostname,
pathname,
} = new URL(url)
if (!hostname) {
return null
}
const proto = /(?:git\+)http:$/.test(protocol) ? 'http:' : 'https:'
const path = pathname.replace(/\.git$/, '')
return `${proto}//${hostname}${path}`
} catch {
return null
}
}
class GitHost {
constructor (type, user, auth, project, committish, defaultRepresentation, opts = {}) {
Object.assign(this, GitHost.#gitHosts[type], {
type,
user,
auth,
project,
committish,
default: defaultRepresentation,
opts,
})
}
static #gitHosts = { byShortcut: {}, byDomain: {} }
static #protocols = {
'git+ssh:': { name: 'sshurl' },
'ssh:': { name: 'sshurl' },
'git+https:': { name: 'https', auth: true },
'git:': { auth: true },
'http:': { auth: true },
'https:': { auth: true },
'git+http:': { auth: true },
}
static addHost (name, host) {
GitHost.#gitHosts[name] = host
GitHost.#gitHosts.byDomain[host.domain] = name
GitHost.#gitHosts.byShortcut[`${name}:`] = name
GitHost.#protocols[`${name}:`] = { name }
}
static fromUrl (giturl, opts) {
if (typeof giturl !== 'string') {
return
}
const key = giturl + JSON.stringify(opts || {})
if (!cache.has(key)) {
const hostArgs = fromUrl(giturl, opts, {
gitHosts: GitHost.#gitHosts,
protocols: GitHost.#protocols,
})
cache.set(key, hostArgs ? new GitHost(...hostArgs) : undefined)
}
return cache.get(key)
}
static fromManifest (manifest, opts = {}) {
if (!manifest || typeof manifest !== 'object') {
return
}
const r = manifest.repository
// TODO: look into also checking the `bugs`/`homepage` URLs
const rurl = r && (
typeof r === 'string'
? r
: typeof r === 'object' && typeof r.url === 'string'
? r.url
: null
)
if (!rurl) {
throw new Error('no repository')
}
const info = (rurl && GitHost.fromUrl(rurl.replace(/^git\+/, ''), opts)) || null
if (info) {
return info
}
const unk = unknownHostedUrl(rurl)
return GitHost.fromUrl(unk, opts) || unk
}
static parseUrl (url) {
return parseUrl(url)
}
#fill (template, opts) {
if (typeof template !== 'function') {
return null
}
const options = { ...this, ...this.opts, ...opts }
// the path should always be set so we don't end up with 'undefined' in urls
if (!options.path) {
options.path = ''
}
// template functions will insert the leading slash themselves
if (options.path.startsWith('/')) {
options.path = options.path.slice(1)
}
if (options.noCommittish) {
options.committish = null
}
const result = template(options)
return options.noGitPlus && result.startsWith('git+') ? result.slice(4) : result
}
hash () {
return this.committish ? `#${this.committish}` : ''
}
ssh (opts) {
return this.#fill(this.sshtemplate, opts)
}
sshurl (opts) {
return this.#fill(this.sshurltemplate, opts)
}
browse (path, ...args) {
// not a string, treat path as opts
if (typeof path !== 'string') {
return this.#fill(this.browsetemplate, path)
}
if (typeof args[0] !== 'string') {
return this.#fill(this.browsetreetemplate, { ...args[0], path })
}
return this.#fill(this.browsetreetemplate, { ...args[1], fragment: args[0], path })
}
// If the path is known to be a file, then browseFile should be used. For some hosts
// the url is the same as browse, but for others like GitHub a file can use both `/tree/`
// and `/blob/` in the path. When using a default committish of `HEAD` then the `/tree/`
// path will redirect to a specific commit. Using the `/blob/` path avoids this and
// does not redirect to a different commit.
browseFile (path, ...args) {
if (typeof args[0] !== 'string') {
return this.#fill(this.browseblobtemplate, { ...args[0], path })
}
return this.#fill(this.browseblobtemplate, { ...args[1], fragment: args[0], path })
}
docs (opts) {
return this.#fill(this.docstemplate, opts)
}
bugs (opts) {
return this.#fill(this.bugstemplate, opts)
}
https (opts) {
return this.#fill(this.httpstemplate, opts)
}
git (opts) {
return this.#fill(this.gittemplate, opts)
}
shortcut (opts) {
return this.#fill(this.shortcuttemplate, opts)
}
path (opts) {
return this.#fill(this.pathtemplate, opts)
}
tarball (opts) {
return this.#fill(this.tarballtemplate, { ...opts, noCommittish: false })
}
file (path, opts) {
return this.#fill(this.filetemplate, { ...opts, path })
}
edit (path, opts) {
return this.#fill(this.edittemplate, { ...opts, path })
}
getDefaultRepresentation () {
return this.default
}
toString (opts) {
if (this.default && typeof this[this.default] === 'function') {
return this[this.default](opts)
}
return this.sshurl(opts)
}
}
for (const [name, host] of Object.entries(hosts)) {
GitHost.addHost(name, host)
}
module.exports = GitHost

View File

@@ -0,0 +1,78 @@
const url = require('url')
const lastIndexOfBefore = (str, char, beforeChar) => {
const startPosition = str.indexOf(beforeChar)
return str.lastIndexOf(char, startPosition > -1 ? startPosition : Infinity)
}
const safeUrl = (u) => {
try {
return new url.URL(u)
} catch {
// this fn should never throw
}
}
// accepts input like git:github.com:user/repo and inserts the // after the first :
const correctProtocol = (arg, protocols) => {
const firstColon = arg.indexOf(':')
const proto = arg.slice(0, firstColon + 1)
if (Object.prototype.hasOwnProperty.call(protocols, proto)) {
return arg
}
const firstAt = arg.indexOf('@')
if (firstAt > -1) {
if (firstAt > firstColon) {
return `git+ssh://${arg}`
} else {
return arg
}
}
const doubleSlash = arg.indexOf('//')
if (doubleSlash === firstColon + 1) {
return arg
}
return `${arg.slice(0, firstColon + 1)}//${arg.slice(firstColon + 1)}`
}
// attempt to correct an scp style url so that it will parse with `new URL()`
const correctUrl = (giturl) => {
// ignore @ that come after the first hash since the denotes the start
// of a committish which can contain @ characters
const firstAt = lastIndexOfBefore(giturl, '@', '#')
// ignore colons that come after the hash since that could include colons such as:
// git@github.com:user/package-2#semver:^1.0.0
const lastColonBeforeHash = lastIndexOfBefore(giturl, ':', '#')
if (lastColonBeforeHash > firstAt) {
// the last : comes after the first @ (or there is no @)
// like it would in:
// proto://hostname.com:user/repo
// username@hostname.com:user/repo
// :password@hostname.com:user/repo
// username:password@hostname.com:user/repo
// proto://username@hostname.com:user/repo
// proto://:password@hostname.com:user/repo
// proto://username:password@hostname.com:user/repo
// then we replace the last : with a / to create a valid path
giturl = giturl.slice(0, lastColonBeforeHash) + '/' + giturl.slice(lastColonBeforeHash + 1)
}
if (lastIndexOfBefore(giturl, ':', '#') === -1 && giturl.indexOf('//') === -1) {
// we have no : at all
// as it would be in:
// username@hostname.com/user/repo
// then we prepend a protocol
giturl = `git+ssh://${giturl}`
}
return giturl
}
module.exports = (giturl, protocols) => {
const withProtocol = protocols ? correctProtocol(giturl, protocols) : giturl
return safeUrl(withProtocol) || safeUrl(correctUrl(withProtocol))
}

View File

@@ -0,0 +1,61 @@
{
"name": "hosted-git-info",
"version": "8.1.0",
"description": "Provides metadata and conversions from repository urls for GitHub, Bitbucket and GitLab",
"main": "./lib/index.js",
"repository": {
"type": "git",
"url": "git+https://github.com/npm/hosted-git-info.git"
},
"keywords": [
"git",
"github",
"bitbucket",
"gitlab"
],
"author": "GitHub Inc.",
"license": "ISC",
"bugs": {
"url": "https://github.com/npm/hosted-git-info/issues"
},
"homepage": "https://github.com/npm/hosted-git-info",
"scripts": {
"posttest": "npm run lint",
"snap": "tap",
"test": "tap",
"test:coverage": "tap --coverage-report=html",
"lint": "npm run eslint",
"postlint": "template-oss-check",
"lintfix": "npm run eslint -- --fix",
"template-oss-apply": "template-oss-apply --force",
"eslint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\""
},
"dependencies": {
"lru-cache": "^10.0.1"
},
"devDependencies": {
"@npmcli/eslint-config": "^5.0.0",
"@npmcli/template-oss": "4.24.3",
"tap": "^16.0.1"
},
"files": [
"bin/",
"lib/"
],
"engines": {
"node": "^18.17.0 || >=20.5.0"
},
"tap": {
"color": 1,
"coverage": true,
"nyc-arg": [
"--exclude",
"tap-snapshots/**"
]
},
"templateOSS": {
"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
"version": "4.24.3",
"publish": "true"
}
}

15
node_modules/pacote/node_modules/lru-cache/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,15 @@
The ISC License
Copyright (c) 2010-2023 Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

331
node_modules/pacote/node_modules/lru-cache/README.md generated vendored Normal file
View File

@@ -0,0 +1,331 @@
# lru-cache
A cache object that deletes the least-recently-used items.
Specify a max number of the most recently used items that you
want to keep, and this cache will keep that many of the most
recently accessed items.
This is not primarily a TTL cache, and does not make strong TTL
guarantees. There is no preemptive pruning of expired items by
default, but you _may_ set a TTL on the cache or on a single
`set`. If you do so, it will treat expired items as missing, and
delete them when fetched. If you are more interested in TTL
caching than LRU caching, check out
[@isaacs/ttlcache](http://npm.im/@isaacs/ttlcache).
As of version 7, this is one of the most performant LRU
implementations available in JavaScript, and supports a wide
diversity of use cases. However, note that using some of the
features will necessarily impact performance, by causing the
cache to have to do more work. See the "Performance" section
below.
## Installation
```bash
npm install lru-cache --save
```
## Usage
```js
// hybrid module, either works
import { LRUCache } from 'lru-cache'
// or:
const { LRUCache } = require('lru-cache')
// or in minified form for web browsers:
import { LRUCache } from 'http://unpkg.com/lru-cache@9/dist/mjs/index.min.mjs'
// At least one of 'max', 'ttl', or 'maxSize' is required, to prevent
// unsafe unbounded storage.
//
// In most cases, it's best to specify a max for performance, so all
// the required memory allocation is done up-front.
//
// All the other options are optional, see the sections below for
// documentation on what each one does. Most of them can be
// overridden for specific items in get()/set()
const options = {
max: 500,
// for use with tracking overall storage size
maxSize: 5000,
sizeCalculation: (value, key) => {
return 1
},
// for use when you need to clean up something when objects
// are evicted from the cache
dispose: (value, key) => {
freeFromMemoryOrWhatever(value)
},
// how long to live in ms
ttl: 1000 * 60 * 5,
// return stale items before removing from cache?
allowStale: false,
updateAgeOnGet: false,
updateAgeOnHas: false,
// async method to use for cache.fetch(), for
// stale-while-revalidate type of behavior
fetchMethod: async (
key,
staleValue,
{ options, signal, context }
) => {},
}
const cache = new LRUCache(options)
cache.set('key', 'value')
cache.get('key') // "value"
// non-string keys ARE fully supported
// but note that it must be THE SAME object, not
// just a JSON-equivalent object.
var someObject = { a: 1 }
cache.set(someObject, 'a value')
// Object keys are not toString()-ed
cache.set('[object Object]', 'a different value')
assert.equal(cache.get(someObject), 'a value')
// A similar object with same keys/values won't work,
// because it's a different object identity
assert.equal(cache.get({ a: 1 }), undefined)
cache.clear() // empty the cache
```
If you put more stuff in the cache, then less recently used items
will fall out. That's what an LRU cache is.
For full description of the API and all options, please see [the
LRUCache typedocs](https://isaacs.github.io/node-lru-cache/)
## Storage Bounds Safety
This implementation aims to be as flexible as possible, within
the limits of safe memory consumption and optimal performance.
At initial object creation, storage is allocated for `max` items.
If `max` is set to zero, then some performance is lost, and item
count is unbounded. Either `maxSize` or `ttl` _must_ be set if
`max` is not specified.
If `maxSize` is set, then this creates a safe limit on the
maximum storage consumed, but without the performance benefits of
pre-allocation. When `maxSize` is set, every item _must_ provide
a size, either via the `sizeCalculation` method provided to the
constructor, or via a `size` or `sizeCalculation` option provided
to `cache.set()`. The size of every item _must_ be a positive
integer.
If neither `max` nor `maxSize` are set, then `ttl` tracking must
be enabled. Note that, even when tracking item `ttl`, items are
_not_ preemptively deleted when they become stale, unless
`ttlAutopurge` is enabled. Instead, they are only purged the
next time the key is requested. Thus, if `ttlAutopurge`, `max`,
and `maxSize` are all not set, then the cache will potentially
grow unbounded.
In this case, a warning is printed to standard error. Future
versions may require the use of `ttlAutopurge` if `max` and
`maxSize` are not specified.
If you truly wish to use a cache that is bound _only_ by TTL
expiration, consider using a `Map` object, and calling
`setTimeout` to delete entries when they expire. It will perform
much better than an LRU cache.
Here is an implementation you may use, under the same
[license](./LICENSE) as this package:
```js
// a storage-unbounded ttl cache that is not an lru-cache
const cache = {
data: new Map(),
timers: new Map(),
set: (k, v, ttl) => {
if (cache.timers.has(k)) {
clearTimeout(cache.timers.get(k))
}
cache.timers.set(
k,
setTimeout(() => cache.delete(k), ttl)
)
cache.data.set(k, v)
},
get: k => cache.data.get(k),
has: k => cache.data.has(k),
delete: k => {
if (cache.timers.has(k)) {
clearTimeout(cache.timers.get(k))
}
cache.timers.delete(k)
return cache.data.delete(k)
},
clear: () => {
cache.data.clear()
for (const v of cache.timers.values()) {
clearTimeout(v)
}
cache.timers.clear()
},
}
```
If that isn't to your liking, check out
[@isaacs/ttlcache](http://npm.im/@isaacs/ttlcache).
## Storing Undefined Values
This cache never stores undefined values, as `undefined` is used
internally in a few places to indicate that a key is not in the
cache.
You may call `cache.set(key, undefined)`, but this is just
an alias for `cache.delete(key)`. Note that this has the effect
that `cache.has(key)` will return _false_ after setting it to
undefined.
```js
cache.set(myKey, undefined)
cache.has(myKey) // false!
```
If you need to track `undefined` values, and still note that the
key is in the cache, an easy workaround is to use a sigil object
of your own.
```js
import { LRUCache } from 'lru-cache'
const undefinedValue = Symbol('undefined')
const cache = new LRUCache(...)
const mySet = (key, value) =>
cache.set(key, value === undefined ? undefinedValue : value)
const myGet = (key, value) => {
const v = cache.get(key)
return v === undefinedValue ? undefined : v
}
```
## Performance
As of January 2022, version 7 of this library is one of the most
performant LRU cache implementations in JavaScript.
Benchmarks can be extremely difficult to get right. In
particular, the performance of set/get/delete operations on
objects will vary _wildly_ depending on the type of key used. V8
is highly optimized for objects with keys that are short strings,
especially integer numeric strings. Thus any benchmark which
tests _solely_ using numbers as keys will tend to find that an
object-based approach performs the best.
Note that coercing _anything_ to strings to use as object keys is
unsafe, unless you can be 100% certain that no other type of
value will be used. For example:
```js
const myCache = {}
const set = (k, v) => (myCache[k] = v)
const get = k => myCache[k]
set({}, 'please hang onto this for me')
set('[object Object]', 'oopsie')
```
Also beware of "Just So" stories regarding performance. Garbage
collection of large (especially: deep) object graphs can be
incredibly costly, with several "tipping points" where it
increases exponentially. As a result, putting that off until
later can make it much worse, and less predictable. If a library
performs well, but only in a scenario where the object graph is
kept shallow, then that won't help you if you are using large
objects as keys.
In general, when attempting to use a library to improve
performance (such as a cache like this one), it's best to choose
an option that will perform well in the sorts of scenarios where
you'll actually use it.
This library is optimized for repeated gets and minimizing
eviction time, since that is the expected need of a LRU. Set
operations are somewhat slower on average than a few other
options, in part because of that optimization. It is assumed
that you'll be caching some costly operation, ideally as rarely
as possible, so optimizing set over get would be unwise.
If performance matters to you:
1. If it's at all possible to use small integer values as keys,
and you can guarantee that no other types of values will be
used as keys, then do that, and use a cache such as
[lru-fast](https://npmjs.com/package/lru-fast), or
[mnemonist's
LRUCache](https://yomguithereal.github.io/mnemonist/lru-cache)
which uses an Object as its data store.
2. Failing that, if at all possible, use short non-numeric
strings (ie, less than 256 characters) as your keys, and use
[mnemonist's
LRUCache](https://yomguithereal.github.io/mnemonist/lru-cache).
3. If the types of your keys will be anything else, especially
long strings, strings that look like floats, objects, or some
mix of types, or if you aren't sure, then this library will
work well for you.
If you do not need the features that this library provides
(like asynchronous fetching, a variety of TTL staleness
options, and so on), then [mnemonist's
LRUMap](https://yomguithereal.github.io/mnemonist/lru-map) is
a very good option, and just slightly faster than this module
(since it does considerably less).
4. Do not use a `dispose` function, size tracking, or especially
ttl behavior, unless absolutely needed. These features are
convenient, and necessary in some use cases, and every attempt
has been made to make the performance impact minimal, but it
isn't nothing.
## Breaking Changes in Version 7
This library changed to a different algorithm and internal data
structure in version 7, yielding significantly better
performance, albeit with some subtle changes as a result.
If you were relying on the internals of LRUCache in version 6 or
before, it probably will not work in version 7 and above.
## Breaking Changes in Version 8
- The `fetchContext` option was renamed to `context`, and may no
longer be set on the cache instance itself.
- Rewritten in TypeScript, so pretty much all the types moved
around a lot.
- The AbortController/AbortSignal polyfill was removed. For this
reason, **Node version 16.14.0 or higher is now required**.
- Internal properties were moved to actual private class
properties.
- Keys and values must not be `null` or `undefined`.
- Minified export available at `'lru-cache/min'`, for both CJS
and MJS builds.
## Breaking Changes in Version 9
- Named export only, no default export.
- AbortController polyfill returned, albeit with a warning when
used.
## Breaking Changes in Version 10
- `cache.fetch()` return type is now `Promise<V | undefined>`
instead of `Promise<V | void>`. This is an irrelevant change
practically speaking, but can require changes for TypeScript
users.
For more info, see the [change log](CHANGELOG.md).

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
{
"type": "commonjs"
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
{
"type": "module"
}

116
node_modules/pacote/node_modules/lru-cache/package.json generated vendored Normal file
View File

@@ -0,0 +1,116 @@
{
"name": "lru-cache",
"publishConfig": {
"tag": "legacy-v10"
},
"description": "A cache object that deletes the least-recently-used items.",
"version": "10.4.3",
"author": "Isaac Z. Schlueter <i@izs.me>",
"keywords": [
"mru",
"lru",
"cache"
],
"sideEffects": false,
"scripts": {
"build": "npm run prepare",
"prepare": "tshy && bash fixup.sh",
"pretest": "npm run prepare",
"presnap": "npm run prepare",
"test": "tap",
"snap": "tap",
"preversion": "npm test",
"postversion": "npm publish",
"prepublishOnly": "git push origin --follow-tags",
"format": "prettier --write .",
"typedoc": "typedoc --tsconfig ./.tshy/esm.json ./src/*.ts",
"benchmark-results-typedoc": "bash scripts/benchmark-results-typedoc.sh",
"prebenchmark": "npm run prepare",
"benchmark": "make -C benchmark",
"preprofile": "npm run prepare",
"profile": "make -C benchmark profile"
},
"main": "./dist/commonjs/index.js",
"types": "./dist/commonjs/index.d.ts",
"tshy": {
"exports": {
".": "./src/index.ts",
"./min": {
"import": {
"types": "./dist/esm/index.d.ts",
"default": "./dist/esm/index.min.js"
},
"require": {
"types": "./dist/commonjs/index.d.ts",
"default": "./dist/commonjs/index.min.js"
}
}
}
},
"repository": {
"type": "git",
"url": "git://github.com/isaacs/node-lru-cache.git"
},
"devDependencies": {
"@types/node": "^20.2.5",
"@types/tap": "^15.0.6",
"benchmark": "^2.1.4",
"esbuild": "^0.17.11",
"eslint-config-prettier": "^8.5.0",
"marked": "^4.2.12",
"mkdirp": "^2.1.5",
"prettier": "^2.6.2",
"tap": "^20.0.3",
"tshy": "^2.0.0",
"tslib": "^2.4.0",
"typedoc": "^0.25.3",
"typescript": "^5.2.2"
},
"license": "ISC",
"files": [
"dist"
],
"prettier": {
"semi": false,
"printWidth": 70,
"tabWidth": 2,
"useTabs": false,
"singleQuote": true,
"jsxSingleQuote": false,
"bracketSameLine": true,
"arrowParens": "avoid",
"endOfLine": "lf"
},
"tap": {
"node-arg": [
"--expose-gc"
],
"plugin": [
"@tapjs/clock"
]
},
"exports": {
".": {
"import": {
"types": "./dist/esm/index.d.ts",
"default": "./dist/esm/index.js"
},
"require": {
"types": "./dist/commonjs/index.d.ts",
"default": "./dist/commonjs/index.js"
}
},
"./min": {
"import": {
"types": "./dist/esm/index.d.ts",
"default": "./dist/esm/index.min.js"
},
"require": {
"types": "./dist/commonjs/index.d.ts",
"default": "./dist/commonjs/index.min.js"
}
}
},
"type": "module",
"module": "./dist/esm/index.js"
}

View File

@@ -0,0 +1,15 @@
The ISC License
Copyright (c) npm, Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@@ -0,0 +1,104 @@
# npm-package-arg
[![Build Status](https://img.shields.io/github/actions/workflow/status/npm/npm-package-arg/ci.yml?branch=main)](https://github.com/npm/npm-package-arg)
Parses package name and specifier passed to commands like `npm install` or
`npm cache add`, or as found in `package.json` dependency sections.
## EXAMPLES
```javascript
const assert = require("assert")
const npa = require("npm-package-arg")
// Pass in the descriptor, and it'll return an object
try {
const parsed = npa("@bar/foo@1.2")
} catch (ex) {
}
```
## USING
`const npa = require('npm-package-arg')`
### const result = npa(*arg*[, *where*])
* *arg* - a string that you might pass to `npm install`, like:
`foo@1.2`, `@bar/foo@1.2`, `foo@user/foo`, `http://x.com/foo.tgz`,
`git+https://github.com/user/foo`, `bitbucket:user/foo`, `foo.tar.gz`,
`../foo/bar/` or `bar`. If the *arg* you provide doesn't have a specifier
part, eg `foo` then the specifier will default to `latest`.
* *where* - Optionally the path to resolve file paths relative to. Defaults to `process.cwd()`
**Throws** if the package name is invalid, a dist-tag is invalid or a URL's protocol is not supported.
### const result = npa.resolve(*name*, *spec*[, *where*])
* *name* - The name of the module you want to install. For example: `foo` or `@bar/foo`.
* *spec* - The specifier indicating where and how you can get this module. Something like:
`1.2`, `^1.7.17`, `http://x.com/foo.tgz`, `git+https://github.com/user/foo`,
`bitbucket:user/foo`, `file:foo.tar.gz` or `file:../foo/bar/`. If not
included then the default is `latest`.
* *where* - Optionally the path to resolve file paths relative to. Defaults to `process.cwd()`
**Throws** if the package name is invalid, a dist-tag is invalid or a URL's protocol is not supported.
### const purl = npa.toPurl(*arg*, *reg*)
Returns the [purl (package URL)](https://github.com/package-url/purl-spec) form of the given package name/spec.
* *arg* - A package/version string. For example: `foo@1.0.0` or `@bar/foo@2.0.0-alpha.1`.
* *reg* - Optionally the URL to the package registry. If not specified, assumes the default
`https://registry.npmjs.org`.
**Throws** if the package name is invalid, or the supplied arg can't be resolved to a purl.
## RESULT OBJECT
The objects that are returned by npm-package-arg contain the following
keys:
* `type` - One of the following strings:
* `git` - A git repo
* `tag` - A tagged version, like `"foo@latest"`
* `version` - A specific version number, like `"foo@1.2.3"`
* `range` - A version range, like `"foo@2.x"`
* `file` - A local `.tar.gz`, `.tar` or `.tgz` file.
* `directory` - A local directory.
* `remote` - An http url (presumably to a tgz)
* `alias` - A specifier with an alias, like `myalias@npm:foo@1.2.3`
* `registry` - If true this specifier refers to a resource hosted on a
registry. This is true for `tag`, `version` and `range` types.
* `name` - If known, the `name` field expected in the resulting pkg.
* `scope` - If a name is something like `@org/module` then the `scope`
field will be set to `@org`. If it doesn't have a scoped name, then
scope is `null`.
* `escapedName` - A version of `name` escaped to match the npm scoped packages
specification. Mostly used when making requests against a registry. When
`name` is `null`, `escapedName` will also be `null`.
* `rawSpec` - The specifier part that was parsed out in calls to `npa(arg)`,
or the value of `spec` in calls to `npa.resolve(name, spec)`.
* `saveSpec` - The normalized specifier, for saving to package.json files.
`null` for registry dependencies. See note below about how this is (not) encoded.
* `fetchSpec` - The version of the specifier to be used to fetch this
resource. `null` for shortcuts to hosted git dependencies as there isn't
just one URL to try with them.
* `gitRange` - If set, this is a semver specifier to match against git tags with
* `gitCommittish` - If set, this is the specific committish to use with a git dependency.
* `hosted` - If `from === 'hosted'` then this will be a `hosted-git-info`
object. This property is not included when serializing the object as
JSON.
* `raw` - The original un-modified string that was provided. If called as
`npa.resolve(name, spec)` then this will be `name + '@' + spec`.
* `subSpec` - If `type === 'alias'`, this is a Result Object for parsing the
target specifier for the alias.
## SAVE SPECS
TLDR: `file:` urls are NOT uri encoded.
Historically, npm would uri decode file package args, but did not do any uri encoding for the `saveSpec`. This meant that it generated incorrect saveSpecs for directories with characters that *looked* like encoded uri characters, and also that it could not parse directories with some unencoded uri characters (such as `%`).
In order to fix this, and to not break all existing versions of npm, this module now parses all file package args as not being uri encoded. And in order to not break all of the package.json files npm has made in the past, it also does not uri encode the saveSpec. This includes package args that start with `file:`. This does mean that npm `file:` package args are not RFC compliant, and making them so constitutes quite a breaking change.

View File

@@ -0,0 +1,481 @@
'use strict'
const isWindows = process.platform === 'win32'
const { URL } = require('node:url')
// We need to use path/win32 so that we get consistent results in tests, but this also means we need to manually convert backslashes to forward slashes when generating file: urls with paths.
const path = isWindows ? require('node:path/win32') : require('node:path')
const { homedir } = require('node:os')
const HostedGit = require('hosted-git-info')
const semver = require('semver')
const validatePackageName = require('validate-npm-package-name')
const { log } = require('proc-log')
const hasSlashes = isWindows ? /\\|[/]/ : /[/]/
const isURL = /^(?:git[+])?[a-z]+:/i
const isGit = /^[^@]+@[^:.]+\.[^:]+:.+$/i
const isFileType = /[.](?:tgz|tar.gz|tar)$/i
const isPortNumber = /:[0-9]+(\/|$)/i
const isWindowsFile = /^(?:[.]|~[/]|[/\\]|[a-zA-Z]:)/
const isPosixFile = /^(?:[.]|~[/]|[/]|[a-zA-Z]:)/
const defaultRegistry = 'https://registry.npmjs.org'
function npa (arg, where) {
let name
let spec
if (typeof arg === 'object') {
if (arg instanceof Result && (!where || where === arg.where)) {
return arg
} else if (arg.name && arg.rawSpec) {
return npa.resolve(arg.name, arg.rawSpec, where || arg.where)
} else {
return npa(arg.raw, where || arg.where)
}
}
const nameEndsAt = arg.indexOf('@', 1) // Skip possible leading @
const namePart = nameEndsAt > 0 ? arg.slice(0, nameEndsAt) : arg
if (isURL.test(arg)) {
spec = arg
} else if (isGit.test(arg)) {
spec = `git+ssh://${arg}`
// eslint-disable-next-line max-len
} else if (!namePart.startsWith('@') && (hasSlashes.test(namePart) || isFileType.test(namePart))) {
spec = arg
} else if (nameEndsAt > 0) {
name = namePart
spec = arg.slice(nameEndsAt + 1) || '*'
} else {
const valid = validatePackageName(arg)
if (valid.validForOldPackages) {
name = arg
spec = '*'
} else {
spec = arg
}
}
return resolve(name, spec, where, arg)
}
function isFileSpec (spec) {
if (!spec) {
return false
}
if (spec.toLowerCase().startsWith('file:')) {
return true
}
if (isWindows) {
return isWindowsFile.test(spec)
}
// We never hit this in windows tests, obviously
/* istanbul ignore next */
return isPosixFile.test(spec)
}
function isAliasSpec (spec) {
if (!spec) {
return false
}
return spec.toLowerCase().startsWith('npm:')
}
function resolve (name, spec, where, arg) {
const res = new Result({
raw: arg,
name: name,
rawSpec: spec,
fromArgument: arg != null,
})
if (name) {
res.name = name
}
if (!where) {
where = process.cwd()
}
if (isFileSpec(spec)) {
return fromFile(res, where)
} else if (isAliasSpec(spec)) {
return fromAlias(res, where)
}
const hosted = HostedGit.fromUrl(spec, {
noGitPlus: true,
noCommittish: true,
})
if (hosted) {
return fromHostedGit(res, hosted)
} else if (spec && isURL.test(spec)) {
return fromURL(res)
} else if (spec && (hasSlashes.test(spec) || isFileType.test(spec))) {
return fromFile(res, where)
} else {
return fromRegistry(res)
}
}
function toPurl (arg, reg = defaultRegistry) {
const res = npa(arg)
if (res.type !== 'version') {
throw invalidPurlType(res.type, res.raw)
}
// URI-encode leading @ of scoped packages
let purl = 'pkg:npm/' + res.name.replace(/^@/, '%40') + '@' + res.rawSpec
if (reg !== defaultRegistry) {
purl += '?repository_url=' + reg
}
return purl
}
function invalidPackageName (name, valid, raw) {
// eslint-disable-next-line max-len
const err = new Error(`Invalid package name "${name}" of package "${raw}": ${valid.errors.join('; ')}.`)
err.code = 'EINVALIDPACKAGENAME'
return err
}
function invalidTagName (name, raw) {
// eslint-disable-next-line max-len
const err = new Error(`Invalid tag name "${name}" of package "${raw}": Tags may not have any characters that encodeURIComponent encodes.`)
err.code = 'EINVALIDTAGNAME'
return err
}
function invalidPurlType (type, raw) {
// eslint-disable-next-line max-len
const err = new Error(`Invalid type "${type}" of package "${raw}": Purl can only be generated for "version" types.`)
err.code = 'EINVALIDPURLTYPE'
return err
}
class Result {
constructor (opts) {
this.type = opts.type
this.registry = opts.registry
this.where = opts.where
if (opts.raw == null) {
this.raw = opts.name ? `${opts.name}@${opts.rawSpec}` : opts.rawSpec
} else {
this.raw = opts.raw
}
this.name = undefined
this.escapedName = undefined
this.scope = undefined
this.rawSpec = opts.rawSpec || ''
this.saveSpec = opts.saveSpec
this.fetchSpec = opts.fetchSpec
if (opts.name) {
this.setName(opts.name)
}
this.gitRange = opts.gitRange
this.gitCommittish = opts.gitCommittish
this.gitSubdir = opts.gitSubdir
this.hosted = opts.hosted
}
// TODO move this to a getter/setter in a semver major
setName (name) {
const valid = validatePackageName(name)
if (!valid.validForOldPackages) {
throw invalidPackageName(name, valid, this.raw)
}
this.name = name
this.scope = name[0] === '@' ? name.slice(0, name.indexOf('/')) : undefined
// scoped packages in couch must have slash url-encoded, e.g. @foo%2Fbar
this.escapedName = name.replace('/', '%2f')
return this
}
toString () {
const full = []
if (this.name != null && this.name !== '') {
full.push(this.name)
}
const spec = this.saveSpec || this.fetchSpec || this.rawSpec
if (spec != null && spec !== '') {
full.push(spec)
}
return full.length ? full.join('@') : this.raw
}
toJSON () {
const result = Object.assign({}, this)
delete result.hosted
return result
}
}
// sets res.gitCommittish, res.gitRange, and res.gitSubdir
function setGitAttrs (res, committish) {
if (!committish) {
res.gitCommittish = null
return
}
// for each :: separated item:
for (const part of committish.split('::')) {
// if the item has no : the n it is a commit-ish
if (!part.includes(':')) {
if (res.gitRange) {
throw new Error('cannot override existing semver range with a committish')
}
if (res.gitCommittish) {
throw new Error('cannot override existing committish with a second committish')
}
res.gitCommittish = part
continue
}
// split on name:value
const [name, value] = part.split(':')
// if name is semver do semver lookup of ref or tag
if (name === 'semver') {
if (res.gitCommittish) {
throw new Error('cannot override existing committish with a semver range')
}
if (res.gitRange) {
throw new Error('cannot override existing semver range with a second semver range')
}
res.gitRange = decodeURIComponent(value)
continue
}
if (name === 'path') {
if (res.gitSubdir) {
throw new Error('cannot override existing path with a second path')
}
res.gitSubdir = `/${value}`
continue
}
log.warn('npm-package-arg', `ignoring unknown key "${name}"`)
}
}
// Taken from: EncodePathChars and lookup_table in src/node_url.cc
// url.pathToFileURL only returns absolute references. We can't use it to encode paths.
// encodeURI mangles windows paths. We can't use it to encode paths.
// Under the hood, url.pathToFileURL does a limited set of encoding, with an extra windows step, and then calls path.resolve.
// The encoding node does without path.resolve is not available outside of the source, so we are recreating it here.
const encodedPathChars = new Map([
['\0', '%00'],
['\t', '%09'],
['\n', '%0A'],
['\r', '%0D'],
[' ', '%20'],
['"', '%22'],
['#', '%23'],
['%', '%25'],
['?', '%3F'],
['[', '%5B'],
['\\', isWindows ? '/' : '%5C'],
[']', '%5D'],
['^', '%5E'],
['|', '%7C'],
['~', '%7E'],
])
function pathToFileURL (str) {
let result = ''
for (let i = 0; i < str.length; i++) {
result = `${result}${encodedPathChars.get(str[i]) ?? str[i]}`
}
if (result.startsWith('file:')) {
return result
}
return `file:${result}`
}
function fromFile (res, where) {
res.type = isFileType.test(res.rawSpec) ? 'file' : 'directory'
res.where = where
let rawSpec = pathToFileURL(res.rawSpec)
if (rawSpec.startsWith('file:/')) {
// XXX backwards compatibility lack of compliance with RFC 8089
// turn file://path into file:/path
if (/^file:\/\/[^/]/.test(rawSpec)) {
rawSpec = `file:/${rawSpec.slice(5)}`
}
// turn file:/../path into file:../path
// for 1 or 3 leading slashes (2 is already ruled out from handling file:// explicitly above)
if (/^\/{1,3}\.\.?(\/|$)/.test(rawSpec.slice(5))) {
rawSpec = rawSpec.replace(/^file:\/{1,3}/, 'file:')
}
}
let resolvedUrl
let specUrl
try {
// always put the '/' on "where", or else file:foo from /path/to/bar goes to /path/to/foo, when we want it to be /path/to/bar/foo
resolvedUrl = new URL(rawSpec, `${pathToFileURL(path.resolve(where))}/`)
specUrl = new URL(rawSpec)
} catch (originalError) {
const er = new Error('Invalid file: URL, must comply with RFC 8089')
throw Object.assign(er, {
raw: res.rawSpec,
spec: res,
where,
originalError,
})
}
// turn /C:/blah into just C:/blah on windows
let specPath = decodeURIComponent(specUrl.pathname)
let resolvedPath = decodeURIComponent(resolvedUrl.pathname)
if (isWindows) {
specPath = specPath.replace(/^\/+([a-z]:\/)/i, '$1')
resolvedPath = resolvedPath.replace(/^\/+([a-z]:\/)/i, '$1')
}
// replace ~ with homedir, but keep the ~ in the saveSpec
// otherwise, make it relative to where param
if (/^\/~(\/|$)/.test(specPath)) {
res.saveSpec = `file:${specPath.substr(1)}`
resolvedPath = path.resolve(homedir(), specPath.substr(3))
} else if (!path.isAbsolute(rawSpec.slice(5))) {
res.saveSpec = `file:${path.relative(where, resolvedPath)}`
} else {
res.saveSpec = `file:${path.resolve(resolvedPath)}`
}
res.fetchSpec = path.resolve(where, resolvedPath)
// re-normalize the slashes in saveSpec due to node:path/win32 behavior in windows
res.saveSpec = res.saveSpec.split('\\').join('/')
// Ignoring because this only happens in windows
/* istanbul ignore next */
if (res.saveSpec.startsWith('file://')) {
// normalization of \\win32\root paths can cause a double / which we don't want
res.saveSpec = `file:/${res.saveSpec.slice(7)}`
}
return res
}
function fromHostedGit (res, hosted) {
res.type = 'git'
res.hosted = hosted
res.saveSpec = hosted.toString({ noGitPlus: false, noCommittish: false })
res.fetchSpec = hosted.getDefaultRepresentation() === 'shortcut' ? null : hosted.toString()
setGitAttrs(res, hosted.committish)
return res
}
function unsupportedURLType (protocol, spec) {
const err = new Error(`Unsupported URL Type "${protocol}": ${spec}`)
err.code = 'EUNSUPPORTEDPROTOCOL'
return err
}
function fromURL (res) {
let rawSpec = res.rawSpec
res.saveSpec = rawSpec
if (rawSpec.startsWith('git+ssh:')) {
// git ssh specifiers are overloaded to also use scp-style git
// specifiers, so we have to parse those out and treat them special.
// They are NOT true URIs, so we can't hand them to URL.
// This regex looks for things that look like:
// git+ssh://git@my.custom.git.com:username/project.git#deadbeef
// ...and various combinations. The username in the beginning is *required*.
const matched = rawSpec.match(/^git\+ssh:\/\/([^:#]+:[^#]+(?:\.git)?)(?:#(.*))?$/i)
// Filter out all-number "usernames" which are really port numbers
// They can either be :1234 :1234/ or :1234/path but not :12abc
if (matched && !matched[1].match(isPortNumber)) {
res.type = 'git'
setGitAttrs(res, matched[2])
res.fetchSpec = matched[1]
return res
}
} else if (rawSpec.startsWith('git+file://')) {
// URL can't handle windows paths
rawSpec = rawSpec.replace(/\\/g, '/')
}
const parsedUrl = new URL(rawSpec)
// check the protocol, and then see if it's git or not
switch (parsedUrl.protocol) {
case 'git:':
case 'git+http:':
case 'git+https:':
case 'git+rsync:':
case 'git+ftp:':
case 'git+file:':
case 'git+ssh:':
res.type = 'git'
setGitAttrs(res, parsedUrl.hash.slice(1))
if (parsedUrl.protocol === 'git+file:' && /^git\+file:\/\/[a-z]:/i.test(rawSpec)) {
// URL can't handle drive letters on windows file paths, the host can't contain a :
res.fetchSpec = `git+file://${parsedUrl.host.toLowerCase()}:${parsedUrl.pathname}`
} else {
parsedUrl.hash = ''
res.fetchSpec = parsedUrl.toString()
}
if (res.fetchSpec.startsWith('git+')) {
res.fetchSpec = res.fetchSpec.slice(4)
}
break
case 'http:':
case 'https:':
res.type = 'remote'
res.fetchSpec = res.saveSpec
break
default:
throw unsupportedURLType(parsedUrl.protocol, rawSpec)
}
return res
}
function fromAlias (res, where) {
const subSpec = npa(res.rawSpec.substr(4), where)
if (subSpec.type === 'alias') {
throw new Error('nested aliases not supported')
}
if (!subSpec.registry) {
throw new Error('aliases only work for registry deps')
}
if (!subSpec.name) {
throw new Error('aliases must have a name')
}
res.subSpec = subSpec
res.registry = true
res.type = 'alias'
res.saveSpec = null
res.fetchSpec = null
return res
}
function fromRegistry (res) {
res.registry = true
const spec = res.rawSpec.trim()
// no save spec for registry components as we save based on the fetched
// version, not on the argument so this can't compute that.
res.saveSpec = null
res.fetchSpec = spec
const version = semver.valid(spec, true)
const range = semver.validRange(spec, true)
if (version) {
res.type = 'version'
} else if (range) {
res.type = 'range'
} else {
if (encodeURIComponent(spec) !== spec) {
throw invalidTagName(spec, res.raw)
}
res.type = 'tag'
}
return res
}
module.exports = npa
module.exports.resolve = resolve
module.exports.toPurl = toPurl
module.exports.Result = Result

View File

@@ -0,0 +1,61 @@
{
"name": "npm-package-arg",
"version": "12.0.2",
"description": "Parse the things that can be arguments to `npm install`",
"main": "./lib/npa.js",
"directories": {
"test": "test"
},
"files": [
"bin/",
"lib/"
],
"dependencies": {
"hosted-git-info": "^8.0.0",
"proc-log": "^5.0.0",
"semver": "^7.3.5",
"validate-npm-package-name": "^6.0.0"
},
"devDependencies": {
"@npmcli/eslint-config": "^5.0.0",
"@npmcli/template-oss": "4.23.5",
"tap": "^16.0.1"
},
"scripts": {
"test": "tap",
"snap": "tap",
"npmclilint": "npmcli-lint",
"lint": "npm run eslint",
"lintfix": "npm run eslint -- --fix",
"posttest": "npm run lint",
"postsnap": "npm run lintfix --",
"postlint": "template-oss-check",
"template-oss-apply": "template-oss-apply --force",
"eslint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\""
},
"repository": {
"type": "git",
"url": "git+https://github.com/npm/npm-package-arg.git"
},
"author": "GitHub Inc.",
"license": "ISC",
"bugs": {
"url": "https://github.com/npm/npm-package-arg/issues"
},
"homepage": "https://github.com/npm/npm-package-arg",
"engines": {
"node": "^18.17.0 || >=20.5.0"
},
"tap": {
"branches": 97,
"nyc-arg": [
"--exclude",
"tap-snapshots/**"
]
},
"templateOSS": {
"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
"version": "4.23.5",
"publish": true
}
}

80
node_modules/pacote/package.json generated vendored Normal file
View File

@@ -0,0 +1,80 @@
{
"name": "pacote",
"version": "21.0.0",
"description": "JavaScript package downloader",
"author": "GitHub Inc.",
"bin": {
"pacote": "bin/index.js"
},
"license": "ISC",
"main": "lib/index.js",
"scripts": {
"test": "tap",
"snap": "tap",
"lint": "npm run eslint",
"postlint": "template-oss-check",
"lintfix": "npm run eslint -- --fix",
"posttest": "npm run lint",
"template-oss-apply": "template-oss-apply --force",
"eslint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\""
},
"tap": {
"timeout": 300,
"nyc-arg": [
"--exclude",
"tap-snapshots/**"
]
},
"devDependencies": {
"@npmcli/arborist": "^8.0.0",
"@npmcli/eslint-config": "^5.0.0",
"@npmcli/template-oss": "4.23.4",
"hosted-git-info": "^8.0.0",
"mutate-fs": "^2.1.1",
"nock": "^13.2.4",
"npm-registry-mock": "^1.3.2",
"rimraf": "^6.0.1",
"tap": "^16.0.1"
},
"files": [
"bin/",
"lib/"
],
"keywords": [
"packages",
"npm",
"git"
],
"dependencies": {
"@npmcli/git": "^6.0.0",
"@npmcli/installed-package-contents": "^3.0.0",
"@npmcli/package-json": "^6.0.0",
"@npmcli/promise-spawn": "^8.0.0",
"@npmcli/run-script": "^9.0.0",
"cacache": "^19.0.0",
"fs-minipass": "^3.0.0",
"minipass": "^7.0.2",
"npm-package-arg": "^12.0.0",
"npm-packlist": "^10.0.0",
"npm-pick-manifest": "^10.0.0",
"npm-registry-fetch": "^18.0.0",
"proc-log": "^5.0.0",
"promise-retry": "^2.0.1",
"sigstore": "^3.0.0",
"ssri": "^12.0.0",
"tar": "^6.1.11"
},
"engines": {
"node": "^20.17.0 || >=22.9.0"
},
"repository": {
"type": "git",
"url": "git+https://github.com/npm/pacote.git"
},
"templateOSS": {
"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
"version": "4.23.4",
"windowsCI": false,
"publish": "true"
}
}