@vltpkg/query 1.4.0 → 1.6.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.
Files changed (213) hide show
  1. package/dist/index.d.ts +7 -2
  2. package/dist/index.js +23 -4
  3. package/dist/pseudo/skill.d.ts +7 -0
  4. package/dist/pseudo/skill.js +52 -0
  5. package/dist/pseudo/skills.d.ts +6 -0
  6. package/dist/pseudo/skills.js +26 -0
  7. package/dist/pseudo.js +7 -0
  8. package/dist/types.d.ts +29 -1
  9. package/package.json +10 -12
  10. package/skills/dss-query/REFERENCE.md +0 -123
  11. package/skills/dss-query/SKILL.md +0 -190
  12. package/skills/dss-query/evals/README.md +0 -81
  13. package/skills/dss-query/evals/evals.json +0 -89
  14. package/skills/dss-query/evals/grade.mjs +0 -481
  15. package/src/attribute.ts +0 -190
  16. package/src/combinator.ts +0 -135
  17. package/src/error.ts +0 -78
  18. package/src/id.ts +0 -46
  19. package/src/index.ts +0 -640
  20. package/src/parser.ts +0 -122
  21. package/src/pseudo/abandoned.ts +0 -9
  22. package/src/pseudo/attr.ts +0 -92
  23. package/src/pseudo/built.ts +0 -19
  24. package/src/pseudo/confused.ts +0 -29
  25. package/src/pseudo/cve.ts +0 -93
  26. package/src/pseudo/cwe.ts +0 -97
  27. package/src/pseudo/debug.ts +0 -9
  28. package/src/pseudo/deprecated.ts +0 -9
  29. package/src/pseudo/dev.ts +0 -18
  30. package/src/pseudo/diff.ts +0 -90
  31. package/src/pseudo/dist.ts +0 -148
  32. package/src/pseudo/dynamic.ts +0 -9
  33. package/src/pseudo/empty.ts +0 -15
  34. package/src/pseudo/entropic.ts +0 -9
  35. package/src/pseudo/env.ts +0 -6
  36. package/src/pseudo/eval.ts +0 -9
  37. package/src/pseudo/fs.ts +0 -9
  38. package/src/pseudo/helpers.ts +0 -106
  39. package/src/pseudo/host.ts +0 -111
  40. package/src/pseudo/hostname.ts +0 -156
  41. package/src/pseudo/license.ts +0 -134
  42. package/src/pseudo/link.ts +0 -30
  43. package/src/pseudo/malware.ts +0 -41
  44. package/src/pseudo/minified.ts +0 -9
  45. package/src/pseudo/missing.ts +0 -16
  46. package/src/pseudo/native.ts +0 -9
  47. package/src/pseudo/network.ts +0 -9
  48. package/src/pseudo/obfuscated.ts +0 -9
  49. package/src/pseudo/optional.ts +0 -18
  50. package/src/pseudo/outdated.ts +0 -345
  51. package/src/pseudo/overridden.ts +0 -20
  52. package/src/pseudo/path.ts +0 -146
  53. package/src/pseudo/peer.ts +0 -18
  54. package/src/pseudo/prerelease.ts +0 -47
  55. package/src/pseudo/private.ts +0 -19
  56. package/src/pseudo/prod.ts +0 -18
  57. package/src/pseudo/published.ts +0 -250
  58. package/src/pseudo/registry.ts +0 -29
  59. package/src/pseudo/root.ts +0 -19
  60. package/src/pseudo/scanned.ts +0 -20
  61. package/src/pseudo/score.ts +0 -186
  62. package/src/pseudo/scripts.ts +0 -61
  63. package/src/pseudo/semver.ts +0 -320
  64. package/src/pseudo/severity.ts +0 -231
  65. package/src/pseudo/shell.ts +0 -9
  66. package/src/pseudo/shrinkwrap.ts +0 -9
  67. package/src/pseudo/spec.ts +0 -131
  68. package/src/pseudo/squat.ts +0 -225
  69. package/src/pseudo/suspicious.ts +0 -9
  70. package/src/pseudo/tracker.ts +0 -9
  71. package/src/pseudo/trivial.ts +0 -9
  72. package/src/pseudo/type.ts +0 -26
  73. package/src/pseudo/undesirable.ts +0 -9
  74. package/src/pseudo/unknown.ts +0 -9
  75. package/src/pseudo/unmaintained.ts +0 -9
  76. package/src/pseudo/unpopular.ts +0 -9
  77. package/src/pseudo/unstable.ts +0 -9
  78. package/src/pseudo/vuln.ts +0 -273
  79. package/src/pseudo/workspace.ts +0 -22
  80. package/src/pseudo.ts +0 -433
  81. package/src/types.ts +0 -176
  82. package/tap-snapshots/test/attribute.ts.test.cjs +0 -304
  83. package/tap-snapshots/test/combinator.ts.test.cjs +0 -333
  84. package/tap-snapshots/test/id.ts.test.cjs +0 -118
  85. package/tap-snapshots/test/parser.ts.test.cjs +0 -344
  86. package/tap-snapshots/test/pseudo/abandoned.ts.test.cjs +0 -18
  87. package/tap-snapshots/test/pseudo/attr.ts.test.cjs +0 -161
  88. package/tap-snapshots/test/pseudo/built.ts.test.cjs +0 -70
  89. package/tap-snapshots/test/pseudo/confused.ts.test.cjs +0 -32
  90. package/tap-snapshots/test/pseudo/cve.ts.test.cjs +0 -49
  91. package/tap-snapshots/test/pseudo/cwe.ts.test.cjs +0 -49
  92. package/tap-snapshots/test/pseudo/debug.ts.test.cjs +0 -18
  93. package/tap-snapshots/test/pseudo/deprecated.ts.test.cjs +0 -18
  94. package/tap-snapshots/test/pseudo/dist.ts.test.cjs +0 -30
  95. package/tap-snapshots/test/pseudo/dynamic.ts.test.cjs +0 -18
  96. package/tap-snapshots/test/pseudo/empty.ts.test.cjs +0 -50
  97. package/tap-snapshots/test/pseudo/entropic.ts.test.cjs +0 -18
  98. package/tap-snapshots/test/pseudo/env.ts.test.cjs +0 -18
  99. package/tap-snapshots/test/pseudo/eval.ts.test.cjs +0 -18
  100. package/tap-snapshots/test/pseudo/fs.ts.test.cjs +0 -18
  101. package/tap-snapshots/test/pseudo/helpers.ts.test.cjs +0 -18
  102. package/tap-snapshots/test/pseudo/hostname.ts.test.cjs +0 -148
  103. package/tap-snapshots/test/pseudo/license.ts.test.cjs +0 -41
  104. package/tap-snapshots/test/pseudo/link.ts.test.cjs +0 -36
  105. package/tap-snapshots/test/pseudo/malware.ts.test.cjs +0 -22
  106. package/tap-snapshots/test/pseudo/minified.ts.test.cjs +0 -18
  107. package/tap-snapshots/test/pseudo/missing.ts.test.cjs +0 -30
  108. package/tap-snapshots/test/pseudo/native.ts.test.cjs +0 -18
  109. package/tap-snapshots/test/pseudo/network.ts.test.cjs +0 -18
  110. package/tap-snapshots/test/pseudo/obfuscated.ts.test.cjs +0 -18
  111. package/tap-snapshots/test/pseudo/outdated.ts.test.cjs +0 -132
  112. package/tap-snapshots/test/pseudo/overridden.ts.test.cjs +0 -75
  113. package/tap-snapshots/test/pseudo/prerelease.ts.test.cjs +0 -101
  114. package/tap-snapshots/test/pseudo/private.ts.test.cjs +0 -38
  115. package/tap-snapshots/test/pseudo/published.ts.test.cjs +0 -171
  116. package/tap-snapshots/test/pseudo/registry.ts.test.cjs +0 -68
  117. package/tap-snapshots/test/pseudo/root.ts.test.cjs +0 -24
  118. package/tap-snapshots/test/pseudo/scanned.ts.test.cjs +0 -18
  119. package/tap-snapshots/test/pseudo/score.ts.test.cjs +0 -239
  120. package/tap-snapshots/test/pseudo/scripts.ts.test.cjs +0 -62
  121. package/tap-snapshots/test/pseudo/semver.ts.test.cjs +0 -575
  122. package/tap-snapshots/test/pseudo/severity.ts.test.cjs +0 -122
  123. package/tap-snapshots/test/pseudo/shell.ts.test.cjs +0 -18
  124. package/tap-snapshots/test/pseudo/shrinkwrap.ts.test.cjs +0 -18
  125. package/tap-snapshots/test/pseudo/spec.ts.test.cjs +0 -148
  126. package/tap-snapshots/test/pseudo/squat.ts.test.cjs +0 -129
  127. package/tap-snapshots/test/pseudo/suspicious.ts.test.cjs +0 -18
  128. package/tap-snapshots/test/pseudo/tracker.ts.test.cjs +0 -18
  129. package/tap-snapshots/test/pseudo/trivial.ts.test.cjs +0 -18
  130. package/tap-snapshots/test/pseudo/type.ts.test.cjs +0 -51
  131. package/tap-snapshots/test/pseudo/undesirable.ts.test.cjs +0 -18
  132. package/tap-snapshots/test/pseudo/unknown.ts.test.cjs +0 -18
  133. package/tap-snapshots/test/pseudo/unmaintained.ts.test.cjs +0 -18
  134. package/tap-snapshots/test/pseudo/unpopular.ts.test.cjs +0 -18
  135. package/tap-snapshots/test/pseudo/unstable.ts.test.cjs +0 -18
  136. package/tap-snapshots/test/pseudo/vuln.ts.test.cjs +0 -208
  137. package/tap-snapshots/test/pseudo/vulnerable.ts.test.cjs +0 -20
  138. package/tap-snapshots/test/pseudo/workspace.ts.test.cjs +0 -19
  139. package/tap-snapshots/test/pseudo.ts.test.cjs +0 -1247
  140. package/test/attribute.ts +0 -273
  141. package/test/combinator.ts +0 -175
  142. package/test/error.ts +0 -60
  143. package/test/fixtures/graph.ts +0 -999
  144. package/test/fixtures/selector.ts +0 -103
  145. package/test/fixtures/types.ts +0 -7
  146. package/test/id.ts +0 -102
  147. package/test/index.ts +0 -1049
  148. package/test/parser.ts +0 -117
  149. package/test/pseudo/abandoned.ts +0 -98
  150. package/test/pseudo/attr.ts +0 -298
  151. package/test/pseudo/built.ts +0 -214
  152. package/test/pseudo/confused.ts +0 -160
  153. package/test/pseudo/cve.ts +0 -249
  154. package/test/pseudo/cwe.ts +0 -255
  155. package/test/pseudo/debug.ts +0 -98
  156. package/test/pseudo/deprecated.ts +0 -98
  157. package/test/pseudo/dev.ts +0 -61
  158. package/test/pseudo/diff.ts +0 -417
  159. package/test/pseudo/dist.ts +0 -215
  160. package/test/pseudo/dynamic.ts +0 -98
  161. package/test/pseudo/empty.ts +0 -108
  162. package/test/pseudo/entropic.ts +0 -101
  163. package/test/pseudo/env.ts +0 -98
  164. package/test/pseudo/eval.ts +0 -98
  165. package/test/pseudo/fs.ts +0 -98
  166. package/test/pseudo/helpers.ts +0 -370
  167. package/test/pseudo/host-context.ts +0 -276
  168. package/test/pseudo/hostname.ts +0 -314
  169. package/test/pseudo/license.ts +0 -265
  170. package/test/pseudo/link.ts +0 -94
  171. package/test/pseudo/malware.ts +0 -184
  172. package/test/pseudo/minified.ts +0 -98
  173. package/test/pseudo/missing.ts +0 -111
  174. package/test/pseudo/native.ts +0 -98
  175. package/test/pseudo/network.ts +0 -98
  176. package/test/pseudo/obfuscated.ts +0 -98
  177. package/test/pseudo/optional.ts +0 -73
  178. package/test/pseudo/outdated.ts +0 -461
  179. package/test/pseudo/overridden.ts +0 -317
  180. package/test/pseudo/path.ts +0 -680
  181. package/test/pseudo/peer.ts +0 -101
  182. package/test/pseudo/prerelease.ts +0 -279
  183. package/test/pseudo/private.ts +0 -108
  184. package/test/pseudo/prod.ts +0 -61
  185. package/test/pseudo/published.ts +0 -557
  186. package/test/pseudo/registry.ts +0 -122
  187. package/test/pseudo/root.ts +0 -66
  188. package/test/pseudo/scanned.ts +0 -144
  189. package/test/pseudo/score.ts +0 -591
  190. package/test/pseudo/scripts.ts +0 -324
  191. package/test/pseudo/semver.ts +0 -822
  192. package/test/pseudo/severity.ts +0 -342
  193. package/test/pseudo/shell.ts +0 -98
  194. package/test/pseudo/shrinkwrap.ts +0 -98
  195. package/test/pseudo/spec.ts +0 -525
  196. package/test/pseudo/squat.ts +0 -565
  197. package/test/pseudo/suspicious.ts +0 -98
  198. package/test/pseudo/tracker.ts +0 -98
  199. package/test/pseudo/trivial.ts +0 -98
  200. package/test/pseudo/type.ts +0 -105
  201. package/test/pseudo/undesirable.ts +0 -98
  202. package/test/pseudo/unknown.ts +0 -98
  203. package/test/pseudo/unmaintained.ts +0 -98
  204. package/test/pseudo/unpopular.ts +0 -98
  205. package/test/pseudo/unstable.ts +0 -98
  206. package/test/pseudo/vuln.ts +0 -625
  207. package/test/pseudo/vulnerable.ts +0 -137
  208. package/test/pseudo/workspace.ts +0 -111
  209. package/test/pseudo.ts +0 -584
  210. package/tsconfig.json +0 -3
  211. package/tsconfig.publish.json +0 -19
  212. package/tsconfig.publish.tsbuildinfo +0 -1
  213. package/typedoc.mjs +0 -2
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { EdgeLike, NodeLike } from '@vltpkg/types';
2
2
  import type { SecurityArchiveLike } from '@vltpkg/security-archive';
3
- import type { DiffFilesProvider, GetAuthHeader, HostContextsMap, ParsedSelectorToken, ParserState, QueryResponse } from './types.ts';
3
+ import type { PartPseudoName } from '@vltpkg/dss-parser';
4
+ import type { DiffFilesProvider, GetAuthHeader, HasSkills, HostContextsMap, ParsedSelectorToken, ParserState, QueryResponse } from './types.ts';
4
5
  import type { DepID } from '@vltpkg/dep-id';
5
6
  export * from './types.ts';
6
7
  export type SearchOptions = {
@@ -17,6 +18,10 @@ export type QueryOptions = {
17
18
  hostContexts?: HostContextsMap;
18
19
  diffFiles?: DiffFilesProvider;
19
20
  getAuthHeader?: GetAuthHeader;
21
+ /** for `:skills`; unset: `:skills` throws */
22
+ hasSkills?: HasSkills;
23
+ /** part selectors to allow, e.g. `['skill']`; others throw */
24
+ partSelectors?: readonly PartPseudoName[];
20
25
  };
21
26
  /**
22
27
  * The Query class is used to search the graph for nodes and edges
@@ -37,7 +42,7 @@ export declare class Query {
37
42
  * the original order is preserved.
38
43
  */
39
44
  static specificitySort(responses: QueryResponse[]): QueryResponse[];
40
- constructor({ edges, nodes, importers, retries, securityArchive, hostContexts, diffFiles, getAuthHeader, }: QueryOptions);
45
+ constructor({ edges, nodes, importers, retries, securityArchive, hostContexts, diffFiles, getAuthHeader, hasSkills, partSelectors, }: QueryOptions);
41
46
  /**
42
47
  * Search the graph for nodes and edges that match the given query.
43
48
  */
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { joinDepIDTuple } from '@vltpkg/dep-id/browser';
2
- import { parse, isPostcssNodeWithChildren, asPostcssNodeWithChildren, isSelectorNode, isPseudoNode, isIdentifierNode, isAttributeNode, } from '@vltpkg/dss-parser';
2
+ import { parse, partPseudoNames, isPostcssNodeWithChildren, asPostcssNodeWithChildren, isSelectorNode, isPseudoNode, isIdentifierNode, isAttributeNode, } from '@vltpkg/dss-parser';
3
3
  import { attribute } from "./attribute.js";
4
4
  import { combinator } from "./combinator.js";
5
5
  import { id } from "./id.js";
@@ -7,6 +7,7 @@ import { pseudo, pseudoSelectorNames } from "./pseudo.js";
7
7
  import { queryError, selectorText } from "./error.js";
8
8
  export * from "./types.js";
9
9
  const noopFn = async (state) => state;
10
+ const partPseudos = new Set(partPseudoNames);
10
11
  /**
11
12
  * `.dev` and a bare `dev` are the two ways a css habit shows up in a
12
13
  * query. Neither is valid DSS, but the intent is usually a pseudo-class
@@ -16,7 +17,8 @@ const unsupportedName = (state) => {
16
17
  const text = selectorText(state.current);
17
18
  const name = text.replace(/^[.#]/, '');
18
19
  return queryError('Unsupported selector', state.current, {
19
- wanted: pseudoSelectorNames.has(name) ? `:${name}`
20
+ wanted: pseudoSelectorNames.has(name) && !partPseudos.has(name) ?
21
+ `:${name}`
20
22
  : name ? `#${name}`
21
23
  : undefined,
22
24
  });
@@ -97,6 +99,13 @@ export const walk = async (state) => {
97
99
  for (const node of state.partial.nodes) {
98
100
  state.collect.nodes.add(node);
99
101
  }
102
+ if (state.parts && node.parent?.type === 'root') {
103
+ state.parts.push({
104
+ ...state.part,
105
+ nodes: [...state.partial.nodes],
106
+ });
107
+ delete state.part;
108
+ }
100
109
  }
101
110
  }
102
111
  return state;
@@ -155,12 +164,14 @@ export class Query {
155
164
  #cache;
156
165
  #diffFiles;
157
166
  #getAuthHeader;
167
+ #hasSkills;
158
168
  #edges;
159
169
  #nodes;
160
170
  #importers;
161
171
  #retries;
162
172
  #securityArchive;
163
173
  #hostContexts;
174
+ #partSelectors;
164
175
  /**
165
176
  * Helper method to determine if a given query string is using any of
166
177
  * the known security selectors. This is useful so that operations can
@@ -194,10 +205,12 @@ export class Query {
194
205
  return 0;
195
206
  });
196
207
  }
197
- constructor({ edges, nodes, importers, retries, securityArchive, hostContexts, diffFiles, getAuthHeader, }) {
208
+ constructor({ edges, nodes, importers, retries, securityArchive, hostContexts, diffFiles, getAuthHeader, hasSkills, partSelectors, }) {
209
+ this.#partSelectors = new Set(partSelectors);
198
210
  this.#cache = new Map();
199
211
  this.#diffFiles = diffFiles;
200
212
  this.#getAuthHeader = getAuthHeader;
213
+ this.#hasSkills = hasSkills;
201
214
  this.#edges = edges;
202
215
  this.#nodes = nodes;
203
216
  this.#importers = importers;
@@ -325,7 +338,7 @@ export class Query {
325
338
  const loose = asPostcssNodeWithChildren(current).nodes.length > 1;
326
339
  // builds initial state and walks over it,
327
340
  // retrieving the collected result
328
- const { collect, comment, importers: stateResultImporters, specificity, } = await walk({
341
+ const { collect, comment, importers: stateResultImporters, parts, specificity, } = await walk({
329
342
  cancellable: async () => {
330
343
  await new Promise(resolve => {
331
344
  setTimeout(resolve, 0);
@@ -344,9 +357,13 @@ export class Query {
344
357
  comment: '',
345
358
  diffFiles: this.#diffFiles,
346
359
  getAuthHeader: this.#getAuthHeader,
360
+ hasSkills: this.#hasSkills,
347
361
  loose,
348
362
  importers,
349
363
  partial: { nodes, edges },
364
+ ...(this.#partSelectors.size ?
365
+ { parts: [], partSelectors: this.#partSelectors }
366
+ : undefined),
350
367
  retries: this.#retries,
351
368
  signal,
352
369
  securityArchive: this.#securityArchive,
@@ -361,6 +378,8 @@ export class Query {
361
378
  importers: this.#getQueryResponseNodes(stateResultImporters),
362
379
  comment,
363
380
  specificity,
381
+ // part nodes are a subset of `nodes`, already hydrated above
382
+ ...(parts ? { parts: parts } : undefined),
364
383
  };
365
384
  this.#cache.set(query, res);
366
385
  return res;
@@ -0,0 +1,7 @@
1
+ import type { ParserState } from '../types.ts';
2
+ /**
3
+ * `:skill(<pattern>, ...)`: picks agent skills of the packages its
4
+ * selector matched. Never filters nodes: the walker stores the
5
+ * patterns in the selector's `parts` entry. Opt-in only.
6
+ */
7
+ export declare const skill: (state: ParserState) => Promise<ParserState>;
@@ -0,0 +1,52 @@
1
+ import { asPseudoNode } from '@vltpkg/dss-parser';
2
+ import { queryError } from "../error.js";
3
+ import { removeQuotes } from "./helpers.js";
4
+ /**
5
+ * `:skill(<pattern>, ...)`: picks agent skills of the packages its
6
+ * selector matched. Never filters nodes: the walker stores the
7
+ * patterns in the selector's `parts` entry. Opt-in only.
8
+ */
9
+ export const skill = async (state) => {
10
+ const curr = asPseudoNode(state.current);
11
+ const sel = curr.parent;
12
+ const fail = (message) => queryError(message, state.current);
13
+ if (sel?.parent?.type !== 'root' ||
14
+ sel.nodes
15
+ .slice(sel.index(curr) + 1)
16
+ .some(n => n.type !== 'comment')) {
17
+ throw fail(':skill() must end a top-level selector');
18
+ }
19
+ if (!state.partSelectors?.has('skill')) {
20
+ throw fail(':skill() only applies to --allow-skills and vlt skills');
21
+ }
22
+ if (!curr.nodes.length)
23
+ throw fail('Missing skill name in :skill()');
24
+ const patterns = curr.nodes.map(arg => {
25
+ let pattern = '';
26
+ for (const n of arg.nodes) {
27
+ if (n.type === 'comment')
28
+ continue;
29
+ // `a|b` is tag `b` in namespace `a`: never drop the `a`
30
+ if ((n.type === 'tag' || n.type === 'universal') &&
31
+ !String(n).includes('|')) {
32
+ pattern += n.value;
33
+ }
34
+ else if (n.type === 'string') {
35
+ if (n.value.startsWith("'")) {
36
+ throw fail('Invalid skill name in :skill(), use double quotes');
37
+ }
38
+ pattern += removeQuotes(n.value);
39
+ }
40
+ else {
41
+ throw fail(n.type === 'combinator' ?
42
+ 'Invalid skill name in :skill(), quote names with spaces'
43
+ : 'Invalid skill name in :skill()');
44
+ }
45
+ }
46
+ if (!pattern)
47
+ throw fail('Missing skill name in :skill()');
48
+ return pattern;
49
+ });
50
+ state.part = { name: 'skill', patterns };
51
+ return state;
52
+ };
@@ -0,0 +1,6 @@
1
+ import type { ParserState } from '../types.ts';
2
+ /**
3
+ * `:skills`: matches installed packages that ship agent skills, as
4
+ * told by the `hasSkills` provider.
5
+ */
6
+ export declare const skills: (state: ParserState) => Promise<ParserState>;
@@ -0,0 +1,26 @@
1
+ import { asPseudoNode } from '@vltpkg/dss-parser';
2
+ import { queryError } from "../error.js";
3
+ import { clear, removeDanglingEdges, removeNode } from "./helpers.js";
4
+ /**
5
+ * `:skills`: matches installed packages that ship agent skills, as
6
+ * told by the `hasSkills` provider.
7
+ */
8
+ export const skills = async (state) => {
9
+ // `:skills(x)`: likely a `:skill(x)` typo
10
+ if (asPseudoNode(state.current).nodes.length) {
11
+ if (state.loose)
12
+ return clear(state);
13
+ throw queryError(':skills takes no arguments', state.current, {
14
+ wanted: ':skill',
15
+ });
16
+ }
17
+ if (!state.hasSkills) {
18
+ throw queryError('The :skills selector is not supported here', state.current);
19
+ }
20
+ for (const node of state.partial.nodes) {
21
+ if (!(await state.hasSkills(node)))
22
+ removeNode(state, node);
23
+ }
24
+ removeDanglingEdges(state);
25
+ return state;
26
+ };
package/dist/pseudo.js CHANGED
@@ -49,6 +49,8 @@ import { semverParser as semver } from "./pseudo/semver.js";
49
49
  import { severity } from "./pseudo/severity.js";
50
50
  import { spec } from "./pseudo/spec.js";
51
51
  import { shrinkwrap } from "./pseudo/shrinkwrap.js";
52
+ import { skill } from "./pseudo/skill.js";
53
+ import { skills } from "./pseudo/skills.js";
52
54
  import { squat } from "./pseudo/squat.js";
53
55
  import { suspicious } from "./pseudo/suspicious.js";
54
56
  import { tracker } from "./pseudo/tracker.js";
@@ -88,6 +90,7 @@ const has = async (state) => {
88
90
  nodes: new Set(state.partial.nodes),
89
91
  },
90
92
  diffFiles: state.diffFiles,
93
+ hasSkills: state.hasSkills,
91
94
  loose: state.loose,
92
95
  hostContexts: state.hostContexts,
93
96
  importers: state.importers,
@@ -164,6 +167,7 @@ const is = async (state) => {
164
167
  },
165
168
  current: node,
166
169
  diffFiles: state.diffFiles,
170
+ hasSkills: state.hasSkills,
167
171
  initial: state.initial,
168
172
  loose: true,
169
173
  partial: {
@@ -210,6 +214,7 @@ const not = async (state) => {
210
214
  },
211
215
  current: node,
212
216
  diffFiles: state.diffFiles,
217
+ hasSkills: state.hasSkills,
213
218
  initial: state.initial,
214
219
  loose: state.loose,
215
220
  partial: {
@@ -331,6 +336,8 @@ const pseudoSelectors = new Map(Object.entries({
331
336
  severity,
332
337
  shell,
333
338
  shrinkwrap,
339
+ skill,
340
+ skills,
334
341
  squat,
335
342
  suspicious,
336
343
  tracker,
package/dist/types.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { EdgeLike, NodeLike } from '@vltpkg/types';
2
2
  import type { SecurityArchiveLike, PackageScore } from '@vltpkg/security-archive';
3
3
  import type { DepID } from '@vltpkg/dep-id';
4
- import type { PostcssNode } from '@vltpkg/dss-parser';
4
+ import type { PartPseudoName, PostcssNode } from '@vltpkg/dss-parser';
5
5
  export type HostContextsMapResult = {
6
6
  initialEdges: EdgeLike[];
7
7
  initialNodes: NodeLike[];
@@ -31,6 +31,22 @@ export type DiffFilesProvider = (commitish: string) => Set<string>;
31
31
  * keychain / registry-client dependency.
32
32
  */
33
33
  export type GetAuthHeader = (url: string) => Promise<string | undefined>;
34
+ /**
35
+ * Whether an installed package ships agent skills; for `:skills`.
36
+ * Keeps fs out of this package.
37
+ */
38
+ export type HasSkills = (node: NodeLike) => Promise<boolean>;
39
+ /**
40
+ * One top-level selector of a query and the nodes it matched. `name`
41
+ * and `patterns` are set when it ends in a part selector, e.g.
42
+ * `#a:skill(x, y*)`.
43
+ */
44
+ export type QueryPart<N = QueryResponseNode> = {
45
+ name?: PartPseudoName;
46
+ /** as typed, `*` = glob; for matching only, never paths */
47
+ patterns?: string[];
48
+ nodes: N[];
49
+ };
34
50
  export type ParserState = {
35
51
  cancellable: () => Promise<void>;
36
52
  collect: GraphSelectionState;
@@ -38,6 +54,7 @@ export type ParserState = {
38
54
  current: PostcssNode;
39
55
  diffFiles?: DiffFilesProvider;
40
56
  getAuthHeader?: GetAuthHeader;
57
+ hasSkills?: HasSkills;
41
58
  hostContexts?: HostContextsMap;
42
59
  importers: Set<NodeLike>;
43
60
  initial: GraphSelectionState;
@@ -48,6 +65,12 @@ export type ParserState = {
48
65
  signal: AbortSignal;
49
66
  walk: ParserFn;
50
67
  partial: GraphSelectionState;
68
+ /** set by `:skill()`, read and cleared by `walk()` */
69
+ part?: Pick<QueryPart, 'name' | 'patterns'>;
70
+ /** one entry per top-level selector, only when opted in */
71
+ parts?: QueryPart<NodeLike>[];
72
+ /** part selectors this consumer allows */
73
+ partSelectors?: ReadonlySet<PartPseudoName>;
51
74
  retries: number;
52
75
  securityArchive: SecurityArchiveLike | undefined;
53
76
  scopeIDs?: DepID[];
@@ -59,6 +82,11 @@ export type QueryResponse = {
59
82
  importers: QueryResponseNode[];
60
83
  comment: string;
61
84
  specificity: Specificity;
85
+ /**
86
+ * one entry per top-level selector, only with `partSelectors` and
87
+ * a non-empty query
88
+ */
89
+ parts?: QueryPart[];
62
90
  };
63
91
  export type QueryResponseEdge = Omit<EdgeLike, 'from' | 'to'> & {
64
92
  from: QueryResponseNode;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vltpkg/query",
3
3
  "description": "Query syntax parser that retrieves items from a graph",
4
- "version": "1.4.0",
4
+ "version": "1.6.0",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/vltpkg/vltpkg.git",
@@ -13,13 +13,13 @@
13
13
  "url": "http://vlt.sh"
14
14
  },
15
15
  "dependencies": {
16
- "@vltpkg/dep-id": "1.4.0",
17
- "@vltpkg/dss-parser": "1.4.0",
18
- "@vltpkg/error-cause": "1.4.0",
19
- "@vltpkg/security-archive": "1.4.0",
20
- "@vltpkg/semver": "1.4.0",
21
- "@vltpkg/types": "1.4.0",
22
- "@vltpkg/user-agent": "1.4.0",
16
+ "@vltpkg/dep-id": "1.6.0",
17
+ "@vltpkg/dss-parser": "1.6.0",
18
+ "@vltpkg/error-cause": "1.6.0",
19
+ "@vltpkg/security-archive": "1.6.0",
20
+ "@vltpkg/semver": "1.6.0",
21
+ "@vltpkg/types": "1.6.0",
22
+ "@vltpkg/user-agent": "1.6.0",
23
23
  "minimatch": "^10.1.1",
24
24
  "p-retry": "^7.1.1",
25
25
  "postcss-selector-parser": "^7.1.1"
@@ -27,7 +27,7 @@
27
27
  "devDependencies": {
28
28
  "@eslint/js": "^9.39.1",
29
29
  "@types/node": "^22.19.2",
30
- "@vltpkg/spec": "1.4.0",
30
+ "@vltpkg/spec": "1.6.0",
31
31
  "eslint": "^9.39.1",
32
32
  "prettier": "^3.7.4",
33
33
  "tap": "^21.8.0",
@@ -65,9 +65,7 @@
65
65
  }
66
66
  },
67
67
  "files": [
68
- "dist",
69
- "skills",
70
- "!skills/dss-query/evals"
68
+ "dist"
71
69
  ],
72
70
  "bugs": [
73
71
  {
@@ -1,123 +0,0 @@
1
- # DSS Reference
2
-
3
- Compiled from https://docs.vlt.io/cli/selectors/ (source:
4
- `www/docs/src/content/docs/cli/selectors/`). When in doubt, check
5
- those docs — they are canonical.
6
-
7
- ## Attribute selectors
8
-
9
- Match against `package.json` fields.
10
-
11
- | Selector | Meaning |
12
- | ---------------- | ------------------------------------------------ |
13
- | `[attr]` | has the property |
14
- | `[attr=value]` | equals |
15
- | `[attr^=value]` | starts with |
16
- | `[attr$=value]` | ends with |
17
- | `[attr*=value]` | contains |
18
- | `[attr~=value]` | whitespace-separated list contains |
19
- | `[attr\|=value]` | equals value or starts with `value-` |
20
- | `[attr=value i]` | case-insensitive (`s` = case-sensitive, default) |
21
-
22
- Nested fields (e.g. `engines.node`) need `:attr()`:
23
- `:attr(engines, [node])`,
24
- `:attr(peerDependenciesMeta, foo, [optional=true])`.
25
-
26
- `#foo` is shorthand for `[name=foo]`.
27
-
28
- ## Combinators
29
-
30
- | Combinator | Meaning |
31
- | ---------- | -------------------------------------------------------------------- |
32
- | `A > B` | B is a **direct** dependency of A (chain for depth: `:root > * > *`) |
33
- | `A B` | B is a direct **or transitive** dependency of A |
34
- | `A ~ B` | B shares a parent with A (sibling) |
35
-
36
- ## Pseudo-states (no arguments)
37
-
38
- Graph structure:
39
-
40
- | Selector | Matches |
41
- | ------------ | --------------------------------------- |
42
- | `:root` | top-level package.json node |
43
- | `:project` | root + all workspaces ("your code") |
44
- | `:workspace` | workspaces from vlt.json |
45
- | `:scope` | current selector scope (with `--scope`) |
46
-
47
- Dependency type:
48
-
49
- | Selector | Matches |
50
- | ---------------------------------------- | ------------------------------------------------------ |
51
- | `:prod` / `:dev` / `:optional` / `:peer` | by dependency type |
52
- | `:missing` | declared but not installed (**edges only, not nodes**) |
53
- | `:overridden` | has an override applied |
54
-
55
- Package properties:
56
-
57
- | Selector | Matches |
58
- | ------------- | -------------------------------------------- |
59
- | `:private` | `"private": true` |
60
- | `:empty` | no dependencies |
61
- | `:link` | linked packages |
62
- | `:prerelease` | version has prerelease part (`1.0.0-beta.1`) |
63
- | `:built` | built during reify |
64
- | `:scanned` | has Socket security metadata |
65
-
66
- ## Pseudo-classes (take arguments)
67
-
68
- | Selector | Meaning | Example |
69
- | ------------------- | --------------------------------------- | ---------------------------------------------- |
70
- | `:attr(...)` | nested package.json property | `:attr(engines, [node])` |
71
- | `:dist(tag)` | registry dist-tag | `:dist(latest)` |
72
- | `:has(sel)` | has matching descendant | `:has(:peer[name=react])` |
73
- | `:host(name)` | switch graph context to another project | `:host(local) :malware` |
74
- | `:is(a, b)` | any of (forgiving list) | `:is([name=a], [name=b])` |
75
- | `:not(sel)` | negation | `:not([license=MIT])` |
76
- | `:outdated(kind?)` | newer version exists | `:outdated(major)` |
77
- | `:published(range)` | by publish date | `:published(">2024")` |
78
- | `:semver(range)` | semver comparison on installed version | `:semver(^1.0.0)` |
79
- | `:spec(spec)` | by declared specifier (edge) | `:spec(^1.0.0)` |
80
- | `:path(glob)` | workspace/file packages by path | `:path("packages/**")` |
81
- | `:type(kind)` | package type | `:type(git)`, `:type(registry)`, `:type(file)` |
82
- | `:diff(ref)` | files changed vs git ref | `:diff(main)` |
83
- | `:hostname(host)` | upstream hostname | `:hostname(github.com)` |
84
- | `:registry(name)` | configured registry name | `:registry(npm)` |
85
-
86
- ## Security insights (Socket-powered, network call)
87
-
88
- Severity args accept names or numbers (`critical`/`0`, `high`/`1`,
89
- `medium`/`2`, `low`/`3`) and comparators: `:severity(">=medium")`.
90
-
91
- Threats: `:malware` (binary, no params), `:squat(sev?)`,
92
- `:suspicious`, `:confused`.
93
-
94
- Vulnerabilities: `:vuln(sev?)` (alias `:vulnerable`),
95
- `:cve(CVE-2023-1234)`, `:cve(*)`, `:cwe(CWE-79)`, `:severity(level)`.
96
- `:vuln` without params matches severity ≥ medium; `:vuln(critical)`
97
- exact-matches; `:vuln(">=high")` uses comparators. Also matches alerts
98
- carrying a `cveId` prop.
99
-
100
- Licensing: `:license(type)` — types: `unlicensed`, `misc`,
101
- `restricted`, `ambiguous`, `copyleft`, `unknown`, `none`, `exception`.
102
-
103
- Code behavior: `:eval`, `:network`, `:fs`, `:env`, `:shell`,
104
- `:scripts` (install scripts), `:debug`, `:dynamic`.
105
-
106
- Obfuscation: `:obfuscated`, `:minified`, `:entropic`, `:native`,
107
- `:shrinkwrap`.
108
-
109
- Health: `:deprecated`, `:unmaintained` (5+ years stale), `:unpopular`,
110
- `:trivial` (<10 LOC), `:abandoned`, `:unknown`, `:unstable`.
111
-
112
- Other: `:tracker` (telemetry), `:undesirable`, `:score(rate, kind?)` —
113
- kinds: `overall` (default), `license`, `maintenance`, `quality`,
114
- `supplyChain`, `vulnerability`; e.g. `:score("<=0.5", "maintenance")`.
115
-
116
- ## Audit query starters
117
-
118
- ```bash
119
- vlt query ':malware, :vuln(critical), :cve(*), :obfuscated' # critical issues
120
- vlt query ':abandoned, :unmaintained, :unknown' # supply chain risk
121
- vlt query ':license(copyleft), :license(unlicensed)' # license compliance
122
- vlt query ':eval, :shell, :network, :fs' # behavior audit
123
- ```