This commit is contained in:
lalBi94
2023-03-05 13:23:23 +01:00
commit 7bc56c09b5
14034 changed files with 1834369 additions and 0 deletions

13
node_modules/hosted-git-info/LICENSE generated vendored Normal file
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.

133
node_modules/hosted-git-info/README.md generated vendored Normal file
View File

@@ -0,0 +1,133 @@
# 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
var hostedGitInfo = require("hosted-git-info")
var 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
### var 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.
## 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
`master` 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/REAMDE.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, Bitbucket and GitLab. Pull requests for
additional hosts welcome.

184
node_modules/hosted-git-info/git-host-info.js generated vendored Normal file
View File

@@ -0,0 +1,184 @@
'use strict'
const maybeJoin = (...args) => args.every(arg => arg) ? args.join('') : ''
const maybeEncode = (arg) => arg ? encodeURIComponent(arg) : ''
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)}`,
browsetemplate: ({ domain, user, project, committish, treepath }) => `https://${domain}/${user}/${project}${maybeJoin('/', treepath, '/', maybeEncode(committish))}`,
browsefiletemplate: ({ domain, user, project, committish, treepath, path, fragment, hashformat }) => `https://${domain}/${user}/${project}/${treepath}/${maybeEncode(committish || 'master')}/${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) || 'master'}/${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 gitHosts = {}
gitHosts.github = Object.assign({}, defaults, {
// 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',
filetemplate: ({ auth, user, project, committish, path }) => `https://${maybeJoin(auth, '@')}raw.githubusercontent.com/${user}/${project}/${maybeEncode(committish) || 'master'}/${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) || 'master'}`,
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 }
}
})
gitHosts.bitbucket = Object.assign({}, defaults, {
protocols: ['git+ssh:', 'git+https:', 'ssh:', 'https:'],
domain: 'bitbucket.org',
treepath: 'src',
tarballtemplate: ({ domain, user, project, committish }) => `https://${domain}/${user}/${project}/get/${maybeEncode(committish) || 'master'}.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) }
}
})
gitHosts.gitlab = Object.assign({}, defaults, {
protocols: ['git+ssh:', 'git+https:', 'ssh:', 'https:'],
domain: 'gitlab.com',
treepath: 'tree',
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) || 'master'}`,
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) }
}
})
gitHosts.gist = Object.assign({}, defaults, {
protocols: ['git:', 'git+ssh:', 'git+https:', 'ssh:', 'https:'],
domain: 'gist.github.com',
sshtemplate: ({ domain, project, committish }) => `git@${domain}:${project}.git${maybeJoin('#', committish)}`,
sshurltemplate: ({ domain, project, committish }) => `git+ssh://git@${domain}/${project}.git${maybeJoin('#', committish)}`,
browsetemplate: ({ domain, project, committish }) => `https://${domain}/${project}${maybeJoin('/', maybeEncode(committish))}`,
browsefiletemplate: ({ 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) || 'master'}`,
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)
}
})
gitHosts.sourcehut = Object.assign({}, defaults, {
protocols: ['git+ssh:', 'https:'],
domain: 'git.sr.ht',
treepath: 'tree',
browsefiletemplate: ({ domain, user, project, committish, treepath, path, fragment, hashformat }) => `https://${domain}/${user}/${project}/${treepath}/${maybeEncode(committish || 'main')}/${path}${maybeJoin('#', hashformat(fragment || ''))}`,
filetemplate: ({ domain, user, project, committish, path }) => `https://${domain}/${user}/${project}/blob/${maybeEncode(committish) || 'main'}/${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) || 'main'}.tar.gz`,
bugstemplate: ({ domain, user, project }) => `https://todo.sr.ht/${user}/${project}`,
docstemplate: ({ domain, user, project, treepath, committish }) => `https://${domain}/${user}/${project}${maybeJoin('/', treepath, '/', maybeEncode(committish))}#readme`,
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) }
}
})
const names = Object.keys(gitHosts)
gitHosts.byShortcut = {}
gitHosts.byDomain = {}
for (const name of names) {
gitHosts.byShortcut[`${name}:`] = name
gitHosts.byDomain[gitHosts[name].domain] = name
}
function formatHashFragment (fragment) {
return fragment.toLowerCase().replace(/^\W+|\/|\W+$/g, '').replace(/\W+/g, '-')
}
module.exports = gitHosts

110
node_modules/hosted-git-info/git-host.js generated vendored Normal file
View File

@@ -0,0 +1,110 @@
'use strict'
const gitHosts = require('./git-host-info.js')
class GitHost {
constructor (type, user, auth, project, committish, defaultRepresentation, opts = {}) {
Object.assign(this, gitHosts[type])
this.type = type
this.user = user
this.auth = auth
this.project = project
this.committish = committish
this.default = defaultRepresentation
this.opts = opts
}
hash () {
return this.committish ? `#${this.committish}` : ''
}
ssh (opts) {
return this._fill(this.sshtemplate, opts)
}
_fill (template, opts) {
if (typeof template === 'function') {
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
}
return null
}
sshurl (opts) {
return this._fill(this.sshurltemplate, opts)
}
browse (path, fragment, opts) {
// not a string, treat path as opts
if (typeof path !== 'string') {
return this._fill(this.browsetemplate, path)
}
if (typeof fragment !== 'string') {
opts = fragment
fragment = null
}
return this._fill(this.browsefiletemplate, { ...opts, fragment, 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 })
}
getDefaultRepresentation () {
return this.default
}
toString (opts) {
if (this.default && typeof this[this.default] === 'function') {
return this[this.default](opts)
}
return this.sshurl(opts)
}
}
module.exports = GitHost

237
node_modules/hosted-git-info/index.js generated vendored Normal file
View File

@@ -0,0 +1,237 @@
'use strict'
const url = require('url')
const gitHosts = require('./git-host-info.js')
const GitHost = module.exports = require('./git-host.js')
const LRU = require('lru-cache')
const cache = new LRU({ max: 1000 })
const protocolToRepresentationMap = {
'git+ssh:': 'sshurl',
'git+https:': 'https',
'ssh:': 'sshurl',
'git:': 'git'
}
function protocolToRepresentation (protocol) {
return protocolToRepresentationMap[protocol] || protocol.slice(0, -1)
}
const authProtocols = {
'git:': true,
'https:': true,
'git+https:': true,
'http:': true,
'git+http:': true
}
const knownProtocols = Object.keys(gitHosts.byShortcut).concat(['http:', 'https:', 'git:', 'git+ssh:', 'git+https:', 'ssh:'])
module.exports.fromUrl = function (giturl, opts) {
if (typeof giturl !== 'string') {
return
}
const key = giturl + JSON.stringify(opts || {})
if (!cache.has(key)) {
cache.set(key, fromUrl(giturl, opts))
}
return cache.get(key)
}
function fromUrl (giturl, opts) {
if (!giturl) {
return
}
const url = isGitHubShorthand(giturl) ? 'github:' + giturl : correctProtocol(giturl)
const parsed = parseGitUrl(url)
if (!parsed) {
return parsed
}
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 (authProtocols[parsed.protocol] && (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 = protocolToRepresentation(parsed.protocol)
}
} catch (err) {
/* istanbul ignore else */
if (err instanceof URIError) {
return
} else {
throw err
}
}
return new GitHost(gitHostName, user, auth, project, committish, defaultRepresentation, opts)
}
// accepts input like git:github.com:user/repo and inserts the // after the first :
const correctProtocol = (arg) => {
const firstColon = arg.indexOf(':')
const proto = arg.slice(0, firstColon + 1)
if (knownProtocols.includes(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)
}
// 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
}
// attempt to correct an scp style url so that it will parse with `new URL()`
const correctUrl = (giturl) => {
const firstAt = giturl.indexOf('@')
const lastHash = giturl.lastIndexOf('#')
let firstColon = giturl.indexOf(':')
let lastColon = giturl.lastIndexOf(':', lastHash > -1 ? lastHash : Infinity)
let corrected
if (lastColon > 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
corrected = giturl.slice(0, lastColon) + '/' + giturl.slice(lastColon + 1)
// // and we find our new : positions
firstColon = corrected.indexOf(':')
lastColon = corrected.lastIndexOf(':')
}
if (firstColon === -1 && giturl.indexOf('//') === -1) {
// we have no : at all
// as it would be in:
// username@hostname.com/user/repo
// then we prepend a protocol
corrected = `git+ssh://${corrected}`
}
return corrected
}
// try to parse the url as its given to us, if that throws
// then we try to clean the url and parse that result instead
// THIS FUNCTION SHOULD NEVER THROW
const parseGitUrl = (giturl) => {
let result
try {
result = new url.URL(giturl)
} catch (err) {}
if (result) {
return result
}
const correctedUrl = correctUrl(giturl)
try {
result = new url.URL(correctedUrl)
} catch (err) {}
return result
}

View File

@@ -0,0 +1,15 @@
The ISC License
Copyright (c) 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.

View File

@@ -0,0 +1,166 @@
# lru cache
A cache object that deletes the least-recently-used items.
[![Build Status](https://travis-ci.org/isaacs/node-lru-cache.svg?branch=master)](https://travis-ci.org/isaacs/node-lru-cache) [![Coverage Status](https://coveralls.io/repos/isaacs/node-lru-cache/badge.svg?service=github)](https://coveralls.io/github/isaacs/node-lru-cache)
## Installation:
```javascript
npm install lru-cache --save
```
## Usage:
```javascript
var LRU = require("lru-cache")
, options = { max: 500
, length: function (n, key) { return n * 2 + key.length }
, dispose: function (key, n) { n.close() }
, maxAge: 1000 * 60 * 60 }
, cache = new LRU(options)
, otherCache = new LRU(50) // sets just the max size
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.reset() // empty the cache
```
If you put more stuff in it, then items will fall out.
If you try to put an oversized thing in it, then it'll fall out right
away.
## Options
* `max` The maximum size of the cache, checked by applying the length
function to all values in the cache. Not setting this is kind of
silly, since that's the whole purpose of this lib, but it defaults
to `Infinity`. Setting it to a non-number or negative number will
throw a `TypeError`. Setting it to 0 makes it be `Infinity`.
* `maxAge` Maximum age in ms. Items are not pro-actively pruned out
as they age, but if you try to get an item that is too old, it'll
drop it and return undefined instead of giving it to you.
Setting this to a negative value will make everything seem old!
Setting it to a non-number will throw a `TypeError`.
* `length` Function that is used to calculate the length of stored
items. If you're storing strings or buffers, then you probably want
to do something like `function(n, key){return n.length}`. The default is
`function(){return 1}`, which is fine if you want to store `max`
like-sized things. The item is passed as the first argument, and
the key is passed as the second argumnet.
* `dispose` Function that is called on items when they are dropped
from the cache. This can be handy if you want to close file
descriptors or do other cleanup tasks when items are no longer
accessible. Called with `key, value`. It's called *before*
actually removing the item from the internal cache, so if you want
to immediately put it back in, you'll have to do that in a
`nextTick` or `setTimeout` callback or it won't do anything.
* `stale` By default, if you set a `maxAge`, it'll only actually pull
stale items out of the cache when you `get(key)`. (That is, it's
not pre-emptively doing a `setTimeout` or anything.) If you set
`stale:true`, it'll return the stale value before deleting it. If
you don't set this, then it'll return `undefined` when you try to
get a stale entry, as if it had already been deleted.
* `noDisposeOnSet` By default, if you set a `dispose()` method, then
it'll be called whenever a `set()` operation overwrites an existing
key. If you set this option, `dispose()` will only be called when a
key falls out of the cache, not when it is overwritten.
* `updateAgeOnGet` When using time-expiring entries with `maxAge`,
setting this to `true` will make each item's effective time update
to the current time whenever it is retrieved from cache, causing it
to not expire. (It can still fall out of cache based on recency of
use, of course.)
## API
* `set(key, value, maxAge)`
* `get(key) => value`
Both of these will update the "recently used"-ness of the key.
They do what you think. `maxAge` is optional and overrides the
cache `maxAge` option if provided.
If the key is not found, `get()` will return `undefined`.
The key and val can be any value.
* `peek(key)`
Returns the key value (or `undefined` if not found) without
updating the "recently used"-ness of the key.
(If you find yourself using this a lot, you *might* be using the
wrong sort of data structure, but there are some use cases where
it's handy.)
* `del(key)`
Deletes a key out of the cache.
* `reset()`
Clear the cache entirely, throwing away all values.
* `has(key)`
Check if a key is in the cache, without updating the recent-ness
or deleting it for being stale.
* `forEach(function(value,key,cache), [thisp])`
Just like `Array.prototype.forEach`. Iterates over all the keys
in the cache, in order of recent-ness. (Ie, more recently used
items are iterated over first.)
* `rforEach(function(value,key,cache), [thisp])`
The same as `cache.forEach(...)` but items are iterated over in
reverse order. (ie, less recently used items are iterated over
first.)
* `keys()`
Return an array of the keys in the cache.
* `values()`
Return an array of the values in the cache.
* `length`
Return total length of objects in cache taking into account
`length` options function.
* `itemCount`
Return total quantity of objects currently in cache. Note, that
`stale` (see options) items are returned as part of this item
count.
* `dump()`
Return an array of the cache entries ready for serialization and usage
with 'destinationCache.load(arr)`.
* `load(cacheEntriesArray)`
Loads another cache entries array, obtained with `sourceCache.dump()`,
into the cache. The destination cache is reset before loading new entries
* `prune()`
Manually iterates over the entire cache proactively pruning old entries

View File

@@ -0,0 +1,334 @@
'use strict'
// A linked list to keep track of recently-used-ness
const Yallist = require('yallist')
const MAX = Symbol('max')
const LENGTH = Symbol('length')
const LENGTH_CALCULATOR = Symbol('lengthCalculator')
const ALLOW_STALE = Symbol('allowStale')
const MAX_AGE = Symbol('maxAge')
const DISPOSE = Symbol('dispose')
const NO_DISPOSE_ON_SET = Symbol('noDisposeOnSet')
const LRU_LIST = Symbol('lruList')
const CACHE = Symbol('cache')
const UPDATE_AGE_ON_GET = Symbol('updateAgeOnGet')
const naiveLength = () => 1
// lruList is a yallist where the head is the youngest
// item, and the tail is the oldest. the list contains the Hit
// objects as the entries.
// Each Hit object has a reference to its Yallist.Node. This
// never changes.
//
// cache is a Map (or PseudoMap) that matches the keys to
// the Yallist.Node object.
class LRUCache {
constructor (options) {
if (typeof options === 'number')
options = { max: options }
if (!options)
options = {}
if (options.max && (typeof options.max !== 'number' || options.max < 0))
throw new TypeError('max must be a non-negative number')
// Kind of weird to have a default max of Infinity, but oh well.
const max = this[MAX] = options.max || Infinity
const lc = options.length || naiveLength
this[LENGTH_CALCULATOR] = (typeof lc !== 'function') ? naiveLength : lc
this[ALLOW_STALE] = options.stale || false
if (options.maxAge && typeof options.maxAge !== 'number')
throw new TypeError('maxAge must be a number')
this[MAX_AGE] = options.maxAge || 0
this[DISPOSE] = options.dispose
this[NO_DISPOSE_ON_SET] = options.noDisposeOnSet || false
this[UPDATE_AGE_ON_GET] = options.updateAgeOnGet || false
this.reset()
}
// resize the cache when the max changes.
set max (mL) {
if (typeof mL !== 'number' || mL < 0)
throw new TypeError('max must be a non-negative number')
this[MAX] = mL || Infinity
trim(this)
}
get max () {
return this[MAX]
}
set allowStale (allowStale) {
this[ALLOW_STALE] = !!allowStale
}
get allowStale () {
return this[ALLOW_STALE]
}
set maxAge (mA) {
if (typeof mA !== 'number')
throw new TypeError('maxAge must be a non-negative number')
this[MAX_AGE] = mA
trim(this)
}
get maxAge () {
return this[MAX_AGE]
}
// resize the cache when the lengthCalculator changes.
set lengthCalculator (lC) {
if (typeof lC !== 'function')
lC = naiveLength
if (lC !== this[LENGTH_CALCULATOR]) {
this[LENGTH_CALCULATOR] = lC
this[LENGTH] = 0
this[LRU_LIST].forEach(hit => {
hit.length = this[LENGTH_CALCULATOR](hit.value, hit.key)
this[LENGTH] += hit.length
})
}
trim(this)
}
get lengthCalculator () { return this[LENGTH_CALCULATOR] }
get length () { return this[LENGTH] }
get itemCount () { return this[LRU_LIST].length }
rforEach (fn, thisp) {
thisp = thisp || this
for (let walker = this[LRU_LIST].tail; walker !== null;) {
const prev = walker.prev
forEachStep(this, fn, walker, thisp)
walker = prev
}
}
forEach (fn, thisp) {
thisp = thisp || this
for (let walker = this[LRU_LIST].head; walker !== null;) {
const next = walker.next
forEachStep(this, fn, walker, thisp)
walker = next
}
}
keys () {
return this[LRU_LIST].toArray().map(k => k.key)
}
values () {
return this[LRU_LIST].toArray().map(k => k.value)
}
reset () {
if (this[DISPOSE] &&
this[LRU_LIST] &&
this[LRU_LIST].length) {
this[LRU_LIST].forEach(hit => this[DISPOSE](hit.key, hit.value))
}
this[CACHE] = new Map() // hash of items by key
this[LRU_LIST] = new Yallist() // list of items in order of use recency
this[LENGTH] = 0 // length of items in the list
}
dump () {
return this[LRU_LIST].map(hit =>
isStale(this, hit) ? false : {
k: hit.key,
v: hit.value,
e: hit.now + (hit.maxAge || 0)
}).toArray().filter(h => h)
}
dumpLru () {
return this[LRU_LIST]
}
set (key, value, maxAge) {
maxAge = maxAge || this[MAX_AGE]
if (maxAge && typeof maxAge !== 'number')
throw new TypeError('maxAge must be a number')
const now = maxAge ? Date.now() : 0
const len = this[LENGTH_CALCULATOR](value, key)
if (this[CACHE].has(key)) {
if (len > this[MAX]) {
del(this, this[CACHE].get(key))
return false
}
const node = this[CACHE].get(key)
const item = node.value
// dispose of the old one before overwriting
// split out into 2 ifs for better coverage tracking
if (this[DISPOSE]) {
if (!this[NO_DISPOSE_ON_SET])
this[DISPOSE](key, item.value)
}
item.now = now
item.maxAge = maxAge
item.value = value
this[LENGTH] += len - item.length
item.length = len
this.get(key)
trim(this)
return true
}
const hit = new Entry(key, value, len, now, maxAge)
// oversized objects fall out of cache automatically.
if (hit.length > this[MAX]) {
if (this[DISPOSE])
this[DISPOSE](key, value)
return false
}
this[LENGTH] += hit.length
this[LRU_LIST].unshift(hit)
this[CACHE].set(key, this[LRU_LIST].head)
trim(this)
return true
}
has (key) {
if (!this[CACHE].has(key)) return false
const hit = this[CACHE].get(key).value
return !isStale(this, hit)
}
get (key) {
return get(this, key, true)
}
peek (key) {
return get(this, key, false)
}
pop () {
const node = this[LRU_LIST].tail
if (!node)
return null
del(this, node)
return node.value
}
del (key) {
del(this, this[CACHE].get(key))
}
load (arr) {
// reset the cache
this.reset()
const now = Date.now()
// A previous serialized cache has the most recent items first
for (let l = arr.length - 1; l >= 0; l--) {
const hit = arr[l]
const expiresAt = hit.e || 0
if (expiresAt === 0)
// the item was created without expiration in a non aged cache
this.set(hit.k, hit.v)
else {
const maxAge = expiresAt - now
// dont add already expired items
if (maxAge > 0) {
this.set(hit.k, hit.v, maxAge)
}
}
}
}
prune () {
this[CACHE].forEach((value, key) => get(this, key, false))
}
}
const get = (self, key, doUse) => {
const node = self[CACHE].get(key)
if (node) {
const hit = node.value
if (isStale(self, hit)) {
del(self, node)
if (!self[ALLOW_STALE])
return undefined
} else {
if (doUse) {
if (self[UPDATE_AGE_ON_GET])
node.value.now = Date.now()
self[LRU_LIST].unshiftNode(node)
}
}
return hit.value
}
}
const isStale = (self, hit) => {
if (!hit || (!hit.maxAge && !self[MAX_AGE]))
return false
const diff = Date.now() - hit.now
return hit.maxAge ? diff > hit.maxAge
: self[MAX_AGE] && (diff > self[MAX_AGE])
}
const trim = self => {
if (self[LENGTH] > self[MAX]) {
for (let walker = self[LRU_LIST].tail;
self[LENGTH] > self[MAX] && walker !== null;) {
// We know that we're about to delete this one, and also
// what the next least recently used key will be, so just
// go ahead and set it now.
const prev = walker.prev
del(self, walker)
walker = prev
}
}
}
const del = (self, node) => {
if (node) {
const hit = node.value
if (self[DISPOSE])
self[DISPOSE](hit.key, hit.value)
self[LENGTH] -= hit.length
self[CACHE].delete(hit.key)
self[LRU_LIST].removeNode(node)
}
}
class Entry {
constructor (key, value, length, now, maxAge) {
this.key = key
this.value = value
this.length = length
this.now = now
this.maxAge = maxAge || 0
}
}
const forEachStep = (self, fn, node, thisp) => {
let hit = node.value
if (isStale(self, hit)) {
del(self, node)
if (!self[ALLOW_STALE])
hit = undefined
}
if (hit)
fn.call(thisp, hit.value, hit.key, self)
}
module.exports = LRUCache

View File

@@ -0,0 +1,34 @@
{
"name": "lru-cache",
"description": "A cache object that deletes the least-recently-used items.",
"version": "6.0.0",
"author": "Isaac Z. Schlueter <i@izs.me>",
"keywords": [
"mru",
"lru",
"cache"
],
"scripts": {
"test": "tap",
"snap": "tap",
"preversion": "npm test",
"postversion": "npm publish",
"prepublishOnly": "git push origin --follow-tags"
},
"main": "index.js",
"repository": "git://github.com/isaacs/node-lru-cache.git",
"devDependencies": {
"benchmark": "^2.1.4",
"tap": "^14.10.7"
},
"license": "ISC",
"dependencies": {
"yallist": "^4.0.0"
},
"files": [
"index.js"
],
"engines": {
"node": ">=10"
}
}

51
node_modules/hosted-git-info/package.json generated vendored Normal file
View File

@@ -0,0 +1,51 @@
{
"name": "hosted-git-info",
"version": "4.1.0",
"description": "Provides metadata and conversions from repository urls for GitHub, Bitbucket and GitLab",
"main": "index.js",
"repository": {
"type": "git",
"url": "git+https://github.com/npm/hosted-git-info.git"
},
"keywords": [
"git",
"github",
"bitbucket",
"gitlab"
],
"author": "Rebecca Turner <me@re-becca.org> (http://re-becca.org)",
"license": "ISC",
"bugs": {
"url": "https://github.com/npm/hosted-git-info/issues"
},
"homepage": "https://github.com/npm/hosted-git-info",
"scripts": {
"posttest": "standard",
"postversion": "npm publish",
"prepublishOnly": "git push origin --follow-tags",
"preversion": "npm test",
"snap": "tap",
"test": "tap",
"test:coverage": "tap --coverage-report=html"
},
"dependencies": {
"lru-cache": "^6.0.0"
},
"devDependencies": {
"standard": "^16.0.3",
"standard-version": "^9.1.0",
"tap": "^15.1.6"
},
"files": [
"index.js",
"git-host.js",
"git-host-info.js"
],
"engines": {
"node": ">=10"
},
"tap": {
"color": 1,
"coverage": true
}
}