@antongolub/lockfile 0.0.0-snapshot.49 → 0.0.0-snapshot.50
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/README.md +66 -46
- package/dist/{_npm-flat-types-BtjAgSy6.d.ts → _npm-flat-types-6ABSGUI9.d.ts} +1 -1
- package/dist/{_pnpm-flat-core-u9QE3m8R.d.ts → _pnpm-flat-core-CGSknThP.d.ts} +1 -1
- package/dist/{_yarn-berry-core-tgctDDH2.d.ts → _yarn-berry-core-E1kzf3tA.d.ts} +1 -1
- package/dist/complete.d.ts +2 -2
- package/dist/formats/bun-text.d.ts +1 -1
- package/dist/formats/bun-text.js +40 -14
- package/dist/formats/npm-1.d.ts +1 -1
- package/dist/formats/npm-1.js +45 -2
- package/dist/formats/npm-2.d.ts +2 -2
- package/dist/formats/npm-2.js +45 -15
- package/dist/formats/npm-3.d.ts +2 -2
- package/dist/formats/npm-3.js +43 -14
- package/dist/formats/pnpm-v5.d.ts +1 -1
- package/dist/formats/pnpm-v5.js +47 -18
- package/dist/formats/pnpm-v6.d.ts +2 -2
- package/dist/formats/pnpm-v6.js +43 -14
- package/dist/formats/pnpm-v9.d.ts +2 -2
- package/dist/formats/pnpm-v9.js +43 -14
- package/dist/formats/yarn-berry-v10.d.ts +2 -2
- package/dist/formats/yarn-berry-v10.js +42 -102
- package/dist/formats/yarn-berry-v4.d.ts +2 -2
- package/dist/formats/yarn-berry-v4.js +42 -102
- package/dist/formats/yarn-berry-v5.d.ts +2 -2
- package/dist/formats/yarn-berry-v5.js +42 -102
- package/dist/formats/yarn-berry-v6.d.ts +2 -2
- package/dist/formats/yarn-berry-v6.js +42 -102
- package/dist/formats/yarn-berry-v7.d.ts +2 -2
- package/dist/formats/yarn-berry-v7.js +42 -102
- package/dist/formats/yarn-berry-v8.d.ts +2 -2
- package/dist/formats/yarn-berry-v8.js +42 -102
- package/dist/formats/yarn-berry-v9.d.ts +2 -2
- package/dist/formats/yarn-berry-v9.js +42 -102
- package/dist/formats/yarn-classic.d.ts +1 -1
- package/dist/formats/yarn-classic.js +61 -24
- package/dist/{graph-DlYNIpXt.d.ts → graph-DL9MNz81.d.ts} +12 -2
- package/dist/index.d.ts +4 -4
- package/dist/index.js +134 -141
- package/dist/{modify-DJj58soJ.d.ts → modify-CT0MxdsI.d.ts} +2 -2
- package/dist/modify.d.ts +3 -3
- package/dist/{optimize-eaNonOKo.d.ts → optimize-DVIavJ_L.d.ts} +1 -1
- package/dist/optimize.d.ts +2 -2
- package/dist/registry.d.ts +3 -3
- package/dist/registry.js +35 -2
- package/dist/{types-Ci06KZkZ.d.ts → types-BJ6TTw2m.d.ts} +4 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,10 +5,9 @@
|
|
|
5
5
|
<p><img alt="@antongolub/lockfile — universal lockfile model and converter for npm, yarn, pnpm, bun" src="./pics/lockfile.svg" align="right" width="300">
|
|
6
6
|
|
|
7
7
|
Each package manager brings its own philosophy of how to describe, store and
|
|
8
|
-
control project dependencies.
|
|
9
|
-
|
|
10
|
-
engineers
|
|
11
|
-
dependency graphs across the ecosystem.
|
|
8
|
+
control project dependencies. Inside a single repo it's invisible to the
|
|
9
|
+
developer — but it becomes a recurring cost for InfoSec, DevOps and release
|
|
10
|
+
engineers, and makes consistent policy unenforceable across the enterprise.
|
|
12
11
|
|
|
13
12
|
This library models the dependency graph independent of any specific
|
|
14
13
|
package manager, then projects it back into the format you need.
|
|
@@ -19,11 +18,23 @@ license filtering) is the headline.
|
|
|
19
18
|
|
|
20
19
|
## Status
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
**Snapshot preview.** Every format below parses and stringifies; conversion is
|
|
22
|
+
*semantically equivalent*, not byte-identical (see [Concept](#concept)).
|
|
23
|
+
Published as `0.0.0-snapshot.*` builds — the first stable release is pending.
|
|
24
|
+
[SCHEMAS.md](./SCHEMAS.md) maps each format id to the package-manager versions
|
|
25
|
+
that emit it.
|
|
26
|
+
|
|
27
|
+
| Format | `detect` | `parse` | `stringify` |
|
|
28
|
+
|--------|:-:|:-:|:-:|
|
|
29
|
+
| `npm-1` · `npm-2` · `npm-3` | ✓ | ✓ | ✓ |
|
|
30
|
+
| `yarn-classic` | ✓ | ✓ | ✓ |
|
|
31
|
+
| `yarn-berry-v4` … `yarn-berry-v10`| ✓ | ✓ | ✓ |
|
|
32
|
+
| `pnpm-v5` · `pnpm-v6` · `pnpm-v9` | ✓ | ✓ | ✓ |
|
|
33
|
+
| `bun-text` | ✓ | ✓ | ✓ |
|
|
34
|
+
|
|
35
|
+
Graph-level operations apply to **any** parsed graph, regardless of source
|
|
36
|
+
format: `convert` (parse any → stringify any), `modify` (audit-fix,
|
|
37
|
+
override-pin, license-filter), and `optimize` (orphan GC / dedup).
|
|
27
38
|
|
|
28
39
|
## Concept
|
|
29
40
|
|
|
@@ -49,66 +60,74 @@ signatures) are the exception — they are never silently lost.
|
|
|
49
60
|
## API
|
|
50
61
|
|
|
51
62
|
```ts
|
|
52
|
-
import { parse, stringify } from '@antongolub/lockfile'
|
|
63
|
+
import { parse, stringify, convert } from '@antongolub/lockfile'
|
|
64
|
+
|
|
65
|
+
// explicit format (it's always the first argument):
|
|
66
|
+
const graph = parse('pnpm-v9', raw)
|
|
67
|
+
const out = stringify('npm-3', graph)
|
|
53
68
|
|
|
54
|
-
|
|
55
|
-
const
|
|
69
|
+
// or one step, auto-detecting the source:
|
|
70
|
+
const npm = convert(raw, { to: 'npm-3' })
|
|
56
71
|
```
|
|
57
72
|
|
|
58
|
-
|
|
73
|
+
The format is **explicit**, never implicit — `parse` and `stringify` both take
|
|
74
|
+
it as the first argument; `detect` sniffs it from the bytes when you don't know
|
|
75
|
+
it. Round-tripping is a choice the caller makes, not a default.
|
|
59
76
|
|
|
60
77
|
```ts
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
78
|
+
detect(input: string): FormatId | undefined
|
|
79
|
+
check(format: FormatId, input: string): boolean
|
|
80
|
+
parse(format: FormatId, input: string, opts?: ParseOptions): Graph
|
|
81
|
+
stringify(format: FormatId, graph: Graph, opts?: StringifyOptions): string
|
|
82
|
+
convert(input: string, opts: ConvertOptions): string // parse(from) → stringify(to)
|
|
64
83
|
```
|
|
65
84
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
85
|
+
`Graph` is the canonical, package-manager-independent model; `FormatId` is a
|
|
86
|
+
string-literal union (the [Status](#status) table lists every id, and
|
|
87
|
+
[SCHEMAS.md](./SCHEMAS.md) maps each to the package-manager versions behind it).
|
|
88
|
+
|
|
89
|
+
### Operating on the graph
|
|
90
|
+
|
|
91
|
+
The graph is where the value lives — `modify` and `optimize` transform it,
|
|
92
|
+
format-agnostically:
|
|
71
93
|
|
|
72
|
-
|
|
73
|
-
`
|
|
74
|
-
|
|
94
|
+
- **`modify`** applies a `Primitive[]` — `replaceVersion`, `pinOverride`,
|
|
95
|
+
`addDependency`, `removeDependency`, `applyPatch`, `filterLicense` — the
|
|
96
|
+
building blocks of audit-fix, override-pinning and license filtering.
|
|
97
|
+
- **`optimize`** runs orphan GC / dedup over the graph.
|
|
98
|
+
- **`overridesOf(graph)`** reads the canonical overrides back out.
|
|
75
99
|
|
|
76
100
|
### Options
|
|
77
101
|
|
|
78
102
|
```ts
|
|
79
103
|
type ParseOptions = {
|
|
80
|
-
|
|
81
|
-
manifests?:
|
|
82
|
-
|
|
83
|
-
installDir?: string // path to node_modules / .pnp.cjs (refinement)
|
|
84
|
-
cache?: CacheAdapter // PM cache (refinement)
|
|
85
|
-
registry?: RegistryAdapter // network access (opt-in)
|
|
104
|
+
workspaceRoot?: string // FS root for out-of-lockfile reads (patch bytes, manifests)
|
|
105
|
+
manifests?: Record<string, Manifest> // package.jsons keyed by workspace path
|
|
106
|
+
onDiagnostic?: (d: Diagnostic) => void
|
|
86
107
|
}
|
|
87
108
|
|
|
88
109
|
type StringifyOptions = {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
installDir?: string
|
|
93
|
-
cache?: CacheAdapter
|
|
94
|
-
registry?: RegistryAdapter
|
|
110
|
+
lineEnding?: 'lf' | 'crlf'
|
|
111
|
+
cacheKey?: string // yarn-berry cache-key prefix
|
|
112
|
+
overrides?: OverrideConstraint[] // canonical overrides → native projection
|
|
95
113
|
onDiagnostic?: (d: Diagnostic) => void
|
|
96
114
|
}
|
|
97
115
|
```
|
|
98
116
|
|
|
99
|
-
`
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
117
|
+
`manifests` supplies the workspace/override context the lockfile bytes cannot
|
|
118
|
+
carry on their own (notably for `yarn-classic` monorepos); everything else
|
|
119
|
+
succeeds offline against the bytes alone. Registry- and cache-backed refinement
|
|
120
|
+
ships as opt-in adapters (see [Sub-imports](#sub-imports)).
|
|
103
121
|
|
|
104
122
|
### Sub-imports
|
|
105
123
|
|
|
106
124
|
| Surface | Importable as | Contains |
|
|
107
125
|
|---------|---------------|----------|
|
|
108
|
-
| Root | `@antongolub/lockfile` | `parse`, `stringify`, plus types
|
|
109
|
-
| Modifiers | `@antongolub/lockfile/modify` | audit-fix, override-pin, license-filter |
|
|
110
|
-
|
|
|
111
|
-
|
|
|
126
|
+
| Root | `@antongolub/lockfile` | `detect`, `check`, `parse`, `stringify`, `convert`, `modify`, `optimize`, `overridesOf`, plus types `Graph`, `FormatId`, `ParseOptions`, `StringifyOptions`, `ConvertOptions`, `Manifest` |
|
|
127
|
+
| Modifiers | `@antongolub/lockfile/modify` | the individual `Primitive` functions behind `modify` (audit-fix, override-pin, license-filter) |
|
|
128
|
+
| Optimize | `@antongolub/lockfile/optimize` | the individual GC passes behind `optimize` |
|
|
129
|
+
| Registry | `@antongolub/lockfile/registry` | `frozenRegistry`, `liveRegistry`, `fsCache`, `npmCache`, `pnpmCache` |
|
|
130
|
+
| Per-format | `@antongolub/lockfile/formats/<id>` | a single adapter directly (test surface; not a primary user API) |
|
|
112
131
|
|
|
113
132
|
### Errors
|
|
114
133
|
|
|
@@ -117,8 +136,9 @@ alone.
|
|
|
117
136
|
|
|
118
137
|
```ts
|
|
119
138
|
'PARSE_FAILED' | 'FORMAT_DETECT_FAILED' | 'FORMAT_MISMATCH'
|
|
120
|
-
| 'CAPABILITY_LACK' | 'MISSING_MANIFEST'
|
|
121
|
-
| '
|
|
139
|
+
| 'CAPABILITY_LACK' | 'MISSING_MANIFEST' | 'MISSING_REQUIRED_FIELD'
|
|
140
|
+
| 'INVALID_INPUT' | 'ENRICH_REQUIRED' | 'IRREDUCIBLE_LOSS'
|
|
141
|
+
| 'PIPELINE_DIVERGED' | 'REGISTRY_UNREACHABLE' | 'INVARIANT_VIOLATION'
|
|
122
142
|
```
|
|
123
143
|
|
|
124
144
|
Reducible losses (e.g. dropped patches when emitting `npm-1` from a
|
package/dist/complete.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { N as NodeId, D as Diagnostic, G as Graph, E as EdgeTriple, a as Node, b as EdgeKind } from './graph-
|
|
2
|
-
import { R as RegistryAdapter } from './types-
|
|
1
|
+
import { N as NodeId, D as Diagnostic, G as Graph, E as EdgeTriple, a as Node, b as EdgeKind } from './graph-DL9MNz81.js';
|
|
2
|
+
import { R as RegistryAdapter } from './types-BJ6TTw2m.js';
|
|
3
3
|
|
|
4
4
|
interface CompletionSeed {
|
|
5
5
|
/** NodeIds the modifier added in the just-completed mutate phase. */
|
package/dist/formats/bun-text.js
CHANGED
|
@@ -214,6 +214,7 @@ function validate(s) {
|
|
|
214
214
|
const inc = s.incoming.get(id) ?? [];
|
|
215
215
|
for (const edge of inc) {
|
|
216
216
|
if (s.nodes.get(edge.src)?.workspacePath !== void 0) continue;
|
|
217
|
+
if (s.tarballs.get(stripPeerContextFromNodeId(edge.src))?.resolution?.type === "directory") continue;
|
|
217
218
|
if (isPublishedSelfLink(edge)) {
|
|
218
219
|
s.diagnostics.push({
|
|
219
220
|
code: "SEAL_PUBLISHED_SELF_LINK",
|
|
@@ -634,16 +635,41 @@ function newBuilder() {
|
|
|
634
635
|
}
|
|
635
636
|
|
|
636
637
|
// src/main/ts/recipe/integrity.ts
|
|
637
|
-
var
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
return
|
|
638
|
+
var SRI_ALGO_BYTES = { sha1: 20, sha256: 32, sha384: 48, sha512: 64 };
|
|
639
|
+
var SRI_MEMBER_RE = /^([a-z][a-z0-9]*)-([A-Za-z0-9+/]+={0,2})(?:\?.*)?$/;
|
|
640
|
+
function hexToBase64(hex) {
|
|
641
|
+
return Buffer.from(hex, "hex").toString("base64");
|
|
642
|
+
}
|
|
643
|
+
function isEmptyIntegrity(i) {
|
|
644
|
+
return i.hashes.length === 0;
|
|
645
|
+
}
|
|
646
|
+
function isTarballOrigin(origin) {
|
|
647
|
+
return origin !== "berry-zip";
|
|
648
|
+
}
|
|
649
|
+
function tarballHashes(i) {
|
|
650
|
+
return i.hashes.filter((h) => isTarballOrigin(h.origin));
|
|
651
|
+
}
|
|
652
|
+
function parseSri(raw, origin = "sri") {
|
|
653
|
+
const hashes = [];
|
|
654
|
+
for (const member of raw.trim().split(/\s+/)) {
|
|
655
|
+
if (member === "") continue;
|
|
656
|
+
const m = SRI_MEMBER_RE.exec(member);
|
|
657
|
+
if (!m) continue;
|
|
658
|
+
const algorithm = m[1];
|
|
659
|
+
const bytes = Buffer.from(m[2], "base64");
|
|
660
|
+
const expected = SRI_ALGO_BYTES[algorithm];
|
|
661
|
+
if (expected !== void 0) {
|
|
662
|
+
if (bytes.length !== expected) continue;
|
|
663
|
+
} else if (bytes.length < 16) {
|
|
664
|
+
continue;
|
|
665
|
+
}
|
|
666
|
+
hashes.push({ algorithm, digest: bytes.toString("hex"), origin });
|
|
667
|
+
}
|
|
668
|
+
return { hashes };
|
|
644
669
|
}
|
|
645
|
-
function
|
|
646
|
-
|
|
670
|
+
function emitSri(i) {
|
|
671
|
+
const members = tarballHashes(i).map((h) => `${h.algorithm}-${hexToBase64(h.digest)}`);
|
|
672
|
+
return members.length > 0 ? members.join(" ") : void 0;
|
|
647
673
|
}
|
|
648
674
|
|
|
649
675
|
// src/main/ts/recipe/diagnostics.ts
|
|
@@ -652,7 +678,7 @@ function invalidIntegrityDiagnostic(prefix, subject, rawIntegrity) {
|
|
|
652
678
|
code: `${prefix}_INVALID_INTEGRITY`,
|
|
653
679
|
severity: "warning",
|
|
654
680
|
subject,
|
|
655
|
-
message: `integrity ${JSON.stringify(rawIntegrity)}
|
|
681
|
+
message: `integrity ${JSON.stringify(rawIntegrity)} has no parseable hash; dropping`
|
|
656
682
|
};
|
|
657
683
|
}
|
|
658
684
|
function emitDropped(nodeId, feature, reason, onDiagnostic) {
|
|
@@ -857,11 +883,11 @@ function parse(input, _options = {}) {
|
|
|
857
883
|
peerContext: []
|
|
858
884
|
});
|
|
859
885
|
if (integrity !== void 0) {
|
|
860
|
-
const
|
|
861
|
-
if (
|
|
886
|
+
const parsed2 = parseSri(integrity, "sri");
|
|
887
|
+
if (isEmptyIntegrity(parsed2)) {
|
|
862
888
|
diagnostics.push(invalidIntegrityDiagnostic("BUN_TEXT", nodeId, integrity));
|
|
863
889
|
} else {
|
|
864
|
-
builder.setTarball({ name, version }, { integrity:
|
|
890
|
+
builder.setTarball({ name, version }, { integrity: parsed2 });
|
|
865
891
|
}
|
|
866
892
|
}
|
|
867
893
|
}
|
|
@@ -962,7 +988,7 @@ function stringify(graph, options = {}) {
|
|
|
962
988
|
emittedNameVersion.add(nameVersion);
|
|
963
989
|
const inner = buildInnerBlock(graph, node, sidecar);
|
|
964
990
|
const integritySrc = graph.tarballOf(node.id)?.integrity;
|
|
965
|
-
const integrity = integritySrc ?? "";
|
|
991
|
+
const integrity = (integritySrc && emitSri(integritySrc)) ?? "";
|
|
966
992
|
const key = chooseNodeEmitKey(node, sidecar, packagesBlock);
|
|
967
993
|
packagesBlock[key] = [`${node.name}@${node.version}`, "", inner, integrity];
|
|
968
994
|
}
|
package/dist/formats/npm-1.d.ts
CHANGED
package/dist/formats/npm-1.js
CHANGED
|
@@ -215,6 +215,7 @@ function validate(s) {
|
|
|
215
215
|
const inc = s.incoming.get(id) ?? [];
|
|
216
216
|
for (const edge of inc) {
|
|
217
217
|
if (s.nodes.get(edge.src)?.workspacePath !== void 0) continue;
|
|
218
|
+
if (s.tarballs.get(stripPeerContextFromNodeId(edge.src))?.resolution?.type === "directory") continue;
|
|
218
219
|
if (isPublishedSelfLink(edge)) {
|
|
219
220
|
s.diagnostics.push({
|
|
220
221
|
code: "SEAL_PUBLISHED_SELF_LINK",
|
|
@@ -634,6 +635,44 @@ function newBuilder() {
|
|
|
634
635
|
};
|
|
635
636
|
}
|
|
636
637
|
|
|
638
|
+
// src/main/ts/recipe/integrity.ts
|
|
639
|
+
var SRI_ALGO_BYTES = { sha1: 20, sha256: 32, sha384: 48, sha512: 64 };
|
|
640
|
+
var SRI_MEMBER_RE = /^([a-z][a-z0-9]*)-([A-Za-z0-9+/]+={0,2})(?:\?.*)?$/;
|
|
641
|
+
function hexToBase64(hex) {
|
|
642
|
+
return Buffer.from(hex, "hex").toString("base64");
|
|
643
|
+
}
|
|
644
|
+
function isEmptyIntegrity(i) {
|
|
645
|
+
return i.hashes.length === 0;
|
|
646
|
+
}
|
|
647
|
+
function isTarballOrigin(origin) {
|
|
648
|
+
return origin !== "berry-zip";
|
|
649
|
+
}
|
|
650
|
+
function tarballHashes(i) {
|
|
651
|
+
return i.hashes.filter((h) => isTarballOrigin(h.origin));
|
|
652
|
+
}
|
|
653
|
+
function parseSri(raw, origin = "sri") {
|
|
654
|
+
const hashes = [];
|
|
655
|
+
for (const member of raw.trim().split(/\s+/)) {
|
|
656
|
+
if (member === "") continue;
|
|
657
|
+
const m = SRI_MEMBER_RE.exec(member);
|
|
658
|
+
if (!m) continue;
|
|
659
|
+
const algorithm = m[1];
|
|
660
|
+
const bytes = Buffer.from(m[2], "base64");
|
|
661
|
+
const expected = SRI_ALGO_BYTES[algorithm];
|
|
662
|
+
if (expected !== void 0) {
|
|
663
|
+
if (bytes.length !== expected) continue;
|
|
664
|
+
} else if (bytes.length < 16) {
|
|
665
|
+
continue;
|
|
666
|
+
}
|
|
667
|
+
hashes.push({ algorithm, digest: bytes.toString("hex"), origin });
|
|
668
|
+
}
|
|
669
|
+
return { hashes };
|
|
670
|
+
}
|
|
671
|
+
function emitSri(i) {
|
|
672
|
+
const members = tarballHashes(i).map((h) => `${h.algorithm}-${hexToBase64(h.digest)}`);
|
|
673
|
+
return members.length > 0 ? members.join(" ") : void 0;
|
|
674
|
+
}
|
|
675
|
+
|
|
637
676
|
// src/main/ts/formats/_npm-flat-types.ts
|
|
638
677
|
var cmpStr2 = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
639
678
|
function sortRecord(record) {
|
|
@@ -971,7 +1010,10 @@ function parse2(input, _options = {}) {
|
|
|
971
1010
|
if (resolved !== void 0) node.resolution = resolved;
|
|
972
1011
|
builder.addNode(node);
|
|
973
1012
|
const payload = {};
|
|
974
|
-
if (entry.integrity !== void 0)
|
|
1013
|
+
if (entry.integrity !== void 0) {
|
|
1014
|
+
const integrity = parseSri(entry.integrity, "sri");
|
|
1015
|
+
if (!isEmptyIntegrity(integrity)) payload.integrity = integrity;
|
|
1016
|
+
}
|
|
975
1017
|
if (resolved !== void 0) {
|
|
976
1018
|
const canonical = parse(resolved, { sourceKind: "npm-resolved" });
|
|
977
1019
|
if (canonical.type === "unknown") {
|
|
@@ -1439,7 +1481,8 @@ function buildEntry(node, installPath, graph, sidecar, treeByParent) {
|
|
|
1439
1481
|
}
|
|
1440
1482
|
}
|
|
1441
1483
|
if (tarball?.integrity !== void 0 && !/^(git[+:]|github:)/.test(entry.version ?? "")) {
|
|
1442
|
-
|
|
1484
|
+
const sri = emitSri(tarball.integrity);
|
|
1485
|
+
if (sri !== void 0) entry.integrity = sri;
|
|
1443
1486
|
}
|
|
1444
1487
|
if (nodeSide?.dev === true) entry.dev = true;
|
|
1445
1488
|
if (nodeSide?.optional === true) entry.optional = true;
|
package/dist/formats/npm-2.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { G as Graph, D as Diagnostic } from '../graph-
|
|
2
|
-
import { N as NpmFamilyEnrichOptions, a as NpmFamilyOptimizeOptions, b as NpmFamilyParseOptions, c as NpmFamilyStringifyOptions } from '../_npm-flat-types-
|
|
1
|
+
import { G as Graph, D as Diagnostic } from '../graph-DL9MNz81.js';
|
|
2
|
+
import { N as NpmFamilyEnrichOptions, a as NpmFamilyOptimizeOptions, b as NpmFamilyParseOptions, c as NpmFamilyStringifyOptions } from '../_npm-flat-types-6ABSGUI9.js';
|
|
3
3
|
|
|
4
4
|
interface Npm2ParseOptions extends NpmFamilyParseOptions {
|
|
5
5
|
}
|
package/dist/formats/npm-2.js
CHANGED
|
@@ -228,6 +228,7 @@ function validate(s) {
|
|
|
228
228
|
const inc = s.incoming.get(id) ?? [];
|
|
229
229
|
for (const edge of inc) {
|
|
230
230
|
if (s.nodes.get(edge.src)?.workspacePath !== void 0) continue;
|
|
231
|
+
if (s.tarballs.get(stripPeerContextFromNodeId(edge.src))?.resolution?.type === "directory") continue;
|
|
231
232
|
if (isPublishedSelfLink(edge)) {
|
|
232
233
|
s.diagnostics.push({
|
|
233
234
|
code: "SEAL_PUBLISHED_SELF_LINK",
|
|
@@ -648,16 +649,41 @@ function newBuilder() {
|
|
|
648
649
|
}
|
|
649
650
|
|
|
650
651
|
// src/main/ts/recipe/integrity.ts
|
|
651
|
-
var
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
return
|
|
652
|
+
var SRI_ALGO_BYTES = { sha1: 20, sha256: 32, sha384: 48, sha512: 64 };
|
|
653
|
+
var SRI_MEMBER_RE = /^([a-z][a-z0-9]*)-([A-Za-z0-9+/]+={0,2})(?:\?.*)?$/;
|
|
654
|
+
function hexToBase64(hex) {
|
|
655
|
+
return Buffer.from(hex, "hex").toString("base64");
|
|
656
|
+
}
|
|
657
|
+
function isEmptyIntegrity(i) {
|
|
658
|
+
return i.hashes.length === 0;
|
|
659
|
+
}
|
|
660
|
+
function isTarballOrigin(origin) {
|
|
661
|
+
return origin !== "berry-zip";
|
|
662
|
+
}
|
|
663
|
+
function tarballHashes(i) {
|
|
664
|
+
return i.hashes.filter((h) => isTarballOrigin(h.origin));
|
|
665
|
+
}
|
|
666
|
+
function parseSri(raw, origin = "sri") {
|
|
667
|
+
const hashes = [];
|
|
668
|
+
for (const member of raw.trim().split(/\s+/)) {
|
|
669
|
+
if (member === "") continue;
|
|
670
|
+
const m = SRI_MEMBER_RE.exec(member);
|
|
671
|
+
if (!m) continue;
|
|
672
|
+
const algorithm = m[1];
|
|
673
|
+
const bytes = Buffer.from(m[2], "base64");
|
|
674
|
+
const expected = SRI_ALGO_BYTES[algorithm];
|
|
675
|
+
if (expected !== void 0) {
|
|
676
|
+
if (bytes.length !== expected) continue;
|
|
677
|
+
} else if (bytes.length < 16) {
|
|
678
|
+
continue;
|
|
679
|
+
}
|
|
680
|
+
hashes.push({ algorithm, digest: bytes.toString("hex"), origin });
|
|
681
|
+
}
|
|
682
|
+
return { hashes };
|
|
658
683
|
}
|
|
659
|
-
function
|
|
660
|
-
|
|
684
|
+
function emitSri(i) {
|
|
685
|
+
const members = tarballHashes(i).map((h) => `${h.algorithm}-${hexToBase64(h.digest)}`);
|
|
686
|
+
return members.length > 0 ? members.join(" ") : void 0;
|
|
661
687
|
}
|
|
662
688
|
|
|
663
689
|
// src/main/ts/recipe/diagnostics.ts
|
|
@@ -666,7 +692,7 @@ function invalidIntegrityDiagnostic(prefix, subject, rawIntegrity) {
|
|
|
666
692
|
code: `${prefix}_INVALID_INTEGRITY`,
|
|
667
693
|
severity: "warning",
|
|
668
694
|
subject,
|
|
669
|
-
message: `integrity ${JSON.stringify(rawIntegrity)}
|
|
695
|
+
message: `integrity ${JSON.stringify(rawIntegrity)} has no parseable hash; dropping`
|
|
670
696
|
};
|
|
671
697
|
}
|
|
672
698
|
function emitDropped(nodeId, feature, reason, onDiagnostic) {
|
|
@@ -1616,11 +1642,11 @@ function hasTarballPayload(entry) {
|
|
|
1616
1642
|
function tarballPayloadOf(entry, subject, diagnostics) {
|
|
1617
1643
|
const payload = {};
|
|
1618
1644
|
if (entry.integrity !== void 0) {
|
|
1619
|
-
const
|
|
1620
|
-
if (
|
|
1645
|
+
const integrity = parseSri(entry.integrity, "sri");
|
|
1646
|
+
if (isEmptyIntegrity(integrity)) {
|
|
1621
1647
|
diagnostics.push(invalidIntegrityDiagnostic("NPM", subject, entry.integrity));
|
|
1622
1648
|
} else {
|
|
1623
|
-
payload.integrity =
|
|
1649
|
+
payload.integrity = integrity;
|
|
1624
1650
|
}
|
|
1625
1651
|
}
|
|
1626
1652
|
if (entry.engines !== void 0) payload.engines = { ...entry.engines };
|
|
@@ -1861,7 +1887,10 @@ function buildNodeModulesEntry(graph, node, nodeSide, config, emitDiagnostic = (
|
|
|
1861
1887
|
const tarball = graph.tarballOf(node.id);
|
|
1862
1888
|
const resolved = node.resolution ?? config.hooks?.recoverResolvedForNode?.(graph, node) ?? deriveResolvedFromCanonical(tarball?.resolution);
|
|
1863
1889
|
if (resolved !== void 0) body.resolved = resolved;
|
|
1864
|
-
if (tarball?.integrity !== void 0)
|
|
1890
|
+
if (tarball?.integrity !== void 0) {
|
|
1891
|
+
const sri = emitSri(tarball.integrity);
|
|
1892
|
+
if (sri !== void 0) body.integrity = sri;
|
|
1893
|
+
}
|
|
1865
1894
|
if (nodeSide?.dev === true) body.dev = true;
|
|
1866
1895
|
if (nodeSide?.optional === true) body.optional = true;
|
|
1867
1896
|
if (nodeSide?.peer === true) body.peer = true;
|
|
@@ -2169,7 +2198,8 @@ function buildLegacyNodeEntry(ctx, node, parentInstallPrefix) {
|
|
|
2169
2198
|
}
|
|
2170
2199
|
}
|
|
2171
2200
|
if (tarball?.integrity !== void 0 && entry.from === void 0) {
|
|
2172
|
-
|
|
2201
|
+
const sri = emitSri(tarball.integrity);
|
|
2202
|
+
if (sri !== void 0) entry.integrity = sri;
|
|
2173
2203
|
}
|
|
2174
2204
|
const nodeSide = ctx.sidecar?.nodes.get(node.id);
|
|
2175
2205
|
if (nodeSide?.dev === true) entry.dev = true;
|
package/dist/formats/npm-3.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { G as Graph, D as Diagnostic } from '../graph-
|
|
2
|
-
import { N as NpmFamilyEnrichOptions, a as NpmFamilyOptimizeOptions, b as NpmFamilyParseOptions, c as NpmFamilyStringifyOptions } from '../_npm-flat-types-
|
|
1
|
+
import { G as Graph, D as Diagnostic } from '../graph-DL9MNz81.js';
|
|
2
|
+
import { N as NpmFamilyEnrichOptions, a as NpmFamilyOptimizeOptions, b as NpmFamilyParseOptions, c as NpmFamilyStringifyOptions } from '../_npm-flat-types-6ABSGUI9.js';
|
|
3
3
|
|
|
4
4
|
interface Npm3ParseOptions extends NpmFamilyParseOptions {
|
|
5
5
|
}
|
package/dist/formats/npm-3.js
CHANGED
|
@@ -228,6 +228,7 @@ function validate(s) {
|
|
|
228
228
|
const inc = s.incoming.get(id) ?? [];
|
|
229
229
|
for (const edge of inc) {
|
|
230
230
|
if (s.nodes.get(edge.src)?.workspacePath !== void 0) continue;
|
|
231
|
+
if (s.tarballs.get(stripPeerContextFromNodeId(edge.src))?.resolution?.type === "directory") continue;
|
|
231
232
|
if (isPublishedSelfLink(edge)) {
|
|
232
233
|
s.diagnostics.push({
|
|
233
234
|
code: "SEAL_PUBLISHED_SELF_LINK",
|
|
@@ -648,16 +649,41 @@ function newBuilder() {
|
|
|
648
649
|
}
|
|
649
650
|
|
|
650
651
|
// src/main/ts/recipe/integrity.ts
|
|
651
|
-
var
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
return
|
|
652
|
+
var SRI_ALGO_BYTES = { sha1: 20, sha256: 32, sha384: 48, sha512: 64 };
|
|
653
|
+
var SRI_MEMBER_RE = /^([a-z][a-z0-9]*)-([A-Za-z0-9+/]+={0,2})(?:\?.*)?$/;
|
|
654
|
+
function hexToBase64(hex) {
|
|
655
|
+
return Buffer.from(hex, "hex").toString("base64");
|
|
656
|
+
}
|
|
657
|
+
function isEmptyIntegrity(i) {
|
|
658
|
+
return i.hashes.length === 0;
|
|
659
|
+
}
|
|
660
|
+
function isTarballOrigin(origin) {
|
|
661
|
+
return origin !== "berry-zip";
|
|
662
|
+
}
|
|
663
|
+
function tarballHashes(i) {
|
|
664
|
+
return i.hashes.filter((h) => isTarballOrigin(h.origin));
|
|
665
|
+
}
|
|
666
|
+
function parseSri(raw, origin = "sri") {
|
|
667
|
+
const hashes = [];
|
|
668
|
+
for (const member of raw.trim().split(/\s+/)) {
|
|
669
|
+
if (member === "") continue;
|
|
670
|
+
const m = SRI_MEMBER_RE.exec(member);
|
|
671
|
+
if (!m) continue;
|
|
672
|
+
const algorithm = m[1];
|
|
673
|
+
const bytes = Buffer.from(m[2], "base64");
|
|
674
|
+
const expected = SRI_ALGO_BYTES[algorithm];
|
|
675
|
+
if (expected !== void 0) {
|
|
676
|
+
if (bytes.length !== expected) continue;
|
|
677
|
+
} else if (bytes.length < 16) {
|
|
678
|
+
continue;
|
|
679
|
+
}
|
|
680
|
+
hashes.push({ algorithm, digest: bytes.toString("hex"), origin });
|
|
681
|
+
}
|
|
682
|
+
return { hashes };
|
|
658
683
|
}
|
|
659
|
-
function
|
|
660
|
-
|
|
684
|
+
function emitSri(i) {
|
|
685
|
+
const members = tarballHashes(i).map((h) => `${h.algorithm}-${hexToBase64(h.digest)}`);
|
|
686
|
+
return members.length > 0 ? members.join(" ") : void 0;
|
|
661
687
|
}
|
|
662
688
|
|
|
663
689
|
// src/main/ts/recipe/diagnostics.ts
|
|
@@ -666,7 +692,7 @@ function invalidIntegrityDiagnostic(prefix, subject, rawIntegrity) {
|
|
|
666
692
|
code: `${prefix}_INVALID_INTEGRITY`,
|
|
667
693
|
severity: "warning",
|
|
668
694
|
subject,
|
|
669
|
-
message: `integrity ${JSON.stringify(rawIntegrity)}
|
|
695
|
+
message: `integrity ${JSON.stringify(rawIntegrity)} has no parseable hash; dropping`
|
|
670
696
|
};
|
|
671
697
|
}
|
|
672
698
|
function emitDropped(nodeId, feature, reason, onDiagnostic) {
|
|
@@ -1616,11 +1642,11 @@ function hasTarballPayload(entry) {
|
|
|
1616
1642
|
function tarballPayloadOf(entry, subject, diagnostics) {
|
|
1617
1643
|
const payload = {};
|
|
1618
1644
|
if (entry.integrity !== void 0) {
|
|
1619
|
-
const
|
|
1620
|
-
if (
|
|
1645
|
+
const integrity = parseSri(entry.integrity, "sri");
|
|
1646
|
+
if (isEmptyIntegrity(integrity)) {
|
|
1621
1647
|
diagnostics.push(invalidIntegrityDiagnostic("NPM", subject, entry.integrity));
|
|
1622
1648
|
} else {
|
|
1623
|
-
payload.integrity =
|
|
1649
|
+
payload.integrity = integrity;
|
|
1624
1650
|
}
|
|
1625
1651
|
}
|
|
1626
1652
|
if (entry.engines !== void 0) payload.engines = { ...entry.engines };
|
|
@@ -1861,7 +1887,10 @@ function buildNodeModulesEntry(graph, node, nodeSide, config, emitDiagnostic = (
|
|
|
1861
1887
|
const tarball = graph.tarballOf(node.id);
|
|
1862
1888
|
const resolved = node.resolution ?? config.hooks?.recoverResolvedForNode?.(graph, node) ?? deriveResolvedFromCanonical(tarball?.resolution);
|
|
1863
1889
|
if (resolved !== void 0) body.resolved = resolved;
|
|
1864
|
-
if (tarball?.integrity !== void 0)
|
|
1890
|
+
if (tarball?.integrity !== void 0) {
|
|
1891
|
+
const sri = emitSri(tarball.integrity);
|
|
1892
|
+
if (sri !== void 0) body.integrity = sri;
|
|
1893
|
+
}
|
|
1865
1894
|
if (nodeSide?.dev === true) body.dev = true;
|
|
1866
1895
|
if (nodeSide?.optional === true) body.optional = true;
|
|
1867
1896
|
if (nodeSide?.peer === true) body.peer = true;
|