checkly 8.22.0 → 8.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-context/skills-command/references/configure-playwright-checks.md +24 -19
- package/dist/commands/debug/parse-project.js +3 -0
- package/dist/commands/debug/parse-project.js.map +1 -1
- package/dist/commands/deploy.js +3 -0
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/pw-test.js +3 -0
- package/dist/commands/pw-test.js.map +1 -1
- package/dist/commands/test.js +3 -0
- package/dist/commands/test.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js.map +1 -1
- package/dist/constructs/playwright-check-bundle.d.ts +5 -5
- package/dist/constructs/playwright-check-bundle.js.map +1 -1
- package/dist/constructs/project.js +5 -2
- package/dist/constructs/project.js.map +1 -1
- package/dist/constructs/session.d.ts +2 -1
- package/dist/constructs/session.js +2 -1
- package/dist/constructs/session.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/services/check-parser/bundler.d.ts +79 -8
- package/dist/services/check-parser/bundler.js +567 -9
- package/dist/services/check-parser/bundler.js.map +1 -1
- package/dist/services/check-parser/cache-hash.d.ts +141 -17
- package/dist/services/check-parser/cache-hash.js +78 -12
- package/dist/services/check-parser/cache-hash.js.map +1 -1
- package/dist/services/check-parser/faux-package.d.ts +5 -0
- package/dist/services/check-parser/faux-package.js +9 -1
- package/dist/services/check-parser/faux-package.js.map +1 -1
- package/dist/services/check-parser/lockfile-pruner.d.ts +104 -0
- package/dist/services/check-parser/lockfile-pruner.js +1107 -0
- package/dist/services/check-parser/lockfile-pruner.js.map +1 -0
- package/dist/services/check-parser/package-files/package-manager.d.ts +34 -0
- package/dist/services/check-parser/package-files/package-manager.js +131 -3
- package/dist/services/check-parser/package-files/package-manager.js.map +1 -1
- package/dist/services/check-parser/package-files/pnpmfile.d.ts +40 -0
- package/dist/services/check-parser/package-files/pnpmfile.js +322 -0
- package/dist/services/check-parser/package-files/pnpmfile.js.map +1 -0
- package/dist/services/check-parser/package-files/resolver.d.ts +6 -1
- package/dist/services/check-parser/package-files/resolver.js +34 -0
- package/dist/services/check-parser/package-files/resolver.js.map +1 -1
- package/dist/services/check-parser/package-files/workspace.d.ts +16 -1
- package/dist/services/check-parser/package-files/workspace.js +17 -2
- package/dist/services/check-parser/package-files/workspace.js.map +1 -1
- package/dist/services/check-parser/package-prune.d.ts +68 -0
- package/dist/services/check-parser/package-prune.js +274 -0
- package/dist/services/check-parser/package-prune.js.map +1 -0
- package/dist/services/check-parser/parser.js +9 -0
- package/dist/services/check-parser/parser.js.map +1 -1
- package/dist/services/check-parser/patched-dependencies.d.ts +133 -0
- package/dist/services/check-parser/patched-dependencies.js +428 -0
- package/dist/services/check-parser/patched-dependencies.js.map +1 -0
- package/dist/services/checkly-config-loader.d.ts +164 -10
- package/dist/services/checkly-config-loader.js +39 -0
- package/dist/services/checkly-config-loader.js.map +1 -1
- package/dist/services/embedded-packages/cache.d.ts +8 -4
- package/dist/services/embedded-packages/cache.js +27 -6
- package/dist/services/embedded-packages/cache.js.map +1 -1
- package/dist/services/embedded-packages/diagnostics.d.ts +129 -0
- package/dist/services/embedded-packages/diagnostics.js +212 -0
- package/dist/services/embedded-packages/diagnostics.js.map +1 -0
- package/dist/services/embedded-packages/lockfile-filter.d.ts +20 -0
- package/dist/services/embedded-packages/lockfile-filter.js +40 -0
- package/dist/services/embedded-packages/lockfile-filter.js.map +1 -0
- package/dist/services/embedded-packages/lockfile-packages.d.ts +31 -2
- package/dist/services/embedded-packages/lockfile-packages.js +234 -30
- package/dist/services/embedded-packages/lockfile-packages.js.map +1 -1
- package/dist/services/embedded-packages/materializer.d.ts +19 -5
- package/dist/services/embedded-packages/materializer.js +445 -91
- package/dist/services/embedded-packages/materializer.js.map +1 -1
- package/dist/services/embedded-packages/npmrc.d.ts +163 -21
- package/dist/services/embedded-packages/npmrc.js +440 -75
- package/dist/services/embedded-packages/npmrc.js.map +1 -1
- package/dist/services/embedded-packages/spec.d.ts +89 -17
- package/dist/services/embedded-packages/spec.js +199 -30
- package/dist/services/embedded-packages/spec.js.map +1 -1
- package/dist/services/embedded-packages/url.d.ts +39 -0
- package/dist/services/embedded-packages/url.js +60 -0
- package/dist/services/embedded-packages/url.js.map +1 -0
- package/dist/services/playwright-project-bundler.js +27 -23
- package/dist/services/playwright-project-bundler.js.map +1 -1
- package/dist/services/runner/registries.d.ts +144 -0
- package/dist/services/runner/registries.js +232 -0
- package/dist/services/runner/registries.js.map +1 -0
- package/oclif.manifest.json +1 -1
- package/package.json +5 -5
|
@@ -1,47 +1,119 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A parsed `bundle.packages.embed` entry: a package name — or a name
|
|
3
3
|
* pattern with `*` wildcards — with an optional exact version pin
|
|
4
|
-
* (`name` or `name@version`)
|
|
4
|
+
* (`name` or `name@version`), optionally prefixed with `!` to make it an
|
|
5
|
+
* exclusion.
|
|
5
6
|
*/
|
|
6
7
|
export interface EmbeddedPackageSpec {
|
|
7
|
-
/** The raw config entry, kept for error messages. */
|
|
8
|
+
/** The raw config entry, `!` prefix included, kept for error messages. */
|
|
8
9
|
raw: string;
|
|
9
10
|
/**
|
|
10
11
|
* The package name, e.g. `@acme/private-utils` — or, when
|
|
11
|
-
* {@link
|
|
12
|
+
* {@link wildcard} is set, the raw name pattern, e.g. `@acme/*`.
|
|
12
13
|
*/
|
|
13
14
|
name: string;
|
|
14
15
|
/** The exact pinned version, if the entry included one. */
|
|
15
16
|
version?: string;
|
|
16
17
|
/**
|
|
17
|
-
*
|
|
18
|
-
* Each `*` matches any run of
|
|
19
|
-
* never crosses the scope separator
|
|
20
|
-
* that scope; a bare `*` matches
|
|
18
|
+
* True when the name contains `*` wildcards and matches as a pattern
|
|
19
|
+
* rather than by exact comparison. Each single `*` matches any run of
|
|
20
|
+
* characters except `/`, so it never crosses the scope separator
|
|
21
|
+
* (`@acme/*` matches only packages in that scope; a bare `*` matches
|
|
22
|
+
* only unscoped names). A run of two or more stars (`**`) matches any
|
|
23
|
+
* run of characters *including* `/`: `**` alone matches every package,
|
|
24
|
+
* and `**-foo` matches scoped and unscoped names alike. Directly before
|
|
25
|
+
* a `/`, a `**` may also match nothing together with that `/` — the
|
|
26
|
+
* glob habit, so `'**' + '/utils'` (one string; split here only because
|
|
27
|
+
* `*` followed by `/` would end this comment) matches both `utils` and
|
|
28
|
+
* `@acme/utils`.
|
|
21
29
|
*/
|
|
22
|
-
|
|
30
|
+
wildcard: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* True when the entry was prefixed with `!`: instead of selecting
|
|
33
|
+
* packages, it removes the ones it matches from what the entries before
|
|
34
|
+
* it selected.
|
|
35
|
+
*/
|
|
36
|
+
exclude: boolean;
|
|
37
|
+
}
|
|
38
|
+
/** The parts of a lockfile entry a spec is matched against. */
|
|
39
|
+
export interface PackageRef {
|
|
40
|
+
name: string;
|
|
41
|
+
/**
|
|
42
|
+
* Absent on entries the lockfile records without one, e.g. git, file and
|
|
43
|
+
* URL resolutions and workspace links.
|
|
44
|
+
*/
|
|
45
|
+
version?: string;
|
|
23
46
|
}
|
|
24
47
|
/**
|
|
25
48
|
* Whether a spec selects the given package name: exact comparison for
|
|
26
49
|
* plain specs, pattern match for wildcard specs.
|
|
27
50
|
*/
|
|
28
|
-
export declare function specMatchesPackageName(spec:
|
|
51
|
+
export declare function specMatchesPackageName(spec: PackageNamePattern, packageName: string): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Whether an ordered pattern list selects the given name: a matching
|
|
54
|
+
* plain entry selects it, a matching `!` exclusion deselects it, and the
|
|
55
|
+
* last matching entry wins. This is the canonical definition of the
|
|
56
|
+
* order-sensitive exclusion rule both `bundle.packages` options share
|
|
57
|
+
* (`['@acme/*', '!@acme/keep']` selects the scope except `@acme/keep`,
|
|
58
|
+
* while the reverse order selects the whole scope because the exclusion
|
|
59
|
+
* ran before anything was selected); the embed materializer applies the
|
|
60
|
+
* same rule per spec so it can attribute diagnostics to entries.
|
|
61
|
+
*/
|
|
62
|
+
export declare function patternsSelectName(patterns: PackageNamePattern[], name: string): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Whether a spec selects the given package: the name must match, and so
|
|
65
|
+
* must an exact version pin. An entry the lockfile records without a
|
|
66
|
+
* version never satisfies a pin.
|
|
67
|
+
*/
|
|
68
|
+
export declare function specMatchesPackage(spec: EmbeddedPackageSpec, entry: PackageRef): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* As {@link specMatchesPackage}, except that a version-less entry matches
|
|
71
|
+
* any pin. Used where such an entry is still worth reporting against a
|
|
72
|
+
* pinned spec: a git resolution or a workspace link may well be the package
|
|
73
|
+
* the user meant, recorded in a form that has no version to compare.
|
|
74
|
+
*/
|
|
75
|
+
export declare function specLooselyMatchesPackage(spec: EmbeddedPackageSpec, entry: PackageRef): boolean;
|
|
29
76
|
export declare class InvalidEmbeddedPackageSpecError extends Error {
|
|
30
77
|
/** The failure alone, without the `Invalid embedded package '<spec>':` prefix. */
|
|
31
78
|
readonly reason: string;
|
|
32
79
|
constructor(spec: string, reason: string);
|
|
33
80
|
}
|
|
34
81
|
/**
|
|
35
|
-
* Parses a `bundle.packages.embed` entry into a package name
|
|
36
|
-
*
|
|
82
|
+
* Parses a `bundle.packages.embed` entry into a package name, an optional
|
|
83
|
+
* exact version pin and whether the entry excludes rather than selects.
|
|
37
84
|
*
|
|
38
85
|
* Accepts `name` (embed every lockfile version of the package) and
|
|
39
86
|
* `name@version` with an exact semver version. The name may contain `*`
|
|
40
|
-
* wildcards (`@acme/*`, `acme-*`,
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
87
|
+
* and `**` wildcards (`@acme/*`, `acme-*`, `**-foo`), matching as
|
|
88
|
+
* described on {@link EmbeddedPackageSpec.wildcard}. A `!`
|
|
89
|
+
* prefix (`!@acme/legacy`, `!@acme/*`,
|
|
90
|
+
* `!legacy@2.1.0`) marks the entry as an exclusion, subtracting from what
|
|
91
|
+
* the entries before it selected. Version ranges are rejected: the
|
|
92
|
+
* embedded tarball must be the exact artifact the lockfile resolved, so a
|
|
93
|
+
* range has nothing meaningful to select against. A leading `v` is
|
|
94
|
+
* stripped, but the version is otherwise kept as written (including any
|
|
95
|
+
* build metadata) so it compares exactly against lockfile versions.
|
|
46
96
|
*/
|
|
47
97
|
export declare function parseEmbeddedPackageSpec(raw: string): EmbeddedPackageSpec;
|
|
98
|
+
/**
|
|
99
|
+
* A parsed package name pattern: a plain package name, or a name pattern
|
|
100
|
+
* with `*` wildcards, optionally prefixed with `!` to make it an
|
|
101
|
+
* exclusion — the name-matching subset of {@link EmbeddedPackageSpec},
|
|
102
|
+
* without the version pin. See the spec's field docs for the wildcard
|
|
103
|
+
* and exclusion semantics.
|
|
104
|
+
*/
|
|
105
|
+
export type PackageNamePattern = Pick<EmbeddedPackageSpec, 'name' | 'wildcard' | 'exclude'>;
|
|
106
|
+
export declare class InvalidPackageNamePatternError extends Error {
|
|
107
|
+
constructor(raw: string, reason: string);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Parses a package name pattern: a package name that may contain `*`
|
|
111
|
+
* and `**` wildcards (`@acme/*`, `acme-*`, `**-foo`), matching as
|
|
112
|
+
* described on {@link EmbeddedPackageSpec.wildcard}. A `!` prefix
|
|
113
|
+
* marks the entry as an exclusion, subtracting from what the entries
|
|
114
|
+
* before it selected, exactly as in `bundle.packages.embed`. Unlike
|
|
115
|
+
* {@link parseEmbeddedPackageSpec} there are no `name@version` pins;
|
|
116
|
+
* they are rejected with a pointed message so an embed-style pin fails
|
|
117
|
+
* loudly instead of silently matching nothing.
|
|
118
|
+
*/
|
|
119
|
+
export declare function parsePackageNamePattern(raw: string): PackageNamePattern;
|
|
@@ -4,26 +4,142 @@ import semver from 'semver';
|
|
|
4
4
|
* plain specs, pattern match for wildcard specs.
|
|
5
5
|
*/
|
|
6
6
|
export function specMatchesPackageName(spec, packageName) {
|
|
7
|
-
if (spec.
|
|
8
|
-
return spec.
|
|
7
|
+
if (spec.wildcard) {
|
|
8
|
+
return matchesNamePattern(spec.name, packageName);
|
|
9
9
|
}
|
|
10
10
|
return spec.name === packageName;
|
|
11
11
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Whether an ordered pattern list selects the given name: a matching
|
|
14
|
+
* plain entry selects it, a matching `!` exclusion deselects it, and the
|
|
15
|
+
* last matching entry wins. This is the canonical definition of the
|
|
16
|
+
* order-sensitive exclusion rule both `bundle.packages` options share
|
|
17
|
+
* (`['@acme/*', '!@acme/keep']` selects the scope except `@acme/keep`,
|
|
18
|
+
* while the reverse order selects the whole scope because the exclusion
|
|
19
|
+
* ran before anything was selected); the embed materializer applies the
|
|
20
|
+
* same rule per spec so it can attribute diagnostics to entries.
|
|
21
|
+
*/
|
|
22
|
+
export function patternsSelectName(patterns, name) {
|
|
23
|
+
let selected = false;
|
|
24
|
+
for (const pattern of patterns) {
|
|
25
|
+
if (specMatchesPackageName(pattern, name)) {
|
|
26
|
+
selected = !pattern.exclude;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return selected;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Whether a spec selects the given package: the name must match, and so
|
|
33
|
+
* must an exact version pin. An entry the lockfile records without a
|
|
34
|
+
* version never satisfies a pin.
|
|
35
|
+
*/
|
|
36
|
+
export function specMatchesPackage(spec, entry) {
|
|
37
|
+
return specMatchesPackageName(spec, entry.name)
|
|
38
|
+
&& (spec.version === undefined || entry.version === spec.version);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* As {@link specMatchesPackage}, except that a version-less entry matches
|
|
42
|
+
* any pin. Used where such an entry is still worth reporting against a
|
|
43
|
+
* pinned spec: a git resolution or a workspace link may well be the package
|
|
44
|
+
* the user meant, recorded in a form that has no version to compare.
|
|
45
|
+
*/
|
|
46
|
+
export function specLooselyMatchesPackage(spec, entry) {
|
|
47
|
+
return specMatchesPackageName(spec, entry.name)
|
|
48
|
+
&& (spec.version === undefined || entry.version === undefined || entry.version === spec.version);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Matches a wildcard name pattern against a package name, without a
|
|
52
|
+
* regex: a regex spelling each `*` as `[^/]*` backtracks catastrophically
|
|
53
|
+
* on a mismatch once several stars are separated by literals (measured in
|
|
54
|
+
* the minutes for a dozen stars against a 50-character name), and the
|
|
55
|
+
* pattern comes straight from the user's config. The tokenized pattern —
|
|
56
|
+
* with the wildcard semantics {@link EmbeddedPackageSpec.wildcard}
|
|
57
|
+
* documents — is applied token by token to a boolean row over the name,
|
|
58
|
+
* where entry `j` records whether the tokens so far can match the first
|
|
59
|
+
* `j` characters: a literal shifts the row by one matching character, a
|
|
60
|
+
* star extends every reachable position rightwards one character at a
|
|
61
|
+
* time (refusing `/` unless doubled), and the star-run-plus-`/` pair
|
|
62
|
+
* either leaves the row as it is or lands right past any reachable `/`.
|
|
63
|
+
* No backtracking, and O(pattern length × name length) like the greedy
|
|
64
|
+
* walk it replaced.
|
|
65
|
+
*
|
|
66
|
+
* These semantics are also a wire contract beyond this repository:
|
|
67
|
+
* `runner.registries` routing rules (`services/runner/registries.ts`)
|
|
68
|
+
* ship raw patterns inside the code bundle for Checkly runners to match
|
|
69
|
+
* with identical semantics. A semantic change here changes how deployed
|
|
70
|
+
* routing configs behave and requires a coordinated runner update plus a
|
|
71
|
+
* `REGISTRIES_FILE_VERSION` bump.
|
|
72
|
+
*/
|
|
73
|
+
function matchesNamePattern(pattern, packageName) {
|
|
74
|
+
const n = packageName.length;
|
|
75
|
+
let row = new Array(n + 1).fill(false);
|
|
76
|
+
row[0] = true;
|
|
77
|
+
for (const token of tokenizePattern(pattern)) {
|
|
78
|
+
const next = new Array(n + 1).fill(false);
|
|
79
|
+
if (token[0] === '*' && token.endsWith('/')) {
|
|
80
|
+
let reachable = false;
|
|
81
|
+
for (let j = 0; j <= n; j++) {
|
|
82
|
+
reachable = reachable || (j > 0 && row[j - 1]);
|
|
83
|
+
next[j] = row[j] || (j > 0 && reachable && packageName[j - 1] === '/');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
else if (token[0] === '*') {
|
|
87
|
+
for (let j = 0; j <= n; j++) {
|
|
88
|
+
next[j] = row[j] || (j > 0 && next[j - 1] && (token.length > 1 || packageName[j - 1] !== '/'));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
for (let j = 1; j <= n; j++) {
|
|
93
|
+
next[j] = row[j - 1] && packageName[j - 1] === token;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
row = next;
|
|
97
|
+
}
|
|
98
|
+
return row[n];
|
|
21
99
|
}
|
|
22
100
|
// npm's name rules for already-published packages: new publishes must be
|
|
23
101
|
// lowercase, but plenty of legitimate older packages (JSONStream) contain
|
|
24
102
|
// uppercase letters, so both cases are accepted. Leading `.` and `_` stay
|
|
25
103
|
// disallowed, as npm has never permitted them.
|
|
26
104
|
const PACKAGE_NAME_RE = /^(@[a-zA-Z0-9-~][a-zA-Z0-9-~._]*\/)?[a-zA-Z0-9-~][a-zA-Z0-9-._~]*$/;
|
|
105
|
+
/**
|
|
106
|
+
* One pattern token per match: a maximal star run with a directly
|
|
107
|
+
* following `/` attached, or a single literal character. Shared by the
|
|
108
|
+
* matcher and the name-shape validation so the two agree on what a
|
|
109
|
+
* globstar-plus-`/` pair is.
|
|
110
|
+
*/
|
|
111
|
+
function tokenizePattern(pattern) {
|
|
112
|
+
return pattern.match(/\*{2,}\/?|\*|[^*]/g) ?? [];
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Whether a wildcard pattern can produce a valid package name. Every star
|
|
116
|
+
* token is replaced with a default probe — `a` for a star run, nothing at
|
|
117
|
+
* all for a `**`-plus-`/` pair, mirroring its zero-segment match — and,
|
|
118
|
+
* since a valid name contains at most one scope (one leading `@` and one
|
|
119
|
+
* `/`), one globstar token at a time may additionally stand in for scope
|
|
120
|
+
* structure: a plain `**` probes as the scope's `@` (`@a`) or its `/`
|
|
121
|
+
* (`a/a`), and a `**`-plus-`/` pair as a segment (`a/`) or a whole scope
|
|
122
|
+
* prefix (`@a/`). The pattern is name-shaped when any substitution
|
|
123
|
+
* satisfies {@link PACKAGE_NAME_RE}. This is a deliberately conservative
|
|
124
|
+
* subset of what the matcher could match — a shape needing two tokens to
|
|
125
|
+
* take scope probes at once is rejected — and single `*` runs are never
|
|
126
|
+
* probed as scope structure, even though a lone star does match a leading
|
|
127
|
+
* `@` in the matcher: scope-crossing spellings must be written with `**`,
|
|
128
|
+
* so `'*' + '/utils'` stays rejected while `'**' + '/utils'` is accepted.
|
|
129
|
+
*/
|
|
130
|
+
function isNameShapedPattern(pattern) {
|
|
131
|
+
const tokens = tokenizePattern(pattern);
|
|
132
|
+
const filled = tokens.map(token => token[0] !== '*' ? token : token.endsWith('/') ? '' : 'a');
|
|
133
|
+
if (PACKAGE_NAME_RE.test(filled.join(''))) {
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
const withProbe = (index, probe) => filled
|
|
137
|
+
.map((token, i) => i === index ? probe : token)
|
|
138
|
+
.join('');
|
|
139
|
+
return tokens.some((token, i) => token[1] === '*'
|
|
140
|
+
&& (token.endsWith('/') ? ['a/', '@a/'] : ['@a', 'a/a'])
|
|
141
|
+
.some(probe => PACKAGE_NAME_RE.test(withProbe(i, probe))));
|
|
142
|
+
}
|
|
27
143
|
export class InvalidEmbeddedPackageSpecError extends Error {
|
|
28
144
|
/** The failure alone, without the `Invalid embedded package '<spec>':` prefix. */
|
|
29
145
|
reason;
|
|
@@ -34,37 +150,54 @@ export class InvalidEmbeddedPackageSpecError extends Error {
|
|
|
34
150
|
}
|
|
35
151
|
}
|
|
36
152
|
/**
|
|
37
|
-
* Parses a `bundle.packages.embed` entry into a package name
|
|
38
|
-
*
|
|
153
|
+
* Parses a `bundle.packages.embed` entry into a package name, an optional
|
|
154
|
+
* exact version pin and whether the entry excludes rather than selects.
|
|
39
155
|
*
|
|
40
156
|
* Accepts `name` (embed every lockfile version of the package) and
|
|
41
157
|
* `name@version` with an exact semver version. The name may contain `*`
|
|
42
|
-
* wildcards (`@acme/*`, `acme-*`,
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
158
|
+
* and `**` wildcards (`@acme/*`, `acme-*`, `**-foo`), matching as
|
|
159
|
+
* described on {@link EmbeddedPackageSpec.wildcard}. A `!`
|
|
160
|
+
* prefix (`!@acme/legacy`, `!@acme/*`,
|
|
161
|
+
* `!legacy@2.1.0`) marks the entry as an exclusion, subtracting from what
|
|
162
|
+
* the entries before it selected. Version ranges are rejected: the
|
|
163
|
+
* embedded tarball must be the exact artifact the lockfile resolved, so a
|
|
164
|
+
* range has nothing meaningful to select against. A leading `v` is
|
|
165
|
+
* stripped, but the version is otherwise kept as written (including any
|
|
166
|
+
* build metadata) so it compares exactly against lockfile versions.
|
|
48
167
|
*/
|
|
49
168
|
export function parseEmbeddedPackageSpec(raw) {
|
|
50
169
|
if (typeof raw !== 'string' || raw === '') {
|
|
51
170
|
throw new InvalidEmbeddedPackageSpecError(String(raw), `must be a non-empty string`);
|
|
52
171
|
}
|
|
172
|
+
// The `!` has to come off before anything else is read: on the raw
|
|
173
|
+
// `!@acme/foo` the version separator below would land on the scope marker
|
|
174
|
+
// at index 1 and parse the entry as name `!` at version `acme/foo`.
|
|
175
|
+
const exclude = raw.startsWith('!');
|
|
176
|
+
const pattern = exclude ? raw.slice(1) : raw;
|
|
177
|
+
if (pattern === '') {
|
|
178
|
+
throw new InvalidEmbeddedPackageSpecError(raw, `must name a package or pattern after '!'`);
|
|
179
|
+
}
|
|
53
180
|
// A version separator is any `@` past the first character, which keeps the
|
|
54
181
|
// scope marker of `@scope/name` intact.
|
|
55
|
-
const versionSeparator =
|
|
56
|
-
const name = versionSeparator > 0 ?
|
|
57
|
-
const rawVersion = versionSeparator > 0 ?
|
|
58
|
-
// A wildcard name must still be name-shaped once every
|
|
59
|
-
// name characters. (`*` itself appears in npm's legacy name charset,
|
|
60
|
-
// no real-world package uses it; here it always means a wildcard.)
|
|
182
|
+
const versionSeparator = pattern.lastIndexOf('@');
|
|
183
|
+
const name = versionSeparator > 0 ? pattern.slice(0, versionSeparator) : pattern;
|
|
184
|
+
const rawVersion = versionSeparator > 0 ? pattern.slice(versionSeparator + 1) : undefined;
|
|
185
|
+
// A wildcard name must still be name-shaped once every star run stands in
|
|
186
|
+
// for name characters. (`*` itself appears in npm's legacy name charset,
|
|
187
|
+
// but no real-world package uses it; here it always means a wildcard.)
|
|
61
188
|
const wildcard = name.includes('*');
|
|
62
|
-
if (
|
|
63
|
-
|
|
189
|
+
if (wildcard ? !isNameShapedPattern(name) : !PACKAGE_NAME_RE.test(name)) {
|
|
190
|
+
// A lone `*` next to `/` is the classic near-miss, since only `**`
|
|
191
|
+
// crosses the scope separator; point at the doubled spelling when
|
|
192
|
+
// promoting the star is all it takes to fix the pattern.
|
|
193
|
+
const promoted = name.replace(/(?<=^|[^*])\*(?=\/)/g, '**');
|
|
194
|
+
const hint = promoted !== name && isNameShapedPattern(promoted)
|
|
195
|
+
? ` (a single '*' never crosses '/'; did you mean '${promoted}'?)`
|
|
196
|
+
: '';
|
|
197
|
+
throw new InvalidEmbeddedPackageSpecError(raw, `'${name}' is not a valid npm package name${wildcard ? ' pattern' : ''}${hint}`);
|
|
64
198
|
}
|
|
65
|
-
const namePattern = wildcard ? compileNamePattern(name) : undefined;
|
|
66
199
|
if (rawVersion === undefined) {
|
|
67
|
-
return { raw, name,
|
|
200
|
+
return { raw, name, wildcard, exclude };
|
|
68
201
|
}
|
|
69
202
|
// Trim before validating: semver.valid() tolerates surrounding whitespace,
|
|
70
203
|
// so an untrimmed version would pass validation yet never compare equal to
|
|
@@ -74,6 +207,42 @@ export function parseEmbeddedPackageSpec(raw) {
|
|
|
74
207
|
if (semver.valid(version) === null) {
|
|
75
208
|
throw new InvalidEmbeddedPackageSpecError(raw, `'${rawVersion}' is not an exact semver version (use 'name' or 'name@1.2.3')`);
|
|
76
209
|
}
|
|
77
|
-
return { raw, name, version,
|
|
210
|
+
return { raw, name, version, wildcard, exclude };
|
|
211
|
+
}
|
|
212
|
+
export class InvalidPackageNamePatternError extends Error {
|
|
213
|
+
constructor(raw, reason) {
|
|
214
|
+
super(`Invalid package name pattern '${raw}': ${reason}`);
|
|
215
|
+
this.name = 'InvalidPackageNamePatternError';
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Parses a package name pattern: a package name that may contain `*`
|
|
220
|
+
* and `**` wildcards (`@acme/*`, `acme-*`, `**-foo`), matching as
|
|
221
|
+
* described on {@link EmbeddedPackageSpec.wildcard}. A `!` prefix
|
|
222
|
+
* marks the entry as an exclusion, subtracting from what the entries
|
|
223
|
+
* before it selected, exactly as in `bundle.packages.embed`. Unlike
|
|
224
|
+
* {@link parseEmbeddedPackageSpec} there are no `name@version` pins;
|
|
225
|
+
* they are rejected with a pointed message so an embed-style pin fails
|
|
226
|
+
* loudly instead of silently matching nothing.
|
|
227
|
+
*/
|
|
228
|
+
export function parsePackageNamePattern(raw) {
|
|
229
|
+
// The pin rejection must run before the delegation below, which would
|
|
230
|
+
// otherwise accept `name@version` the way embed does. The `!` comes off
|
|
231
|
+
// first so the scope marker of `!@scope/name` at index 1 is not read as
|
|
232
|
+
// a version separator; any `@` past the first character of what remains
|
|
233
|
+
// would be one.
|
|
234
|
+
if (typeof raw === 'string' && raw.replace(/^!/, '').lastIndexOf('@') > 0) {
|
|
235
|
+
throw new InvalidPackageNamePatternError(raw, `'name@version' pins are not supported here`);
|
|
236
|
+
}
|
|
237
|
+
try {
|
|
238
|
+
const { name, wildcard, exclude } = parseEmbeddedPackageSpec(raw);
|
|
239
|
+
return { name, wildcard, exclude };
|
|
240
|
+
}
|
|
241
|
+
catch (err) {
|
|
242
|
+
if (err instanceof InvalidEmbeddedPackageSpecError) {
|
|
243
|
+
throw new InvalidPackageNamePatternError(String(raw), err.reason);
|
|
244
|
+
}
|
|
245
|
+
throw err;
|
|
246
|
+
}
|
|
78
247
|
}
|
|
79
248
|
//# sourceMappingURL=spec.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spec.js","sourceRoot":"","sources":["../../../src/services/embedded-packages/spec.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAA;
|
|
1
|
+
{"version":3,"file":"spec.js","sourceRoot":"","sources":["../../../src/services/embedded-packages/spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses a URL that something is about to be fetched from, or returns
|
|
3
|
+
* undefined when nothing can be.
|
|
4
|
+
*
|
|
5
|
+
* The scheme has to be one this CLI actually fetches over: `ftp://host/x`
|
|
6
|
+
* parses and has a host, but axios cannot retrieve it, and tarball URLs
|
|
7
|
+
* read out of lockfiles and registry metadata are already held to the same
|
|
8
|
+
* http(s) rule. That also settles the shapes a bare parse lets through,
|
|
9
|
+
* such as `admin:s3cret@nexus.local/x` — the opaque scheme `admin:` with an
|
|
10
|
+
* empty host, and a credential sitting in what looks like a path. The host
|
|
11
|
+
* is checked as well, belt and braces: the WHATWG parser refuses a
|
|
12
|
+
* host-less `https://` today, and this rule means "a request can be made",
|
|
13
|
+
* which needs somewhere to send it.
|
|
14
|
+
*
|
|
15
|
+
* This decides both whether a request can be made and whether the value is
|
|
16
|
+
* safe to echo, which must be the same rule: a URL that reaches the network
|
|
17
|
+
* is one `redactUrl` can rebuild from parts that cannot carry a secret.
|
|
18
|
+
*/
|
|
19
|
+
export declare function parseFetchableUrl(url: string): URL | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* The rule `parseFetchableUrl` enforces, as a sentence for a message. Kept
|
|
22
|
+
* beside the check so the two cannot drift into describing different rules.
|
|
23
|
+
*/
|
|
24
|
+
export declare const FETCHABLE_URL_REQUIREMENT = "an absolute http or https URL with a host";
|
|
25
|
+
/**
|
|
26
|
+
* Parses a URL that a path will be appended to, or returns undefined when
|
|
27
|
+
* appending would not do what it looks like.
|
|
28
|
+
*
|
|
29
|
+
* Everything `parseFetchableUrl` requires, plus no query and no fragment:
|
|
30
|
+
* those swallow whatever follows them. `https://host/npm/?token=abc` with
|
|
31
|
+
* `bar/-/bar-2.0.0.tgz` appended parses with its pathname still `/npm/` and
|
|
32
|
+
* the whole package path inside the query, so the request would go to the
|
|
33
|
+
* registry root and fail later as something that reads like the registry's
|
|
34
|
+
* fault. Excluding them is what makes "appending a path leaves the host and
|
|
35
|
+
* the path intact" true rather than nearly true.
|
|
36
|
+
*/
|
|
37
|
+
export declare function parseComposableUrl(url: string): URL | undefined;
|
|
38
|
+
/** The rule `parseComposableUrl` enforces, as a sentence for a message. */
|
|
39
|
+
export declare const COMPOSABLE_URL_REQUIREMENT = "an absolute http or https URL with a host, carrying no query or fragment";
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses a URL that something is about to be fetched from, or returns
|
|
3
|
+
* undefined when nothing can be.
|
|
4
|
+
*
|
|
5
|
+
* The scheme has to be one this CLI actually fetches over: `ftp://host/x`
|
|
6
|
+
* parses and has a host, but axios cannot retrieve it, and tarball URLs
|
|
7
|
+
* read out of lockfiles and registry metadata are already held to the same
|
|
8
|
+
* http(s) rule. That also settles the shapes a bare parse lets through,
|
|
9
|
+
* such as `admin:s3cret@nexus.local/x` — the opaque scheme `admin:` with an
|
|
10
|
+
* empty host, and a credential sitting in what looks like a path. The host
|
|
11
|
+
* is checked as well, belt and braces: the WHATWG parser refuses a
|
|
12
|
+
* host-less `https://` today, and this rule means "a request can be made",
|
|
13
|
+
* which needs somewhere to send it.
|
|
14
|
+
*
|
|
15
|
+
* This decides both whether a request can be made and whether the value is
|
|
16
|
+
* safe to echo, which must be the same rule: a URL that reaches the network
|
|
17
|
+
* is one `redactUrl` can rebuild from parts that cannot carry a secret.
|
|
18
|
+
*/
|
|
19
|
+
export function parseFetchableUrl(url) {
|
|
20
|
+
let parsed;
|
|
21
|
+
try {
|
|
22
|
+
parsed = new URL(url);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
if (parsed.host === '' || (parsed.protocol !== 'http:' && parsed.protocol !== 'https:')) {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
return parsed;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The rule `parseFetchableUrl` enforces, as a sentence for a message. Kept
|
|
34
|
+
* beside the check so the two cannot drift into describing different rules.
|
|
35
|
+
*/
|
|
36
|
+
export const FETCHABLE_URL_REQUIREMENT = 'an absolute http or https URL with a host';
|
|
37
|
+
/**
|
|
38
|
+
* Parses a URL that a path will be appended to, or returns undefined when
|
|
39
|
+
* appending would not do what it looks like.
|
|
40
|
+
*
|
|
41
|
+
* Everything `parseFetchableUrl` requires, plus no query and no fragment:
|
|
42
|
+
* those swallow whatever follows them. `https://host/npm/?token=abc` with
|
|
43
|
+
* `bar/-/bar-2.0.0.tgz` appended parses with its pathname still `/npm/` and
|
|
44
|
+
* the whole package path inside the query, so the request would go to the
|
|
45
|
+
* registry root and fail later as something that reads like the registry's
|
|
46
|
+
* fault. Excluding them is what makes "appending a path leaves the host and
|
|
47
|
+
* the path intact" true rather than nearly true.
|
|
48
|
+
*/
|
|
49
|
+
export function parseComposableUrl(url) {
|
|
50
|
+
// Tested on the raw string, not on `search` and `hash`: a URL ending in a
|
|
51
|
+
// bare `?` or `#` parses with both of those empty, yet still absorbs
|
|
52
|
+
// whatever is appended after the delimiter.
|
|
53
|
+
if (/[?#]/.test(url)) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
return parseFetchableUrl(url);
|
|
57
|
+
}
|
|
58
|
+
/** The rule `parseComposableUrl` enforces, as a sentence for a message. */
|
|
59
|
+
export const COMPOSABLE_URL_REQUIREMENT = `${FETCHABLE_URL_REQUIREMENT}, carrying no query or fragment`;
|
|
60
|
+
//# sourceMappingURL=url.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.js","sourceRoot":"","sources":["../../../src/services/embedded-packages/url.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,iBAAiB,CAAE,GAAW;IAC5C,IAAI,MAAW,CAAA;IACf,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAA;IACvB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,KAAK,OAAO,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAC,EAAE,CAAC;QACxF,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,2CAA2C,CAAA;AAEpF;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,kBAAkB,CAAE,GAAW;IAC7C,0EAA0E;IAC1E,qEAAqE;IACrE,4CAA4C;IAC5C,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,OAAO,iBAAiB,CAAC,GAAG,CAAC,CAAA;AAC/B,CAAC;AAED,2EAA2E;AAC3E,MAAM,CAAC,MAAM,0BAA0B,GAAG,GAAG,yBAAyB,iCAAiC,CAAA"}
|
|
@@ -2,6 +2,7 @@ import fs from 'node:fs/promises';
|
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import semver from 'semver';
|
|
5
|
+
import { PNPM_PATCHES_DIR } from './check-parser/patched-dependencies.js';
|
|
5
6
|
import { detectNearestPackageJson } from './check-parser/package-files/package-manager.js';
|
|
6
7
|
import { PackageJsonFile } from './check-parser/package-files/package-json-file.js';
|
|
7
8
|
import { lineage } from './check-parser/package-files/walk.js';
|
|
@@ -108,23 +109,10 @@ export class PlaywrightProjectBundler {
|
|
|
108
109
|
workspaceMembers: workspaceInfo.members,
|
|
109
110
|
}));
|
|
110
111
|
}
|
|
111
|
-
// Embedded package tarballs
|
|
112
|
-
//
|
|
113
|
-
//
|
|
114
|
-
//
|
|
115
|
-
// prefix. The materializer memoizes, so concurrent bundles share one
|
|
116
|
-
// download run, and the Bundler dedupes registrations by archive path
|
|
117
|
-
// across checks.
|
|
118
|
-
const materializer = Session.getEmbeddedPackagesMaterializer();
|
|
119
|
-
if (materializer !== undefined) {
|
|
120
|
-
for (const tarball of await materializer.materialize()) {
|
|
121
|
-
files.push({
|
|
122
|
-
filePath: tarball.filePath,
|
|
123
|
-
physical: true,
|
|
124
|
-
archivePath: tarball.archivePath,
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
}
|
|
112
|
+
// Embedded package tarballs are deliberately NOT part of a check's
|
|
113
|
+
// files: the Bundler materializes them once during finalize(), after
|
|
114
|
+
// the bundled lockfile has been pruned, so tarballs the shipped
|
|
115
|
+
// lockfile no longer references are never downloaded.
|
|
128
116
|
return {
|
|
129
117
|
browsers: pwConfigParsed.getBrowsers(),
|
|
130
118
|
playwrightVersion,
|
|
@@ -141,12 +129,28 @@ export function normalizeVersion(v) {
|
|
|
141
129
|
}
|
|
142
130
|
export function getAutoIncludes(basePath, globCwd, packageManager, existingIncludes) {
|
|
143
131
|
const autoIncludes = [];
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
132
|
+
// The prefix comparison appends a separator so a sibling directory whose
|
|
133
|
+
// name merely starts with the patches dir (e.g. `patches-archive`) does
|
|
134
|
+
// not suppress the auto-include.
|
|
135
|
+
const includesUnder = (dir) => existingIncludes.some(p => {
|
|
136
|
+
const resolved = path.resolve(globCwd, p);
|
|
137
|
+
return resolved === dir || resolved.startsWith(dir + path.sep);
|
|
138
|
+
});
|
|
139
|
+
// Dependency patches live in a conventional per-manager directory that
|
|
140
|
+
// the remote install (and the lockfile pruner's temp-dir install) needs
|
|
141
|
+
// alongside the manifests: `patches/` for pnpm and bun, `.yarn/patches`
|
|
142
|
+
// for Yarn Berry's patch: protocol. A patch kept at a nonconventional
|
|
143
|
+
// path makes the pruner fail closed with a warning instead.
|
|
144
|
+
const patchesDirByManager = {
|
|
145
|
+
pnpm: [PNPM_PATCHES_DIR],
|
|
146
|
+
bun: [PNPM_PATCHES_DIR],
|
|
147
|
+
yarn: ['.yarn', 'patches'],
|
|
148
|
+
};
|
|
149
|
+
const patchesSegments = patchesDirByManager[packageManager.name];
|
|
150
|
+
if (patchesSegments !== undefined) {
|
|
151
|
+
const patchesDir = path.join(basePath, ...patchesSegments);
|
|
152
|
+
if (!includesUnder(patchesDir)) {
|
|
153
|
+
autoIncludes.push(pathToPosix(path.join(path.relative(globCwd, basePath), ...patchesSegments, '*.patch')));
|
|
150
154
|
}
|
|
151
155
|
}
|
|
152
156
|
return autoIncludes;
|