@preventive/upstream 1.0.0-alpha.5 → 1.0.0-alpha.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/npm.d.ts +5 -1
- package/package.d.ts +1 -1
- package/package.json +2 -1
- package/src/npm/registry.js +19 -0
- package/src/npm/repos.js +12 -11
- package/src/npm/tarball.js +5 -4
- package/src/package.js +3 -1
package/npm.d.ts
CHANGED
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
// cache path, otherwise. Responses are read up to a size limit and within
|
|
7
7
|
// a timeout, as github.d.ts describes, and lookups for many names go to
|
|
8
8
|
// the registry eight at a time.
|
|
9
|
+
//
|
|
10
|
+
// Where NPM_TOKEN is set, it is sent with the requests for a scoped
|
|
11
|
+
// package, so a private one can be read.
|
|
9
12
|
|
|
10
13
|
// Where cached answers live, resolved when set. Unset by default, and
|
|
11
14
|
// unset means no cache: every read misses and every write is skipped.
|
|
@@ -47,7 +50,8 @@ export interface PackageRepo {
|
|
|
47
50
|
export function resolvePackageRepos(packageNames: Iterable<string>, options?: { cachedOnly?: boolean }): Promise<Map<string, PackageRepo>>
|
|
48
51
|
|
|
49
52
|
// The disk cache resolvePackageRepos reads and writes, a month per entry.
|
|
50
|
-
// A read answers null for anything but a usable entry
|
|
53
|
+
// A read answers null for anything but a usable entry, one written by a
|
|
54
|
+
// release that resolved repos by other rules included; a write answers
|
|
51
55
|
// false where it could not write, and never throws.
|
|
52
56
|
export function readPackageRepoCache(name: string): Promise<PackageRepo | null>
|
|
53
57
|
export function writePackageRepoCache(name: string, github: string, directory?: string): Promise<boolean>
|
package/package.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export interface PackageRepoLink {
|
|
|
11
11
|
url?: string
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
// Read off `
|
|
14
|
+
// Read off `repository`, `bugs` and `homepage`, in that order, from a
|
|
15
15
|
// package.json or the registry's document for a version of one. Both
|
|
16
16
|
// fields absent where nothing in it names a GitHub repo; a throw only
|
|
17
17
|
// for something that is not an object at all.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@preventive/upstream",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.7",
|
|
4
4
|
"description": "Minimal npm, crates.io and Soldeer registry clients, a GitHub client, and a git checkout reader: the calls we use, nothing more",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -78,6 +78,7 @@
|
|
|
78
78
|
"src/github/read.js",
|
|
79
79
|
"src/github/write.js",
|
|
80
80
|
"src/http.js",
|
|
81
|
+
"src/npm/registry.js",
|
|
81
82
|
"src/npm/repos.js",
|
|
82
83
|
"src/npm/tarball.js",
|
|
83
84
|
"src/package.js",
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
|
|
3
|
+
import { show } from '../args.js'
|
|
4
|
+
import { NPM_REGISTRY, buildUrl, request } from '../http.js'
|
|
5
|
+
|
|
6
|
+
// Private packages are always scoped.
|
|
7
|
+
export function withNpmToken(name, url, options = {}) {
|
|
8
|
+
const method = options.method ?? 'GET'
|
|
9
|
+
assert.ok(method === 'GET' && `${new URL(url)}` === url && url.startsWith(`${NPM_REGISTRY}/${name}/`), `Unexpected request for ${name}: ${method} ${url}`)
|
|
10
|
+
const token = process.env.NPM_TOKEN
|
|
11
|
+
return token && name.startsWith('@') ? { ...options, method, headers: { ...options.headers, Authorization: `Bearer ${token}` } } : options
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function getDocument(method, name, spec) {
|
|
15
|
+
const url = buildUrl(NPM_REGISTRY, [...name.split('/'), spec])
|
|
16
|
+
const json = await request(url, withNpmToken(name, url, { as: 'json' }))
|
|
17
|
+
assert.ok(json?.name === name, `${method}: the registry answered for ${show(json?.name)}, not ${name}`)
|
|
18
|
+
return json
|
|
19
|
+
}
|
package/src/npm/repos.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import assert from 'node:assert/strict'
|
|
2
2
|
|
|
3
|
-
import { assertNames, assertPackageName, assertRepo, isRepo
|
|
3
|
+
import { assertNames, assertPackageName, assertRepo, isRepo } from '../args.js'
|
|
4
4
|
import { readRecord, writeRecord } from '../cache.js'
|
|
5
|
-
import {
|
|
5
|
+
import { isNotFound, recover } from '../http.js'
|
|
6
6
|
import { assertRepoDirectory, getRepo, isRepoDirectory } from '../package.js'
|
|
7
7
|
import { pool } from '../pool.js'
|
|
8
|
+
import { getDocument } from './registry.js'
|
|
8
9
|
|
|
9
10
|
const DIR = 'npm/repos'
|
|
10
11
|
const CONCURRENCY = 8
|
|
12
|
+
// Stamped on each entry, and raised when getRepo would answer differently:
|
|
13
|
+
// 2 is `repository` taking precedence over `bugs`.
|
|
14
|
+
const VERSION = 2
|
|
11
15
|
|
|
12
16
|
// `latest`, not the full packument, which is megabytes of version history.
|
|
13
|
-
async
|
|
14
|
-
const json = await request(buildUrl(NPM_REGISTRY, [...name.split('/'), 'latest']), { as: 'json' })
|
|
15
|
-
assert.ok(json?.name === name, `${method}: the registry answered for ${show(json?.name)}, not ${name}`)
|
|
16
|
-
return getRepo(json)
|
|
17
|
-
}
|
|
17
|
+
const fetchRepo = async (method, name) => getRepo(await getDocument(method, name, 'latest'))
|
|
18
18
|
|
|
19
19
|
export async function getGitHub(name) {
|
|
20
20
|
assertPackageName('getGitHub', 'name', name)
|
|
@@ -23,12 +23,13 @@ export async function getGitHub(name) {
|
|
|
23
23
|
return link
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
// An entry
|
|
27
|
-
//
|
|
26
|
+
// An entry under another VERSION was resolved by other rules, and may
|
|
27
|
+
// name the repo a stale tracker does: a miss, as is one without
|
|
28
|
+
// `directory`, which would read as a package at the repo root.
|
|
28
29
|
export async function readPackageRepoCache(name) {
|
|
29
30
|
assertPackageName('readPackageRepoCache', 'name', name)
|
|
30
31
|
const entry = await readRecord(DIR, name)
|
|
31
|
-
if (!isRepo(entry
|
|
32
|
+
if (entry?.v !== VERSION || !isRepo(entry.github) || !isRepoDirectory(entry.directory)) return null
|
|
32
33
|
return { github: entry.github, ...(entry.directory && { directory: entry.directory }) }
|
|
33
34
|
}
|
|
34
35
|
|
|
@@ -38,7 +39,7 @@ export async function writePackageRepoCache(name, github, directory = '') {
|
|
|
38
39
|
assertPackageName('writePackageRepoCache', 'name', name)
|
|
39
40
|
assertRepo('writePackageRepoCache', 'github', github)
|
|
40
41
|
assertRepoDirectory('writePackageRepoCache', 'directory', directory)
|
|
41
|
-
return await writeRecord(DIR, name, { github, directory })
|
|
42
|
+
return await writeRecord(DIR, name, { v: VERSION, github, directory })
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
export async function resolvePackageRepos(packageNames, options = {}) {
|
package/src/npm/tarball.js
CHANGED
|
@@ -5,7 +5,8 @@ import { join, resolve } from 'node:path'
|
|
|
5
5
|
|
|
6
6
|
import { assertArgs, assertPackage, assertion, matches, show } from '../args.js'
|
|
7
7
|
import { verifiedDownload } from '../download.js'
|
|
8
|
-
import { NPM_REGISTRY, buildUrl
|
|
8
|
+
import { NPM_REGISTRY, buildUrl } from '../http.js'
|
|
9
|
+
import { getDocument, withNpmToken } from './registry.js'
|
|
9
10
|
|
|
10
11
|
const DIR = 'npm/tarballs' // No expiry: the registry never takes a version twice.
|
|
11
12
|
// One sha512 and nothing else, as the registry writes it: a sha1, a second
|
|
@@ -29,8 +30,8 @@ function checkedDist(method, name, version, dist) {
|
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
async function getDist(method, name, version) {
|
|
32
|
-
const json = await
|
|
33
|
-
assert.ok(json
|
|
33
|
+
const json = await getDocument(method, name, version)
|
|
34
|
+
assert.ok(json.version === version, `${method}: the registry answered for ${name}@${show(json.version)}, not ${name}@${version}`)
|
|
34
35
|
return checkedDist(method, name, version, { tarball: json.dist?.tarball, integrity: json.dist?.integrity })
|
|
35
36
|
}
|
|
36
37
|
|
|
@@ -70,5 +71,5 @@ export async function verifyDist(name, version, dist) {
|
|
|
70
71
|
export async function getTarball(name, version, dist) {
|
|
71
72
|
assertPackage('getTarball', name, version)
|
|
72
73
|
const { tarball, integrity } = dist === undefined ? await getDist('getTarball', name, version) : checkedDist('getTarball', name, version, dist)
|
|
73
|
-
return await verifiedDownload({ method: 'getTarball', dir: DIR, what: `${name}@${version}`, ext: 'tgz', algorithm: 'sha512', expected: integrity, local: localPaths(name, version, integrity), locate: () => tarball })
|
|
74
|
+
return await verifiedDownload({ method: 'getTarball', dir: DIR, what: `${name}@${version}`, ext: 'tgz', algorithm: 'sha512', expected: integrity, local: localPaths(name, version, integrity), locate: () => tarball, options: withNpmToken(name, tarball) })
|
|
74
75
|
}
|
package/src/package.js
CHANGED
|
@@ -41,7 +41,9 @@ export function getRepo(pkg) {
|
|
|
41
41
|
const { bugs, repository } = pkg
|
|
42
42
|
const declared = repositoryRepo(urlOf(repository))
|
|
43
43
|
const homepage = homepageRegex.exec(homepageUrl(pkg.homepage))?.groups
|
|
44
|
-
|
|
44
|
+
// `repository` first: it is the field a publisher maintains, where a
|
|
45
|
+
// `bugs` tracker can be left pointing at a former owner or misspelt.
|
|
46
|
+
const github = [declared, repoIn(bugsRegex, urlOf(bugs)), homepage?.repo].find(isRepo)
|
|
45
47
|
if (github === undefined) return {}
|
|
46
48
|
const directory = (sameName(declared, github) && repoSubdirectory(repository?.directory))
|
|
47
49
|
|| (sameName(homepage?.repo, github) && repoSubdirectory(decodePath(homepage.directory)))
|