-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmanifest.ts
More file actions
187 lines (173 loc) · 5.88 KB
/
Copy pathmanifest.ts
File metadata and controls
187 lines (173 loc) · 5.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/**
* @file Lazy-loader for socket-btm's `node:smol-manifest`. `node:smol-manifest`
* is the manifest + lockfile parser exposed by socket-btm's smol Node binary.
* It parses package.json, package-lock (npm v1/v2/v3), yarn.lock (classic +
* berry), and pnpm-lock.yaml (v5/v6/v9) with internal primordial-hardened
* parsing. Returns `undefined` on stock Node + non-Node runtimes. Result is
* cached across calls. Callers fall back to the JS parsers under
* `src/eco/<pm>/parse-*` on the undefined path.
*
* @internal — used by `src/eco/manifest/*` leaves to resolve the
* smol-aware parsers. Most callers should import the specific leaf
* (`@socketsecurity/lib/eco/manifest/parse`), which already routes
* through this when smol is present.
*/
import { isNodeBuiltin, requireBuiltin } from '../node/module'
import type { EcosystemString } from '../eco/purl'
/**
* Dependency-relationship tag on a parsed package reference.
*
* - `prod` — runtime dependency
* - `dev` — devDependency
* - `optional` — optionalDependency
* - `peer` — peerDependency
*/
export type DepType = 'prod' | 'dev' | 'optional' | 'peer'
/**
* A single package entry inside a parsed lockfile. Frozen plain object with
* `__proto__: null` on smol; JS-fallback parsers return the same shape so
* consumers can't tell which impl ran.
*/
export interface PackageRef {
readonly name: string
readonly version: string
readonly resolved: string | undefined
readonly integrity: string | undefined
readonly ecosystem: EcosystemString
readonly depType: DepType
readonly isDev: boolean
readonly isOptional: boolean
readonly isPeer: boolean
readonly isBundled: boolean
readonly license?: string | undefined
readonly vcsUrl: string | undefined
readonly vcsCommit: string | undefined
readonly dependencies: readonly string[]
}
/**
* A single dependency entry inside a parsed manifest (package.json).
*/
export interface ManifestDep {
readonly name: string
readonly versionRange: string
readonly type: DepType
readonly optional: boolean
}
/**
* Result of parsing a manifest (e.g. `package.json`).
*/
export interface ParsedManifest {
readonly type: 'manifest'
readonly name: string | undefined
readonly version: string | undefined
readonly description: string | undefined
readonly license: string | undefined
readonly repository: string | undefined
readonly dependencies: readonly ManifestDep[]
readonly ecosystem: EcosystemString
}
/**
* Result of parsing a lockfile (npm/yarn/pnpm). `_index` is a private
* name→index map (or `name→number[]` for multi-version) used by `getPackage` /
* `getPackageVersions` for O(1) lookup.
*/
export interface ParsedLockfile {
readonly type: 'lockfile'
readonly lockVersion: string
readonly ecosystem: EcosystemString
readonly packages: readonly PackageRef[]
readonly _index: Readonly<Record<string, number | readonly number[]>>
}
/**
* Format descriptor returned by `detectFormat`.
*/
export interface FormatDescriptor {
readonly ecosystem: EcosystemString
readonly type: 'manifest' | 'lockfile'
readonly format?: 'npm' | 'yarn' | 'pnpm' | 'composer' | 'cargo' | undefined
}
/**
* Supported-files index returned by `supportedFiles`.
*/
export interface SupportedFiles {
readonly manifests: readonly string[]
readonly lockfiles: readonly string[]
}
/**
* Statistics returned by `analyzeLockfile`.
*/
export interface LockfileStats {
readonly totalPackages: number
readonly prodDeps: number
readonly devDeps: number
readonly optionalDeps: number
readonly byEcosystem: Readonly<Record<string, number>>
readonly maxDepth: number
readonly avgDepth: number
}
/**
* Error class thrown by every parser on invalid / unsupported input. The `code`
* field is one of:
*
* - `ERR_INVALID_JSON` — JSON.parse failure
* - `ERR_UNKNOWN_FORMAT` — filename or content didn't match a parser
* - `ERR_UNSUPPORTED` — ecosystem not yet implemented
*/
export interface ManifestErrorLike extends Error {
readonly name: 'ManifestError'
readonly code: string
}
/**
* Surface of `node:smol-manifest`. See socket-btm's
* additions/source-patched/lib/smol-manifest.js for the canonical shape.
*/
export interface SmolManifestBinding {
parse(filename: string, content: string): ParsedManifest | ParsedLockfile
parseManifest(content: string, ecosystem: EcosystemString): ParsedManifest
parseLockfile(
content: string,
ecosystem: EcosystemString,
format?: 'npm' | 'yarn' | 'pnpm' | 'composer' | 'cargo',
): ParsedLockfile
createStreamingParser(
content: string,
ecosystem: EcosystemString,
): AsyncIterableIterator<PackageRef>
analyzeLockfile(lockfile: ParsedLockfile): LockfileStats
getPackage(lockfile: ParsedLockfile, name: string): PackageRef | undefined
getPackageVersions(
lockfile: ParsedLockfile,
name: string,
): readonly PackageRef[]
findPackages(
lockfile: ParsedLockfile,
pattern: string | RegExp,
): readonly PackageRef[]
detectFormat(filename: string): FormatDescriptor | undefined
readonly supportedFiles: SupportedFiles
readonly ManifestError: new (
message: string,
code: string,
) => ManifestErrorLike
}
let cachedSmolManifest: SmolManifestBinding | undefined
let smolManifestProbed = false
/**
* Returns `node:smol-manifest` when running on the smol Node binary, otherwise
* `undefined`. Result is cached across calls.
*/
export function getSmolManifest(): SmolManifestBinding | undefined {
if (!smolManifestProbed) {
smolManifestProbed = true
/* c8 ignore start - smol Node binary only. */
if (isNodeBuiltin('node:smol-manifest')) {
// requireBuiltin passes a non-literal specifier so AOT bundlers and
// compilers keep this optional binding external; unreached on stock Node.
cachedSmolManifest = requireBuiltin(
'node:smol-manifest',
) as SmolManifestBinding
}
/* c8 ignore stop */
}
return cachedSmolManifest
}