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,54 @@
|
|
|
1
|
+
// The scan-config id (§16 Scan configuration identity, ADR 0017): a hash of the settings that decide which findings
|
|
2
|
+
// can exist, recorded in a baseline so that it is never compared with a scan of something else.
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
import { posix } from 'node:path';
|
|
5
|
+
import { realms } from "../../../catalog.js";
|
|
6
|
+
/**
|
|
7
|
+
* The fields the id is made of, in the order of its parts. The mode, the source-map directories and the lockfile are
|
|
8
|
+
* not among them: remote gaps already become `unconfirmed`, and maps and lockfile are optional evidence (ADR 0006).
|
|
9
|
+
*/
|
|
10
|
+
export const scanConfigFields = ['entries', 'mappings', 'surface', 'realms'];
|
|
11
|
+
/**
|
|
12
|
+
* The scan-config id: one 12-hex-digit part per field of `scanConfigFields`, joined with `.`, so that two ids name the
|
|
13
|
+
* fields that differ (§16). Each part is the start of the SHA-256 of the field's canonical JSON:
|
|
14
|
+
*
|
|
15
|
+
* - entries: `[file, url]` pairs, sorted
|
|
16
|
+
* - mappings: `[url, dir]` pairs, sorted
|
|
17
|
+
* - surface: the scope
|
|
18
|
+
* - realms: the realm set, sorted. R1 has no setting that narrows it, so every scan covers all four realms (§2).
|
|
19
|
+
*
|
|
20
|
+
* Only what decides the findings counts: the order of entries and mappings doesn't (the longest mapping prefix wins),
|
|
21
|
+
* and a path is normalized, without a trailing `/`.
|
|
22
|
+
*/
|
|
23
|
+
export function scanConfigId(config) {
|
|
24
|
+
const fields = {
|
|
25
|
+
entries: config.entries.map(({ file, url }) => [path(file), url]).sort(comparePairs),
|
|
26
|
+
mappings: config.mappings.map(({ url, dir }) => [url, path(dir)]).sort(comparePairs),
|
|
27
|
+
surface: config.surface,
|
|
28
|
+
realms: [...realms].sort(),
|
|
29
|
+
};
|
|
30
|
+
return scanConfigFields.map((field) => digest(fields[field])).join('.');
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The fields in which two scan-config ids differ, in the order of `scanConfigFields`: none when they are the same id.
|
|
34
|
+
* An id that doesn't have one part per field can't be read part by part, so it differs in every field.
|
|
35
|
+
*/
|
|
36
|
+
export function differingScanConfigFields(a, b) {
|
|
37
|
+
const [ours, theirs] = [a.split('.'), b.split('.')];
|
|
38
|
+
const readable = ours.length === scanConfigFields.length && theirs.length === scanConfigFields.length;
|
|
39
|
+
return scanConfigFields.filter((_, i) => !readable || ours[i] !== theirs[i]);
|
|
40
|
+
}
|
|
41
|
+
function digest(value) {
|
|
42
|
+
return createHash('sha256').update(JSON.stringify(value)).digest('hex').slice(0, 12);
|
|
43
|
+
}
|
|
44
|
+
/** A relative path in one spelling: `./assets/` and `assets` are the same directory. */
|
|
45
|
+
function path(value) {
|
|
46
|
+
const normalized = posix.normalize(value);
|
|
47
|
+
return normalized.length > 1 && normalized.endsWith('/') ? normalized.slice(0, -1) : normalized;
|
|
48
|
+
}
|
|
49
|
+
function comparePairs(a, b) {
|
|
50
|
+
return compare(a[0], b[0]) || compare(a[1], b[1]);
|
|
51
|
+
}
|
|
52
|
+
function compare(a, b) {
|
|
53
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
54
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// The config JSON Schema (§16) that ships in the package, at `schema/config.schema.json`. It validates the file.
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { Ajv2020 } from 'ajv/dist/2020.js';
|
|
4
|
+
let validate;
|
|
5
|
+
/** Validates a parsed config file against the shipped schema. Returns one line per problem, each naming its path. */
|
|
6
|
+
export function schemaProblems(value) {
|
|
7
|
+
validate ??= new Ajv2020({ allErrors: true, strict: true }).compile(JSON.parse(readFileSync(new URL('../../../../schema/config.schema.json', import.meta.url), 'utf8')));
|
|
8
|
+
return validate(value) ? [] : (validate.errors ?? []).map(problem);
|
|
9
|
+
}
|
|
10
|
+
/** A validation error as `<JSON Pointer>: <what is wrong>`. */
|
|
11
|
+
function problem({ keyword, instancePath, params, message }) {
|
|
12
|
+
switch (keyword) {
|
|
13
|
+
case 'additionalProperties':
|
|
14
|
+
return `${instancePath}/${escape(params.additionalProperty)}: unknown key`;
|
|
15
|
+
case 'required':
|
|
16
|
+
return `${instancePath}/${escape(params.missingProperty)}: is required`;
|
|
17
|
+
case 'enum': {
|
|
18
|
+
const allowed = params.allowedValues;
|
|
19
|
+
return `${at(instancePath)}: must be one of ${allowed.map((value) => JSON.stringify(value)).join(', ')}`;
|
|
20
|
+
}
|
|
21
|
+
default:
|
|
22
|
+
return `${at(instancePath)}: ${message}`;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function at(pointer) {
|
|
26
|
+
return pointer === '' ? '(top level)' : pointer;
|
|
27
|
+
}
|
|
28
|
+
/** Escapes a key as a JSON Pointer reference token (RFC 6901). */
|
|
29
|
+
function escape(key) {
|
|
30
|
+
return key.replaceAll('~', '~0').replaceAll('/', '~1');
|
|
31
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/** A URL→path mapping (§2): URLs under `url` are files under `dir`, relative to the build directory. */
|
|
2
|
+
export interface Mapping {
|
|
3
|
+
url: string;
|
|
4
|
+
dir: string;
|
|
5
|
+
}
|
|
6
|
+
/** An entry document (§2): its file, relative to the build directory, and its URL. */
|
|
7
|
+
export interface Entry {
|
|
8
|
+
file: string;
|
|
9
|
+
url: string;
|
|
10
|
+
}
|
|
11
|
+
/** The surface a scan reports, baselines and gates (§12, §16 `--surface`). */
|
|
12
|
+
export type SurfaceScope = 'protected' | 'all';
|
|
13
|
+
/** The remote retrieval mode (§17 Modes, §16 `--mode`): `offline` never fetches, and `resolve` does. */
|
|
14
|
+
export type Mode = 'offline' | 'resolve';
|
|
15
|
+
/** The baseline states the gate can fail on (§11 The gate, §16 `--fail-on`): `removed` and `unconfirmed` can't gate. */
|
|
16
|
+
export declare const failOnStates: readonly ["added", "changed"];
|
|
17
|
+
export type FailOnState = (typeof failOnStates)[number];
|
|
18
|
+
/** The commands (§16 Commands) built so far. */
|
|
19
|
+
export type Command = 'scan' | BaselineCommand;
|
|
20
|
+
/** The baseline commands (§11 Commands): each writes the baseline `--out` names. */
|
|
21
|
+
export type BaselineCommand = 'baseline init' | 'baseline update';
|
|
22
|
+
/** Bad flags or configuration: exit code 2 (§16). */
|
|
23
|
+
export declare class UsageError extends Error {
|
|
24
|
+
name: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* What the config file and the flags each give (§16): every field is optional, and a flag overrides the file field by
|
|
28
|
+
* field. An entry's URL may be left out; it defaults to the first mapping's URL.
|
|
29
|
+
*/
|
|
30
|
+
export interface Settings {
|
|
31
|
+
name?: string;
|
|
32
|
+
entries?: {
|
|
33
|
+
file: string;
|
|
34
|
+
url?: string;
|
|
35
|
+
}[];
|
|
36
|
+
mappings?: Mapping[];
|
|
37
|
+
sourceMaps?: string[];
|
|
38
|
+
surface?: SurfaceScope;
|
|
39
|
+
mode?: Mode;
|
|
40
|
+
/**
|
|
41
|
+
* The lockfile (§10 Introducing dependencies): `--lockfile` sets `path`, and the config key `lockfile.importer` names
|
|
42
|
+
* the workspace of the scanned application.
|
|
43
|
+
*/
|
|
44
|
+
lockfile?: {
|
|
45
|
+
path?: string;
|
|
46
|
+
importer?: string;
|
|
47
|
+
};
|
|
48
|
+
json?: string;
|
|
49
|
+
out?: string;
|
|
50
|
+
baseline?: string;
|
|
51
|
+
/** `--previous`: a previous JSON scan report, for the reachability and install-context notes (§16). */
|
|
52
|
+
previous?: string;
|
|
53
|
+
failOn?: FailOnState[];
|
|
54
|
+
allowIncomplete?: boolean;
|
|
55
|
+
allowDowngrade?: boolean;
|
|
56
|
+
/** The saved JSON report `explain` reads (§16 `--report`). */
|
|
57
|
+
report?: string;
|
|
58
|
+
/** `--verbose`: the terminal report cards existing findings too (§15 Terminal). */
|
|
59
|
+
verbose?: boolean;
|
|
60
|
+
/** `--list-ineffective`: the terminal report lists the ineffective-write records (§3, §16). */
|
|
61
|
+
listIneffective?: boolean;
|
|
62
|
+
/** `--no-color`: the terminal report is never colored (§15 Formatting). */
|
|
63
|
+
noColor?: boolean;
|
|
64
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** The baseline states the gate can fail on (§11 The gate, §16 `--fail-on`): `removed` and `unconfirmed` can't gate. */
|
|
2
|
+
export const failOnStates = ['added', 'changed'];
|
|
3
|
+
/** Bad flags or configuration: exit code 2 (§16). */
|
|
4
|
+
export class UsageError extends Error {
|
|
5
|
+
name = 'UsageError';
|
|
6
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** The canonical form of an absolute `http:` or `https:` URL, or why `value` is not one. */
|
|
2
|
+
export function httpUrl(value) {
|
|
3
|
+
const url = URL.parse(value);
|
|
4
|
+
if (url === null)
|
|
5
|
+
return { problem: `'${value}' is not an absolute URL` };
|
|
6
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:')
|
|
7
|
+
return { problem: `'${value}' is not an http: or https: URL` };
|
|
8
|
+
return { href: url.href };
|
|
9
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import type { Realm, SerializedCatalog } from '../../catalog.ts';
|
|
2
|
+
import type { GeneratedLocation } from '../analysis/index.ts';
|
|
3
|
+
import type { HtmlCounters, LimitationRecord, SourceMapDirectoryCounts, SourceMapPairing, SweptFiles, UnreadMaps } from '../completeness/index.ts';
|
|
4
|
+
import type { Entry } from '../config/index.ts';
|
|
5
|
+
import { type Retrieval, type SourceStore } from '../source-store/index.ts';
|
|
6
|
+
import { type LineReading } from './lib/source-maps.ts';
|
|
7
|
+
/** A script artifact of the artifact graph (§2). */
|
|
8
|
+
export interface Artifact {
|
|
9
|
+
url: string;
|
|
10
|
+
/** Build-relative POSIX path, for a local file; null for an inline script. */
|
|
11
|
+
path: string | null;
|
|
12
|
+
/**
|
|
13
|
+
* The content hash: `sha256:` and the hex digest of the artifact's bytes (an inline script's UTF-8 text); null when
|
|
14
|
+
* its bytes could not be read, or were withheld because they don't match their integrity value (§17 Integrity).
|
|
15
|
+
*/
|
|
16
|
+
hash: string | null;
|
|
17
|
+
/**
|
|
18
|
+
* Where it is served from (§2): the build (`local-build`, through a URL mapping), the entry document itself
|
|
19
|
+
* (`inline`), or elsewhere, on the entry document's origin (`same-origin`) or another (`cross-origin`).
|
|
20
|
+
*/
|
|
21
|
+
origin: 'local-build' | 'same-origin' | 'cross-origin' | 'inline';
|
|
22
|
+
/**
|
|
23
|
+
* How it was found (§2): by following a reference, as a script the page's code injects (`injected`), or only by the
|
|
24
|
+
* build-directory sweep.
|
|
25
|
+
*/
|
|
26
|
+
discovery: 'reference' | 'injected' | 'sweep';
|
|
27
|
+
/**
|
|
28
|
+
* `module` for a module script: a `type=module` script, a `<link rel=modulepreload>`, what a module request (a
|
|
29
|
+
* static or dynamic import, or a re-export) names, or a worker created with `type: "module"`. `script` for a classic
|
|
30
|
+
* one. A file only a `<link rel=preload as=script>` names, or only preloads that disagree, and a worker created with
|
|
31
|
+
* options that can't be read statically, are typed by their syntax, as a sweep-only file is (§2). A URL a page runs
|
|
32
|
+
* both ways is an artifact each way.
|
|
33
|
+
*/
|
|
34
|
+
sourceType: 'script' | 'module';
|
|
35
|
+
/**
|
|
36
|
+
* The realm it runs in, from how it was referenced (§2 Realm): `window` for what an entry document references, the
|
|
37
|
+
* worker's realm for a worker script (`new Worker`, `new SharedWorker`, `navigator.serviceWorker.register`), and the
|
|
38
|
+
* realm of the code that references it for an import or an `importScripts`. A URL referenced in two realms is an
|
|
39
|
+
* artifact in each.
|
|
40
|
+
*/
|
|
41
|
+
realm: Realm;
|
|
42
|
+
/**
|
|
43
|
+
* How its realm is known (§2 Realm): from how it was referenced (`reference`), or assumed to be `window` because only
|
|
44
|
+
* the sweep found it, only a `<link rel=preload as=script>` names it, or only the module requests of code whose realm
|
|
45
|
+
* is assumed name it (`assumed`).
|
|
46
|
+
*/
|
|
47
|
+
realmBasis: 'reference' | 'assumed';
|
|
48
|
+
/**
|
|
49
|
+
* The URL of what first references it in its realm (§2 parent or importer): the entry document whose element names
|
|
50
|
+
* it, or the script or module that imports it or creates the worker. Null for an artifact only the sweep found.
|
|
51
|
+
*/
|
|
52
|
+
parent: string | null;
|
|
53
|
+
/**
|
|
54
|
+
* Its source map (§2 Source maps): the first candidate that verifies, found by the `SourceMap` header of a fetched
|
|
55
|
+
* artifact, then its `sourceMappingURL` annotation, then a `<file>.map` sidecar, then a map in the build directory with
|
|
56
|
+
* its `debugId`, then in each source-map directory, a map with its `debugId`, its annotation mirrored there, and its
|
|
57
|
+
* sidecar mirrored there.
|
|
58
|
+
*/
|
|
59
|
+
sourceMap: SourceMapPairing;
|
|
60
|
+
/**
|
|
61
|
+
* The source map that verified, for attribution (§10), which reads it from its raw JSON: that JSON, and the line
|
|
62
|
+
* reading of the artifact's text that verified it (§2 Verification). Null when none did. It isn't reported.
|
|
63
|
+
*/
|
|
64
|
+
map: PairedMap | null;
|
|
65
|
+
/**
|
|
66
|
+
* How many candidate maps failed verification against it (§2 Source maps), each counted `stale`: those before the
|
|
67
|
+
* one that verified, or every one, when none did. A source-map directory's map verified only to count it, after one
|
|
68
|
+
* already verified, is not counted.
|
|
69
|
+
*/
|
|
70
|
+
staleCandidates: number;
|
|
71
|
+
/**
|
|
72
|
+
* The maps it names, as a remote artifact, that weren't read (§17 Content types): refused by the safety floor, failed,
|
|
73
|
+
* or a build file that exists but can't be read. They change counters only, never completeness.
|
|
74
|
+
*/
|
|
75
|
+
unreadMaps: UnreadMaps;
|
|
76
|
+
/** How its bytes were retrieved (§17 What is recorded): whether they were read (`ok`), or why not. */
|
|
77
|
+
retrieval: Retrieval;
|
|
78
|
+
/**
|
|
79
|
+
* Whether a reference runs it. It does not when only preloads name it (`<link rel=preload as=script>` or `<link
|
|
80
|
+
* rel=modulepreload>`), or module requests of files that don't run: a preload joins the artifact graph, but never
|
|
81
|
+
* makes a file run (§19 HTML rules), and a module runs only when its importer does. Nor does it when only the sweep
|
|
82
|
+
* found it.
|
|
83
|
+
*/
|
|
84
|
+
runs: boolean;
|
|
85
|
+
/** The script's source text, when its bytes were read. */
|
|
86
|
+
text: string | null;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* A verified source map (§2 Source maps): its raw JSON, the line reading of the artifact that verified it, and `base`,
|
|
90
|
+
* the build-relative path whose directory its relative sources resolve against (§10): the map's own path. A
|
|
91
|
+
* source-map directory's map has its path inside the directory, as if the directory mirrored the build, however it
|
|
92
|
+
* was paired, and a `data:` URL map has its script's path (§10 Roots with no shared place). Null for a fetched map,
|
|
93
|
+
* which has no place in the build.
|
|
94
|
+
*/
|
|
95
|
+
export interface PairedMap {
|
|
96
|
+
raw: unknown;
|
|
97
|
+
reading: LineReading;
|
|
98
|
+
base: string | null;
|
|
99
|
+
}
|
|
100
|
+
export type { LineReading };
|
|
101
|
+
/** What discovery finds: the artifact graph, and what it could not follow (§13). */
|
|
102
|
+
export interface Discovered {
|
|
103
|
+
/** Every artifact, in the order it was first found. */
|
|
104
|
+
artifacts: Artifact[];
|
|
105
|
+
limitations: LimitationRecord[];
|
|
106
|
+
/** HTML content that is counted, not analyzed (§19 HTML rules). */
|
|
107
|
+
html: HtmlCounters;
|
|
108
|
+
/** The build files the sweep found and did not analyze (§2 Sweep, §13 Counters), by path, sorted. */
|
|
109
|
+
swept: SweptFiles;
|
|
110
|
+
/** What became of each source-map directory's maps (§13 Attribution block), in the order they were declared. */
|
|
111
|
+
sourceMapDirectories: SourceMapDirectoryCounts[];
|
|
112
|
+
/**
|
|
113
|
+
* Each module request a script's code makes that resolves to a URL (§6 Module links): the realm and URL of the script
|
|
114
|
+
* it is in, the entry document that followed it, whose import maps resolved it, its specifier, and the URL it names.
|
|
115
|
+
* A sweep-only file's requests are recorded too, with no document, resolved against its URL with no import map: they
|
|
116
|
+
* are followed (§2 Sweep), except that an import of another swept file leaves it the sweep's.
|
|
117
|
+
*/
|
|
118
|
+
imports: ModuleImport[];
|
|
119
|
+
/**
|
|
120
|
+
* The artifacts that module requests name and no other reference runs, by realm and URL: no script element, worker
|
|
121
|
+
* or other reference does, though a preload may name them. They are analyzed only as members of their importers' link
|
|
122
|
+
* groups (§19 Link groups).
|
|
123
|
+
*/
|
|
124
|
+
importedOnly: {
|
|
125
|
+
realm: Realm;
|
|
126
|
+
url: string;
|
|
127
|
+
}[];
|
|
128
|
+
/**
|
|
129
|
+
* The entry documents whose references other than module requests name each artifact, by realm and URL, in the order
|
|
130
|
+
* of the entries: each runs it with its own import maps.
|
|
131
|
+
*/
|
|
132
|
+
documents: {
|
|
133
|
+
realm: Realm;
|
|
134
|
+
url: string;
|
|
135
|
+
documents: string[];
|
|
136
|
+
}[];
|
|
137
|
+
/**
|
|
138
|
+
* Each remote chunk a loader's folded chunk URL names (§2, §19 References only the resolver can see): the realm, the
|
|
139
|
+
* loader (its URL, and the URL it was fetched at when that isn't its URL) and the chunk's URL. A remote chunk joins
|
|
140
|
+
* the link group of a loader that loads it, and no other group, through the registry it pushes into (§6 idiom 6).
|
|
141
|
+
*/
|
|
142
|
+
remoteChunks: {
|
|
143
|
+
realm: Realm;
|
|
144
|
+
loader: {
|
|
145
|
+
url: string;
|
|
146
|
+
fetched?: string;
|
|
147
|
+
};
|
|
148
|
+
chunk: string;
|
|
149
|
+
}[];
|
|
150
|
+
}
|
|
151
|
+
/** A module request that resolves to a URL (`Discovered.imports`). */
|
|
152
|
+
export interface ModuleImport {
|
|
153
|
+
realm: Realm;
|
|
154
|
+
document: string | null;
|
|
155
|
+
/** The importing script's URL, and the URL it was fetched at when that isn't its URL (§11). */
|
|
156
|
+
from: {
|
|
157
|
+
url: string;
|
|
158
|
+
fetched?: string;
|
|
159
|
+
};
|
|
160
|
+
specifier: string;
|
|
161
|
+
/** The module's URL, and the URL it is fetched at when that isn't its URL (§11). */
|
|
162
|
+
to: {
|
|
163
|
+
url: string;
|
|
164
|
+
fetched?: string;
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Finds the classic and module scripts each entry document runs, following the HTML rules of §19: its inline scripts,
|
|
169
|
+
* and the files its `script[src]` elements name, resolved against the document's base URL. A file that only a
|
|
170
|
+
* `<link rel=preload as=script>` or a `<link rel=modulepreload>` names joins the graph too, and does not run. It
|
|
171
|
+
* counts the HTML content it does not analyze: classic `nomodule` scripts, inline event handlers and `javascript:`
|
|
172
|
+
* URLs.
|
|
173
|
+
*
|
|
174
|
+
* Then it follows the module requests of every script it found, and of every module those name, in turn (§2): static
|
|
175
|
+
* imports, re-exports and dynamic imports with a literal specifier, resolved through the document's import maps in
|
|
176
|
+
* browser order (§2 Import maps). An inline module script's requests resolve through the maps before it in tree order,
|
|
177
|
+
* and every other request through all of them. Each names a module, which runs when its importer does. A request with
|
|
178
|
+
* an import attribute names no JavaScript module, and is not followed. A static import or re-export with an attribute a
|
|
179
|
+
* browser doesn't support fails every module graph that contains it (§2 Import attributes): each root of such a graph
|
|
180
|
+
* (a module script element, a dynamic import or a module worker) gives nothing, and nothing is followed through it
|
|
181
|
+
* from a hint. A dynamic import whose specifier is not a literal, or whose options have a part that can't be read and
|
|
182
|
+
* could decide whether it names a JavaScript module, and a specifier that doesn't resolve, such as a bare specifier no
|
|
183
|
+
* import map maps, give an `unresolved-reference` record.
|
|
184
|
+
*
|
|
185
|
+
* It follows the worker scripts they load with a literal URL too (§2, §19 Discovery), where the realm has the function
|
|
186
|
+
* that loads them: a `new Worker` is a dedicated worker, a `new SharedWorker` a shared worker, and a
|
|
187
|
+
* `navigator.serviceWorker.register` a service worker, each in its own realm, and an `importScripts` loads a classic
|
|
188
|
+
* script into its own worker. A service worker whose script falls outside every URL mapping, like a `<link
|
|
189
|
+
* rel=modulepreload as=serviceworker>` outside every mapping, gives an `unresolved-reference` record instead. A
|
|
190
|
+
* dedicated or shared worker's script on another origin than its creator's is not followed, as a browser won't run it,
|
|
191
|
+
* and a `data:` worker's is code the page carries, as a `data:` script is.
|
|
192
|
+
*
|
|
193
|
+
* A file a `<link rel=preload as=script>` names that no running reference names, in any realm, is followed last, as the
|
|
194
|
+
* syntax of the preload's own bytes says, in URL order. Its realm is assumed to be `window` when nothing else names it,
|
|
195
|
+
* as it is for the modules it requests (§2 Files that don't run as referenced). One a running reference runs in another
|
|
196
|
+
* realm has no window artifact. One whose code has worker markers gives a `realm-uncertain` record, as a file only the
|
|
197
|
+
* sweep found does.
|
|
198
|
+
*
|
|
199
|
+
* A remote script is fetched with the integrity metadata its reference carries (§17 Integrity): a `script` or `link`
|
|
200
|
+
* element's `integrity` attribute, or, for a module with none, its URL's entry in the document's import maps. A
|
|
201
|
+
* fetched script's own references are fetched against the URL its bytes came from, after any redirects, though it
|
|
202
|
+
* keeps the URL it was requested at. They are keyed, as artifacts, by the URL resolved against the script's own URL as
|
|
203
|
+
* requested (its key URL), so a key doesn't change when a redirect target moves (§11 Remote URL normalization). One
|
|
204
|
+
* fetched URL reached under two key URLs is an artifact of each, sharing the one fetch, and one key URL reached through
|
|
205
|
+
* two fetched URLs is an artifact of each too, both analyzed, told apart by `retrieval.requestedUrl`.
|
|
206
|
+
*
|
|
207
|
+
* A `data:` URL is code the page carries (§2 Other script URLs): its script is an `inline` artifact of the referencing
|
|
208
|
+
* realm, loaded as the reference loads it, unless a browser would refuse to run it, when it gives nothing, as a `data:`
|
|
209
|
+
* service worker always does. A script URL with any other scheme but `http:` and `https:` (`javascript:`, `blob:`,
|
|
210
|
+
* `file:`, `about:`, …) gives nothing wherever it appears.
|
|
211
|
+
*
|
|
212
|
+
* An entry document or a local script that can't be read gives an `unreadable` record, and a remote script that isn't
|
|
213
|
+
* read gives an `offline-skipped`, `remote-refused`, `fetch-failed` or `sri-mismatch` one (§17 Outcomes); the script
|
|
214
|
+
* is still an artifact. Each URL, and each inline script, is an artifact of its own, whatever bytes it shares with
|
|
215
|
+
* another (§2 Artifacts): the scan analyzes identical bytes once, and gives each copy its findings.
|
|
216
|
+
*
|
|
217
|
+
* Last, it pairs each artifact with the first source map that verifies against it (§2 Source maps): one a fetched
|
|
218
|
+
* artifact's `SourceMap` header names, then one its annotation names, then its sidecar in the build directory, then a
|
|
219
|
+
* map there with its `debugId`. A map that fails verification is `stale`, and counted. A missing or broken map gives no
|
|
220
|
+
* record: it is counted in the attribution block (§13), and never changes completeness. A map the annotation of a
|
|
221
|
+
* local-build or inline artifact names, or its sidecar, that exists but can't be read gives an `unreadable` record. A
|
|
222
|
+
* remote artifact's maps are fetched only here, after every script: each one the safety floor refuses, whose fetch
|
|
223
|
+
* fails, or that is a build file that exists but can't be read, is only counted (§17, `unreadMaps`).
|
|
224
|
+
* Then each source-map directory is searched, one at a time, in the order they were declared (`pairInDirectory`): one
|
|
225
|
+
* that can't be listed gives an `unreadable` record, and one none of whose maps verifies against an artifact a
|
|
226
|
+
* `source-maps-unpaired` record.
|
|
227
|
+
*/
|
|
228
|
+
export declare function discover(input: {
|
|
229
|
+
entries: readonly Entry[];
|
|
230
|
+
store: SourceStore;
|
|
231
|
+
/**
|
|
232
|
+
* The source-map directories (§2 Source maps), by their names as declared, in the order they are searched. The store
|
|
233
|
+
* reads them.
|
|
234
|
+
*/
|
|
235
|
+
sourceMaps?: readonly string[];
|
|
236
|
+
/** The catalog, read for the names of event handlers and for the globals each realm has. */
|
|
237
|
+
catalog: SerializedCatalog;
|
|
238
|
+
/**
|
|
239
|
+
* The references only the resolver can see that earlier rounds found (§19 References only the resolver can see), in
|
|
240
|
+
* URL order. Discovery follows them as it follows its own, so a round's artifact graph depends only on the set.
|
|
241
|
+
*/
|
|
242
|
+
references?: readonly ResolvedReference[];
|
|
243
|
+
}): Promise<Discovered>;
|
|
244
|
+
/**
|
|
245
|
+
* A reference only the resolver can see (§19 References only the resolver can see), as a round found it: the realm of
|
|
246
|
+
* the link group it was found in, the artifact whose code holds it (its URL, and the URL it was fetched at when that
|
|
247
|
+
* isn't its URL), its kind, and its URL as the code builds it, unresolved.
|
|
248
|
+
* - A `chunk` reference's `load` is how the load site loads it: a created script element's `src`, `importScripts` or
|
|
249
|
+
* `import()`. A `partial` record's URL is only the constant prefix of an alternative that doesn't fold ('' for none),
|
|
250
|
+
* which decides whether that chunk URL is local (§2).
|
|
251
|
+
* - An `injected` reference is a script element's `src` (§2), with the element's `type` as it folds (absent when
|
|
252
|
+
* none is set, null when it doesn't fold) and its folded `integrity`, if any.
|
|
253
|
+
* - A `worker` reference is a worker URL that isn't a literal, located at the call's URL argument.
|
|
254
|
+
* A `partial` injected or worker record stands for one alternative that doesn't fold, located at `alternative`.
|
|
255
|
+
*/
|
|
256
|
+
export interface ResolvedReference {
|
|
257
|
+
realm: Realm;
|
|
258
|
+
from: {
|
|
259
|
+
url: string;
|
|
260
|
+
fetched?: string;
|
|
261
|
+
};
|
|
262
|
+
kind: 'chunk' | 'injected' | 'worker';
|
|
263
|
+
url: string;
|
|
264
|
+
load?: 'element' | 'import-scripts' | 'import';
|
|
265
|
+
partial?: true;
|
|
266
|
+
alternative?: GeneratedLocation;
|
|
267
|
+
/** For a `worker` site: its URL folds to no value, as when it throws (`new URL("x.js")`), so it loads nothing. */
|
|
268
|
+
none?: true;
|
|
269
|
+
type?: string | null;
|
|
270
|
+
/** The integrity value code sets on the chunk's script element, when it folds (§17 Integrity). */
|
|
271
|
+
integrity?: string;
|
|
272
|
+
/** For an `injected` reference: the constants its element's `integrity` may be, when each write folds. */
|
|
273
|
+
integrityAlternatives?: string[];
|
|
274
|
+
/** For an `injected` reference: its element's `noModule` is set (`yes`), or may be (`maybe`). */
|
|
275
|
+
noModule?: 'yes' | 'maybe';
|
|
276
|
+
location: GeneratedLocation;
|
|
277
|
+
}
|