@packall/core 0.0.1 → 0.1.0
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/dist/{Archive.d.ts → archive.d.ts} +5 -5
- package/dist/archive.d.ts.map +1 -0
- package/dist/{Bundle.d.ts → bundle.d.ts} +51 -36
- package/dist/bundle.d.ts.map +1 -0
- package/dist/{DependencyRange.d.ts → dependency-range.d.ts} +2 -2
- package/dist/dependency-range.d.ts.map +1 -0
- package/dist/{Download.d.ts → download.d.ts} +7 -7
- package/dist/download.d.ts.map +1 -0
- package/dist/enums/artifact-kind.d.ts +8 -0
- package/dist/enums/artifact-kind.d.ts.map +1 -0
- package/dist/enums/edge-kind.d.ts +29 -0
- package/dist/enums/edge-kind.d.ts.map +1 -0
- package/dist/enums/input-file-kind.d.ts +12 -0
- package/dist/enums/input-file-kind.d.ts.map +1 -0
- package/dist/enums/layout.d.ts +23 -0
- package/dist/enums/layout.d.ts.map +1 -0
- package/dist/enums/lockfile-format.d.ts +9 -0
- package/dist/enums/lockfile-format.d.ts.map +1 -0
- package/dist/enums/phase.d.ts +11 -0
- package/dist/enums/phase.d.ts.map +1 -0
- package/dist/{Errors.d.ts → errors.d.ts} +49 -7
- package/dist/errors.d.ts.map +1 -0
- package/dist/index.d.ts +44 -28
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2416 -1180
- package/dist/index.js.map +1 -1
- package/dist/input-file.d.ts +77 -0
- package/dist/input-file.d.ts.map +1 -0
- package/dist/{Integrity.d.ts → integrity.d.ts} +3 -3
- package/dist/integrity.d.ts.map +1 -0
- package/dist/{Layout.d.ts → layout.d.ts} +1 -1
- package/dist/{Layout.d.ts.map → layout.d.ts.map} +1 -1
- package/dist/locked-resolve.d.ts +47 -0
- package/dist/locked-resolve.d.ts.map +1 -0
- package/dist/lockfile/bun.d.ts +3 -0
- package/dist/lockfile/bun.d.ts.map +1 -0
- package/dist/lockfile/detect.d.ts +31 -0
- package/dist/lockfile/detect.d.ts.map +1 -0
- package/dist/lockfile/json.d.ts +23 -0
- package/dist/lockfile/json.d.ts.map +1 -0
- package/dist/lockfile/names.d.ts +16 -0
- package/dist/lockfile/names.d.ts.map +1 -0
- package/dist/lockfile/npm.d.ts +3 -0
- package/dist/lockfile/npm.d.ts.map +1 -0
- package/dist/lockfile/parse.d.ts +23 -0
- package/dist/lockfile/parse.d.ts.map +1 -0
- package/dist/lockfile/pnpm.d.ts +20 -0
- package/dist/lockfile/pnpm.d.ts.map +1 -0
- package/dist/lockfile/tree-builder.d.ts +70 -0
- package/dist/lockfile/tree-builder.d.ts.map +1 -0
- package/dist/lockfile/types.d.ts +70 -0
- package/dist/lockfile/types.d.ts.map +1 -0
- package/dist/{Manifest.d.ts → manifest.d.ts} +7 -15
- package/dist/manifest.d.ts.map +1 -0
- package/dist/{Options.d.ts → options.d.ts} +9 -27
- package/dist/options.d.ts.map +1 -0
- package/dist/{Platform.d.ts → platform.d.ts} +9 -9
- package/dist/platform.d.ts.map +1 -0
- package/dist/{Progress.d.ts → progress.d.ts} +10 -9
- package/dist/progress.d.ts.map +1 -0
- package/dist/{Registry.d.ts → registry.d.ts} +42 -44
- package/dist/registry.d.ts.map +1 -0
- package/dist/{Resolve.d.ts → resolve.d.ts} +25 -26
- package/dist/resolve.d.ts.map +1 -0
- package/dist/schemas/lenient.d.ts +38 -0
- package/dist/schemas/lenient.d.ts.map +1 -0
- package/dist/schemas/package-json.d.ts +44 -0
- package/dist/schemas/package-json.d.ts.map +1 -0
- package/dist/{Spec.d.ts → spec.d.ts} +4 -4
- package/dist/spec.d.ts.map +1 -0
- package/dist/types/value-of.d.ts +9 -0
- package/dist/types/value-of.d.ts.map +1 -0
- package/dist/utils/is-record.d.ts +8 -0
- package/dist/utils/is-record.d.ts.map +1 -0
- package/package.json +3 -2
- package/src/{Archive.ts → archive.ts} +92 -92
- package/src/{Bundle.ts → bundle.ts} +616 -570
- package/src/{DependencyRange.ts → dependency-range.ts} +115 -115
- package/src/{Download.ts → download.ts} +133 -132
- package/src/enums/artifact-kind.ts +9 -0
- package/src/enums/edge-kind.ts +32 -0
- package/src/enums/input-file-kind.ts +13 -0
- package/src/enums/layout.ts +25 -0
- package/src/enums/lockfile-format.ts +10 -0
- package/src/enums/phase.ts +12 -0
- package/src/{Errors.ts → errors.ts} +87 -5
- package/src/index.ts +133 -35
- package/src/{InputFile.ts → input-file.ts} +127 -44
- package/src/{Integrity.ts → integrity.ts} +121 -122
- package/src/locked-resolve.ts +461 -0
- package/src/lockfile/bun.ts +207 -0
- package/src/lockfile/detect.ts +80 -0
- package/src/lockfile/json.ts +132 -0
- package/src/lockfile/names.ts +20 -0
- package/src/lockfile/npm.ts +308 -0
- package/src/lockfile/parse.ts +56 -0
- package/src/lockfile/pnpm.ts +225 -0
- package/src/lockfile/tree-builder.ts +166 -0
- package/src/lockfile/types.ts +74 -0
- package/src/{Manifest.ts → manifest.ts} +133 -133
- package/src/{Options.ts → options.ts} +7 -27
- package/src/{Platform.ts → platform.ts} +23 -23
- package/src/{Progress.ts → progress.ts} +106 -106
- package/src/{Registry.ts → registry.ts} +115 -121
- package/src/{Resolve.ts → resolve.ts} +537 -528
- package/src/schemas/lenient.ts +123 -0
- package/src/schemas/package-json.ts +27 -0
- package/src/{Spec.ts → spec.ts} +11 -19
- package/src/types/value-of.ts +8 -0
- package/src/utils/is-record.ts +8 -0
- package/dist/Archive.d.ts.map +0 -1
- package/dist/Bundle.d.ts.map +0 -1
- package/dist/DependencyRange.d.ts.map +0 -1
- package/dist/Download.d.ts.map +0 -1
- package/dist/Errors.d.ts.map +0 -1
- package/dist/InputFile.d.ts +0 -46
- package/dist/InputFile.d.ts.map +0 -1
- package/dist/Integrity.d.ts.map +0 -1
- package/dist/Manifest.d.ts.map +0 -1
- package/dist/Options.d.ts.map +0 -1
- package/dist/Platform.d.ts.map +0 -1
- package/dist/Progress.d.ts.map +0 -1
- package/dist/Registry.d.ts.map +0 -1
- package/dist/Resolve.d.ts.map +0 -1
- package/dist/Spec.d.ts.map +0 -1
- /package/src/{Layout.ts → layout.ts} +0 -0
|
@@ -1,122 +1,121 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Checksum verification for downloaded tarballs.
|
|
3
|
-
*
|
|
4
|
-
* A bundle is a supply-chain artifact that gets imported wholesale into a
|
|
5
|
-
* corporate registry, so "the bytes I got are the bytes the registry promised"
|
|
6
|
-
* is the one property worth being strict about. A failed check discards the
|
|
7
|
-
* download rather than warning and carrying on.
|
|
8
|
-
*
|
|
9
|
-
* Implemented directly against `node:crypto` rather than pulling in `ssri`,
|
|
10
|
-
* because the subset of Subresource Integrity that npm actually publishes is
|
|
11
|
-
* small and entirely mechanical.
|
|
12
|
-
*/
|
|
13
|
-
import { createHash, timingSafeEqual } from "node:crypto"
|
|
14
|
-
|
|
15
|
-
/** One parsed SRI hash. */
|
|
16
|
-
export
|
|
17
|
-
readonly algorithm: string
|
|
18
|
-
/** Base64 digest, exactly as written in the SRI string. */
|
|
19
|
-
readonly digest: string
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const SUPPORTED = new Set(["sha512", "sha384", "sha256", "sha1"])
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Parses an SRI string such as `sha512-abc...==`.
|
|
26
|
-
*
|
|
27
|
-
* npm permits several space-separated hashes; we keep every one we can verify
|
|
28
|
-
* and ignore algorithms Node does not implement.
|
|
29
|
-
*/
|
|
30
|
-
export const parseIntegrity = (integrity: string): ReadonlyArray<IntegrityHash> => {
|
|
31
|
-
const hashes: Array<IntegrityHash> = []
|
|
32
|
-
for (const token of integrity.trim().split(/\s+/)) {
|
|
33
|
-
if (token.length === 0) continue
|
|
34
|
-
const dash = token.indexOf("-")
|
|
35
|
-
if (dash <= 0) continue
|
|
36
|
-
const algorithm = token.slice(0, dash).toLowerCase()
|
|
37
|
-
const digest = token.slice(dash + 1)
|
|
38
|
-
if (!SUPPORTED.has(algorithm) || digest.length === 0) continue
|
|
39
|
-
hashes.push({ algorithm, digest })
|
|
40
|
-
}
|
|
41
|
-
return hashes
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** Computes the base64 digest of some bytes under one algorithm. */
|
|
45
|
-
export const digestOf = (data: Uint8Array, algorithm: string): string =>
|
|
46
|
-
createHash(algorithm).update(data).digest("base64")
|
|
47
|
-
|
|
48
|
-
/** Computes the hex digest of some bytes — the form legacy `shasum` uses. */
|
|
49
|
-
export const hexDigestOf = (data: Uint8Array, algorithm: string): string =>
|
|
50
|
-
createHash(algorithm).update(data).digest("hex")
|
|
51
|
-
|
|
52
|
-
/** Result of checking a download. */
|
|
53
|
-
export type VerificationResult =
|
|
54
|
-
/** Verified against at least one checksum. */
|
|
55
|
-
| { readonly _tag: "Verified"; readonly using: string }
|
|
56
|
-
/** The registry advertised no checksum we could use. */
|
|
57
|
-
| { readonly _tag: "Unverifiable"; readonly reason: string }
|
|
58
|
-
/** A checksum was present and did not match. */
|
|
59
|
-
| { readonly _tag: "Mismatch"; readonly expected: string; readonly actual: string }
|
|
60
|
-
|
|
61
|
-
/** Constant-time comparison of two digest strings of equal length. */
|
|
62
|
-
const digestsEqual = (a: string, b: string): boolean => {
|
|
63
|
-
const left = Buffer.from(a)
|
|
64
|
-
const right = Buffer.from(b)
|
|
65
|
-
// `timingSafeEqual` throws on unequal lengths, which is itself a mismatch.
|
|
66
|
-
return left.length === right.length && timingSafeEqual(left, right)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Checks bytes against whichever checksums the registry supplied.
|
|
71
|
-
*
|
|
72
|
-
* SRI is preferred; `shasum` is the fallback for versions published before
|
|
73
|
-
* integrity strings existed. If the registry gave us neither, that is reported
|
|
74
|
-
* as `Unverifiable` rather than silently treated as a pass — the caller decides
|
|
75
|
-
* whether to tolerate it.
|
|
76
|
-
*/
|
|
77
|
-
export const verify = (
|
|
78
|
-
data: Uint8Array,
|
|
79
|
-
dist: { readonly integrity?: string | undefined; readonly shasum?: string | undefined }
|
|
80
|
-
): VerificationResult => {
|
|
81
|
-
if (dist.integrity !== undefined && dist.integrity.length > 0) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Checksum verification for downloaded tarballs.
|
|
3
|
+
*
|
|
4
|
+
* A bundle is a supply-chain artifact that gets imported wholesale into a
|
|
5
|
+
* corporate registry, so "the bytes I got are the bytes the registry promised"
|
|
6
|
+
* is the one property worth being strict about. A failed check discards the
|
|
7
|
+
* download rather than warning and carrying on.
|
|
8
|
+
*
|
|
9
|
+
* Implemented directly against `node:crypto` rather than pulling in `ssri`,
|
|
10
|
+
* because the subset of Subresource Integrity that npm actually publishes is
|
|
11
|
+
* small and entirely mechanical.
|
|
12
|
+
*/
|
|
13
|
+
import { createHash, timingSafeEqual } from "node:crypto"
|
|
14
|
+
|
|
15
|
+
/** One parsed SRI hash. */
|
|
16
|
+
export type IntegrityHash = {
|
|
17
|
+
readonly algorithm: string
|
|
18
|
+
/** Base64 digest, exactly as written in the SRI string. */
|
|
19
|
+
readonly digest: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const SUPPORTED = new Set(["sha512", "sha384", "sha256", "sha1"])
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Parses an SRI string such as `sha512-abc...==`.
|
|
26
|
+
*
|
|
27
|
+
* npm permits several space-separated hashes; we keep every one we can verify
|
|
28
|
+
* and ignore algorithms Node does not implement.
|
|
29
|
+
*/
|
|
30
|
+
export const parseIntegrity = (integrity: string): ReadonlyArray<IntegrityHash> => {
|
|
31
|
+
const hashes: Array<IntegrityHash> = []
|
|
32
|
+
for (const token of integrity.trim().split(/\s+/)) {
|
|
33
|
+
if (token.length === 0) continue
|
|
34
|
+
const dash = token.indexOf("-")
|
|
35
|
+
if (dash <= 0) continue
|
|
36
|
+
const algorithm = token.slice(0, dash).toLowerCase()
|
|
37
|
+
const digest = token.slice(dash + 1)
|
|
38
|
+
if (!SUPPORTED.has(algorithm) || digest.length === 0) continue
|
|
39
|
+
hashes.push({ algorithm, digest })
|
|
40
|
+
}
|
|
41
|
+
return hashes
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Computes the base64 digest of some bytes under one algorithm. */
|
|
45
|
+
export const digestOf = (data: Uint8Array, algorithm: string): string =>
|
|
46
|
+
createHash(algorithm).update(data).digest("base64")
|
|
47
|
+
|
|
48
|
+
/** Computes the hex digest of some bytes — the form legacy `shasum` uses. */
|
|
49
|
+
export const hexDigestOf = (data: Uint8Array, algorithm: string): string =>
|
|
50
|
+
createHash(algorithm).update(data).digest("hex")
|
|
51
|
+
|
|
52
|
+
/** Result of checking a download. */
|
|
53
|
+
export type VerificationResult =
|
|
54
|
+
/** Verified against at least one checksum. */
|
|
55
|
+
| { readonly _tag: "Verified"; readonly using: string }
|
|
56
|
+
/** The registry advertised no checksum we could use. */
|
|
57
|
+
| { readonly _tag: "Unverifiable"; readonly reason: string }
|
|
58
|
+
/** A checksum was present and did not match. */
|
|
59
|
+
| { readonly _tag: "Mismatch"; readonly expected: string; readonly actual: string }
|
|
60
|
+
|
|
61
|
+
/** Constant-time comparison of two digest strings of equal length. */
|
|
62
|
+
const digestsEqual = (a: string, b: string): boolean => {
|
|
63
|
+
const left = Buffer.from(a)
|
|
64
|
+
const right = Buffer.from(b)
|
|
65
|
+
// `timingSafeEqual` throws on unequal lengths, which is itself a mismatch.
|
|
66
|
+
return left.length === right.length && timingSafeEqual(left, right)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Checks bytes against whichever checksums the registry supplied.
|
|
71
|
+
*
|
|
72
|
+
* SRI is preferred; `shasum` is the fallback for versions published before
|
|
73
|
+
* integrity strings existed. If the registry gave us neither, that is reported
|
|
74
|
+
* as `Unverifiable` rather than silently treated as a pass — the caller decides
|
|
75
|
+
* whether to tolerate it.
|
|
76
|
+
*/
|
|
77
|
+
export const verify = (
|
|
78
|
+
data: Uint8Array,
|
|
79
|
+
dist: { readonly integrity?: string | undefined; readonly shasum?: string | undefined }
|
|
80
|
+
): VerificationResult => {
|
|
81
|
+
if (dist.integrity !== undefined && dist.integrity.length > 0) {
|
|
82
|
+
// Strongest first, so a mismatch is reported against the best hash present.
|
|
83
|
+
const [best] = parseIntegrity(dist.integrity).toSorted(
|
|
84
|
+
(a, b) => strength(b.algorithm) - strength(a.algorithm)
|
|
85
|
+
)
|
|
86
|
+
if (best === undefined) {
|
|
87
|
+
return { _tag: "Unverifiable", reason: `no supported algorithm in "${dist.integrity}"` }
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const actual = digestOf(data, best.algorithm)
|
|
91
|
+
return digestsEqual(actual, best.digest)
|
|
92
|
+
? { _tag: "Verified", using: best.algorithm }
|
|
93
|
+
: {
|
|
94
|
+
_tag: "Mismatch",
|
|
95
|
+
expected: `${best.algorithm}-${best.digest}`,
|
|
96
|
+
actual: `${best.algorithm}-${actual}`
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (dist.shasum !== undefined && dist.shasum.length > 0) {
|
|
101
|
+
const actual = hexDigestOf(data, "sha1")
|
|
102
|
+
return digestsEqual(actual.toLowerCase(), dist.shasum.toLowerCase())
|
|
103
|
+
? { _tag: "Verified", using: "sha1" }
|
|
104
|
+
: { _tag: "Mismatch", expected: `sha1-${dist.shasum}`, actual: `sha1-${actual}` }
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return { _tag: "Unverifiable", reason: "registry advertised no integrity or shasum" }
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const strength = (algorithm: string): number => {
|
|
111
|
+
switch (algorithm) {
|
|
112
|
+
case "sha512":
|
|
113
|
+
return 4
|
|
114
|
+
case "sha384":
|
|
115
|
+
return 3
|
|
116
|
+
case "sha256":
|
|
117
|
+
return 2
|
|
118
|
+
default:
|
|
119
|
+
return 1
|
|
120
|
+
}
|
|
121
|
+
}
|