monkeypatchlist 0.0.0-stage → 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/LICENSE +202 -0
- package/NOTICE +61 -0
- package/README.md +183 -2
- package/catalog/catalog.json +552211 -0
- package/catalog/catalog.schema.json +787 -0
- package/catalog/changelog.json +3 -0
- package/dist/bin/monkeypatchlist.d.ts +2 -0
- package/dist/bin/monkeypatchlist.js +3 -0
- package/dist/catalog.d.ts +561 -0
- package/dist/catalog.js +58 -0
- package/dist/cli.d.ts +84 -0
- package/dist/cli.js +470 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/lockfile-input.d.ts +9 -0
- package/dist/lockfile-input.js +91 -0
- package/dist/packages/analysis/binding.d.ts +2 -0
- package/dist/packages/analysis/binding.js +5 -0
- package/dist/packages/analysis/index.d.ts +10 -0
- package/dist/packages/analysis/index.js +13 -0
- package/dist/packages/analysis/job.d.ts +33 -0
- package/dist/packages/analysis/job.js +85 -0
- package/dist/packages/analysis/lib/absence-tags.d.ts +34 -0
- package/dist/packages/analysis/lib/absence-tags.js +340 -0
- package/dist/packages/analysis/lib/ast.d.ts +28 -0
- package/dist/packages/analysis/lib/ast.js +64 -0
- package/dist/packages/analysis/lib/binding.d.ts +36 -0
- package/dist/packages/analysis/lib/binding.js +66 -0
- package/dist/packages/analysis/lib/budget.d.ts +80 -0
- package/dist/packages/analysis/lib/budget.js +31 -0
- package/dist/packages/analysis/lib/catch-arms.d.ts +19 -0
- package/dist/packages/analysis/lib/catch-arms.js +351 -0
- package/dist/packages/analysis/lib/chunk-loads.d.ts +160 -0
- package/dist/packages/analysis/lib/chunk-loads.js +882 -0
- package/dist/packages/analysis/lib/chunk-registry.d.ts +45 -0
- package/dist/packages/analysis/lib/chunk-registry.js +334 -0
- package/dist/packages/analysis/lib/code-routes.d.ts +52 -0
- package/dist/packages/analysis/lib/code-routes.js +921 -0
- package/dist/packages/analysis/lib/collisions.d.ts +22 -0
- package/dist/packages/analysis/lib/collisions.js +103 -0
- package/dist/packages/analysis/lib/definitions.d.ts +76 -0
- package/dist/packages/analysis/lib/definitions.js +782 -0
- package/dist/packages/analysis/lib/early-exits.d.ts +39 -0
- package/dist/packages/analysis/lib/early-exits.js +161 -0
- package/dist/packages/analysis/lib/effect.d.ts +138 -0
- package/dist/packages/analysis/lib/effect.js +800 -0
- package/dist/packages/analysis/lib/global-object.d.ts +581 -0
- package/dist/packages/analysis/lib/global-object.js +8240 -0
- package/dist/packages/analysis/lib/keys.d.ts +33 -0
- package/dist/packages/analysis/lib/keys.js +82 -0
- package/dist/packages/analysis/lib/line-starts.d.ts +41 -0
- package/dist/packages/analysis/lib/line-starts.js +79 -0
- package/dist/packages/analysis/lib/link-group.d.ts +51 -0
- package/dist/packages/analysis/lib/link-group.js +487 -0
- package/dist/packages/analysis/lib/module-links.d.ts +117 -0
- package/dist/packages/analysis/lib/module-links.js +1060 -0
- package/dist/packages/analysis/lib/nested-code.d.ts +38 -0
- package/dist/packages/analysis/lib/nested-code.js +200 -0
- package/dist/packages/analysis/lib/numeric.d.ts +16 -0
- package/dist/packages/analysis/lib/numeric.js +216 -0
- package/dist/packages/analysis/lib/once-flags.d.ts +12 -0
- package/dist/packages/analysis/lib/once-flags.js +190 -0
- package/dist/packages/analysis/lib/parameter-arguments.d.ts +12 -0
- package/dist/packages/analysis/lib/parameter-arguments.js +34 -0
- package/dist/packages/analysis/lib/parse.d.ts +10 -0
- package/dist/packages/analysis/lib/parse.js +39 -0
- package/dist/packages/analysis/lib/partial.d.ts +60 -0
- package/dist/packages/analysis/lib/partial.js +518 -0
- package/dist/packages/analysis/lib/print.d.ts +3 -0
- package/dist/packages/analysis/lib/print.js +150 -0
- package/dist/packages/analysis/lib/reachability.d.ts +137 -0
- package/dist/packages/analysis/lib/reachability.js +1295 -0
- package/dist/packages/analysis/lib/resolver.d.ts +13 -0
- package/dist/packages/analysis/lib/resolver.js +2886 -0
- package/dist/packages/analysis/lib/restores.d.ts +42 -0
- package/dist/packages/analysis/lib/restores.js +482 -0
- package/dist/packages/analysis/lib/rewritten-deletes.d.ts +6 -0
- package/dist/packages/analysis/lib/rewritten-deletes.js +142 -0
- package/dist/packages/analysis/lib/scope.d.ts +110 -0
- package/dist/packages/analysis/lib/scope.js +443 -0
- package/dist/packages/analysis/lib/script-loads.d.ts +56 -0
- package/dist/packages/analysis/lib/script-loads.js +359 -0
- package/dist/packages/analysis/lib/summaries.d.ts +177 -0
- package/dist/packages/analysis/lib/summaries.js +498 -0
- package/dist/packages/analysis/lib/types.d.ts +632 -0
- package/dist/packages/analysis/lib/types.js +128 -0
- package/dist/packages/analysis/lib/wrap.d.ts +34 -0
- package/dist/packages/analysis/lib/wrap.js +407 -0
- package/dist/packages/analysis/lib/writes.d.ts +358 -0
- package/dist/packages/analysis/lib/writes.js +1612 -0
- package/dist/packages/analysis/visit-order.d.ts +3 -0
- package/dist/packages/analysis/visit-order.js +23 -0
- package/dist/packages/attribution/index.d.ts +233 -0
- package/dist/packages/attribution/index.js +444 -0
- package/dist/packages/attribution/lib/containing-package.d.ts +76 -0
- package/dist/packages/attribution/lib/containing-package.js +304 -0
- package/dist/packages/attribution/lib/first-party.d.ts +37 -0
- package/dist/packages/attribution/lib/first-party.js +153 -0
- package/dist/packages/attribution/lib/lockfile-graph.d.ts +99 -0
- package/dist/packages/attribution/lib/lockfile-graph.js +158 -0
- package/dist/packages/attribution/lib/lockfile.d.ts +33 -0
- package/dist/packages/attribution/lib/lockfile.js +330 -0
- package/dist/packages/attribution/lib/path-rules.d.ts +29 -0
- package/dist/packages/attribution/lib/path-rules.js +84 -0
- package/dist/packages/attribution/lib/pnpm.d.ts +4 -0
- package/dist/packages/attribution/lib/pnpm.js +133 -0
- package/dist/packages/attribution/lib/remote-url.d.ts +7 -0
- package/dist/packages/attribution/lib/remote-url.js +24 -0
- package/dist/packages/attribution/lib/source-map.d.ts +51 -0
- package/dist/packages/attribution/lib/source-map.js +125 -0
- package/dist/packages/attribution/lib/yaml.d.ts +9 -0
- package/dist/packages/attribution/lib/yaml.js +277 -0
- package/dist/packages/attribution/lib/yarn.d.ts +7 -0
- package/dist/packages/attribution/lib/yarn.js +227 -0
- package/dist/packages/baseline/index.d.ts +45 -0
- package/dist/packages/baseline/index.js +41 -0
- package/dist/packages/baseline/lib/error.d.ts +15 -0
- package/dist/packages/baseline/lib/error.js +25 -0
- package/dist/packages/baseline/lib/format.d.ts +49 -0
- package/dist/packages/baseline/lib/format.js +22 -0
- package/dist/packages/baseline/lib/migrations.d.ts +19 -0
- package/dist/packages/baseline/lib/migrations.js +7 -0
- package/dist/packages/baseline/lib/previous.d.ts +23 -0
- package/dist/packages/baseline/lib/previous.js +64 -0
- package/dist/packages/baseline/lib/read.d.ts +39 -0
- package/dist/packages/baseline/lib/read.js +219 -0
- package/dist/packages/baseline/lib/report.d.ts +25 -0
- package/dist/packages/baseline/lib/report.js +104 -0
- package/dist/packages/baseline/lib/skew.d.ts +51 -0
- package/dist/packages/baseline/lib/skew.js +75 -0
- package/dist/packages/compare/index.d.ts +120 -0
- package/dist/packages/compare/index.js +274 -0
- package/dist/packages/compare/lib/catalog-notes.d.ts +73 -0
- package/dist/packages/compare/lib/catalog-notes.js +224 -0
- package/dist/packages/completeness/index.d.ts +246 -0
- package/dist/packages/completeness/index.js +249 -0
- package/dist/packages/confidence/index.d.ts +54 -0
- package/dist/packages/confidence/index.js +71 -0
- package/dist/packages/config/index.d.ts +107 -0
- package/dist/packages/config/index.js +95 -0
- package/dist/packages/config/lib/command-line.d.ts +23 -0
- package/dist/packages/config/lib/command-line.js +239 -0
- package/dist/packages/config/lib/config-file.d.ts +11 -0
- package/dist/packages/config/lib/config-file.js +119 -0
- package/dist/packages/config/lib/scan-config-id.d.ts +28 -0
- package/dist/packages/config/lib/scan-config-id.js +54 -0
- package/dist/packages/config/lib/schema.d.ts +2 -0
- package/dist/packages/config/lib/schema.js +31 -0
- package/dist/packages/config/lib/types.d.ts +64 -0
- package/dist/packages/config/lib/types.js +6 -0
- package/dist/packages/config/lib/urls.d.ts +6 -0
- package/dist/packages/config/lib/urls.js +9 -0
- package/dist/packages/discovery/index.d.ts +277 -0
- package/dist/packages/discovery/index.js +1950 -0
- package/dist/packages/discovery/lib/import-map.d.ts +68 -0
- package/dist/packages/discovery/lib/import-map.js +180 -0
- package/dist/packages/discovery/lib/requests.d.ts +58 -0
- package/dist/packages/discovery/lib/requests.js +591 -0
- package/dist/packages/discovery/lib/scopes.d.ts +6 -0
- package/dist/packages/discovery/lib/scopes.js +286 -0
- package/dist/packages/discovery/lib/source-maps.d.ts +56 -0
- package/dist/packages/discovery/lib/source-maps.js +209 -0
- package/dist/packages/discovery/lib/swept-code.d.ts +27 -0
- package/dist/packages/discovery/lib/swept-code.js +107 -0
- package/dist/packages/discovery/lib/syntax.d.ts +17 -0
- package/dist/packages/discovery/lib/syntax.js +39 -0
- package/dist/packages/findings/index.d.ts +106 -0
- package/dist/packages/findings/index.js +254 -0
- package/dist/packages/findings/lib/scope.d.ts +38 -0
- package/dist/packages/findings/lib/scope.js +110 -0
- package/dist/packages/parallel/index.d.ts +2 -0
- package/dist/packages/parallel/index.js +5 -0
- package/dist/packages/parallel/lib/protocol.d.ts +15 -0
- package/dist/packages/parallel/lib/protocol.js +2 -0
- package/dist/packages/parallel/lib/runner.d.ts +36 -0
- package/dist/packages/parallel/lib/runner.js +96 -0
- package/dist/packages/parallel/lib/schedule.d.ts +16 -0
- package/dist/packages/parallel/lib/schedule.js +52 -0
- package/dist/packages/parallel/lib/worker.d.ts +1 -0
- package/dist/packages/parallel/lib/worker.js +18 -0
- package/dist/packages/policy/index.d.ts +36 -0
- package/dist/packages/policy/index.js +22 -0
- package/dist/packages/report/explain.d.ts +26 -0
- package/dist/packages/report/explain.js +99 -0
- package/dist/packages/report/index.d.ts +210 -0
- package/dist/packages/report/index.js +388 -0
- package/dist/packages/report/terminal.d.ts +11 -0
- package/dist/packages/report/terminal.js +331 -0
- package/dist/packages/source-store/fixture.d.ts +22 -0
- package/dist/packages/source-store/fixture.js +58 -0
- package/dist/packages/source-store/index.d.ts +278 -0
- package/dist/packages/source-store/index.js +322 -0
- package/dist/packages/source-store/lib/addresses.d.ts +6 -0
- package/dist/packages/source-store/lib/addresses.js +91 -0
- package/dist/packages/source-store/lib/connection.d.ts +15 -0
- package/dist/packages/source-store/lib/connection.js +135 -0
- package/dist/packages/source-store/lib/content-types.d.ts +16 -0
- package/dist/packages/source-store/lib/content-types.js +75 -0
- package/dist/packages/source-store/lib/data-urls.d.ts +10 -0
- package/dist/packages/source-store/lib/data-urls.js +85 -0
- package/dist/packages/source-store/lib/deadline.d.ts +18 -0
- package/dist/packages/source-store/lib/deadline.js +44 -0
- package/dist/packages/source-store/lib/exchange.d.ts +29 -0
- package/dist/packages/source-store/lib/exchange.js +112 -0
- package/dist/packages/source-store/lib/integrity.d.ts +19 -0
- package/dist/packages/source-store/lib/integrity.js +86 -0
- package/dist/packages/source-store/lib/outcomes.d.ts +12 -0
- package/dist/packages/source-store/lib/outcomes.js +22 -0
- package/dist/packages/source-store/lib/proxy.d.ts +11 -0
- package/dist/packages/source-store/lib/proxy.js +53 -0
- package/dist/packages/source-store/lib/session.d.ts +12 -0
- package/dist/packages/source-store/lib/session.js +139 -0
- package/dist/packages/source-store/lib/urls.d.ts +6 -0
- package/dist/packages/source-store/lib/urls.js +15 -0
- package/dist/packages/source-store/resolve.d.ts +59 -0
- package/dist/packages/source-store/resolve.js +45 -0
- package/dist/scan.d.ts +65 -0
- package/dist/scan.js +592 -0
- package/package.json +95 -4
- package/schema/config.schema.json +133 -0
- package/schema/report.schema.json +1905 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
// `yarn.lock` (§10 Introducing dependencies): Yarn v1, and Berry at every `__metadata.version`. It resolves each
|
|
2
|
+
// descriptor (`name@range`) to a package, but records neither the importer's direct dependencies nor the dev/prod
|
|
3
|
+
// split, so those come from the `package.json` manifests beside it: its own directory's, and the workspaces'
|
|
4
|
+
// (v1 through its `workspaces` globs, Berry through its workspace entries). A missing or unreadable one leaves the
|
|
5
|
+
// importer unresolved. It isn't keyed by install path, so it gives no version (§10 Version), and what it derives is
|
|
6
|
+
// capped at `medium`. Pure.
|
|
7
|
+
import { graph, isRecord, normalizeFolder, placeByName, readManifest, } from "./lockfile-graph.js";
|
|
8
|
+
import { readYaml } from "./yaml.js";
|
|
9
|
+
const v1Header = /^# yarn lockfile v1\s*$/m;
|
|
10
|
+
/** A descriptor's package name: `name@range`, a scope's `@` kept. */
|
|
11
|
+
function nameOf(descriptor) {
|
|
12
|
+
const split = descriptor.indexOf('@', 1);
|
|
13
|
+
return split <= 0 ? null : descriptor.slice(0, split);
|
|
14
|
+
}
|
|
15
|
+
/** A dependency map's `[name, range]` pairs. */
|
|
16
|
+
function ranges(value) {
|
|
17
|
+
if (!isRecord(value))
|
|
18
|
+
return [];
|
|
19
|
+
return Object.entries(value).flatMap(([name, range]) => (typeof range === 'string' ? [[name, range]] : []));
|
|
20
|
+
}
|
|
21
|
+
/** A key's descriptors: `a@1, "a@2"` lists several. */
|
|
22
|
+
function descriptorsOf(key) {
|
|
23
|
+
return key
|
|
24
|
+
.split(/,\s*/)
|
|
25
|
+
.map((part) => part.trim().replace(/^"(.*)"$/, '$1'))
|
|
26
|
+
.filter((part) => part !== '');
|
|
27
|
+
}
|
|
28
|
+
/** The entries of a Yarn v1 lockfile, or null when it isn't one. */
|
|
29
|
+
function readV1(text) {
|
|
30
|
+
const entries = [];
|
|
31
|
+
/** The open maps, by indent: the entry's fields, then a dependency block. */
|
|
32
|
+
let stack = [];
|
|
33
|
+
for (const raw of text.split(/\r?\n/)) {
|
|
34
|
+
if (raw.trim() === '' || raw.trimStart().startsWith('#'))
|
|
35
|
+
continue;
|
|
36
|
+
const indent = raw.length - raw.trimStart().length;
|
|
37
|
+
const line = raw.trim();
|
|
38
|
+
if (indent === 0) {
|
|
39
|
+
if (!line.endsWith(':'))
|
|
40
|
+
return null;
|
|
41
|
+
const fields = {};
|
|
42
|
+
entries.push({ descriptors: descriptorsOf(line.slice(0, -1)), fields });
|
|
43
|
+
stack = [{ indent: 0, map: fields }];
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
while (stack.length > 1 && stack.at(-1).indent >= indent)
|
|
47
|
+
stack.pop();
|
|
48
|
+
const holder = stack.at(-1);
|
|
49
|
+
if (holder === undefined)
|
|
50
|
+
return null;
|
|
51
|
+
const pair = /^("(?:[^"\\]|\\.)*"|[^\s"]+)(?::$|\s+(.*)$)/.exec(line);
|
|
52
|
+
if (pair === null)
|
|
53
|
+
return null;
|
|
54
|
+
const key = unquote(pair[1]);
|
|
55
|
+
if (pair[2] === undefined) {
|
|
56
|
+
const map = {};
|
|
57
|
+
Object.defineProperty(holder.map, key, { value: map, enumerable: true, writable: true, configurable: true });
|
|
58
|
+
stack.push({ indent, map });
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
Object.defineProperty(holder.map, key, { value: unquote(pair[2]), enumerable: true, writable: true, configurable: true });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return entries;
|
|
65
|
+
}
|
|
66
|
+
function unquote(text) {
|
|
67
|
+
if (!text.startsWith('"'))
|
|
68
|
+
return text;
|
|
69
|
+
try {
|
|
70
|
+
const value = JSON.parse(text);
|
|
71
|
+
return typeof value === 'string' ? value : text;
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return text.slice(1, -1);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** The entries of a Yarn Berry lockfile and its `__metadata.version`, or null when it isn't one. */
|
|
78
|
+
function readBerry(text) {
|
|
79
|
+
const doc = readYaml(text);
|
|
80
|
+
if (!isRecord(doc) || !isRecord(doc.__metadata) || typeof doc.__metadata.version !== 'string')
|
|
81
|
+
return null;
|
|
82
|
+
return Object.entries(doc).flatMap(([key, fields]) => key === '__metadata' || !isRecord(fields) ? [] : [{ descriptors: descriptorsOf(key), fields }]);
|
|
83
|
+
}
|
|
84
|
+
/** A Berry workspace entry's folder, from its resolution `name@workspace:<folder>`, or null for a package. */
|
|
85
|
+
function workspaceFolder(fields) {
|
|
86
|
+
const resolution = typeof fields.resolution === 'string' ? fields.resolution : null;
|
|
87
|
+
const name = resolution === null ? null : nameOf(resolution);
|
|
88
|
+
if (resolution === null || name === null)
|
|
89
|
+
return null;
|
|
90
|
+
const reference = resolution.slice(name.length + 1);
|
|
91
|
+
return reference.startsWith('workspace:') ? normalizeFolder(reference.slice('workspace:'.length)) : null;
|
|
92
|
+
}
|
|
93
|
+
/** The manifests a `yarn.lock` needs besides its own directory's: Berry's workspace folders, or v1's globs. */
|
|
94
|
+
export function yarnManifests(text, root) {
|
|
95
|
+
if (v1Header.test(text)) {
|
|
96
|
+
let value;
|
|
97
|
+
try {
|
|
98
|
+
value = root === null ? null : JSON.parse(root.startsWith('') ? root.slice(1) : root);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
value = null;
|
|
102
|
+
}
|
|
103
|
+
const declared = isRecord(value) ? value.workspaces : undefined;
|
|
104
|
+
const list = Array.isArray(declared) ? declared : isRecord(declared) && Array.isArray(declared.packages) ? declared.packages : [];
|
|
105
|
+
return { folders: [], globs: list.filter((each) => typeof each === 'string') };
|
|
106
|
+
}
|
|
107
|
+
const entries = readBerry(text) ?? [];
|
|
108
|
+
const folders = entries.flatMap(({ fields }) => {
|
|
109
|
+
const folder = workspaceFolder(fields);
|
|
110
|
+
return folder === null || folder === '' ? [] : [folder];
|
|
111
|
+
});
|
|
112
|
+
return { folders, globs: [] };
|
|
113
|
+
}
|
|
114
|
+
export function readYarn(text, file, manifests) {
|
|
115
|
+
const v1 = v1Header.test(text);
|
|
116
|
+
const entries = v1 ? readV1(text) : readBerry(text);
|
|
117
|
+
if (entries === null)
|
|
118
|
+
return { unusable: `${file} is no Yarn lockfile: it has neither the v1 header nor a __metadata version` };
|
|
119
|
+
const byDescriptor = new Map();
|
|
120
|
+
const nodes = [];
|
|
121
|
+
/** Each workspace's folder, with its node, and its manifest when it could be read. */
|
|
122
|
+
const workspaces = new Map();
|
|
123
|
+
const unreadable = [];
|
|
124
|
+
const manifestAt = (folder) => {
|
|
125
|
+
const manifest = readManifest(manifests[folder]);
|
|
126
|
+
if (manifest === null)
|
|
127
|
+
unreadable.push(folder === '' ? 'package.json' : `${folder}/package.json`);
|
|
128
|
+
return manifest;
|
|
129
|
+
};
|
|
130
|
+
for (const { descriptors, fields } of entries) {
|
|
131
|
+
const folder = v1 ? null : workspaceFolder(fields);
|
|
132
|
+
const resolution = typeof fields.resolution === 'string' ? fields.resolution : null;
|
|
133
|
+
const name = (resolution === null || v1 ? null : nameOf(resolution)) ?? nameOf(descriptors[0] ?? '');
|
|
134
|
+
if (name === null)
|
|
135
|
+
continue;
|
|
136
|
+
const production = [...ranges(fields.dependencies), ...ranges(fields.optionalDependencies)];
|
|
137
|
+
const peers = Object.keys(isRecord(fields.peerDependencies) ? fields.peerDependencies : {});
|
|
138
|
+
let node;
|
|
139
|
+
if (folder !== null) {
|
|
140
|
+
// A Berry workspace entry merges dev dependencies in: its manifest splits them.
|
|
141
|
+
const manifest = manifestAt(folder);
|
|
142
|
+
node = { key: `workspace:${folder}`, name, version: manifest?.version ?? '', production: manifest?.production ?? production, peers: [], folder };
|
|
143
|
+
workspaces.set(folder, { node, name, manifest });
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
const version = typeof fields.version === 'string' ? fields.version : '';
|
|
147
|
+
node = { key: resolution ?? descriptors.join(', '), name, version, production, peers };
|
|
148
|
+
}
|
|
149
|
+
nodes.push(node);
|
|
150
|
+
for (const descriptor of descriptors)
|
|
151
|
+
byDescriptor.set(descriptor, node);
|
|
152
|
+
}
|
|
153
|
+
if (v1) {
|
|
154
|
+
// v1 records no workspace: the manifests the root's `workspaces` globs match are them, the root's own included.
|
|
155
|
+
for (const folder of ['', ...Object.keys(manifests).filter((each) => each !== '')].sort()) {
|
|
156
|
+
const manifest = manifestAt(folder);
|
|
157
|
+
const node = {
|
|
158
|
+
key: `workspace:${folder}`,
|
|
159
|
+
name: manifest?.name ?? '',
|
|
160
|
+
version: manifest?.version ?? '',
|
|
161
|
+
production: manifest?.production ?? [],
|
|
162
|
+
peers: [],
|
|
163
|
+
folder,
|
|
164
|
+
};
|
|
165
|
+
workspaces.set(folder, { node, name: null, manifest });
|
|
166
|
+
if (folder !== '' && node.name !== '')
|
|
167
|
+
nodes.push(node);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
else if (!workspaces.has('')) {
|
|
171
|
+
manifestAt('');
|
|
172
|
+
}
|
|
173
|
+
const byName = new Map();
|
|
174
|
+
// A named workspace is installed too (linked into `node_modules`), so code under its name there is no vendored code.
|
|
175
|
+
// The root is no package anything installs.
|
|
176
|
+
for (const node of nodes) {
|
|
177
|
+
if (node.folder === '' || node.name === '')
|
|
178
|
+
continue;
|
|
179
|
+
const list = byName.get(node.name);
|
|
180
|
+
if (list === undefined)
|
|
181
|
+
byName.set(node.name, [node]);
|
|
182
|
+
else
|
|
183
|
+
list.push(node);
|
|
184
|
+
}
|
|
185
|
+
const workspaceNamed = (name) => [...workspaces.values()].find(({ node }) => node.name === name)?.node;
|
|
186
|
+
/** What a dependency `name@range` resolves to: its descriptor's entry, or a workspace of that name. */
|
|
187
|
+
const target = (name, range) => {
|
|
188
|
+
const found = byDescriptor.get(`${name}@${range}`) ?? (v1 ? undefined : byDescriptor.get(`${name}@npm:${range}`));
|
|
189
|
+
if (found !== undefined)
|
|
190
|
+
return [found];
|
|
191
|
+
const workspace = v1 || range.startsWith('workspace:') ? workspaceNamed(name) : undefined;
|
|
192
|
+
return workspace === undefined ? [] : [workspace];
|
|
193
|
+
};
|
|
194
|
+
/** A peer resolves when the lockfile installs one version of it. */
|
|
195
|
+
const peer = (name) => {
|
|
196
|
+
const found = byName.get(name) ?? [];
|
|
197
|
+
return found.length > 0 && found.every((node) => node.version === found[0].version) ? found : [];
|
|
198
|
+
};
|
|
199
|
+
const edges = (node) => {
|
|
200
|
+
const own = node;
|
|
201
|
+
return [...own.production.flatMap(([name, range]) => target(name, range)), ...own.peers.flatMap(peer)];
|
|
202
|
+
};
|
|
203
|
+
const direct = (workspace) => {
|
|
204
|
+
const manifest = workspaces.get(workspace.folder)?.manifest;
|
|
205
|
+
if (manifest === null || manifest === undefined)
|
|
206
|
+
return [];
|
|
207
|
+
return [...manifest.production, ...manifest.development].flatMap(([name, range]) => target(name, range));
|
|
208
|
+
};
|
|
209
|
+
const listed = [...workspaces.entries()]
|
|
210
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
|
|
211
|
+
// Berry's entry names a workspace; its `0.0.0-use.local` version is none.
|
|
212
|
+
.map(([folder, { name }]) => ({ folder, name, version: null }));
|
|
213
|
+
const missing = [...new Set(unreadable)].sort();
|
|
214
|
+
return {
|
|
215
|
+
lockfile: graph({
|
|
216
|
+
format: 'yarn',
|
|
217
|
+
keyedByInstallPath: false,
|
|
218
|
+
confidence: 'medium',
|
|
219
|
+
nodes,
|
|
220
|
+
workspaces: listed,
|
|
221
|
+
unresolved: missing.length === 0 ? null : `the manifest ${missing[0]} beside the lockfile is missing or unreadable`,
|
|
222
|
+
edges,
|
|
223
|
+
direct,
|
|
224
|
+
place: (directory) => placeByName(directory, byName, listed, edges, direct),
|
|
225
|
+
}),
|
|
226
|
+
};
|
|
227
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Level } from '../completeness/index.ts';
|
|
2
|
+
import type { SurfaceScope } from '../config/index.ts';
|
|
3
|
+
import { type Baseline, type BaselineEntry, type PackageVersion } from './lib/format.ts';
|
|
4
|
+
export { formatVersion, type Baseline, type BaselineEntry, type PackageVersion } from './lib/format.ts';
|
|
5
|
+
export { migrations, type Migrations } from './lib/migrations.ts';
|
|
6
|
+
export type { ReportReading } from './lib/report.ts';
|
|
7
|
+
export { readPrevious, type PreviousValue, type PreviousValues, type ReadPrevious } from './lib/previous.ts';
|
|
8
|
+
export { BaselineError, readBaseline, writtenVersions, type ReadBaseline } from './lib/read.ts';
|
|
9
|
+
export { newerVersions, versionSkew, type BaselineNote, type NewerVersion, type VersionPair, type Versions, type VersionSkew, } from './lib/skew.ts';
|
|
10
|
+
/**
|
|
11
|
+
* How a finding compares with the baseline (§11 Baseline states and matching). The compare module gives it, and a
|
|
12
|
+
* baseline accepts every finding except a `removed` one.
|
|
13
|
+
*/
|
|
14
|
+
export type BaselineState = 'added' | 'existing' | 'changed' | 'removed' | 'unconfirmed';
|
|
15
|
+
/**
|
|
16
|
+
* A finding of a scan, as a baseline accepts it: its entry fields, and its baseline state when the scan was compared
|
|
17
|
+
* with a baseline (§11 Baseline states and matching).
|
|
18
|
+
*/
|
|
19
|
+
export interface ScannedFinding extends BaselineEntry {
|
|
20
|
+
baselineState?: BaselineState | null;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The baseline that accepts the whole current finding set of a scan (§11 Commands: `baseline init` and `baseline
|
|
24
|
+
* update`). That is every finding except a `removed` one, which is a baseline entry the scan no longer finds. A paired
|
|
25
|
+
* finding is accepted under its current fingerprint (ADR 0020).
|
|
26
|
+
*/
|
|
27
|
+
export declare function buildBaseline(input: {
|
|
28
|
+
scanConfigId: string;
|
|
29
|
+
surface: SurfaceScope;
|
|
30
|
+
tool: {
|
|
31
|
+
fingerprintScheme: number;
|
|
32
|
+
catalogVersion: number;
|
|
33
|
+
monkeypatchlistVersion: string;
|
|
34
|
+
};
|
|
35
|
+
completeness: Level;
|
|
36
|
+
/** The scan's findings: an entry keeps only their entry fields. */
|
|
37
|
+
findings: readonly ScannedFinding[];
|
|
38
|
+
/** The scan's `packages` map: its findings' containing packages, strong versions only (§10, §11). */
|
|
39
|
+
packages: Readonly<Record<string, PackageVersion>>;
|
|
40
|
+
}): Baseline;
|
|
41
|
+
/**
|
|
42
|
+
* The baseline file's text (§11 Determinism): 2-space indent, LF line endings and a trailing newline. With the fixed
|
|
43
|
+
* key order and sorted entries, an unchanged build rewrites it byte for byte.
|
|
44
|
+
*/
|
|
45
|
+
export declare function renderBaseline(baseline: Baseline): string;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Baseline (§11 Baseline, ADR 0001): the committed, deterministic JSON file of accepted findings that `baseline init`
|
|
2
|
+
// writes, `baseline update` rewrites and a scan is compared with. It holds one entry per finding, with the attributes that decide `changed` and
|
|
3
|
+
// readable context, and nothing that churns on a rebuild. It knows nothing of files: the CLI reads and writes the text.
|
|
4
|
+
import { compareStrings } from "../findings/index.js";
|
|
5
|
+
import { entry, formatVersion } from "./lib/format.js";
|
|
6
|
+
export { formatVersion } from "./lib/format.js";
|
|
7
|
+
export { migrations } from "./lib/migrations.js";
|
|
8
|
+
export { readPrevious } from "./lib/previous.js";
|
|
9
|
+
export { BaselineError, readBaseline, writtenVersions } from "./lib/read.js";
|
|
10
|
+
export { newerVersions, versionSkew, } from "./lib/skew.js";
|
|
11
|
+
/**
|
|
12
|
+
* The baseline that accepts the whole current finding set of a scan (§11 Commands: `baseline init` and `baseline
|
|
13
|
+
* update`). That is every finding except a `removed` one, which is a baseline entry the scan no longer finds. A paired
|
|
14
|
+
* finding is accepted under its current fingerprint (ADR 0020).
|
|
15
|
+
*/
|
|
16
|
+
export function buildBaseline(input) {
|
|
17
|
+
const { tool } = input;
|
|
18
|
+
return {
|
|
19
|
+
formatVersion,
|
|
20
|
+
scanConfigId: input.scanConfigId,
|
|
21
|
+
fingerprintScheme: tool.fingerprintScheme,
|
|
22
|
+
catalogVersion: tool.catalogVersion,
|
|
23
|
+
monkeypatchlistVersion: tool.monkeypatchlistVersion,
|
|
24
|
+
completeness: input.completeness,
|
|
25
|
+
surface: input.surface,
|
|
26
|
+
packages: Object.fromEntries(Object.entries(input.packages)
|
|
27
|
+
.sort(([a], [b]) => compareStrings(a, b))
|
|
28
|
+
.map(([name, { version, versionSource }]) => [name, { version, versionSource }])),
|
|
29
|
+
entries: input.findings
|
|
30
|
+
.filter((finding) => finding.baselineState !== 'removed')
|
|
31
|
+
.map(entry)
|
|
32
|
+
.sort((a, b) => compareStrings(a.fingerprint, b.fingerprint)),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The baseline file's text (§11 Determinism): 2-space indent, LF line endings and a trailing newline. With the fixed
|
|
37
|
+
* key order and sorted entries, an unchanged build rewrites it byte for byte.
|
|
38
|
+
*/
|
|
39
|
+
export function renderBaseline(baseline) {
|
|
40
|
+
return `${JSON.stringify(baseline, null, 2)}\n`;
|
|
41
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ErrorObject } from 'ajv/dist/2020.js';
|
|
2
|
+
/** A baseline text that isn't a baseline this monkeypatchlist reads. The message says what is wrong with it. */
|
|
3
|
+
export declare class BaselineError extends Error {
|
|
4
|
+
name: string;
|
|
5
|
+
/**
|
|
6
|
+
* Whether the text is a baseline in a `formatVersion` newer than this monkeypatchlist's, which is a hard error (§11 Format
|
|
7
|
+
* versions). Otherwise the text is not a readable baseline at all.
|
|
8
|
+
*/
|
|
9
|
+
readonly newerFormat: boolean;
|
|
10
|
+
constructor(message: string, options?: {
|
|
11
|
+
newerFormat?: boolean;
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
/** A validation error as `<JSON Pointer>: <what is wrong>`. */
|
|
15
|
+
export declare function problem({ keyword, instancePath, params, message }: ErrorObject): string;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Why a text is no baseline this monkeypatchlist reads.
|
|
2
|
+
/** A baseline text that isn't a baseline this monkeypatchlist reads. The message says what is wrong with it. */
|
|
3
|
+
export class BaselineError extends Error {
|
|
4
|
+
name = 'BaselineError';
|
|
5
|
+
/**
|
|
6
|
+
* Whether the text is a baseline in a `formatVersion` newer than this monkeypatchlist's, which is a hard error (§11 Format
|
|
7
|
+
* versions). Otherwise the text is not a readable baseline at all.
|
|
8
|
+
*/
|
|
9
|
+
newerFormat;
|
|
10
|
+
constructor(message, options = {}) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.newerFormat = options.newerFormat ?? false;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/** A validation error as `<JSON Pointer>: <what is wrong>`. */
|
|
16
|
+
export function problem({ keyword, instancePath, params, message }) {
|
|
17
|
+
const at = instancePath === '' ? '(top level)' : instancePath;
|
|
18
|
+
if (keyword === 'required')
|
|
19
|
+
return `${instancePath}/${params.missingProperty}: is required`;
|
|
20
|
+
if (keyword === 'enum') {
|
|
21
|
+
const allowed = params.allowedValues;
|
|
22
|
+
return `${at}: must be one of ${allowed.map((value) => JSON.stringify(value)).join(', ')}`;
|
|
23
|
+
}
|
|
24
|
+
return `${at}: ${message}`;
|
|
25
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { Surface } from '../../../catalog.ts';
|
|
2
|
+
import type { Effect, Locks, Target } from '../../analysis/index.ts';
|
|
3
|
+
import type { Attribution } from '../../attribution/index.ts';
|
|
4
|
+
import type { Level } from '../../completeness/index.ts';
|
|
5
|
+
import type { SurfaceScope } from '../../config/index.ts';
|
|
6
|
+
/** The baseline format's version (§11 Format versions). */
|
|
7
|
+
export declare const formatVersion = 1;
|
|
8
|
+
/**
|
|
9
|
+
* One accepted finding. The fingerprint, and the attributes that decide `changed` (attribution key, effect and
|
|
10
|
+
* `locks`), with the readable context that reports it when it is `removed` (target and its realm, surface, provenance,
|
|
11
|
+
* and for a `pkg:` key the containing package in `attribution`).
|
|
12
|
+
*/
|
|
13
|
+
export interface BaselineEntry {
|
|
14
|
+
fingerprint: string;
|
|
15
|
+
target: Target;
|
|
16
|
+
surface: Surface;
|
|
17
|
+
effect: Effect;
|
|
18
|
+
locks: Locks;
|
|
19
|
+
attribution: Attribution;
|
|
20
|
+
}
|
|
21
|
+
/** A package's strong version, and where it came from (§10 Version): `store-path` or `lockfile`. */
|
|
22
|
+
export interface PackageVersion {
|
|
23
|
+
version: string;
|
|
24
|
+
versionSource: string;
|
|
25
|
+
}
|
|
26
|
+
/** A baseline: its header, then its entries, sorted by fingerprint. */
|
|
27
|
+
export interface Baseline {
|
|
28
|
+
formatVersion: typeof formatVersion;
|
|
29
|
+
/** The scan-config id of the scan it was written from (§16). */
|
|
30
|
+
scanConfigId: string;
|
|
31
|
+
fingerprintScheme: number;
|
|
32
|
+
catalogVersion: number;
|
|
33
|
+
monkeypatchlistVersion: string;
|
|
34
|
+
/** The completeness level it was written under (§13: only the summary level is stored). */
|
|
35
|
+
completeness: Level;
|
|
36
|
+
/** The surface scope it covers (§12): each scope has its own baseline. */
|
|
37
|
+
surface: SurfaceScope;
|
|
38
|
+
/**
|
|
39
|
+
* Name → version and version source, strong versions only (§10), for the containing packages of the scan's findings. A
|
|
40
|
+
* name with more than one strong version in the scan has no entry.
|
|
41
|
+
*/
|
|
42
|
+
packages: Record<string, PackageVersion>;
|
|
43
|
+
entries: BaselineEntry[];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The entry of a finding, or of an entry read from a file, with its keys in a fixed order: the fingerprint first, then
|
|
47
|
+
* the fields in the report's order. Nothing else is kept.
|
|
48
|
+
*/
|
|
49
|
+
export declare function entry({ fingerprint, target, surface, effect, locks, attribution }: BaselineEntry): BaselineEntry;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// The baseline format (§11 Baseline): its header and entries, in the fixed key order `baseline init` writes.
|
|
2
|
+
/** The baseline format's version (§11 Format versions). */
|
|
3
|
+
export const formatVersion = 1;
|
|
4
|
+
/**
|
|
5
|
+
* The entry of a finding, or of an entry read from a file, with its keys in a fixed order: the fingerprint first, then
|
|
6
|
+
* the fields in the report's order. Nothing else is kept.
|
|
7
|
+
*/
|
|
8
|
+
export function entry({ fingerprint, target, surface, effect, locks, attribution }) {
|
|
9
|
+
const { realm, holder, name, shadows } = target;
|
|
10
|
+
return {
|
|
11
|
+
fingerprint,
|
|
12
|
+
target: { realm, holder, name, ...(shadows === undefined ? {} : { shadows }) },
|
|
13
|
+
surface,
|
|
14
|
+
effect,
|
|
15
|
+
locks,
|
|
16
|
+
attribution: {
|
|
17
|
+
key: attribution.key,
|
|
18
|
+
provenance: attribution.provenance,
|
|
19
|
+
...(attribution.package === undefined ? {} : { package: attribution.package }),
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { BaselineEntry } from './format.ts';
|
|
2
|
+
export interface Migrations {
|
|
3
|
+
/**
|
|
4
|
+
* Format migrations, by the `formatVersion` each reads. Each turns a baseline file's parsed JSON in that format into
|
|
5
|
+
* the next format's, `formatVersion` included. The JSON is unchecked: a migration that throws on it makes the file
|
|
6
|
+
* not a readable baseline, and the schema check judges what the last one gives.
|
|
7
|
+
*/
|
|
8
|
+
format: ReadonlyMap<number, (baseline: unknown) => unknown>;
|
|
9
|
+
/**
|
|
10
|
+
* Fingerprint scheme migrations, by the scheme each reads (§4: a scheme bump ships one). Each gives the baseline's
|
|
11
|
+
* entries under the next scheme.
|
|
12
|
+
*/
|
|
13
|
+
fingerprintScheme: ReadonlyMap<number, (entries: readonly BaselineEntry[]) => BaselineEntry[]>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The migrations this monkeypatchlist ships. There are none yet: `formatVersion` 1 and fingerprint scheme 1 are the first. A
|
|
17
|
+
* scheme change that can't be migrated needs a major release (§4).
|
|
18
|
+
*/
|
|
19
|
+
export declare const migrations: Migrations;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Migrations (§11 Format versions, §4 Version skew): how this monkeypatchlist reads a baseline written in an older format, or
|
|
2
|
+
// under an older fingerprint scheme. The baseline is migrated in memory, and `baseline update` rewrites it.
|
|
3
|
+
/**
|
|
4
|
+
* The migrations this monkeypatchlist ships. There are none yet: `formatVersion` 1 and fingerprint scheme 1 are the first. A
|
|
5
|
+
* scheme change that can't be migrated needs a major release (§4).
|
|
6
|
+
*/
|
|
7
|
+
export const migrations = { format: new Map(), fingerprintScheme: new Map() };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type InstallContext, type Reachability } from '../../analysis/index.ts';
|
|
2
|
+
/** A finding's reachability and install context in the previous scan (§7). */
|
|
3
|
+
export interface PreviousValue {
|
|
4
|
+
reachability: Reachability;
|
|
5
|
+
installContext: InstallContext;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* The previous scan's values by fingerprint. A finding the previous scan reported with no occurrences (`removed` or
|
|
9
|
+
* `unconfirmed`) has none.
|
|
10
|
+
*/
|
|
11
|
+
export type PreviousValues = ReadonlyMap<string, PreviousValue>;
|
|
12
|
+
/** A previous report as `--previous` reads it: the scan-config id it must share with the scan, and its values. */
|
|
13
|
+
export interface ReadPrevious {
|
|
14
|
+
scanConfigId: string;
|
|
15
|
+
values: PreviousValues;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The previous values in a parsed report `value` (§11), or a `BaselineError` that says why it isn't a report they can
|
|
19
|
+
* be read from.
|
|
20
|
+
*/
|
|
21
|
+
export declare function previousOfReport(value: unknown, reportVersion: number): ReadPrevious;
|
|
22
|
+
/** The previous report in `text` (§16 `--previous`), or a `BaselineError` that says why it can't be read. */
|
|
23
|
+
export declare function readPrevious(text: string, reportVersion: number): ReadPrevious;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// A previous scan's values (§11 Never `changed`, §16 `--previous`): each finding's reachability and install context in
|
|
2
|
+
// a full JSON scan report, which the reachability-change and install-context-change notes compare with. A baseline
|
|
3
|
+
// stores neither (ADR 0014), so they come from `--previous`, or from `--baseline` when it is a full report.
|
|
4
|
+
import { Ajv2020 } from 'ajv/dist/2020.js';
|
|
5
|
+
import { installContexts, reachabilities } from "../../analysis/index.js";
|
|
6
|
+
import { BaselineError, problem } from "./error.js";
|
|
7
|
+
import { isReport } from "./report.js";
|
|
8
|
+
/** The parts of a report the previous values are read from. */
|
|
9
|
+
const schema = {
|
|
10
|
+
type: 'object',
|
|
11
|
+
required: ['reportVersion', 'config', 'findings'],
|
|
12
|
+
properties: {
|
|
13
|
+
reportVersion: { type: 'integer' },
|
|
14
|
+
config: { type: 'object', required: ['scanConfigId'], properties: { scanConfigId: { type: 'string' } } },
|
|
15
|
+
findings: {
|
|
16
|
+
type: 'array',
|
|
17
|
+
items: {
|
|
18
|
+
type: 'object',
|
|
19
|
+
required: ['fingerprint', 'reachability', 'installContext'],
|
|
20
|
+
properties: {
|
|
21
|
+
fingerprint: { type: 'string', minLength: 1 },
|
|
22
|
+
reachability: { enum: [...reachabilities, null] },
|
|
23
|
+
installContext: { enum: [...installContexts, null] },
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
let validate;
|
|
30
|
+
/**
|
|
31
|
+
* The previous values in a parsed report `value` (§11), or a `BaselineError` that says why it isn't a report they can
|
|
32
|
+
* be read from.
|
|
33
|
+
*/
|
|
34
|
+
export function previousOfReport(value, reportVersion) {
|
|
35
|
+
if (!isReport(value))
|
|
36
|
+
throw new BaselineError('it is not a scan report');
|
|
37
|
+
validate ??= new Ajv2020({ allErrors: true, strict: true }).compile(schema);
|
|
38
|
+
if (!validate(value)) {
|
|
39
|
+
const problems = (validate.errors ?? []).map(problem);
|
|
40
|
+
throw new BaselineError(`it is not a scan report:\n${problems.map((line) => ` ${line}`).join('\n')}`);
|
|
41
|
+
}
|
|
42
|
+
const report = value;
|
|
43
|
+
if (report.reportVersion !== reportVersion) {
|
|
44
|
+
const relation = report.reportVersion > reportVersion ? 'newer' : 'older';
|
|
45
|
+
throw new BaselineError(`it is a scan report in reportVersion ${report.reportVersion}, ${relation} than this monkeypatchlist reads (${reportVersion})`);
|
|
46
|
+
}
|
|
47
|
+
const values = new Map();
|
|
48
|
+
for (const { fingerprint, reachability, installContext } of report.findings) {
|
|
49
|
+
if (reachability !== null && installContext !== null)
|
|
50
|
+
values.set(fingerprint, { reachability, installContext });
|
|
51
|
+
}
|
|
52
|
+
return { scanConfigId: report.config.scanConfigId, values };
|
|
53
|
+
}
|
|
54
|
+
/** The previous report in `text` (§16 `--previous`), or a `BaselineError` that says why it can't be read. */
|
|
55
|
+
export function readPrevious(text, reportVersion) {
|
|
56
|
+
let value;
|
|
57
|
+
try {
|
|
58
|
+
value = JSON.parse(text.startsWith('\uFEFF') ? text.slice(1) : text);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
throw new BaselineError(`it is not valid JSON: ${error.message}`);
|
|
62
|
+
}
|
|
63
|
+
return previousOfReport(value, reportVersion);
|
|
64
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type Baseline } from './format.ts';
|
|
2
|
+
import { type Migrations } from './migrations.ts';
|
|
3
|
+
import { type PreviousValues } from './previous.ts';
|
|
4
|
+
import { type ReportReading } from './report.ts';
|
|
5
|
+
import { type Versions } from './skew.ts';
|
|
6
|
+
export { BaselineError } from './error.ts';
|
|
7
|
+
/** A baseline as this monkeypatchlist reads it, and the versions its file was written with (§4 Version skew). */
|
|
8
|
+
export interface ReadBaseline {
|
|
9
|
+
baseline: Baseline;
|
|
10
|
+
written: Versions;
|
|
11
|
+
/**
|
|
12
|
+
* The previous scan's values when the text is a full report (§11 Never `changed`, §16 `--previous`): a report used as
|
|
13
|
+
* the baseline also supplies them. Null for a baseline file, which stores none (ADR 0014).
|
|
14
|
+
*/
|
|
15
|
+
previous: PreviousValues | null;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The baseline in `text`, or a `BaselineError` that says why it isn't one. The baseline comes back with its keys in the
|
|
19
|
+
* fixed order `baseline init` writes, without keys this monkeypatchlist doesn't read.
|
|
20
|
+
*
|
|
21
|
+
* - A `formatVersion` newer than this monkeypatchlist's is an error, and an older one is migrated in memory (§11 Format
|
|
22
|
+
* versions), by `migrations`. An older one that no migration reads is not a readable baseline.
|
|
23
|
+
* - A fingerprint scheme older than this monkeypatchlist's is migrated in memory, by `migrations`. A scheme that can't be
|
|
24
|
+
* migrated (a newer one, or an older one that no migration reads) is not a readable baseline: a hard error under
|
|
25
|
+
* `--fail-on`, and a scan without a baseline otherwise (§4 Version skew, §11 No baseline).
|
|
26
|
+
* - A fingerprint that two entries share is an error.
|
|
27
|
+
* - With `options.report`, the text may be a full scan report instead, read as the baseline `baseline update` would
|
|
28
|
+
* have written from its scan (§11 Baseline, ADR 0020). Without it, a report is not a readable baseline.
|
|
29
|
+
*/
|
|
30
|
+
export declare function readBaseline(text: string, migrations?: Migrations, options?: {
|
|
31
|
+
report?: ReportReading;
|
|
32
|
+
}): ReadBaseline;
|
|
33
|
+
/**
|
|
34
|
+
* The versions a baseline's text was written with (§4 Version skew), for `baseline init` to tell whether the file it
|
|
35
|
+
* replaces came from a newer version. They are the versions `readBaseline` reads. A text that isn't a baseline this
|
|
36
|
+
* monkeypatchlist reads (a newer format or fingerprint scheme, or a newer catalog's holder) gives each version its header
|
|
37
|
+
* records in a well-formed value, and a text that isn't a JSON object gives none.
|
|
38
|
+
*/
|
|
39
|
+
export declare function writtenVersions(text: string, migrations?: Migrations): Partial<Versions>;
|