@vltpkg/query 1.0.0-rc.23 → 1.0.0-rc.24

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.
Files changed (129) hide show
  1. package/dist/attribute.d.ts +14 -0
  2. package/dist/attribute.js +132 -0
  3. package/dist/combinator.d.ts +5 -0
  4. package/dist/combinator.js +111 -0
  5. package/dist/id.d.ts +5 -0
  6. package/dist/id.js +35 -0
  7. package/dist/index.d.ts +48 -0
  8. package/dist/index.js +410 -0
  9. package/dist/parser.d.ts +14 -0
  10. package/dist/parser.js +92 -0
  11. package/dist/pseudo/abandoned.d.ts +6 -0
  12. package/dist/pseudo/abandoned.js +5 -0
  13. package/dist/pseudo/attr.d.ts +18 -0
  14. package/dist/pseudo/attr.js +57 -0
  15. package/dist/pseudo/built.d.ts +7 -0
  16. package/dist/pseudo/built.js +15 -0
  17. package/dist/pseudo/confused.d.ts +8 -0
  18. package/dist/pseudo/confused.js +18 -0
  19. package/dist/pseudo/cve.d.ts +12 -0
  20. package/dist/pseudo/cve.js +43 -0
  21. package/dist/pseudo/cwe.d.ts +12 -0
  22. package/dist/pseudo/cwe.js +42 -0
  23. package/dist/pseudo/debug.d.ts +6 -0
  24. package/dist/pseudo/debug.js +5 -0
  25. package/dist/pseudo/deprecated.d.ts +6 -0
  26. package/dist/pseudo/deprecated.js +5 -0
  27. package/dist/pseudo/dev.d.ts +5 -0
  28. package/dist/pseudo/dev.js +14 -0
  29. package/dist/pseudo/diff.d.ts +26 -0
  30. package/dist/pseudo/diff.js +75 -0
  31. package/dist/pseudo/dynamic.d.ts +6 -0
  32. package/dist/pseudo/dynamic.js +5 -0
  33. package/dist/pseudo/empty.d.ts +6 -0
  34. package/dist/pseudo/empty.js +13 -0
  35. package/dist/pseudo/entropic.d.ts +6 -0
  36. package/dist/pseudo/entropic.js +5 -0
  37. package/dist/pseudo/env.d.ts +6 -0
  38. package/dist/pseudo/env.js +5 -0
  39. package/dist/pseudo/eval.d.ts +6 -0
  40. package/dist/pseudo/eval.js +5 -0
  41. package/dist/pseudo/fs.d.ts +6 -0
  42. package/dist/pseudo/fs.js +5 -0
  43. package/dist/pseudo/helpers.d.ts +38 -0
  44. package/dist/pseudo/helpers.js +79 -0
  45. package/dist/pseudo/host.d.ts +19 -0
  46. package/dist/pseudo/host.js +79 -0
  47. package/dist/pseudo/hostname.d.ts +11 -0
  48. package/dist/pseudo/hostname.js +138 -0
  49. package/dist/pseudo/license.d.ts +12 -0
  50. package/dist/pseudo/license.js +74 -0
  51. package/dist/pseudo/link.d.ts +8 -0
  52. package/dist/pseudo/link.js +24 -0
  53. package/dist/pseudo/malware.d.ts +23 -0
  54. package/dist/pseudo/malware.js +186 -0
  55. package/dist/pseudo/minified.d.ts +6 -0
  56. package/dist/pseudo/minified.js +5 -0
  57. package/dist/pseudo/missing.d.ts +7 -0
  58. package/dist/pseudo/missing.js +14 -0
  59. package/dist/pseudo/native.d.ts +6 -0
  60. package/dist/pseudo/native.js +5 -0
  61. package/dist/pseudo/network.d.ts +6 -0
  62. package/dist/pseudo/network.js +5 -0
  63. package/dist/pseudo/obfuscated.d.ts +6 -0
  64. package/dist/pseudo/obfuscated.js +5 -0
  65. package/dist/pseudo/optional.d.ts +5 -0
  66. package/dist/pseudo/optional.js +14 -0
  67. package/dist/pseudo/outdated.d.ts +53 -0
  68. package/dist/pseudo/outdated.js +211 -0
  69. package/dist/pseudo/overridden.d.ts +7 -0
  70. package/dist/pseudo/overridden.js +16 -0
  71. package/dist/pseudo/path.d.ts +18 -0
  72. package/dist/pseudo/path.js +110 -0
  73. package/dist/pseudo/peer.d.ts +5 -0
  74. package/dist/pseudo/peer.js +14 -0
  75. package/dist/pseudo/prerelease.d.ts +17 -0
  76. package/dist/pseudo/prerelease.js +40 -0
  77. package/dist/pseudo/private.d.ts +6 -0
  78. package/dist/pseudo/private.js +15 -0
  79. package/dist/pseudo/prod.d.ts +5 -0
  80. package/dist/pseudo/prod.js +14 -0
  81. package/dist/pseudo/published.d.ts +39 -0
  82. package/dist/pseudo/published.js +179 -0
  83. package/dist/pseudo/registry.d.ts +10 -0
  84. package/dist/pseudo/registry.js +24 -0
  85. package/dist/pseudo/root.d.ts +6 -0
  86. package/dist/pseudo/root.js +17 -0
  87. package/dist/pseudo/scanned.d.ts +8 -0
  88. package/dist/pseudo/scanned.js +16 -0
  89. package/dist/pseudo/score.d.ts +15 -0
  90. package/dist/pseudo/score.js +132 -0
  91. package/dist/pseudo/scripts.d.ts +9 -0
  92. package/dist/pseudo/scripts.js +43 -0
  93. package/dist/pseudo/semver.d.ts +16 -0
  94. package/dist/pseudo/semver.js +166 -0
  95. package/dist/pseudo/severity.d.ts +14 -0
  96. package/dist/pseudo/severity.js +159 -0
  97. package/dist/pseudo/shell.d.ts +6 -0
  98. package/dist/pseudo/shell.js +5 -0
  99. package/dist/pseudo/shrinkwrap.d.ts +6 -0
  100. package/dist/pseudo/shrinkwrap.js +5 -0
  101. package/dist/pseudo/spec.d.ts +16 -0
  102. package/dist/pseudo/spec.js +101 -0
  103. package/dist/pseudo/squat.d.ts +14 -0
  104. package/dist/pseudo/squat.js +171 -0
  105. package/dist/pseudo/suspicious.d.ts +6 -0
  106. package/dist/pseudo/suspicious.js +5 -0
  107. package/dist/pseudo/tracker.d.ts +6 -0
  108. package/dist/pseudo/tracker.js +5 -0
  109. package/dist/pseudo/trivial.d.ts +6 -0
  110. package/dist/pseudo/trivial.js +5 -0
  111. package/dist/pseudo/type.d.ts +7 -0
  112. package/dist/pseudo/type.js +21 -0
  113. package/dist/pseudo/undesirable.d.ts +6 -0
  114. package/dist/pseudo/undesirable.js +5 -0
  115. package/dist/pseudo/unknown.d.ts +6 -0
  116. package/dist/pseudo/unknown.js +5 -0
  117. package/dist/pseudo/unmaintained.d.ts +6 -0
  118. package/dist/pseudo/unmaintained.js +5 -0
  119. package/dist/pseudo/unpopular.d.ts +6 -0
  120. package/dist/pseudo/unpopular.js +5 -0
  121. package/dist/pseudo/unstable.d.ts +6 -0
  122. package/dist/pseudo/unstable.js +5 -0
  123. package/dist/pseudo/workspace.d.ts +5 -0
  124. package/dist/pseudo/workspace.js +19 -0
  125. package/dist/pseudo.d.ts +5 -0
  126. package/dist/pseudo.js +366 -0
  127. package/dist/types.d.ts +124 -0
  128. package/dist/types.js +1 -0
  129. package/package.json +8 -8
@@ -0,0 +1,179 @@
1
+ import pRetry, { AbortError } from 'p-retry';
2
+ import { hydrate, splitDepID } from '@vltpkg/dep-id/browser';
3
+ import { error } from '@vltpkg/error-cause';
4
+ import { asError } from '@vltpkg/types';
5
+ import { asPostcssNodeWithChildren, asStringNode, asTagNode, isStringNode, isTagNode, } from '@vltpkg/dss-parser';
6
+ import { removeDanglingEdges, removeNode, removeQuotes, } from "./helpers.js";
7
+ /**
8
+ * Fetches the published date of a package version from the npm registry.
9
+ */
10
+ export const retrieveRemoteDate = async (node, signal) => {
11
+ const spec = hydrate(node.id, String(node.name), node.options);
12
+ if (!spec.registry || !node.name || !node.version) {
13
+ return undefined;
14
+ }
15
+ const url = new URL(spec.registry);
16
+ url.pathname = `/${node.name}`;
17
+ const response = await fetch(String(url), {
18
+ signal,
19
+ });
20
+ // on missing valid auth or API, it should abort the retry logic
21
+ if (response.status === 404) {
22
+ throw new AbortError('Missing API');
23
+ }
24
+ if (!response.ok) {
25
+ throw error('Failed to fetch packument', {
26
+ name: node.name,
27
+ spec,
28
+ response,
29
+ });
30
+ }
31
+ const packument = (await response.json());
32
+ const res = packument.time?.[node.version];
33
+ return res;
34
+ };
35
+ /**
36
+ * Retrieves what kind of check the :published selector should perform.
37
+ */
38
+ export const parseInternals = (nodes) => {
39
+ let value = '';
40
+ if (isStringNode(asPostcssNodeWithChildren(nodes[0]).nodes[0])) {
41
+ value = removeQuotes(asStringNode(asPostcssNodeWithChildren(nodes[0]).nodes[0])
42
+ .value);
43
+ }
44
+ else if (isTagNode(asPostcssNodeWithChildren(nodes[0]).nodes[0])) {
45
+ const tagNode = asTagNode(asPostcssNodeWithChildren(nodes[0]).nodes[0]);
46
+ value = tagNode.value;
47
+ }
48
+ // Check if the value starts with a comparator
49
+ let comparator;
50
+ let relativeDate = value;
51
+ if (value.startsWith('>=')) {
52
+ comparator = '>=';
53
+ relativeDate = value.slice(2);
54
+ }
55
+ else if (value.startsWith('<=')) {
56
+ comparator = '<=';
57
+ relativeDate = value.slice(2);
58
+ }
59
+ else if (value.startsWith('>')) {
60
+ comparator = '>';
61
+ relativeDate = value.slice(1);
62
+ }
63
+ else if (value.startsWith('<')) {
64
+ comparator = '<';
65
+ relativeDate = value.slice(1);
66
+ }
67
+ return { relativeDate, comparator };
68
+ };
69
+ /**
70
+ * Filter nodes by queueing up for removal those that don't match the date criteria.
71
+ */
72
+ export const queueNode = async (state, node, relativeDate, comparator) => {
73
+ if (!node.name || !node.version) {
74
+ return node;
75
+ }
76
+ let publishedDate;
77
+ try {
78
+ publishedDate = await pRetry(() => retrieveRemoteDate(node, state.signal), {
79
+ retries: state.retries,
80
+ signal: state.signal,
81
+ });
82
+ }
83
+ catch (err) {
84
+ // eslint-disable-next-line no-console
85
+ console.warn(error('Could not retrieve registry publish date', {
86
+ name: node.name,
87
+ cause: err,
88
+ }));
89
+ return node;
90
+ }
91
+ if (!publishedDate) {
92
+ return node;
93
+ }
94
+ // only matches the same amount of date information
95
+ // as provided in the relative date
96
+ const nodeDate = new Date(publishedDate.slice(0, relativeDate.length));
97
+ const compareDate = new Date(relativeDate);
98
+ switch (comparator) {
99
+ case '>':
100
+ return nodeDate > compareDate ? undefined : node;
101
+ case '<':
102
+ return nodeDate < compareDate ? undefined : node;
103
+ case '>=':
104
+ return nodeDate >= compareDate ? undefined : node;
105
+ case '<=':
106
+ return nodeDate <= compareDate ? undefined : node;
107
+ default:
108
+ return nodeDate.getTime() === compareDate.getTime() ?
109
+ undefined
110
+ : node;
111
+ }
112
+ };
113
+ /**
114
+ * Filters out nodes that don't match the published date criteria.
115
+ *
116
+ * The :published() pseudo selector supports a date parameter that can be prefixed
117
+ * with a comparator (>, <, >=, <=). If no comparator is provided, it will match
118
+ * exact dates.
119
+ *
120
+ * Examples:
121
+ * - :published("2024-01-01") - Matches packages published exactly on January 1st, 2024
122
+ * - :published(">2024-01-01") - Matches packages published after January 1st, 2024
123
+ * - :published("<=2023-12-31") - Matches packages published on or before December 31st, 2023
124
+ */
125
+ export const published = async (state) => {
126
+ let internals;
127
+ try {
128
+ internals = parseInternals(asPostcssNodeWithChildren(state.current).nodes);
129
+ }
130
+ catch (err) {
131
+ if (asError(err).message === 'Expected a query node') {
132
+ // No parameters provided - pseudo state form: match ANY published metadata
133
+ for (const node of state.partial.nodes) {
134
+ // filter out nodes that are always ignored by the published selector
135
+ if (node.mainImporter ||
136
+ node.manifest?.private ||
137
+ splitDepID(node.id)[0] !== 'registry') {
138
+ removeNode(state, node);
139
+ continue;
140
+ }
141
+ // For pseudo state form, we just check if the node has published metadata
142
+ // This is equivalent to checking if it's a registry package with version info
143
+ if (!node.name || !node.version) {
144
+ removeNode(state, node);
145
+ }
146
+ }
147
+ removeDanglingEdges(state);
148
+ return state;
149
+ }
150
+ else {
151
+ throw error('Failed to parse :published selector', {
152
+ cause: err,
153
+ });
154
+ }
155
+ }
156
+ const { relativeDate, comparator } = internals;
157
+ const queue = [];
158
+ for (const node of state.partial.nodes) {
159
+ // filter out nodes that are always ignored by the published selector
160
+ if (node.mainImporter ||
161
+ node.manifest?.private ||
162
+ splitDepID(node.id)[0] !== 'registry') {
163
+ removeNode(state, node);
164
+ continue;
165
+ }
166
+ // fetch published date info and perform checks to define
167
+ // whether or not a node should be filtered out
168
+ queue.push(queueNode(state, node, relativeDate, comparator));
169
+ }
170
+ // nodes queued for removal are then finally removed
171
+ const removeNodeQueue = await Promise.all(queue);
172
+ for (const node of removeNodeQueue) {
173
+ if (node) {
174
+ removeNode(state, node);
175
+ }
176
+ }
177
+ removeDanglingEdges(state);
178
+ return state;
179
+ };
@@ -0,0 +1,10 @@
1
+ import type { ParserState } from '../types.ts';
2
+ /**
3
+ * :registry(name) Pseudo-Selector, matches only nodes that
4
+ * belong to the specified registry configuration alias.
5
+ *
6
+ * For example, `:registry(npm)` matches deps from the default
7
+ * npm registry, and `:registry(custom)` matches deps from a
8
+ * custom-named registry.
9
+ */
10
+ export declare const registry: (state: ParserState) => Promise<ParserState>;
@@ -0,0 +1,24 @@
1
+ import { splitDepID } from '@vltpkg/dep-id/browser';
2
+ import { asPostcssNodeWithChildren, asTagNode, } from '@vltpkg/dss-parser';
3
+ import { removeDanglingEdges, removeNode } from "./helpers.js";
4
+ /**
5
+ * :registry(name) Pseudo-Selector, matches only nodes that
6
+ * belong to the specified registry configuration alias.
7
+ *
8
+ * For example, `:registry(npm)` matches deps from the default
9
+ * npm registry, and `:registry(custom)` matches deps from a
10
+ * custom-named registry.
11
+ */
12
+ export const registry = async (state) => {
13
+ const top = asPostcssNodeWithChildren(state.current);
14
+ const selector = asPostcssNodeWithChildren(top.nodes[0]);
15
+ const name = asTagNode(selector.nodes[0]).value;
16
+ for (const node of state.partial.nodes) {
17
+ const tuple = splitDepID(node.id);
18
+ if (tuple[0] !== 'registry' || tuple[1] !== name) {
19
+ removeNode(state, node);
20
+ }
21
+ }
22
+ removeDanglingEdges(state);
23
+ return state;
24
+ };
@@ -0,0 +1,6 @@
1
+ import type { ParserState } from '../types.ts';
2
+ /**
3
+ * :root Pseudo-Element will return the project root node for the graph.
4
+ * It matches only nodes marked as `mainImporter`.
5
+ */
6
+ export declare const root: (state: ParserState) => Promise<ParserState>;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * :root Pseudo-Element will return the project root node for the graph.
3
+ * It matches only nodes marked as `mainImporter`.
4
+ */
5
+ export const root = async (state) => {
6
+ for (const edge of state.partial.edges) {
7
+ if (!edge.to?.mainImporter) {
8
+ state.partial.edges.delete(edge);
9
+ }
10
+ }
11
+ for (const node of state.partial.nodes) {
12
+ if (!node.mainImporter) {
13
+ state.partial.nodes.delete(node);
14
+ }
15
+ }
16
+ return state;
17
+ };
@@ -0,0 +1,8 @@
1
+ import type { ParserState } from '../types.ts';
2
+ /**
3
+ * :scanned pseudo selector.
4
+ *
5
+ * Remove all nodes that do not have available metadata
6
+ * in the security archive.
7
+ */
8
+ export declare const scanned: (state: ParserState) => Promise<ParserState>;
@@ -0,0 +1,16 @@
1
+ import { removeDanglingEdges, removeNode } from "./helpers.js";
2
+ /**
3
+ * :scanned pseudo selector.
4
+ *
5
+ * Remove all nodes that do not have available metadata
6
+ * in the security archive.
7
+ */
8
+ export const scanned = async (state) => {
9
+ for (const node of state.partial.nodes) {
10
+ if (!state.securityArchive?.has(node.id)) {
11
+ removeNode(state, node);
12
+ }
13
+ }
14
+ removeDanglingEdges(state);
15
+ return state;
16
+ };
@@ -0,0 +1,15 @@
1
+ import type { PackageScore } from '@vltpkg/security-archive';
2
+ import type { ParserState } from '../types.ts';
3
+ import type { PostcssNode } from '@vltpkg/dss-parser';
4
+ export type ScoreKinds = keyof PackageScore;
5
+ export type ScoreComparator = '>' | '<' | '>=' | '<=' | '=' | undefined;
6
+ export declare const isScoreKind: (value?: string) => value is ScoreKinds;
7
+ export declare const asScoreKind: (value?: string) => ScoreKinds;
8
+ export declare const parseInternals: (nodes: PostcssNode[]) => {
9
+ comparator: ScoreComparator;
10
+ rate: number;
11
+ kind: ScoreKinds;
12
+ };
13
+ export declare const score: (state: ParserState) => Promise<ParserState & {
14
+ securityArchive: NonNullable<ParserState["securityArchive"]>;
15
+ }>;
@@ -0,0 +1,132 @@
1
+ import { error } from '@vltpkg/error-cause';
2
+ import { asError } from '@vltpkg/types';
3
+ import { asPostcssNodeWithChildren, asStringNode, asTagNode, isStringNode, isTagNode, } from '@vltpkg/dss-parser';
4
+ import { assertSecurityArchive, removeDanglingEdges, removeNode, removeQuotes, } from "./helpers.js";
5
+ const kinds = new Set([
6
+ 'overall',
7
+ 'license',
8
+ 'maintenance',
9
+ 'quality',
10
+ 'supplyChain',
11
+ 'vulnerability',
12
+ undefined,
13
+ ]);
14
+ export const isScoreKind = (value) => kinds.has(value);
15
+ export const asScoreKind = (value) => {
16
+ if (!isScoreKind(value)) {
17
+ throw error('Expected a valid score kind', {
18
+ found: value,
19
+ validOptions: Array.from(kinds),
20
+ });
21
+ }
22
+ return value;
23
+ };
24
+ export const parseInternals = (nodes) => {
25
+ let rateStr = '';
26
+ let comparator = '=';
27
+ let kind = 'overall';
28
+ // Parse the first parameter (rate with optional comparator)
29
+ if (isStringNode(asPostcssNodeWithChildren(nodes[0]).nodes[0])) {
30
+ rateStr = removeQuotes(asStringNode(asPostcssNodeWithChildren(nodes[0]).nodes[0])
31
+ .value);
32
+ }
33
+ else if (isTagNode(asPostcssNodeWithChildren(nodes[0]).nodes[0])) {
34
+ const tagNode = asTagNode(asPostcssNodeWithChildren(nodes[0]).nodes[0]);
35
+ rateStr = tagNode.value;
36
+ }
37
+ // Extract comparator if present
38
+ if (rateStr.startsWith('>=')) {
39
+ comparator = '>=';
40
+ rateStr = rateStr.substring(2);
41
+ }
42
+ else if (rateStr.startsWith('<=')) {
43
+ comparator = '<=';
44
+ rateStr = rateStr.substring(2);
45
+ }
46
+ else if (rateStr.startsWith('>')) {
47
+ comparator = '>';
48
+ rateStr = rateStr.substring(1);
49
+ }
50
+ else if (rateStr.startsWith('<')) {
51
+ comparator = '<';
52
+ rateStr = rateStr.substring(1);
53
+ }
54
+ // Parse rate as number
55
+ let rate = parseFloat(rateStr);
56
+ // Normalize to 0-1 range if needed
57
+ if (rate > 1) {
58
+ rate = rate / 100;
59
+ }
60
+ // Validate rate is in acceptable range
61
+ if (rate < 0 || rate > 1) {
62
+ throw error('Expected rate to be between 0 and 100', {
63
+ found: rateStr,
64
+ });
65
+ }
66
+ // Parse the second parameter (kind) if present
67
+ if (nodes.length > 1) {
68
+ if (isStringNode(asPostcssNodeWithChildren(nodes[1]).nodes[0])) {
69
+ kind = asScoreKind(removeQuotes(asStringNode(asPostcssNodeWithChildren(nodes[1]).nodes[0])
70
+ .value));
71
+ }
72
+ else if (isTagNode(asPostcssNodeWithChildren(nodes[1]).nodes[0])) {
73
+ kind = asScoreKind(asTagNode(asPostcssNodeWithChildren(nodes[1]).nodes[0]).value);
74
+ }
75
+ }
76
+ return { comparator, rate, kind };
77
+ };
78
+ export const score = async (state) => {
79
+ assertSecurityArchive(state, 'score');
80
+ let internals;
81
+ try {
82
+ internals = parseInternals(asPostcssNodeWithChildren(state.current).nodes);
83
+ }
84
+ catch (err) {
85
+ if (asError(err).message === 'Expected a query node') {
86
+ // No parameters provided - pseudo state form: match ANY score (scanned packages)
87
+ for (const node of state.partial.nodes) {
88
+ const report = state.securityArchive.get(node.id);
89
+ if (!report) {
90
+ removeNode(state, node);
91
+ }
92
+ }
93
+ removeDanglingEdges(state);
94
+ return state;
95
+ }
96
+ else {
97
+ throw error('Failed to parse :score selector', { cause: err });
98
+ }
99
+ }
100
+ const { comparator, rate, kind } = internals;
101
+ for (const node of state.partial.nodes) {
102
+ const report = state.securityArchive.get(node.id);
103
+ if (!report) {
104
+ removeNode(state, node);
105
+ continue;
106
+ }
107
+ const scoreValue = report.score[kind];
108
+ let exclude = false;
109
+ switch (comparator) {
110
+ case '>':
111
+ exclude = scoreValue <= rate;
112
+ break;
113
+ case '<':
114
+ exclude = scoreValue >= rate;
115
+ break;
116
+ case '>=':
117
+ exclude = scoreValue < rate;
118
+ break;
119
+ case '<=':
120
+ exclude = scoreValue > rate;
121
+ break;
122
+ default: // '='
123
+ exclude = scoreValue !== rate;
124
+ break;
125
+ }
126
+ if (exclude) {
127
+ removeNode(state, node);
128
+ }
129
+ }
130
+ removeDanglingEdges(state);
131
+ return state;
132
+ };
@@ -0,0 +1,9 @@
1
+ import type { ParserState } from '../types.ts';
2
+ /**
3
+ * :scripts Pseudo-Selector filters nodes based on whether they need to be built.
4
+ *
5
+ * A node needs to be built if it has:
6
+ * - Install lifecycle scripts (install, preinstall, postinstall)
7
+ * - Prepare scripts on importers or git dependencies (prepare, preprepare, postprepare)
8
+ */
9
+ export declare const scripts: (state: ParserState) => Promise<ParserState>;
@@ -0,0 +1,43 @@
1
+ import { removeNode, removeDanglingEdges } from "./helpers.js";
2
+ /**
3
+ * Checks if a node needs to be built based on the conditions from the reify build process:
4
+ * 1. Has install lifecycle scripts (install, preinstall, postinstall)
5
+ * 2. Is an importer or git dependency with prepare scripts (prepare, preprepare, postprepare)
6
+ */
7
+ const nodeNeedsBuild = (node) => {
8
+ const { manifest } = node;
9
+ /* c8 ignore next */
10
+ if (!manifest)
11
+ return false;
12
+ const { scripts = {} } = manifest;
13
+ // Check for install lifecycle scripts
14
+ const runInstall = !!(scripts.install ||
15
+ scripts.preinstall ||
16
+ scripts.postinstall);
17
+ if (runInstall)
18
+ return true;
19
+ // Check for prepare scripts on importers or git dependencies
20
+ const prepable = node.id.startsWith('git') || node.importer;
21
+ const runPrepare = !!((scripts.prepare || scripts.preprepare || scripts.postprepare)
22
+ /* c8 ignore next 2 */
23
+ ) && prepable;
24
+ if (runPrepare)
25
+ return true;
26
+ return false;
27
+ };
28
+ /**
29
+ * :scripts Pseudo-Selector filters nodes based on whether they need to be built.
30
+ *
31
+ * A node needs to be built if it has:
32
+ * - Install lifecycle scripts (install, preinstall, postinstall)
33
+ * - Prepare scripts on importers or git dependencies (prepare, preprepare, postprepare)
34
+ */
35
+ export const scripts = async (state) => {
36
+ for (const node of state.partial.nodes) {
37
+ if (!nodeNeedsBuild(node)) {
38
+ removeNode(state, node);
39
+ }
40
+ }
41
+ removeDanglingEdges(state);
42
+ return state;
43
+ };
@@ -0,0 +1,16 @@
1
+ import type { Version } from '@vltpkg/semver';
2
+ import type { AttrInternals } from './attr.ts';
3
+ import type { ParserState } from '../types.ts';
4
+ import type { PostcssNode } from '@vltpkg/dss-parser';
5
+ export type SemverInternals = {
6
+ semverValue: string;
7
+ semverFunction: SemverComparatorFn;
8
+ compareAttribute: SemverCompareAttribute;
9
+ };
10
+ export type SemverFunctionNames = 'satisfies' | 'gt' | 'gte' | 'lt' | 'lte' | 'eq' | 'neq';
11
+ export type SemverComparatorFn = (version: Version | string, range: string) => boolean;
12
+ export type SemverCompareAttribute = Pick<AttrInternals, 'attribute' | 'properties'> | undefined;
13
+ export declare const isSemverFunctionName: (name: string) => name is SemverFunctionNames;
14
+ export declare const asSemverFunctionName: (name: string) => SemverFunctionNames;
15
+ export declare const parseInternals: (nodes: PostcssNode[], loose: boolean) => SemverInternals;
16
+ export declare const semverParser: (state: ParserState) => Promise<ParserState>;
@@ -0,0 +1,166 @@
1
+ import { satisfies, gt, gte, lt, lte, eq, neq, parse, parseRange, } from '@vltpkg/semver';
2
+ import { error } from '@vltpkg/error-cause';
3
+ import { asError } from '@vltpkg/types';
4
+ import { parseInternals as parseAttrInternals } from "./attr.js";
5
+ import { getManifestPropertyValues } from "../attribute.js";
6
+ import { asAttributeNode, asPostcssNodeWithChildren, asPseudoNode, asStringNode, asTagNode, isAttributeNode, isPseudoNode, isStringNode, isTagNode, } from '@vltpkg/dss-parser';
7
+ import { removeNode, removeQuotes } from "./helpers.js";
8
+ const semverFunctionNames = new Set([
9
+ 'satisfies',
10
+ 'gt',
11
+ 'gte',
12
+ 'lt',
13
+ 'lte',
14
+ 'eq',
15
+ 'neq',
16
+ ]);
17
+ export const isSemverFunctionName = (name) => semverFunctionNames.has(name);
18
+ export const asSemverFunctionName = (name) => {
19
+ if (!isSemverFunctionName(name)) {
20
+ throw error('Invalid semver function name', {
21
+ found: name,
22
+ validOptions: Array.from(semverFunctionNames),
23
+ });
24
+ }
25
+ return name;
26
+ };
27
+ const semverFunctions = new Map([
28
+ ['satisfies', satisfies],
29
+ ['gt', gt],
30
+ ['gte', gte],
31
+ ['lt', lt],
32
+ ['lte', lte],
33
+ ['eq', eq],
34
+ ['neq', neq],
35
+ ]);
36
+ export const parseInternals = (nodes, loose) => {
37
+ // tries to parse the first param as a string node, otherwise defaults
38
+ // to reading all postcss nodes as just strings, since it just means
39
+ // the value was defined as an unquoted string
40
+ let semverValue = '';
41
+ try {
42
+ semverValue = removeQuotes(asStringNode(asPostcssNodeWithChildren(nodes[0]).nodes[0])
43
+ .value);
44
+ }
45
+ catch (err) {
46
+ if (asError(err).message === 'Mismatching query node' &&
47
+ isTagNode(asPostcssNodeWithChildren(nodes[0]).nodes[0])) {
48
+ // Handle tag node (unquoted values like >=2.0.0)
49
+ const tagNode = asTagNode(asPostcssNodeWithChildren(nodes[0]).nodes[0]);
50
+ semverValue = tagNode.value;
51
+ }
52
+ else {
53
+ throw err;
54
+ }
55
+ }
56
+ // second param is the function name
57
+ let fnName = 'satisfies';
58
+ try {
59
+ // if there is a second node defined, try to parse it as a string node
60
+ // first and if that fails, then parse it as a tag node which just means
61
+ // it was defined as an unquoted string
62
+ if (nodes[1]) {
63
+ try {
64
+ fnName = asSemverFunctionName(removeQuotes(asStringNode(asPostcssNodeWithChildren(nodes[1]).nodes[0])
65
+ .value));
66
+ }
67
+ catch (err) {
68
+ if (asError(err).message === 'Mismatching query node') {
69
+ fnName = asSemverFunctionName(asTagNode(asPostcssNodeWithChildren(nodes[1]).nodes[0])
70
+ .value);
71
+ }
72
+ else {
73
+ throw err;
74
+ }
75
+ }
76
+ }
77
+ }
78
+ catch (e) {
79
+ // allow invalid semver function names in loose mode, defaults to satisfies
80
+ if (!loose) {
81
+ throw e;
82
+ }
83
+ }
84
+ const semverFunction = semverFunctions.get(fnName);
85
+ // the following should never happen as long as the semver function names
86
+ // type and Set are correctly mirroring each other values
87
+ /* c8 ignore start */
88
+ if (!semverFunction) {
89
+ throw error('Invalid semver function name', {
90
+ found: fnName,
91
+ validOptions: Array.from(semverFunctions.keys()),
92
+ });
93
+ }
94
+ /* c8 ignore stop */
95
+ // optional third param is the compare value
96
+ let compareAttribute;
97
+ if (nodes[2]) {
98
+ const parentNode = asPostcssNodeWithChildren(nodes[2]);
99
+ const currNode = parentNode.nodes[0];
100
+ if (isAttributeNode(currNode)) {
101
+ const { attribute } = asAttributeNode(currNode);
102
+ compareAttribute = {
103
+ attribute,
104
+ properties: [attribute],
105
+ };
106
+ }
107
+ else if (isPseudoNode(currNode)) {
108
+ compareAttribute = parseAttrInternals(asPseudoNode(currNode).nodes);
109
+ }
110
+ else if (isStringNode(currNode)) {
111
+ const attribute = removeQuotes(asStringNode(currNode).value);
112
+ compareAttribute = {
113
+ attribute,
114
+ properties: [attribute],
115
+ };
116
+ }
117
+ }
118
+ return {
119
+ semverValue,
120
+ semverFunction,
121
+ compareAttribute,
122
+ };
123
+ };
124
+ export const semverParser = async (state) => {
125
+ let internals;
126
+ try {
127
+ internals = parseInternals(asPostcssNodeWithChildren(state.current).nodes, !!state.loose);
128
+ }
129
+ catch (err) {
130
+ throw error('Failed to parse :semver selector', {
131
+ cause: err,
132
+ });
133
+ }
134
+ const { semverValue, semverFunction, compareAttribute } = internals;
135
+ for (const node of state.partial.nodes) {
136
+ if (compareAttribute) {
137
+ const compareValues = getManifestPropertyValues(node, compareAttribute.properties, compareAttribute.attribute);
138
+ // if the provided semver value is a fixed semver version and the
139
+ // compare attribute is resolving to a range value, then we flip the
140
+ // order of comparison, in case it's a "satisfies" function check
141
+ const compareValue = compareValues?.[0];
142
+ const semverValueVersion = parse(semverValue);
143
+ const compareValueRange = compareValue && parseRange(compareValue);
144
+ if (semverFunction === satisfies &&
145
+ semverValueVersion &&
146
+ compareValueRange) {
147
+ if (!satisfies(semverValueVersion, compareValueRange)) {
148
+ removeNode(state, node);
149
+ }
150
+ // otherwise just compares the read attribute to the semver value
151
+ }
152
+ else if (!compareValue ||
153
+ !semverFunction(compareValue, semverValue)) {
154
+ removeNode(state, node);
155
+ }
156
+ }
157
+ else {
158
+ const manifestVersion = node.manifest?.version;
159
+ if (!manifestVersion ||
160
+ !semverFunction(manifestVersion, semverValue)) {
161
+ removeNode(state, node);
162
+ }
163
+ }
164
+ }
165
+ return state;
166
+ };
@@ -0,0 +1,14 @@
1
+ import type { ParserState } from '../types.ts';
2
+ import type { PostcssNode } from '@vltpkg/dss-parser';
3
+ export type SeverityKinds = '0' | '1' | '2' | '3' | 'critical' | 'high' | 'medium' | 'low' | undefined;
4
+ export type SeverityAlertTypes = 'criticalCVE' | 'cve' | 'potentialVulnerability' | 'mildCVE' | undefined;
5
+ export type SeverityComparator = '>' | '<' | '>=' | '<=' | undefined;
6
+ export declare const isSeverityKind: (value?: string) => value is SeverityKinds;
7
+ export declare const asSeverityKind: (value?: string) => SeverityKinds;
8
+ export declare const parseInternals: (nodes: PostcssNode[]) => {
9
+ kind: SeverityKinds;
10
+ comparator: SeverityComparator;
11
+ };
12
+ export declare const severity: (state: ParserState) => Promise<ParserState & {
13
+ securityArchive: NonNullable<ParserState["securityArchive"]>;
14
+ }>;